关于 arcgis-interaction
面向 ArcGIS Maps SDK 开发者的交互实现技能,涵盖要素命中检测、高亮、绘制、事件处理和坐标转换,并提醒妥善清理句柄与监听器。
主要功能
- 01命中检测与图层筛选
- 02高亮单个或多个要素
- 03绘制和编辑几何图形
- 04处理视图与图层事件
- 05屏幕与地图坐标转换
适用场景
- 01点击地图要素并读取属性
- 02悬停或点击时突出显示要素
- 03添加绘图、撤销和重做操作
- 04响应视图状态及图层更新
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ saschabrunnerch/ arcgis-maps-sdk-js-ai-context --skill arcgis-interaction安装后在 Claude Code 中输入/arcgis-interaction确认已加载。
附带文件AGENTS.md
详细介绍
ArcGIS 交互
实现命中测试、要素高亮、绘制、坐标转换和事件处理等用户交互时,请使用此技能。
导入模式
直接 ESM 导入
import Draw from "@arcgis/core/views/draw/Draw.js";
import * as reactiveUtils from "@arcgis/core/core/reactiveUtils.js";
动态导入(CDN)
const Draw = await $arcgis.import("@arcgis/core/views/draw/Draw.js");
const reactiveUtils = await $arcgis.import(
"@arcgis/core/core/reactiveUtils.js",
);
**注意:**此技能中的示例使用直接 ESM 导入。使用 CDN 时,将
import X from "path"替换为const X = await $arcgis.import("path")。
命中测试
基本命中测试
view.on("click", async (event) => {
const response = await view.hitTest(event);
if (response.results.length > 0) {
const graphic = response.results[0].graphic;
console.log("Clicked feature:", graphic.attributes);
}
});
带图层筛选器的命中测试
view.on("click", async (event) => {
const response = await view.hitTest(event, {
include: [featureLayer], // Only test this layer
});
// Or exclude layers
const response2 = await view.hitTest(event, {
exclude: [graphicsLayer],
});
});
指针移动命中测试
view.on("pointer-move", async (event) => {
const response = await view.hitTest(event, {
include: featureLayer,
});
if (response.results.length > 0) {
document.body.style.cursor = "pointer";
} else {
document.body.style.cursor = "default";
}
});
命中测试结果类型
hitTest 返回 ViewHitTestResult,其中包含一个结果对象数组。每个结果都有一个 type:
| 结果类型 | 描述 |
|---|---|
graphic | 来自图层或 GraphicsLayer 的图形 |
media | 命中的媒体元素(弹出窗口中的图像) |
route | 来自 RouteLayer 的命中路线 |
view.on("click", async (event) => {
const response = await view.hitTest(event);
response.results.forEach((result) => {
if (result.type === "graphic") {
console.log("Layer:", result.graphic.layer.title);
console.log("Attributes:", result.graphic.attributes);
}
});
});
高亮
高亮要素
const layerView = await view.whenLayerView(featureLayer);
// Highlight a single feature
const highlight = layerView.highlight(graphic);
// Highlight multiple features
const highlight = layerView.highlight([graphic1, graphic2]);
// Highlight by object IDs
const highlight = layerView.highlight([1, 2, 3]);
// Remove highlight
highlight.remove();
单击时高亮
let highlightHandle;
view.on("click", async (event) => {
// Remove previous highlight
if (highlightHandle) {
highlightHandle.remove();
}
const response = await view.hitTest(event, { include: featureLayer });
if (response.results.length > 0) {
const graphic = response.results[0].graphic;
const layerView = await view.whenLayerView(featureLayer);
highlightHandle = layerView.highlight(graphic);
}
});
高亮选项
// Set highlight options on the view
view.highlightOptions = {
color: [255, 255, 0, 1],
haloOpacity: 0.9,
fillOpacity: 0.2,
};
绘制
Sketch 组件(最简单)
<arcgis-map basemap="topo-vector" center="139.5716,35.696" zoom="18">
<arcgis-sketch slot="top-right" creation-mode="update"></arcgis-sketch>
</arcgis-map>
arcgis-sketch 组件提供点、折线、多边形、矩形和圆形几何图形的绘制工具,并支持捕捉、撤销/重做和选择。
主要属性:
| 属性 | 类型 | 描述 |
|---|---|---|
creation-mode | "single" | "update" | "continuous" | 创建后如何处理绘制的图形 |
available-create-tools | string | 以逗号分隔的已启用创建工具列表 |
multiple-selection-enabled | boolean | 是否允许选择多个图形 |
update-on-graphic-click | boolean | 单击图形时是否开始更新 |
主要事件:
| 事件 | 描述 |
|---|---|
arcgisCreate | 在创建图形期间触发 |
arcgisUpdate | 在更新图形期间触发 |
arcgisDelete | 删除图形时触发 |
arcgisUndo | 撤销时触发 |
arcgisRedo | 重做时触发 |
主要方法:
| 方法 | 描述 |
|---|---|
create(tool) | 开始创建图形("point"、"polyline"、"polygon"、"rectangle"、"circle") |
update(graphics) | 开始更新图形 |
delete() | 删除所选图形 |
undo() / redo() | 撤销/重做上一个操作 |
带事件的 Sketch 组件
<arcgis-map basemap="topo-vector">
<arcgis-sketch slot="top-right" creation-mode="update"></arcgis-sketch>
</arcgis-map>
<script type="module">
const sketch = document.querySelector("arcgis-sketch");
sketch.addEventListener("arcgisCreate", (event) => {
const { state, graphic } = event.detail;
if (state === "complete") {
console.log("Created:", graphic.geometry.type);
}
});
sketch.addEventListener("arcgisUpdate", (event) => {
const { state, graphics } = event.detail;
if (state === "complete") {
console.log("Updated:", graphics.length, "graphics");
}
});
sketch.addEventListener("arcgisDelete", (event) => {
console.log("Deleted:", event.detail.graphics.length, "graphics");
});
</script>
Sketch 微件(核心 API)
import Sketch from "@arcgis/core/widgets/Sketch.js";
import GraphicsLayer from "@arcgis/core/layers/GraphicsLayer.js";
const graphicsLayer = new GraphicsLayer();
map.add(graphicsLayer);
const sketch = new Sketch({
view: view,
layer: graphicsLayer,
creationMode: "update", // or "single", "continuous"
});
view.ui.add(sketch, "top-right");
// Listen for events
sketch.on("create", (event) => {
if (event.state === "complete") {
console.log("Created:", event.graphic);
}
});
sketch.on("update", (event) => {
if (event.state === "complete") {
console.log("Updated:", event.graphics);
}
});
sketch.on("delete", (event) => {
console.log("Deleted:", event.graphics);
});
Draw 工具(底层)
import Draw from "@arcgis/core/views/draw/Draw.js";
const draw = new Draw({ view: view });
// Create a polygon
const action = draw.create("polygon");
action.on("vertex-add", (event) => {
console.log("Vertex added:", event.vertices);
});
action.on("draw-complete", (event) => {
const polygon = {
type: "polygon",
rings: event.vertices,
spatialReference: view.spatialReference,
};
// Create graphic with polygon
});
事件处理
视图事件
// Click (waits for potential double-click delay)
view.on("click", (event) => {
console.log("Map point:", event.mapPoint);
console.log("Screen point:", event.x, event.y);
});
// Immediate-click (fires without delay, recommended for hit testing)
view.on("immediate-click", (event) => {
console.log("Immediate click at:", event.mapPoint);
});
// Double-click
view.on("double-click", (event) => {
event.stopPropagation(); // Prevent default zoom
});
// Immediate-double-click (cannot be prevented by immediate-click stopPropagation)
view.on("immediate-double-click", (event) => {
console.log("Double clicked");
});
// Pointer move
view.on("pointer-move", (event) => {
const point = view.toMap(event);
console.log("Coordinates:", point.longitude, point.latitude);
});
// Drag
view.on("drag", (event) => {
if (event.action === "start") {
}
if (event.action === "update") {
}
if (event.action === "end") {
}
});
// Key events
view.on("key-down", (event) => {
if (event.key === "Escape") {
// Cancel operation
}
});
**提示:**在需要立即响应用户单击(例如进行命中测试)时,请使用
immediate-click,而不是click。click事件会等待确认是否发生双击,因此会增加延迟。
使用 reactiveUtils 监视属性
import * as reactiveUtils from "@arcgis/core/core/reactiveUtils.js";
// Watch for view stationary state
reactiveUtils.when(
() => view.stationary === true,
() => {
console.log("Navigation complete, extent:", view.extent);
},
);
// Watch zoom changes
reactiveUtils.watch(
() => view.zoom,
(zoom) => {
console.log("Zoom changed to:", zoom);
},
);
// Watch multiple properties
reactiveUtils.watch(
() => [view.center, view.zoom],
([center, zoom]) => {
console.log("View changed:", center, zoom);
},
);
// One-time wait
await reactiveUtils.whenOnce(() => view.ready);
console.log("View is ready");
图层事件
const layerView = await view.whenLayerView(featureLayer);
reactiveUtils.watch(
() => layerView.updating,
(updating) => {
if (!updating) {
console.log("Layer update complete");
}
},
);
坐标转换
// Screen to map coordinates
const mapPoint = view.toMap({ x: screenX, y: screenY });
// Map to screen coordinates
const screenPoint = view.toScreen(mapPoint);
以编程方式控制弹出窗口
有关 PopupTemplate 的详细配置,请参阅
arcgis-popup-templates。
// Open popup programmatically
view.openPopup({
title: "Custom Popup",
content: "Hello World",
location: view.center,
});
// Close popup
view.closePopup();
完整示例:带高亮的命中测试
地图组件
<!DOCTYPE html>
<html>
<head>
<script src="https://js.arcgis.com/5.0/"></script>
<script
type="module"
src="https://js.arcgis.com/5.0/map-components/"
></script>
<style>
html,
body {
height: 100%;
margin: 0;
}
</style>
</head>
<body>
<arcgis-map
basemap="streets-navigation-vector"
center="-118.805,34.027"
zoom="13"
>
<arcgis-zoom slot="top-left"></arcgis-zoom>
</arcgis-map>
<script type="module">
const [FeatureLayer] = await $arcgis.import([
"@arcgis/core/layers/FeatureLayer.js",
]);
const mapElement = document.querySelector("arcgis-map");
const view = await mapElement.view;
await view.when();
const featureLayer = new FeatureLayer({
url: "https://services3.arcgis.com/GVgbJbqm8hXASVYi/arcgis/rest/services/Trailheads/FeatureServer/0",
});
mapElement.map.add(featureLayer);
let highlightHandle;
view.on("pointer-move", async (event) => {
const response = await view.hitTest(event, { include: featureLayer });
if (highlightHandle) {
highlightHandle.remove();
highlightHandle = null;
}
if (response.results.length > 0) {
const graphic = response.results[0].graphic;
const layerView = await view.whenLayerView(featureLayer);
highlightHandle = layerView.highlight(graphic);
document.body.style.cursor = "pointer";
} else {
document.body.style.cursor = "default";
}
});
</script>
</body>
</html>
参考示例
view-hittest- 命中测试,识别屏幕坐标处的要素map-component-hittest- 使用 Map Components 通过 hitTest 访问要素sketch- 用于绘制几何图形的 Sketch 组件sketch-geometries- 使用 Sketch 微件绘制几何图形sketch-update-validation- 验证草图更新sketch-snapping-magnifier- 使用 Sketch 进行捕捉和放大
常见问题
-
高亮句柄泄漏:创建新高亮却未移除之前的高亮,会导致内存泄漏。
// Anti-pattern: creating highlights without cleaning up previous ones view.on("pointer-move", async (event) => { const response = await view.hitTest(event); if (response.results.length > 0) { const feature = response.results[0].graphic; // New highlight created every pointer-move - old ones never removed layerView.highlight(feature); } });// Correct: store handle and remove before creating a new highlight let highlightHandle = null; view.on("pointer-move", async (event) => { const response = await view.hitTest(event); if (highlightHandle) { highlightHandle.remove(); highlightHandle = null; } if (response.results.length > 0) { const feature = response.results[0].graphic; highlightHandle = layerView.highlight(feature); } });影响: 每次指针移动都会添加另一个高亮,而不移除之前的高亮。高亮会在视觉上和内存中不断累积,导致性能下降。
-
事件多次触发:反复添加事件监听器却不清理,会导致处理程序不断累积。
// Anti-pattern: adding listeners in a function that gets called multiple times function setupInteraction() { view.on("click", async (event) => { const result = await view.hitTest(event); console.log("Clicked:", result); }); } setupInteraction(); // First handler setupInteraction(); // Second handler - now click fires twice// Correct: store handle and remove before re-adding, or add once let clickHandle = null; function setupInteraction() { if (clickHandle) { clickHandle.remove(); } clickHandle = view.on("click", async (event) => { const result = await view.hitTest(event); console.log("Clicked:", result); }); }影响: 每次调用设置函数都会添加另一个监听器。点击会触发多个重复的回调。
-
命中测试没有返回结果:检查图层是否已正确包含或排除,并确保视图已就绪。
-
使用 click 而不是 immediate-click:
click事件内置了检查双击的延迟。要实现响应迅速的命中测试,请使用immediate-click。 -
未显示弹出窗口:确保图层设置了
popupEnabled: true(默认值)和popupTemplate。
相关技能
- 有关 PopupTemplate 配置的详细信息,请参阅
arcgis-popup-templates。 - 有关要素编辑工作流,请参阅
arcgis-editing。 - 有关视图初始化和导航,请参阅
arcgis-core-maps。