Search code examples
javascriptthree.jscurvespline

Three.js Animated curve


I try to animate a 2D curve in Three.js over time. I'll need more than 4 control points, so I'm not using Bezier curves. I created a Three.Line based on a SplineCurve.

If I log the geometry.vertices position of my line they'll change over time, but geometry.attributes.position remains the same. Is it possible to animate the line based on the curve animation ? I managed to do this with Bezier Curves, but can't find a way with SplineCurve. Thank you for your help, here's my code :

First I create the line :

var curve = new THREE.SplineCurve( [
                new THREE.Vector3( -10, 0, 10 ),
                new THREE.Vector3( -5, 5, 5 ),
                new THREE.Vector3( 0, 0, 0 ),
                new THREE.Vector3( 5, -5, 5 ),
                new THREE.Vector3( 10, 0, 10 )
            ] );

            var points = curve.getPoints( 50 );

            var geometry = new THREE.BufferGeometry().setFromPoints( points );

            var material = new THREE.LineBasicMaterial( { color : 0xff0000 } );

            // Create the final object to add to the scene
            curveObject = new THREE.Line( geometry, material );

            scene.add( curveObject );

            curveObject.curve = curve;

Then I try to update it :

curveObject.curve.points[0].x += 1;

curveObject.geometry.vertices = curveObject.curve.getPoints( 50 );
curveObject.geometry.verticesNeedUpdate = true;
curveObject.geometry.attributes.needsUpdate = true;

Solution

  • You can do it like that:

    var scene = new THREE.Scene();
    var camera = new THREE.PerspectiveCamera(60, 1, 1, 1000);
    camera.position.set(8, 13, 25);
    var renderer = new THREE.WebGLRenderer({
      antialias: true
    });
    var canvas = renderer.domElement;
    document.body.appendChild(canvas);
    
    var controls = new THREE.OrbitControls(camera, renderer.domElement);
    
    scene.add(new THREE.GridHelper(20, 40));
    
    var curve = new THREE.CatmullRomCurve3([
      new THREE.Vector3(-10, 0, 10),
      new THREE.Vector3(-5, 5, 5),
      new THREE.Vector3(0, 0, 0),
      new THREE.Vector3(5, -5, 5),
      new THREE.Vector3(10, 0, 10)
    ]);
    
    var points = curve.getPoints(50);
    
    var geometry = new THREE.BufferGeometry().setFromPoints(points);
    
    var material = new THREE.LineBasicMaterial({
      color: 0x00ffff
    });
    
    var curveObject = new THREE.Line(geometry, material);
    
    scene.add(curveObject);
    
    var clock = new THREE.Clock();
    var time = 0;
    
    render();
    
    function resize(renderer) {
      const canvas = renderer.domElement;
      const width = canvas.clientWidth;
      const height = canvas.clientHeight;
      const needResize = canvas.width !== width || canvas.height !== height;
      if (needResize) {
        renderer.setSize(width, height, false);
      }
      return needResize;
    }
    
    function render() {
      if (resize(renderer)) {
        camera.aspect = canvas.clientWidth / canvas.clientHeight;
        camera.updateProjectionMatrix();
      }
      renderer.render(scene, camera);
    
      time += clock.getDelta();
    
      curve.points[1].y = Math.sin(time) * 2.5;
    
      geometry = new THREE.BufferGeometry().setFromPoints(curve.getPoints(50));
    
      curveObject.geometry.dispose();
      curveObject.geometry = geometry;
    
    
      requestAnimationFrame(render);
    }
    html,
    body {
      height: 100%;
      margin: 0;
      overflow: hidden;
    }
    
    canvas {
      width: 100%;
      height: 100%;
      display;
      block;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/97/three.min.js"></script>
    <script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>