three.js实战-Sprite实现标签效果

1. demo效果

three.js实战-Sprite实现标签效果_第1张图片

2 .什么是精灵(Sprite)

按照Three.js官网的解释是:精灵是一个总是面朝着摄像机的平面,通常含有使用一个半透明的纹理。精灵不会投射任何阴影,即使设置了也将不会有任何效果。

3. 代码大致逻辑

  1. 创建一个canvas对象,首先调用ctx.measureText方法计算画布渲染的文字宽度。
  2. 根据上一步计算的宽度,调用roundRect1方法绘制圆角矩形背景框。
  3. 在canvas上绘制字体。
  4. 将canvas传入new THREE.Texture方法生成纹理贴图。
  5. 调用new THREE.Sprite方法创建Sprite对象添加到scene种。

4. demo代码

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>

你可能感兴趣的:(three.js实战,javascript,前端,开发语言)