按照Three.js官网的解释是:精灵是一个总是面朝着摄像机的平面,通常含有使用一个半透明的纹理。精灵不会投射任何阴影,即使设置了也将不会有任何效果。
DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgltitle>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
/>
<style>
body {
color: #fff;
font-family: Monospace;
font-size: 13px;
text-align: center;
background-color: #fff;
margin: 0px;
overflow: hidden;
}
style>
<script src="./three.js">script>
<script src="./OrbitControls.js">script>
head>
<body>
<div id="container">div>
<script>
let scene, camera, controls, renderer;
let container = document.getElementById("container");
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
container.appendChild(renderer.domElement);
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(
40,
window.innerWidth / window.innerHeight,
1,
100000
);
camera.position.set(5, 5, 5);
scene.add(camera);
controls = new THREE.OrbitControls(camera, renderer.domElement);
scene.add(new THREE.AxesHelper(50));
animate();
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
//绘制圆角矩形
const roundRect1 = (ctx, x, y, w, h, r) => {
//Canvas 2D API 通过清空子路径列表开始一个新路径的方法。当你想创建一个新的路径时,调用此方法。
ctx.beginPath();
//moveTo方法移动画笔到起始点绘制一条线。
ctx.moveTo(x + r, y);
ctx.lineTo(x + w - r, y);
ctx.quadraticCurveTo(x + w, y, x + w, y + r);
ctx.lineTo(x + w, y + h - r);
ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
ctx.lineTo(x + r, y + h);
ctx.quadraticCurveTo(x, y + h, x, y + h - r);
ctx.lineTo(x, y + r);
ctx.quadraticCurveTo(x, y, x + r, y);
ctx.closePath();
ctx.fill();
ctx.stroke();
};
const makeTextTexture = (context2d, message, parameters) => {
const { canvas, context } = context2d;
if (!context) {
return;
}
const textHeight = Math.ceil(parameters.fontsize * 1.18);
/* 绘制圆角矩形 */
roundRect1(
context,
parameters.margin + parameters.border,
parameters.margin + parameters.border,
parameters.width - parameters.margin,
parameters.height - parameters.margin,
parameters.radius
);
/* 字体颜色 */
context.fillStyle = "rgba(0, 0, 0, 1.0)";
let xOffset = parameters.fontsize * 0.4;
let yOffset = parameters.fontsize * 0.2;
//(msg,x,y) msg绘制的文本,x,y文本绘制的位置
context.fillText(
message,
parameters.margin + parameters.border + xOffset,
textHeight + parameters.border + parameters.margin + yOffset
);
/* 画布内容用于纹理贴图 */
texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
return texture;
};
const createCanvas2D = (width = 512, height = 256) => {
let canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
return canvas;
};
const getContext2D = (canvas, parameters) => {
let context = null;
do {
context = canvas.getContext("2d");
if (!context) {
break;
}
let fontface = parameters?.hasOwnProperty("fontface")
? parameters["fontface"]
: "Arial";
/* 字体大小 */
let fontsize =
parameters?.hasOwnProperty("fontsize") && parameters["fontsize"]
? parameters["fontsize"]
: 18;
/* 边框厚度 */
let border =
parameters?.hasOwnProperty("border") && parameters["border"]
? parameters["border"]
: 4;
/* 边框颜色 */
let borderColor =
parameters?.hasOwnProperty("borderColor") &&
parameters["borderColor"]
? parameters["borderColor"]
: { r: 0, g: 0, b: 0, a: 1.0 };
/* 背景颜色 */
let backgroundColor = { r: 255, g: 255, b: 255, a: 1.0 };
if (
parameters &&
"backgroundColor" in parameters &&
parameters["backgroundColor"]
) {
backgroundColor = parameters["backgroundColor"];
}
if (parameters.bold) {
/* 字体加粗 */
context.font = "Bold " + fontsize + "px " + fontface;
} else {
/* 字体加粗 */
context.font = fontsize.toString() + "px " + fontface;
}
/* 获取文字的大小数据,高度取决于文字的大小 */
//let metrics = context.measureText( message );
//let textWidth = metrics.width;
/* 背景颜色 */
context.fillStyle =
"rgba(" +
backgroundColor.r +
"," +
backgroundColor.g +
"," +
backgroundColor.b +
"," +
backgroundColor.a +
")";
/* 边框的颜色 */
context.strokeStyle =
"rgba(" +
borderColor.r +
"," +
borderColor.g +
"," +
borderColor.b +
"," +
borderColor.a +
")";
context.lineWidth = border;
//width = textWidth + borderThickness;
//height = fontsize * 1.4 + borderThickness;
} while (false);
return context;
};
/* 初始化canvas和ctx */
const initializeContext2d = (parameters) => {
const canvas = createCanvas2D(parameters.width, parameters.height);
const context = getContext2D(canvas, parameters);
return {
canvas: canvas,
context: context,
};
};
//创建sprite标签
const makeTextSprite = (message, parameters, context2d) => {
const texture = makeTextTexture(context2d, message, parameters);
const spriteMaterial = new THREE.SpriteMaterial({
size: { x: 0.2, y: 0.2 },
center: {
x: 0,
y: 0.6,
},
map: texture,
});
const sprite = new THREE.Sprite(spriteMaterial);
/* 缩放比例 */
return sprite;
};
const measureText = (context2d, message) => {
const { canvas, context } = context2d;
if (!context) {
return;
}
/* 获取文字的大小数据,高度取决于文字的大小 */
let textMetrics = context.measureText(message);
return textMetrics;
};
const initializeParammeters = (context2d, text, textureParameters) => {
const textSize = measureText(context2d, text);
textureParameters.width = Math.ceil(
textSize.width +
textureParameters.border * 2 +
textureParameters.margin * 2
);
textureParameters.height = Math.ceil(
textureParameters.fontsize * 1.18 +
textureParameters.border * 2 +
textureParameters.margin * 2
);
return textureParameters;
};
const params = {
width: 128,
height: 128,
radius: 6, //3,
border: 2, //3,
margin: 8, //2,
fontsize: 16,
bold: false,
fontface: "Arial",
borderColor: { r: 136, g: 136, b: 136, a: 1 } /* 边框颜色 */,
backgroundColor: { r: 255, g: 255, b: 255, a: 1 } /* 背景颜色 */,
};
const msg = "我是标签39km"
//初始化canvas
const context2d = initializeContext2d(params);
//根据文字动态计算parameters的宽度属性
const parameters = initializeParammeters(context2d, msg, params);
scene.add(makeTextSprite(msg, parameters, context2d));
script>
body>
html>