Я смог сделать это с помощью следующих шагов:
- Подождите, пока загрузится веб-страница.
- Используйте XPath, чтобы найти элемент
<img>
. - Используйте
usemap
значение <img>
, чтобы найти соответствующий элемент <map>
. - Получите список всех
<area>
дочерних элементов <map>
. - Добавить щелчок слушатель элемента
<map>
, который: - использует вызов JavaScript
getBoundingClientRect()
для получения границ элемента <img>
относительно области просмотра. - Получает координаты щелчка мыши относительно в область просмотра.
- Перебирает дочерние элементы
<area>
и для каждого анализирует атрибут coords
в списке значений, затем создает Shape, соответствующий атрибуту shape
. - Корректирует координаты щелчка мыши, чтобы они относились к началу координат
<img>
. - Проверяет, находятся ли эти координаты щелчка мыши внутри фигуры.
Вот полная демонстрация:
import java.net.URI;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;
import javax.xml.xpath.XPathException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.w3c.dom.events.EventTarget;
import org.w3c.dom.events.MouseEvent;
import javafx.application.Application;
import javafx.concurrent.Worker;
import javafx.geometry.Insets;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.BorderPane;
import javafx.scene.shape.Shape;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Polygon;
import javafx.scene.web.WebView;
import javafx.scene.web.WebEngine;
import netscape.javascript.JSObject;
public class WebViewImageMapTest
extends Application {
/**
* A single coordinate in the list which comprises the value of
* the {@code coords} attribute of an {@code <area>} element.
*/
private static class Coordinate {
final double value;
final boolean percentage;
Coordinate(double value,
boolean percentage) {
this.value = value;
this.percentage = percentage;
}
double resolveAgainst(double size) {
return percentage ? value * size / 100 : value;
}
static Coordinate parse(String s) {
if (s.endsWith("%")) {
return new Coordinate(
Double.parseDouble(s.substring(0, s.length() - 1)), true);
} else {
return new Coordinate(Double.parseDouble(s), false);
}
}
@Override
public String toString() {
return getClass().getName() +
"[" + value + (percentage ? "%" : "") + "]";
}
}
@Override
public void start(Stage stage) {
WebView view = new WebView();
Label destination = new Label(" ");
destination.setPadding(new Insets(12));
WebEngine engine = view.getEngine();
engine.getLoadWorker().stateProperty().addListener((o, old, state) -> {
if (state != Worker.State.SUCCEEDED) {
return;
}
Document doc = engine.getDocument();
try {
XPath xpath = XPathFactory.newInstance().newXPath();
Element img = (Element)
xpath.evaluate("//*[local-name()='img']",
doc, XPathConstants.NODE);
String mapURI = img.getAttribute("usemap");
String mapID = URI.create(mapURI).getFragment();
Element map = doc.getElementById(mapID);
if (map == null) {
// No <map> with matching id.
// Look for <map> with matching name instead.
map = (Element) xpath.evaluate(
"//*[local-name()='map'][@name='" + mapID + "']",
doc, XPathConstants.NODE);
}
NodeList areas = (NodeList)
xpath.evaluate("//*[local-name()='area']",
map, XPathConstants.NODESET);
((EventTarget) map).addEventListener("click", e -> {
Element area = getClickedArea(
(MouseEvent) e, areas, getClientBounds(img, engine));
if (area != null) {
destination.setText(area.getAttribute("href"));
} else {
destination.setText(" ");
}
}, false);
} catch (XPathException e) {
e.printStackTrace();
}
});
engine.load(
WebViewImageMapTest.class.getResource("imgmap.html").toString());
stage.setScene(new Scene(
new BorderPane(view, null, null, destination, null)));
stage.setTitle("Image Map Test");
stage.show();
}
/**
* Returns the bounds of an element relative to the viewport.
*/
private Rectangle getClientBounds(Element element,
WebEngine engine) {
JSObject window = (JSObject) engine.executeScript("window");
window.setMember("desiredBoundsElement", element);
JSObject bounds = (JSObject) engine.executeScript(
"desiredBoundsElement.getBoundingClientRect();");
Number n;
n = (Number) bounds.getMember("x");
double x = n.doubleValue();
n = (Number) bounds.getMember("y");
double y = n.doubleValue();
n = (Number) bounds.getMember("width");
double width = n.doubleValue();
n = (Number) bounds.getMember("height");
double height = n.doubleValue();
return new Rectangle(x, y, width, height);
}
private Element getClickedArea(MouseEvent event,
NodeList areas,
Rectangle imgClientBounds) {
int clickX = event.getClientX();
int clickY = event.getClientY();
double imgX = imgClientBounds.getX();
double imgY = imgClientBounds.getY();
double imgWidth = imgClientBounds.getWidth();
double imgHeight = imgClientBounds.getHeight();
int count = areas.getLength();
for (int i = 0; i < count; i++) {
Element area = (Element) areas.item(i);
String shapeType = area.getAttribute("shape");
if (shapeType == null) {
shapeType = "";
}
String[] rawCoords = area.getAttribute("coords").split(",");
int numCoords = rawCoords.length;
Coordinate[] coords = new Coordinate[numCoords];
for (int c = 0; c < numCoords; c++) {
coords[c] = Coordinate.parse(rawCoords[c].trim());
}
Shape shape = null;
switch (shapeType) {
case "rect":
double left = coords[0].resolveAgainst(imgWidth);
double top = coords[1].resolveAgainst(imgHeight);
double right = coords[2].resolveAgainst(imgWidth);
double bottom = coords[3].resolveAgainst(imgHeight);
shape = new Rectangle(
left, top, right - left, bottom - top);
break;
case "circle":
double centerX = coords[0].resolveAgainst(imgWidth);
double centerY = coords[1].resolveAgainst(imgHeight);
double radius = coords[2].resolveAgainst(
Math.min(imgWidth, imgHeight));
shape = new Circle(centerX, centerY, radius);
break;
case "poly":
double[] polygonCoords = new double[coords.length];
for (int c = polygonCoords.length - 1; c >= 0; c--) {
polygonCoords[c] = coords[c].resolveAgainst(
c % 2 == 0 ? imgWidth : imgHeight);
}
shape = new Polygon(polygonCoords);
break;
default:
shape = new Rectangle(imgWidth, imgHeight);
break;
}
if (shape.contains(clickX - imgX, clickY - imgY)) {
return area;
}
}
return null;
}
public static class Main {
public static void main(String[] args) {
Application.launch(WebViewImageMapTest.class, args);
}
}
}
А вот и planets.gif, который я использовал: * 1 043 *