Creating fluid motion with a bezier curve in ThreeJS

I am looking to animate a bezier curve in ThreeJS with updating start, end, and control points. Eventually, I will have multiple curves animating simultaneously. What is the most efficient approach to achieve this?

When running the code snippet below, you will notice that I am creating the Bezier object, Geometry, and Line every time the frame renders. I remove the previous line from the scene and then add the new updated line. Is there a more optimal method for this? Perhaps by only updating the geometry without adding the line again?

var camera, scene, renderer, geometry, material, mesh;

init();
animate();

/**
 Create the scene, camera, renderer
*/
function init() {
  scene = new THREE.Scene();
  camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000);
  camera.position.z = 500;
  scene.add(camera);
  addCurve();
  renderer = new THREE.WebGLRenderer();
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);
}

/**
 Add the initial bezier curve to the scene
*/
function addCurve() {
  testPoint = 0;
  curve = new THREE.CubicBezierCurve3(
    new THREE.Vector3( testPoint, 0, 0 ),
    new THREE.Vector3( -5, 150, 0 ),
    new THREE.Vector3( 20, 150, 0 ),
    new THREE.Vector3( 10, 0, 0 )
  );
  curveGeometry = new THREE.Geometry();
  curveGeometry.vertices = curve.getPoints( 50 );
  curveMaterial = new THREE.LineBasicMaterial( { color : 0xff0000 } );
  curveLine = new THREE.Line( curveGeometry, curveMaterial );
  scene.add(curveLine);
}

/**
 On each frame render, remove the old line, create new curve, geometry and add the new line
*/
function updateCurve() {
  testPoint ++;
  scene.remove(curveLine);
  curve = new THREE.CubicBezierCurve3(
    new THREE.Vector3( testPoint, 0, 0 ),
    new THREE.Vector3( -5, 150, 0 ),
    new THREE.Vector3( 20, 150, 0 ),
    new THREE.Vector3( 10, 0, 0 )
  );
  curveGeometry = new THREE.Geometry();
  curveGeometry.vertices = curve.getPoints( 50 );
  curveLine = new THREE.Line( curveGeometry, curveMaterial );
  scene.add(curveLine);
}

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

function render() {
  updateCurve();
  renderer.render(scene, camera);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r83/three.min.js"></script>

Answer №1

Creating multiple new lines for each frame can be a costly operation.

What is the most efficient way to create animated curves?

One option could be utilizing shaders. However, implementing shaders may require more time. If you find the following suggestions sufficient for your needs, consider using them.

Enhancing curve updates in your code

I attempted to make minimal changes to your existing code and marked edited sections with "// EDITED" comments. I also introduced an array as you mentioned having multiple curves.

Clarification

  • Following advice from @WestLangley, it's best to avoid using the new keyword within the animation loop.
  • The CubicBezierCurve3 includes attributes like v0, v1, v2, and v3, which are instances of THREE.Vector3 provided initially. By modifying these attributes instead of creating new instances, you can update the vertices array without using the new keyword. Refer to this line for further details: See here.
  • Rather than recreating the line object, consider updating the geometry directly. Since you're working with a THREE.Line, only the vertices need modification. After altering the vertices, ensure to set
    geometry.verticesNeedUpdate = true
    to inform Three.js about the changes.

var camera, scene, renderer, geometry, material, mesh, curves = [];

init();
animate();

/**
 Create the scene, camera, renderer
*/
function init() {
  scene = new THREE.Scene();
  camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000);
  camera.position.z = 500;
  scene.add(camera);
  addCurve();
  renderer = new THREE.WebGLRenderer();
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);
}

/**
 Add the initial bezier curve to the scene
*/
function addCurve() {
  testPoint = 0;
  curve = new THREE.CubicBezierCurve3(
    new THREE.Vector3( testPoint, 0, 0 ),
    new THREE.Vector3( -5, 150, 0 ),
    new THREE.Vector3( 20, 150, 0 ),
    new THREE.Vector3( 10, 0, 0 )
  );
  curveGeometry = new THREE.Geometry();
  curveGeometry.vertices = curve.getPoints( 50 );
  curveMaterial = new THREE.LineBasicMaterial( { color : 0xff0000 } );
  curveLine = new THREE.Line( curveGeometry, curveMaterial );
  scene.add(curveLine);
  
  // EDITED
  curves.push(curveLine); // Include curve in curves array
  curveLine.curve = curve; // Connect curve object to this curveLine
}

/**
 On each frame render, remove the old line, create a new curve, geometry, and add the updated line
*/
function updateCurve() {
  testPoint ++;
  
  // EDITED
  for (var i = 0, l = curves.length; i < l; i++) {
    var curveLine = curves[i];
    
    // Update x value of v0 vector
    curveLine.curve.v0.x = testPoint;
    // Update vertices
    curveLine.geometry.vertices = curveLine.curve.getPoints( 50 ); 
    // Notify three.js that vertices have been modified
    curveLine.geometry.verticesNeedUpdate = true;
  }
}

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

