例子
(简易版)
场景选中生成精灵图(例子是在三维场景内)
代码
当作工具类放入代码段,直接可用
import THREE from 'three';
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
const clickPosition = new THREE.Vector2();
class modelPicking {
_CAMERA;
dom; // 场景视图
constructor(domElement, camera) {
this.dom = domElement;
this._CAMERA = camera;
}
// 射线
collectHandle(event, objects, edge = true) {
const array = this.getEdgePosition(this.dom, event.clientX, event.clientY);
clickPosition.fromArray(array);
let raycasterIntersects = this.getIntersects(clickPosition);
return raycasterIntersects.intersectObjects(objects, edge);
}
// 射线适应
getEdgePosition(dom, x, y) {
// 环境判断包围盒的边距(简单规避射线不准确)
const rect = dom.getBoundingClientRect();
return [(x - rect.left) / rect.width, (y - rect.top) / rect.height];
}
// 射线判定
getIntersects(point) {
mouse.set(point.x * 2 - 1, -(point.y * 2) + 1);
raycaster.setFromCamera(mouse, this._CAMERA);
return raycaster;
}
/**
* param : {width,height,value,image}
* 简易生成canvas文字图片 当作贴图放置到精灵图(有特殊需求自行设计)
*/
showMaterial(param) {
let { width, height, value, image } = param;
let canvas = document.createElement('canvas');
let ctx = canvas.getContext('2d');
canvas.width = width;
canvas.height = height;
ctx.fillStyle = 'rgba(0, 0, 0,1)';
if (image) {
ctx.drawImage(image, 0, 0, width, height);
}
//设置文字
if (value) {
value.forEach((val) => {
ctx.fillStyle = val.color ? val.color : '#fff';
val.font && (ctx.font = val.font)
ctx.fillText(val.textbox, val.coordX, val.coordY);
})
}
let url = canvas.toDataURL('image/png');
return url
}
/**
* coordBox : {x,y,z}
*/
createSprite(coordBox, url = './') {
let { x, y, z } = coordBox;
var spriteMaterial = new THREE.SpriteMaterial({
map: new THREE.TextureLoader().load(url), //设置精灵纹理贴图
transparent: true, //开启透明(纹理图片png有透明信息)
});
// 创建精灵模型对象,不需要几何体geometry参数
var sprite = new THREE.Sprite(spriteMaterial);
// 这里是为了配合点击模型 生成精灵图到模型位置
sprite.position.set(x, y + 2, z);
return sprite
}
}
export default modelPicking;
调用工具类
let camera;// 假设是相机
let dom; //假设是场景载体
let pickingTool = modelPicking(dom ,camera);
// 点击事件
onClickModel(event){
//scene.children = 你想射线判定的模型列表
const intersects = pickingTool.collectHandle(event,scene.children);
//根据需求判定
if(intersects){
let targetWorld = new THREE.Vector3();
intersects[0].object.getWorldPosition(targetWorld);
let textValue = [
{ textbox: "测试文字1", coordX: 20, coordY: 50,font:'100 24px Arial',color:'#47F4FF' },
{ textbox: "我是文字", coordX: 20, coordY: 80,font:'100 14px Arial',color:'#DFF0FF' },
];
let param = {
value: textValue,
width: 200,
height: 200,
image: 'https://img-blog.csdnimg.cn/2d39572277b24ac78455d15f69589cc4.png'//图片路径
};
let canvasImg = pickingTool.showMaterial(param);
let sprite = pickingTool.createSprite(targetWorld,canvasImg.toDataURL('image/png'));
scene.add(sprite);
}
}
射线简单理解
以平面来说x,y轴 ,选中某一个物体(比如正方形)只需要计算该点是否属于范围内
傻瓜式理解,吧三维场景脑补成 平面。射线换算成点。
分享
注:比较实用的小技巧之一,开始一件事可以把它想简单一些,以简单的思维去创建一个dom。和想要的效果有那么一点点挂钩的时候再去考虑如何扩展及丰富后续。(万事开头难,想半天头绪。也许可以试试实现一个简单的)