海蟾 發表於 2025-11-26 11:00:00

JSAPIThree 事件系统学习笔记:处理交互的基础

<blockquote>
<p>作为刚接触 mapvthree 的新手,今天我专门学习了事件系统。这里汇总最实用的内容,帮助和我一样的初学者快速掌握交互的核心用法。</p>
</blockquote>
<h2 id="绑定与移除事件只需记住-addremove">绑定与移除事件,只需记住 add/remove</h2>
<p>mapvthree 复用了 Three.js 的 <code>addEventListener</code> / <code>removeEventListener</code>,所以任何继承自 <code>Object3D</code> 的对象都能直接绑定事件。引擎内部的调度器会帮我们处理拾取、命中检测等复杂逻辑,我们只需要专注于“监听哪一个对象”和“响应什么事件”。</p>
<pre><code class="language-js">const box = engine.add(new THREE.Mesh(
    new THREE.BoxGeometry(10, 10, 10),
    new THREE.MeshBasicMaterial({color: 0xaa0000}),
));

const handleClick = (e) =&gt; {
    console.log('盒子被点击,地理坐标:', e.point);
    box.removeEventListener('click', handleClick); // 一次性事件
};

box.addEventListener('click', handleClick);
</code></pre>
<ul>
<li>常见事件:<code>click</code>、<code>dblclick</code>、<code>mousemove</code>、<code>mouseenter</code>、<code>mouseleave</code>、<code>pointerdown</code>、<code>pointerup</code> 等</li>
<li>一定记得在物体移除或销毁前调用 <code>removeEventListener</code>,避免内存泄漏</li>
</ul>
<h2 id="eventparam-里的信息足够丰富">EventParam 里的信息足够丰富</h2>
<p>每次事件触发,回调都会收到统一的 <code>EventParam</code>,其中最常用的字段如下:</p>
<table>
<thead>
<tr>
<th>属性</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>target</code> / <code>currentTarget</code></td>
<td>触发事件的对象、绑定事件的对象</td>
</tr>
<tr>
<td><code>position</code></td>
<td>鼠标在世界坐标中的位置(Array)</td>
</tr>
<tr>
<td><code>point</code></td>
<td>鼠标在地理坐标中的位置(经纬度)</td>
</tr>
<tr>
<td><code>index</code> / <code>entity</code></td>
<td>当事件来自支持 <code>dataSource</code> 的图层时,可获取对应的数据索引和实体</td>
</tr>
<tr>
<td><code>event</code></td>
<td>原始浏览器事件对象</td>
</tr>
</tbody>
</table>
<pre><code class="language-js">box.addEventListener('mousemove', (e) =&gt; {
    if (e.position) {
      helper.position.fromArray(e.position); // 在 3D 空间显示鼠标所在点
    }
});
</code></pre>
<blockquote>
<p>注意:当事件绑定在 <code>engine.map</code> 这类场景根对象上时,只能拿到 <code>position</code> 和 <code>point</code>,其它字段不存在。</p>
</blockquote>
<h2 id="事件不仅限于几何体根对象也能监听">事件不仅限于几何体,根对象也能监听</h2>
<p>可以在可视化图层、地图根节点甚至 <code>engine.map</code> 上绑定事件,从而实现不同层级的交互。例如:</p>
<pre><code class="language-js">engine.map.addEventListener('pointerdown', () =&gt; {
    engine.clock.currentTime = new Date('2025-05-15 18:00:00');
});
engine.map.addEventListener('pointerup', () =&gt; {
    engine.clock.currentTime = new Date('2025-05-15 14:00:00');
});
</code></pre>
<p>这段代码演示了如何在地图级别监听按下/抬起事件,并根据交互切换天空时间。</p>
<h2 id="事件冒泡必要时调用-stoppropagation">事件冒泡:必要时调用 stopPropagation</h2>
<p>事件从子节点向父节点冒泡,沿着场景树一直传递到根对象。我们可以利用冒泡做统一处理,也可以在需要时阻止冒泡。</p>
<pre><code class="language-js">ear.addEventListener('click', (e) =&gt; {
    alert('点击到了耳朵');
    e.stopPropagation(); // 阻止继续触发头部或整个人的点击事件
});
head.addEventListener('click', () =&gt; alert('点击到了头像'));
human.addEventListener('click', () =&gt; alert('点击到了整个人'));
</code></pre>
<p>当需要在某个层级拦截事件时,记得调用 <code>stopPropagation()</code>。</p>
<h2 id="使用建议">使用建议</h2>
<ul>
<li><strong>善用一次性事件</strong>:如果某个事件只需要触发一次,回调里立即调用 <code>removeEventListener</code>。</li>
<li><strong>场景根对象的事件</strong>:仅能获取位置坐标,适合做“全局点击”或“拖拽地图切换模式”之类的功能。</li>
<li><strong>粒度控制</strong>:能在具体对象上监听就不要放在根节点,粒度越精细,代码越容易维护。</li>
<li><strong>性能提示</strong>:事件调度器会在帧末集中处理,并做异步、去重等优化,正常使用不必担心性能问题。</li>
</ul>
<hr>
<blockquote>
<p>学习笔记就到这里!事件系统本身并不复杂,关键是熟悉 <code>addEventListener</code>、<code>EventParam</code> 和冒泡这几个核心概念。掌握它们之后,构建交互式地图就顺手多了。希望这份笔记也能帮到你!</p>
</blockquote><br><br>
来源:https://www.cnblogs.com/map-3d-vis/p/19271750
頁: [1]
查看完整版本: JSAPIThree 事件系统学习笔记:处理交互的基础