既有方向又会动的线,包你学会制作按箭头方向流动的线(three.js实战3)

1.demo效果

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2. 实现思路

实现思路比较简单,使用纹理材质创建网格对象,然后在render函数中改变纹理的偏移量(texture.offset.x),实现流动的效果

3. 实现要点

3.1 创建箭头流动线

  • 创建长条状平面
    使用THREE.PlaneGeometry类创建一个长条状平面,用来填充箭头纹理

  • 创建纹理材质
    使用TextureLoader纹理加载器加载纹理,然后需要设置水平方向重复10次,用该纹理作为map属性创建MeshBasicMaterial材质,同时将材质的side属性设置为THREE.DoubleSide,这样正反面都可以看到效果

  • 创建网格对象
    使用上面创建的几何体和材质使用THREE.Mesh直接创建网格对象并添加到场景中

示例代码如下

//创建条形平面-箭头流动的路径
const geometry = new THREE.PlaneGeometry(20, 2, 32);

//加载纹理
arrowLineTexture = new THREE.TextureLoader().load('../assets/textures/right.png');
arrowLineTexture.wrapS = arrowLineTexture.wrapT = THREE.RepeatWrapping; //每个都重复
arrowLineTexture.repeat.set(10, 1); //水平重复10次
arrowLineTexture.needsUpdate = true;

// 加载的纹理作为纹理贴图创建材质
let materials = new THREE.MeshBasicMaterial({
  map: arrowLineTexture,
  side: THREE.DoubleSide
});
const mesh = new THREE.Mesh(geometry, materials);
scene.add(mesh);

3.2 创建流动线

  • 创建线条
    使用THREE.CatmullRomCurve3类创建一个线条路径,然后用该路径创建一个管道几何体

  • 创建纹理材质
    使用TextureLoader纹理加载器加载纹理,这次设置水平方向重复20次,使用纹理作为map属性创建MeshBasicMaterial材质,同时将材质的side属性设置为THREE.BackSide,transparent属性设置为true

  • 创建网格对象
    使用上面创建的几何体和材质使用THREE.Mesh直接创建网格对象并添加到场景中

// 创建线条路径
let curve = new THREE.CatmullRomCurve3([
  new THREE.Vector3(0, 0, 10),
  new THREE.Vector3(10, 0, 10),
  new THREE.Vector3(10, 0, 0),
  new THREE.Vector3(20, 0, -10)
]);

//依据线条路径创建管道几何体
let tubeGeometry = new THREE.TubeGeometry(curve, 80, 0.2);
//加载纹理
flowingLineTexture = new THREE.TextureLoader().load('../assets/textures/roadflowing1.png')

flowingLineTexture.wrapS = THREE.RepeatWrapping;
flowingLineTexture.wrapT = THREE.RepeatWrapping;
flowingLineTexture.repeat.set(20, 1); //水平重复20次
flowingLineTexture.needsUpdate = true;

//创建纹理贴图材质
let material = new THREE.MeshBasicMaterial({
  map: flowingLineTexture,
  side: THREE.BackSide, //显示背面
  transparent: true
});

let mesh = new THREE.Mesh(tubeGeometry, material);
mesh.position.z = 10;
scene.add(mesh);

3.3 更新纹理偏移

在render函数中更新纹理偏移,有了这一步,线条就可以动起来

arrowLineTexture.offset.x -= 0.03; //更新箭头纹理偏移量
flowingLineTexture.offset.x -= 0.05; //更新流动线纹理偏移量

4. demo代码

<!DOCTYPE html>

<html>

<head>
  <title>流动的方向线</title>
  <script type="text/javascript" src="../three/build/three.js"></script>
  <script type="text/javascript" src="../three/examples/js/controls/OrbitControls.js"></script>
  <script type="text/javascript" src="../three/examples/js/libs/stats.min.js"></script>
  <style>
    body {
      margin: 0;
      overflow: hidden;
    }
  </style>
</head>

