ThreeJS 实现精灵标签方法

最近开发一个功能,在3D楼层模型上显示房间名称,方案选型有 CSS3DSprite、ToolTip、Sprite有三种。CSS3DSprite、ToolTip属于插入DIV元素,实现简单,但缩放及界面尺寸变化时标签和房间相对位置发生改变,放弃。Sprite 精灵模型是 ThreeJS 3D 一种对象,优点是缩放时定位精准,缺点是精灵模型不能设置长宽、Canvas 文本失真、文本长度不能根据文字长度自适应。

通过实践发现,Sprite 的缺点可以完美解决。Sprite可以设置缩放,其长宽缩放比和Canvas长宽保持一致,即能实现自适应文本长度且不失真。具体实现代码如下:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
import * as THREE from 'three'; /** * 创建精灵标签 * @param {*} text 标签名称 * @param {*} position 标签位置 * @param {*} options 标签配置 * @returns */ export default function createSpriteLabel(text, position = [0, 0, 0], options = {}) { // 默认配置 const defaultOptions = { zoom: true, // 标签 font: '500 48px microsoft yahei', color: 'white', TextHeight: 80, textPadding: 12, // 左右 padding backgroundColor: 'rgba(21, 23, 30, 0.8)', // 包角线 cornerLineColor: '#24cdff', cornerLineWidth: 8, cornerLineLenghtScale: 0.3, }; // eslint-disable-next-line no-param-reassign options = Object.assign(defaultOptions, options); if (!options.scale) { options.scale = options.zoom ? 5 : 0.04; } // 创建画布并设置宽高 const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); ctx.font = options.font; const textWidth = ctx.measureText(text).width; const width = textWidth + options.textPadding * 2; const height = options.TextHeight; canvas.width = width; canvas.height = height; // 设置标签背景 ctx.fillStyle = options.backgroundColor; ctx.fillRect(0, 0, width, height); // 设置背景包角线 let len = (width * options.cornerLineLenghtScale) / (width / height); ctx.strokeStyle = options.cornerLineColor; ctx.lineWidth = options.cornerLineWidth; const lines = [ // 左上顶点 [ [0, 0], [0, 0 + len], ], [ [0, 0], [len, 0], ], // 右下顶点 [ [width, height], [width, height - len], ], [ [width, height], [width - len, height], ], // 右上顶点 [ [width, 0], [width, len], ], [ [width - len, 0], [width, 0], ], // 左下顶点 [ [0, height], [len, height], ], [ [0, height], [0, height - len], ], ]; const drawLine = (ctx, [startX, startY], [endX, endY]) => { ctx.beginPath(); ctx.moveTo(startX, startY); ctx.lineTo(endX, endY); ctx.stroke(); }; lines.map((it) => { drawLine(ctx, it[0], it[1]); }); // 设置标签(居中) ctx.font = options.font; ctx.fillStyle = options.color; ctx.textBaseline = 'middle'; ctx.textAlign = 'center'; ctx.fillText(text, width / 2, height / 2 + 4); // 创建精灵 const texture = new THREE.Texture(canvas); texture.needsUpdate = true; const material = new THREE.SpriteMaterial({ map: texture }); material.sizeAttenuation = options.zoom; // 控制标签缩放 const sprite = new THREE.Sprite(material); sprite.scale.set(options.scale * (width / height), options.scale, 1); sprite.position.set(...position); return sprite; }

实现效果如下:
image.png

雪花 loading 实现归档
评论
luoqiangweb开发China
文章28
分类13
标签7