Paper.js 如何将一条路径转换为另一条路径?

Paper.js How to morph one path to another path?

所以,我有 2 条路径:

var path1 = "M2337.8,0.1c-346.8,7.6-415.8,270.8-934.3,244.7  c-330.4-16.6-389.1-110.8-677.8-101.3c-321,10.5-403.4,252.6-592.3,252.6C73,396.1,29.8,372.8,0,341.9v451.8h2778V200  C2692.9,103.1,2538.6-4.3,2337.8,0.1z",
path2 = M2337.8,326.3C1991,333.9,1845,45.9,1472,45.9  c-334.4,0-390,181.9-639,181.9C473,227.8,400.3,0,195.7,0C84.5,0,0,98.3,0,146.1v562.6h2778V62.9  C2686,199.8,2538.6,321.9,2337.8,326.3z

我可以使用 paper.js 制作动画吗? 谢谢

这是我在我之前的一个项目中使用的算法:

  • 确保两条路径的点数相同
  • (可选)在两个路径中添加额外的点以获得更平滑的变形
  • 根据原始路径值对每个点及其句柄进行插值

看看这个 Sketch 进行演示。

// create paths
var pathB = new Path.Circle(view.center, 200);
var pathA = new Path.Rectangle(view.center - 100, new Size(200));

var morphingPath = new MorphingPath(pathA, pathB);

// apply some styling
pathA.strokeColor               = 'red';
pathB.strokeColor               = 'blue';
morphingPath.path.fullySelected = true;

// animate morphing
function onFrame(event)
{
    // with values from 0 to 1
    morphingPath.morph(Math.abs(Math.sin(event.count * 0.01)));
}

/**
 * A path that can be morphed between two other paths.
 * @param {Path} path1
 * @param {Path} path2
 * @constructor
 */
function MorphingPath(path1, path2)
{
    // internal variables
    var self = this,
        clone1,
        clone2;

    //
    // API
    //

    // allow direct access to morphing path
    self.path = null;

    /**
     * interpolate path from path1 to path2
     * @param time must be a value from 0 to 1
     */
    self.morph = function (time)
    {
        var segments = [];
        for (var i = 0; i < self.path.segments.length; i++)
        {
            // morph segments
            var segment1  = clone1.segments[ i ],
                segment2  = clone2.segments[ i ],
                point     = rampPoint(segment1.point, segment2.point, time),
                handleIn  = rampPoint(segment1.handleIn, segment2.handleIn, time),
                handleOut = rampPoint(segment1.handleOut, segment2.handleOut, time);

            segments.push(new Segment(point, handleIn, handleOut));
        }
        self.path.segments = segments;
    };


    //
    // INTERNAL METHODS
    //

    function init()
    {
        // store local copies of source paths
        clone1 = path1.clone();
        clone2 = path2.clone();

        // hide them
        clone1.visible = false;
        clone2.visible = false;

        // init morphing path
        self.path = createMorphingPath();
    }

    /**
     * Create the path that will later be morphed.
     * Points are added when needed, for a smoother result.
     * @returns {Path}
     */
    function createMorphingPath()
    {
        var paths   = [ clone1, clone2 ],
            offsets = [ [], [] ];

        // store paths segments offsets (except for first and last)
        for (var i = 0; i < paths.length; i++)
        {
            var path = paths[ i ];
            // loop segments
            for (var j = 1; j < path.segments.length - 1; j++)
            {
                // store offset
                offsets[ i ].push(path.segments[ j ].location.offset);
            }
        }

        // add missing points
        for (var i = 0; i < paths.length; i++)
        {
            // get current path offsets array
            var pathOffsets = offsets[ i ];
            // get a reference to the other path
            var otherPath   = i == 0 ? paths[ i + 1 ] : paths[ 0 ];
            // loop current path offsets
            for (var j = 0; j < pathOffsets.length; j++)
            {
                // add a corresponding point for that offset in the other path
                otherPath.divideAt(otherPath.getLocationAt(pathOffsets[ j ]));
            }
        }

        return clone1.clone();
    }

    function rampPoint(p1, p2, t)
    {
        return p1 + (p2 - p1) * t;
    }

    init();
}