function render() {
  updateCurve();
  renderer.render(scene, camera);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r83/three.min.js"></script>

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Enhancing JavaScript Variables Through Dynamic Ajax/PHP Interaction

I've been encountering some difficulties when trying to refresh my game. Initially, I attempted echoing the entire script each time... but that method was not aesthetically pleasing. Currently, I am experimenting with updating the variables through an ...

Cannot utilize the subscribed output value within the filter function

I am in need of assistance with my Angular 7 project. I have successfully implemented a service to call a Json file and output an object array. However, I am facing an issue when trying to filter the objects in the array based on a specific property called ...

What causes duplicate packages to appear in Bower at times?

There appears to be two identical packages available for the Sugar javascript library, sugar and sugarjs. Are these packages interchangeable or are they being maintained separately by different individuals? ➔ bower info sugarjs bower sugarjs#* ...

What is the best way to pass only the second parameter to a function in TypeScript?

Let's consider a TypeScript function as shown below: openMultipleAddFormModal(commission?: Commission, people?: People): void { // some data } To make a parameter optional, I have added the Optional Chaining operator. Now, how can I modify the code ...

How can I prevent overwriting previous input data in LocalStorage using Draftjs?

I have implemented a draftjs rich text editor to store my text input in local storage. The current functionality allows me to enter text and save it to local storage by clicking the save button. See the code snippet below: import React from "react"; impor ...

The output is generated by React useContext with a delay

When using the data obtained from useContext to verify if the logged-in user is an Admin, there seems to be a delay where it initially returns "undefined" and then after about 5 seconds, it provides the actual value. This causes the code to break since it ...

No Angularjs redirection without the "onload" event

In my search for a solution, I came across this answer but it did not quite fit my needs: Pass onload event through redirect The issue I'm facing is that I want the AngularJS section of my application to reload something when the user is redirected ...

One way to eliminate a prefix from a downloaded file path is by trimming the URL. For example, if you have a URL like "http://localhost

As my web app runs on port number, I am looking to download some files in a specific section. However, the download file path is being prefixed with "". <a href={file_path} download={file_name}> <Button variant={"link"}> <b>Dow ...

Is it okay to incorporate this code into the existing plugin?

My plugin, known as "pluggable," has the ability to take any element with the class .plugin and inject the following markup into it: <span class="colorable">color me</span> Below is the original markup containing elements with the class "plug ...

Upon clicking on a different div, a div will elegantly slide down from above the footer

I'm struggling with sliding a div from the bottom of the page. Here is my JSFIDDLE Currently, when I click on the blue box (arrow-hover), the div slides up (box-main). However, to hide the div (box-main) again, I have to click inside the box-main d ...

Ways to specify the styles for tr, td, and label elements within a material table

Hey there! Currently, I'm working with material table in react. I came across this page: https://material-table.com/#/docs/features/styling, where it mentions that we can use cellStyle, headerStyle. But what if I want to add more detailed styles to e ...

Transforming vertex coordinates of a polygon using Pixi.js

Greetings! I am a beginner in the world of pixijs (pixi.js - v5.2.4). I recently came across some intriguing examples on the official pixijs website. I decided to experiment by adding a simple slider. By adjusting the slider value, the position of a vertex ...

Utilizing a jQuery AJAX request to invoke a web method within a

My goal is to integrate jQuery mobile into an existing ASP.NET webform application. I am currently considering using pure HTML controls to create the jQuery Mobile page. I am aware that when making an AJAX call, I can access code-behind static web methods, ...

Issue with AdminLite 2.4.0 data table functionality malfunctioning

Check out this template that I'm using. I've copied all the contents for the bower_components and dist folders, and made sure to link and require everything properly. There are no 404 errors, only status code 200. Here is a snippet of my code: ...

Issue with ngFor displaying only the second item in the array

There are supposed to be two editable input fields for each section, with corresponding data. However, only the second JSON from the sample is being displayed in both sections. The JSON in the TypeScript file appears as follows: this.sample = [ { "se ...

Generatively generating a new element for the react application to be mounted on

I am looking to enhance my JavaScript application using React by creating a build. Currently, the application consists of just a single file structured as follows. This file essentially creates a div element and continuously changes the color of the text & ...

Present a Java-generated JSON object on a JSP page using JavaScript

Hello, I am currently working on creating a Json object in Java and would like to display the same JSON object in JSP using JavaScript. Essentially, I am looking to add two more options in my select box using Ajax. The Ajax is being called and I can see th ...

Submit a collection of images and an object to the server using ReactJS

I'm currently working with Reactjs on the frontend. In order to set the profile picture to state, I've implemented the following code: handleImageChange = (event) => { event.preventDefault(); var file = event.target.files[ ...

During the development of my project using the MERN Stack, I faced a challenge that needed to be

I recently ran into an issue while working on my MERN Stack project. The React app is running on port 3000 and the Express API on port 5000. The problem arose when I tried to add OAuth functionality using Redux, as I started getting an error message that ...

Leveraging JavaScript within a Polymer component

I have an object made with polymer: <newfolder-element id="newfolderelement" popupStyle="width: 362px; height: 100px;"> <span class="title">Create a new folder</span> <input type="text" class="ginput" style="width: 350px; padd ...