首页 > 解决方案 > 如何使三个 JS 中的 TextGeometry 跟随鼠标?

问题描述

这是我的源代码。我正在尝试使文本根据鼠标位置旋转。

// Initialization
            const scene = new THREE.Scene();
      let camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
      let renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
            let body = document.getElementsByTagName("body");
            let pageX = 0.5;
            let pageY = 0.5;

      renderer.setSize( window.innerWidth, window.innerHeight );

      document.getElementById("board").appendChild(renderer.domElement);

      // Handle resize event
      window.addEventListener('resize', () => {
        renderer.setSize( window.innerWidth, window.innerHeight );
        camera.aspect = window.innerWidth / window.innerHeight;

        camera.updateProjectionMatrix();
      });

      camera.position.z = 20;

            // Create light
            let directLight = new THREE.DirectionalLight('#fff', 4);
            directLight.position.set(0, 7, 5);
            scene.add(directLight);

            var light = new THREE.AmbientLight( 0x404040 ); // soft white light
            scene.add( light );

            function animate (){
              requestAnimationFrame( animate );
                var loader = new THREE.FontLoader();
                loader.load( 'https://threejs.org/examples/fonts/helvetiker_regular.typeface.json', function ( font ) {
                    var geometry = new THREE.TextGeometry( 'Hello three.js!', {
                        font: font,
                        size: 3,
                        height: 0.5,
                        curveSegments: 4,
                        bevelEnabled: true,
                        bevelThickness: 0.02,
                        bevelSize: 0.05,
                        bevelSegments: 3
                    } );
                    geometry.center();
                    var material = new THREE.MeshPhongMaterial(
                        { color: '#dbe4eb', specular: '#dbe4eb' }
                    );
                    var mesh = new THREE.Mesh( geometry, material );

                    mesh.rotation.x = (pageY - 0.5) * 2;
                    mesh.rotation.y = (pageX - 0.5) * 2;

                    scene.add( mesh );
                } );
        renderer.render(scene, camera);
      }

      animate();

            // Get mouse coordinates inside the browser
            document.body.addEventListener('mousemove', (event) => {

                pageX = event.pageX / window.innerWidth;
                pageY = event.pageY / window.innerHeight;

            });
      renderer.render(scene, camera);

        </script>

这是我能得到的最好的。问题是每次我移动鼠标时,它都会实例化一个新的网格并相应地旋转它,而我只需要一个网格来跟随鼠标。任何人都可以帮忙吗?提前致谢!

标签: javascriptthree.js3d

解决方案


正如您已经知道的那样,每一帧您都在重新加载字体并最终每次都重新创建网格。

为了解决这个问题,您需要在一些初始化函数中移动字体加载和对象创建,所以它只发生一次。

您想要保留在渲染循环中的唯一部分代码是根据鼠标移动更新文本的旋转:

mesh.rotation.x = (pageY - 0.5) * 2;
mesh.rotation.y = (pageX - 0.5) * 2;

但这会带来另一个问题。由于mesh是在字体加载器的回调函数中定义的本地对象,因此外部无法访问它。幸运的是,three.js 提供了一个名为.name的属性,您可以使用它为对象命名。例如

var mesh = new THREE.Mesh(geometry, material);
mesh.name = "myText";
scene.add(mesh);

稍后您可以使用以下方法获取对此对象的引用:

scene.getObjectByName("myText")

这是一个例子:

var container, scene, camera, renderer, pageX, pageY;

function init() {
  scene = new THREE.Scene();
  camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
  renderer = new THREE.WebGLRenderer({
    antialias: true,
    alpha: true
  });
  pageX = 0.5;
  pageY = 0.5;

  renderer.setSize(window.innerWidth, window.innerHeight);
  document.getElementById("container").appendChild(renderer.domElement);

  window.addEventListener('resize', () => {
    renderer.setSize(window.innerWidth, window.innerHeight);
    camera.aspect = window.innerWidth / window.innerHeight;

    camera.updateProjectionMatrix();
  });

  camera.position.z = 20;

  let directLight = new THREE.DirectionalLight('#fff', 4);
  directLight.position.set(0, 7, 5);
  scene.add(directLight);

  var light = new THREE.AmbientLight(0x404040); // soft white light
  scene.add(light);

  var loader = new THREE.FontLoader();
  loader.load('https://threejs.org/examples/fonts/helvetiker_regular.typeface.json', function(font) {

    var geometry = new THREE.TextGeometry('Hello three.js!', {
      font: font,
      size: 3,
      height: 0.5,
      curveSegments: 4,
      bevelEnabled: true,
      bevelThickness: 0.02,
      bevelSize: 0.05,
      bevelSegments: 3
    });
    geometry.center();
    var material = new THREE.MeshPhongMaterial({
      color: '#dbe4eb',
      specular: '#dbe4eb'
    });
    var mesh = new THREE.Mesh(geometry, material);
    mesh.name = "myText";


    scene.add(mesh);
    animate();
  });

  document.body.addEventListener('mousemove', (event) => {
    pageX = event.pageX / window.innerWidth;
    pageY = event.pageY / window.innerHeight;
  });
}

function animate() {
  requestAnimationFrame(animate);
  render();
}

function render() {
  scene.getObjectByName("myText").rotation.x = (pageY - 0.5) * 2;
  scene.getObjectByName("myText").rotation.y = (pageX - 0.5) * 2;
  renderer.render(scene, camera);
}

init();
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r120/three.min.js"></script>
<div id="container"></div>


推荐阅读