更新于 

Subdivide Plane 选中网格

鼠标选中网格高亮

要实现的效果是:
网格上的高亮块时刻跟踪鼠标的移动。

可以分成三步实现:

  • 获取鼠标在网格上的坐标A
  • 根据坐标A获取网格上方块的坐标B
  • 高亮平面的坐标移动到坐标B位置

实际显示的网格是THREE.GridHelper(网格助手),
需要创建一个不可见的辅助平面Plane,
Plane的尺寸和位置和grid完全一致,
鼠标在移动时,计算相机-鼠标raycaster射线和Plane的交点point。

point需要经过偏移得到高亮块绘制位置:

  • floor() 向下取整
  • addScale(num) xz轴正向偏移0.5
  • y轴为0

创建辅助平面和高亮平面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const plane = new THREE.Mesh(
new THREE.PlaneGeometry(12, 12),
new THREE.MeshBasicMaterial({
visible:false,
})
)
plane.rotateX(-Math.PI/2)
scene.add(plane)

const highLight = new THREE.Mesh(
new THREE.PlaneGeometry(1, 1),
new THREE.MeshBasicMaterial({
color: 0xffffff
})
)
highLight.rotateX(-Math.PI/2)
highLight.position.set(0.5, 0, 0.5)
scene.add(highLight)

选中移动高亮块:

1
2
3
4
5
6
7
8
9
10
11
12
13
const mousePosition = new THREE.Vector2()
const raycaster = new THREE.Raycaster()

window.onmousemove = e => {
mousePosition.x = e.offsetX / window.innerWidth * 2 - 1
mousePosition.y = 1 - e.offsetY / window.innerHeight * 2
raycaster.setFromCamera(mousePosition, camera)
const interSections = raycaster.intersectObject(plane)
if (interSections[0]) {
const position = interSections[0].point.floor().addScalar(0.5)
highLight.position.set(position.x, 0, position.z)
}
}

创建物体

鼠标点击事件中添加创建物体逻辑,
但是在物体创建之前需要先判断当前网格内是否已经存在物体:
使用的是坐标匹配的方式。

为了做提示,鼠标移动到已经存在物体的网格中时,需要将网格标红。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
const mousePosition = new THREE.Vector2()
const raycaster = new THREE.Raycaster()
const objects = []
const isExist = () => objects.find(obj => obj.position.x === highLight.position.x && obj.position.z === highLight.position.z)
window.onmousemove = e => {
mousePosition.x = e.offsetX / window.innerWidth * 2 - 1
mousePosition.y = 1 - e.offsetY / window.innerHeight * 2
raycaster.setFromCamera(mousePosition, camera)
const interSections = raycaster.intersectObject(plane)
if (interSections[0]) {
const position = interSections[0].point.floor().addScalar(0.5)
highLight.position.set(position.x, 0, position.z)
highLight.material.color.set(
isExist()?0xff0000:0xffffff
)
}
}

const sphereMesh = new THREE.Mesh(
new THREE.SphereGeometry(0.4, 4, 2),
new THREE.MeshBasicMaterial({
color: 0xFFEF00,
wireframe:true
})
)
window.onmousedown = e => {
if (!isExist()) {
const sphere = sphereMesh.clone()
sphere.position.copy(highLight.position)
scene.add(sphere)
objects.push(sphere)
highLight.material.color.set(0xff0000)
}
}

动画效果

  • 高亮块闪烁效果
    • 控制透明度
  • 物体旋转浮动效果
1
2
3
4
5
6
7
8
9
function animate(time) {
highLight.material.opacity = 1 + Math.sin(time / 120)
objects.forEach(obj => {
obj.rotation.x = time / 1000
obj.rotation.z = time / 1000
obj.position.y = 0.5 + 0.5 * Math.abs(Math.sin(time/1000))
})
renderer.render(scene, camera);
}