<body>

  <div id="Stats-output"></div>
  <div id="WebGL-output"></div>

  <script type="text/javascript">
    var scene, camera, renderer, arrowLineTexture, flowingLineTexture, stats, controls, clock;

    function initScene() {
      scene = new THREE.Scene();
      //用一张图加载为纹理作为场景背景
      scene.background = new THREE.TextureLoader().load("../assets/textures/starry-deep-outer-space-galaxy.jpg");
    }

    function initCamera() {
      camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
      camera.position.set(20, 30, 50);
      camera.lookAt(new THREE.Vector3(0, 0, 0));
    }

    function initLight() {
      //添加环境光
      const ambientLight = new THREE.AmbientLight(0x0c0c0c);
      scene.add(ambientLight);

      const directionalLight = new THREE.DirectionalLight('#fff')
      directionalLight.position.set(30, 30, 30).normalize()
      scene.add(directionalLight)

      //添加聚光灯
      const spotLight = new THREE.SpotLight(0xffffff);
      spotLight.position.set(-40, 60, -10);
      spotLight.castShadow = true;
      scene.add(spotLight);
    }

    function initModel() {

      initArrowLine()
      initFlowingLine();
      initPlane();

    }

    function initArrowLine() {
      //创建条形平面-箭头流动的路径
      const geometry = new THREE.PlaneGeometry(20, 2, 32);

      //加载纹理
      arrowLineTexture = new THREE.TextureLoader().load('../assets/textures/right.png');
      arrowLineTexture.wrapS = arrowLineTexture.wrapT = THREE.RepeatWrapping; //每个都重复
      arrowLineTexture.repeat.set(10, 1); //水平重复10次
      arrowLineTexture.needsUpdate = true;

      // 加载的纹理作为纹理贴图创建材质
      let materials = new THREE.MeshBasicMaterial({
        map: arrowLineTexture,
        side: THREE.DoubleSide
      });
      const mesh = new THREE.Mesh(geometry, materials);
      scene.add(mesh);
    }

    function initFlowingLine() {
      //加载纹理
      flowingLineTexture = new THREE.TextureLoader().load('../assets/textures/roadflowing1.png')

      flowingLineTexture.wrapS = THREE.RepeatWrapping;
      flowingLineTexture.wrapT = THREE.RepeatWrapping;
      flowingLineTexture.repeat.set(20, 1); //水平重复20次
      flowingLineTexture.needsUpdate = true;

      //创建纹理贴图材质
      let material = new THREE.MeshBasicMaterial({
        map: flowingLineTexture,
        side: THREE.BackSide, //显示背面
        transparent: true
      });

      // 创建线条路径
      let curve = new THREE.CatmullRomCurve3([
        new THREE.Vector3(0, 0, 10),
        new THREE.Vector3(10, 0, 10),
        new THREE.Vector3(10, 0, 0),
        new THREE.Vector3(20, 0, -10)
      ]);

      //依据线条路径创建管道几何体
      let tubeGeometry = new THREE.TubeGeometry(curve, 80, 0.2);
      let mesh = new THREE.Mesh(tubeGeometry, material);
      mesh.position.z = 10;
      scene.add(mesh);
    }

    //创建底面
    function initPlane() {
      const planeGeometry = new THREE.PlaneGeometry(50, 50, 1, 1); //创建一个平面几何对象

      //材质
      const planeMaterial = new THREE.MeshLambertMaterial({
        color: 0x080631,
        transparent: true,
        opacity: 0.8
      });
      const plane = new THREE.Mesh(planeGeometry, planeMaterial);

      //设置平面位置
      plane.rotation.x = -0.5 * Math.PI;
      plane.position.set(0, -2, 0);

      //平面添加到场景中
      scene.add(plane);
    }

    //初始化渲染器
    function initRender() {
      renderer = new THREE.WebGLRenderer({
        antialias: true,
        alpha: true
      });
      renderer.setClearColor(0x111111, 1); //设置背景颜色
      renderer.setSize(window.innerWidth, window.innerHeight);
      //renderer.shadowMap.enabled = true; //显示阴影
      document.getElementById("WebGL-output").appendChild(renderer.domElement);
    }
    //初始化轨道控制器
    function initControls() {
      clock = new THREE.Clock(); //创建THREE.Clock对象,用于计算上次调用经过的时间
      controls = new THREE.OrbitControls(camera, renderer.domElement);
      controls.autoRotate = true; //是否自动旋转
    }


    //性能监控
    function initStats() {
      stats = new Stats();
      stats.setMode(0); //0: fps, 1: ms
      document.getElementById("Stats-output").appendChild(stats.domElement);
    }

    function render() {

      arrowLineTexture.offset.x -= 0.03; //更新箭头纹理偏移量
      flowingLineTexture.offset.x -= 0.05; //更新流动线纹理偏移量

      const delta = clock.getDelta(); //获取自上次调用的时间差
      controls.update(delta); //控制器更新
      stats.update();
      requestAnimationFrame(render);
      renderer.render(scene, camera);
    }

    //页面初始化
    function init() {
      initScene();
      initCamera();
      initLight();
      initModel();
      initRender();
      initStats();
      initControls();
      render();
    }

    window.onload = init;
  </script>
