Search code examples
javascripthtmlanimationcanvaspaperjs

Paper.js pathData animation onFrame and onMouseDrag


I have created a line using pathData method [demo ]. I want the line animate a small wave effect in onFrame function. Also, the line would be rubberband effect when it onMouseDrag function and it will return back to the original shape when onMouseUp function. I'm a newbie in Paper.js, so when I write some codes I get the animation with losing the object shape. check below codes

Codes Updated

var amount = 55;
var center = view.center;

function onFrame(event) {
    // Loop through the segments of the path:
    for (var i = 0; i <= amount; i++) {
        var path = linePath;
        var segment = path.segments[i];

        // A cylic value between -1 and 1
        var sinus = Math.sin(event.time * 3 + i);

        // Change the y position of the segment point:
        segment.point.y = sinus * 1 + center.y;
    }
}

function onMouseDrag(event) {
   var location = linePath.getNearestLocation(event.point);
   var segment = location.segment;
   var point = segment.point;
   if (!point.fixed && location.distance < 600 / 4) {
      var y = event.point.y;
      point.y += (y - point.y) / 6;
      if (segment.previous && !segment.previous.fixed) {
        var previous = segment.previous.point;
        previous.y += (y - previous.y) / 24;
      }
      if (segment.next && !segment.next.fixed) {
        var next = segment.next.point;
        next.y += (y - next.y) / 24;
      }
    }
 }

Live demo ^^

Any paperJs experts please help me to achieve it. Helping would be appreciated , Thanks in Advance.


Solution

  • First of all, the problem with the wave effect. You're only transforming along the Y-axis. Which can be what you want, but I'm assuming, it isn't.

    Usually, the effect will look a lot better if the line is transformed along the normal vector of the line. The normal would be direction perpendicular to the direction of the line. So, when you transform, it looks like the wave is travelling along the line itself.

    Calculating the normal in 2D is easy, because you just need to reflect the current direction on a single plane.

    var deltaX = nextSegment.point.x - segment.point.x;
    var deltaY = nextSegment.point.y - segment.point.y;
    var length = Math.sqrt( deltaX * deltaX + deltaY * deltaY );
    
    var normal = [ deltaX / length, deltaY / length ];
    normal = [ normal[ 1 ], -normal[ 0 ] ];
    

    We can now use that normal to transform the line:

    toManipulate.point.x = segment.point.x + normal[0] * sinus * 3;
    toManipulate.point.y = segment.point.y + normal[1] * sinus * 3;
    

    You can have a look at it in my fork of your fiddle.

    Another key aspect is, you don't want to transform your original data. Which is why I created another copy:

    var linePathCached = new Path(dataLine);
    var linePath = new Path(dataLine);
    

    You're going to always need a copy of your original data, so that you can manipulate a copy and use the old data for a new transformation in the next frame.

    Now, second, your rubber band effect.

    You're already pretty close with your current code. But, again, you're transforming your original data, which is not helpful.

    For this effect, I would suggest creating a second copy of your original data, bringing you to 3 data sets:

    1. Your original line path, which you use as a basis for all transformations.
    2. The line path that is manipulated by dragging with the mouse.
    3. The line path you're rendering.

    In your onMouseDrag handler, you would transform your set 2.

    In your onFrame handler, you would apply the sinus wave transformation you already have on set 2 and produce set 3. Additionally, you would interpolate all segments between their current position and their original position in set 1.

    You can apply the very common elasticOut equation, or look into other spring-related implementations.