</body>

</html>
  • 7
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
可以使用 Canvas 和原生 JavaScript 实现箭头方向流动线。下面是实现步骤: 1. 获取 Canvas 元素和上下文对象 ``` var canvas = document.getElementById("myCanvas"); var ctx = canvas.getContext("2d"); ``` 2. 定义线条颜色和宽度 ``` ctx.strokeStyle = "red"; // 线条颜色 ctx.lineWidth = 2; // 线条宽度 ``` 3. 定义线条的起始点和结束点 ``` var startX = 50; // 起始点 x 坐标 var startY = 50; // 起始点 y 坐标 var endX = 200; // 结束点 x 坐标 var endY = 50; // 结束点 y 坐标 ``` 4. 定义箭头大小和颜色 ``` var arrowSize = 10; // 箭头大小 var arrowColor = "black"; // 箭头颜色 ``` 5. 定义箭头的绘制函数 ``` function drawArrow(fromX, fromY, toX, toY) { var angle = Math.atan2(toY - fromY, toX - fromX); ctx.beginPath(); ctx.moveTo(toX, toY); ctx.lineTo(toX - arrowSize * Math.cos(angle - Math.PI / 6), toY - arrowSize * Math.sin(angle - Math.PI / 6)); ctx.moveTo(toX, toY); ctx.lineTo(toX - arrowSize * Math.cos(angle + Math.PI / 6), toY - arrowSize * Math.sin(angle + Math.PI / 6)); ctx.strokeStyle = arrowColor; ctx.stroke(); } ``` 6. 定义更新线条位置的函数 ``` function update() { ctx.clearRect(0, 0, canvas.width, canvas.height); endX += 2; // 每次更新 x 坐标 ctx.beginPath(); ctx.moveTo(startX, startY); ctx.lineTo(endX, endY); ctx.stroke(); drawArrow(startX, startY, endX, endY); // 绘制箭头 requestAnimationFrame(update); // 循环更新 } ``` 7. 调用更新函数 ``` update(); ``` 完整代码示例: ``` var canvas = document.getElementById("myCanvas"); var ctx = canvas.getContext("2d"); ctx.strokeStyle = "red"; ctx.lineWidth = 2; var startX = 50; var startY = 50; var endX = 200; var endY = 50; var arrowSize = 10; var arrowColor = "black"; function drawArrow(fromX, fromY, toX, toY) { var angle = Math.atan2(toY - fromY, toX - fromX); ctx.beginPath(); ctx.moveTo(toX, toY); ctx.lineTo(toX - arrowSize * Math.cos(angle - Math.PI / 6), toY - arrowSize * Math.sin(angle - Math.PI / 6)); ctx.moveTo(toX, toY); ctx.lineTo(toX - arrowSize * Math.cos(angle + Math.PI / 6), toY - arrowSize * Math.sin(angle + Math.PI / 6)); ctx.strokeStyle = arrowColor; ctx.stroke(); } function update() { ctx.clearRect(0, 0, canvas.width, canvas.height); endX += 2; ctx.beginPath(); ctx.moveTo(startX, startY); ctx.lineTo(endX, endY); ctx.stroke(); drawArrow(startX, startY, endX, endY); requestAnimationFrame(update); } update(); ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值