如何为 svg 路径的渐进式绘制动画?

Posted

技术标签:

【中文标题】如何为 svg 路径的渐进式绘制动画?【英文标题】:How can I animate a progressive drawing of svg path? 【发布时间】:2012-12-25 20:42:58 【问题描述】:

我想仅使用带有 svg/canvas 和 js 的 css 来为线条的渐进式绘制动画。我想画的线的想法可以找到here

<svg   xmlns="http://www.w3.org/2000/svg">
 <!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
 <g>
  <title>Layer 1</title>
  <path d="m33,104c1,0 2.1306,-0.8037 23,3c9.07012,1.65314 10,2 24,2c7,0 29,0 33,0c8,0 9,0 11,0c2,0 8,0 11,0c9,0 17,0 18,0c10,0 12,0 20,0c1,0 6,0 7,0c2,0 3.07613,0.38268 4,0c2.61313,-1.08239 2,-3 2,-6c0,-1 0,-2 0,-3c0,-1 0,-2 0,-3c0,-1 0,-2 0,-3c0,-1 0.30745,-3.186 -1,-5c-0.8269,-1.14727 -0.09789,-2.82443 -2,-4c-0.85065,-0.52573 -2.82443,-0.09789 -4,-2c-0.52573,-0.85065 -2.58578,-0.58578 -4,-2c-0.70711,-0.70711 -1.81265,-1.20681 -4,-3c-2.78833,-2.28588 -3.64749,-2.97251 -8,-4c-2.91975,-0.68926 -4.82375,-2.48626 -7,-3c-2.91975,-0.68926 -5.15224,-0.23463 -7,-1c-1.30656,-0.5412 -4.38687,-1.91761 -7,-3c-1.84776,-0.76537 -5.03609,0.37821 -7,0c-5.28799,-1.01837 -8,-3 -9,-3c-2,0 -5.0535,-0.54049 -7,-1c-2.17625,-0.51374 -4.15224,-0.23463 -6,-1c-1.30656,-0.54119 -3,-1 -4,-1c-2,0 -5,-1 -6,-1c-1,0 -3,-2 -6,-2c-2,0 -5,-2 -6,-2c-2,0 -2.02583,-0.67963 -4,-1c-3.12144,-0.50654 -4.15224,-0.23463 -6,-1c-1.30656,-0.54119 -2,-1 -3,-1c-2,0 -3,0 -5,0c-1,0 -2,0 -3,0c-1,0 -2,0 -3,0c-1,0 -2,0 -3,0c-2,0 -3.85273,0.1731 -5,1c-1.81399,1.30745 -5.18601,1.69255 -7,3c-1.14727,0.8269 -1.82375,2.48626 -4,3c-0.97325,0.22975 -1.69344,1.45881 -3,2c-0.92388,0.38268 -1.45951,1.0535 -1,3c0.51374,2.17625 3.07844,2.78985 6,4c2.06586,0.85571 3.38688,1.91761 6,3c1.84776,0.76537 5.2987,-1.05146 7,0c1.90211,1.17557 3.82375,2.48626 6,3c0.97325,0.22975 3.29289,0.29289 4,1c0.70711,0.70711 4,2 9,4c5,2 8,4 11,4c2,0 5,0 7,0c3,0 5,0 7,0c2,0 4,0 7,0c2,0 4,0 8,0c3,0 7,0 10,0c4,0 7,0 12,0c3,0 5,0 6,0c2,0 3,0 5,0c1,0 1.09789,-0.82443 3,-2c0.85065,-0.52573 3.07613,0.38268 4,0c1.30656,-0.5412 0.71022,-2.04291 1,-3c1.04483,-3.45084 2.84723,-5.04132 4,-9c0.88414,-3.03616 1.85194,-5.22836 3,-8c0.5412,-1.30656 1.5405,-2.0535 2,-4c0.51375,-2.17625 2.71413,-4.21167 5,-7c2.68979,-3.28101 4,-6 5,-7c1,-1 2,-2 2,-4c0,-1 0.70711,-2.29289 0,-3c-0.70711,-0.70711 -2.07613,0.38268 -3,0c-1.30656,-0.54119 -2,-1 -4,-1c-3,0 -6.87856,-2.49346 -10,-3c-2.96126,-0.48055 -6.71201,-0.98162 -12,-2c-2.94586,-0.56732 -5,-1 -9,-1c-3,0 -6,-1 -8,-1c-2,0 -5,-3 -7,-3c-2,0 -5.38687,-0.91761 -8,-2c-0.92388,-0.38268 -3.0535,-0.54049 -5,-1c-2.17625,-0.51374 -4.58578,0.41421 -6,-1c-0.70711,-0.70711 -1,-1 -2,-1c-1,0 -2,0 -3,0c-1,0 -2,0 -4,0c-1,0 -2,0 -3,0c-1,0 -2,0 -4,0c-1,0 -2,0 -3,0c-2,0 -3,0 -5,0c-1,0 -2,0 -3,0c-1,0 -3,0 -4,0c-3,0 -5,0 -7,0c-2,0 -4,0 -6,0c-2,0 -3,0 -5,0c-1,0 -2,0 -3,0c-2,0 -4,0 -5,0c-1,0 -2,0 -4,0c-1,0 -2,0 -3,1l-1,0" id="svg_1" stroke- stroke="#000000" fill="none"/>
 </g>
</svg>

【问题讨论】:

可能重复:***.com/questions/13408144/simple-path-transition 另一个类似的问题:***.com/questions/5924238/… 【参考方案1】:

此答案中列出了三种技术:


有一个全 SVG 解决方案,它涉及逐步修改 stroke-dasharray 的形状以绘制越来越长的“破折号”,然后是巨大的间隙。

演示:http://phrogz.net/svg/progressively-drawing-svg-path-via-dasharray.html

相关代码:

var distancePerPoint = 1;
var drawFPS          = 60;

var orig = document.querySelector('path'), length, timer;
orig.addEventListener('mouseover',startDrawingPath,false);
orig.addEventListener('mouseout', stopDrawingPath, false);

function startDrawingPath()
  length = 0;
  orig.style.stroke = '#f60';
  timer = setInterval(increaseLength,1000/drawFPS);


function increaseLength()
  var pathLength = orig.getTotalLength();
  length += distancePerPoint;
  orig.style.strokeDasharray = [length,pathLength].join(' ');
  if (length >= pathLength) clearInterval(timer);


function stopDrawingPath()
  clearInterval(timer);
  orig.style.stroke = '';
  orig.style.strokeDasharray = '';


或者,您仍然可以使用所有 SVG 并选择一次构建 SVG 路径一个命令:

演示:http://phrogz.net/svg/progressively-cloning-svg-path.html

相关代码:

// Assumes 'orig' and dup' are SVG paths
function addNextPathSegment()
  var nextIndex   = dup.pathSegList.numberOfItems;
  if (nextIndex<orig.pathSegList.numberOfItems)
    var nextSegment = orig.pathSegList.getItem(nextIndex);
    var segmentDup  = cloneSVGPathSeg( dup, nextSegment );
    dup.pathSegList.appendItem( segmentDup );
  


function cloneSVGPathSeg( path, seg )
  switch(seg.pathSegTypeAsLetter)
    case 'M': return path.createSVGPathSegMovetoAbs(seg.x,seg.y);                                                     break;
    case 'm': return path.createSVGPathSegMovetoRel(seg.x,seg.y);                                                     break;
    case 'L': return path.createSVGPathSegLinetoAbs(seg.x,seg.y);                                                     break;
    case 'l': return path.createSVGPathSegLinetoRel(seg.x,seg.y);                                                     break;
    case 'H': return path.createSVGPathSegLinetoHorizontalAbs(seg.x);                                                 break;
    case 'h': return path.createSVGPathSegLinetoHorizontalRel(seg.x);                                                 break;
    case 'V': return path.createSVGPathSegLinetoVerticalAbs(seg.y);                                                   break;
    case 'v': return path.createSVGPathSegLinetoVerticalRel(seg.y);                                                   break;
    case 'C': return path.createSVGPathSegCurvetoCubicAbs(seg.x,seg.y,seg.x1,seg.y1,seg.x2,seg.y2);                   break;
    case 'c': return path.createSVGPathSegCurvetoCubicRel(seg.x,seg.y,seg.x1,seg.y1,seg.x2,seg.y2);                   break;
    case 'S': return path.createSVGPathSegCurvetoCubicSmoothAbs(seg.x,seg.y,seg.x2,seg.y2);                           break;
    case 's': return path.createSVGPathSegCurvetoCubicSmoothRel(seg.x,seg.y,seg.x2,seg.y2);                           break;
    case 'Q': return path.createSVGPathSegCurvetoQuadraticAbs(seg.x,seg.y,seg.x1,seg.y1);                             break;
    case 'q': return path.createSVGPathSegCurvetoQuadraticRel(seg.x,seg.y,seg.x1,seg.y1);                             break;
    case 'T': return path.createSVGPathSegCurvetoQuadraticSmoothAbs(seg.x,seg.y);                                     break;
    case 't': return path.createSVGPathSegCurvetoQuadraticSmoothRel(seg.x,seg.y);                                     break;
    case 'A': return path.createSVGPathSegArcAbs(seg.x,seg.y,seg.r1,seg.r2,seg.angle,seg.largeArcFlag,seg.sweepFlag); break;
    case 'a': return path.createSVGPathSegArcRel(seg.x,seg.y,seg.r1,seg.r2,seg.angle,seg.largeArcFlag,seg.sweepFlag); break;
    case 'z':
    case 'Z': return path.createSVGPathSegClosePath();                                                                break;
  


最后,您可以选择通过定期采样 SVG 路径并绘制到画布来将路径绘制到 HTML5 画布。 (请注意,发生这种情况不需要显示 SVG 路径;您可以完全在 javascript 中构建一个 SVG 路径元素并对其进行采样):

演示:http://phrogz.net/svg/progressively-drawing-svg-path.html

相关代码:

function startDrawingPath()
  points = [];
  timer = setInterval(buildPath,1000/drawFPS);


// Assumes that 'orig' is an SVG path
function buildPath()
  var nextPoint = points.length * distancePerPoint;
  var pathLength = orig.getTotalLength();
  if (nextPoint <= pathLength)
    points.push(orig.getPointAtLength(nextPoint));
    redrawCanvas();
   else stopDrawingPath();


function redrawCanvas()
  clearCanvas();
  ctx.beginPath();
  ctx.moveTo(points[0].x,points[0].y);
  for (var i=1;i<points.length;i++) ctx.lineTo(points[i].x,points[i].y);
  ctx.stroke();

【讨论】:

您需要在 Chrome 和 Firefox 上使用 polyfill 来一次构建一个命令的路径。【参考方案2】:

所以有这个类似的问题with this answer。


我选择了你的路径并将其放入该答案的代码中。

jsfiddle demo

HTML:

<html>
<style>
    #canvas
    
    border-style:solid;
    border-width:1px;
    
</style>
<div id="canvas"> 
    <p>Hover over me</p>        
</div>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>

Javascript (请注意,示例中使用了 Jquery 1.8.3 和 Raphael 1.5.2)

$(function() 

animateLine = function(canvas, hoverDivName, colorNumber, pathString) 
    $('#' + hoverDivName).hover(

    function() 
        var line = canvas.path(pathString).attr(
            stroke: colorNumber
        );
        var length = line.getTotalLength();

        $('path[fill*="none"]').animate(
            'to': 1
        , 
            duration: 5000,
            step: function(pos, fx) 
                var offset = length * fx.pos;
                var subpath = line.getSubpath(0, offset);
                canvas.clear();
                canvas.path(subpath).attr(
                    'stroke-width': 5,
                    stroke: colorNumber
                );

            ,
        );
    , function() 
        $('path[fill*="none"]').stop(true, false).fadeOut();
    );
;

var canvas = Raphael('canvas', 200, 200);
var pathString = "m33,104c1,0 2.1306,-0.8037 23,3c9.07012,1.65314 10,2 24,2c7,0 29,0 33,0c8,0 9,0 11,0c2,0 8,0 11,0c9,0 17,0 18,0c10,0 12,0 20,0c1,0 6,0 7,0c2,0 3.07613,0.38268 4,0c2.61313,-1.08239 2,-3 2,-6c0,-1 0,-2 0,-3c0,-1 0,-2 0,-3c0,-1 0,-2 0,-3c0,-1 0.30745,-3.186 -1,-5c-0.8269,-1.14727 -0.09789,-2.82443 -2,-4c-0.85065,-0.52573 -2.82443,-0.09789 -4,-2c-0.52573,-0.85065 -2.58578,-0.58578 -4,-2c-0.70711,-0.70711 -1.81265,-1.20681 -4,-3c-2.78833,-2.28588 -3.64749,-2.97251 -8,-4c-2.91975,-0.68926 -4.82375,-2.48626 -7,-3c-2.91975,-0.68926 -5.15224,-0.23463 -7,-1c-1.30656,-0.5412 -4.38687,-1.91761 -7,-3c-1.84776,-0.76537 -5.03609,0.37821 -7,0c-5.28799,-1.01837 -8,-3 -9,-3c-2,0 -5.0535,-0.54049 -7,-1c-2.17625,-0.51374 -4.15224,-0.23463 -6,-1c-1.30656,-0.54119 -3,-1 -4,-1c-2,0 -5,-1 -6,-1c-1,0 -3,-2 -6,-2c-2,0 -5,-2 -6,-2c-2,0 -2.02583,-0.67963 -4,-1c-3.12144,-0.50654 -4.15224,-0.23463 -6,-1c-1.30656,-0.54119 -2,-1 -3,-1c-2,0 -3,0 -5,0c-1,0 -2,0 -3,0c-1,0 -2,0 -3,0c-1,0 -2,0 -3,0c-2,0 -3.85273,0.1731 -5,1c-1.81399,1.30745 -5.18601,1.69255 -7,3c-1.14727,0.8269 -1.82375,2.48626 -4,3c-0.97325,0.22975 -1.69344,1.45881 -3,2c-0.92388,0.38268 -1.45951,1.0535 -1,3c0.51374,2.17625 3.07844,2.78985 6,4c2.06586,0.85571 3.38688,1.91761 6,3c1.84776,0.76537 5.2987,-1.05146 7,0c1.90211,1.17557 3.82375,2.48626 6,3c0.97325,0.22975 3.29289,0.29289 4,1c0.70711,0.70711 4,2 9,4c5,2 8,4 11,4c2,0 5,0 7,0c3,0 5,0 7,0c2,0 4,0 7,0c2,0 4,0 8,0c3,0 7,0 10,0c4,0 7,0 12,0c3,0 5,0 6,0c2,0 3,0 5,0c1,0 1.09789,-0.82443 3,-2c0.85065,-0.52573 3.07613,0.38268 4,0c1.30656,-0.5412 0.71022,-2.04291 1,-3c1.04483,-3.45084 2.84723,-5.04132 4,-9c0.88414,-3.03616 1.85194,-5.22836 3,-8c0.5412,-1.30656 1.5405,-2.0535 2,-4c0.51375,-2.17625 2.71413,-4.21167 5,-7c2.68979,-3.28101 4,-6 5,-7c1,-1 2,-2 2,-4c0,-1 0.70711,-2.29289 0,-3c-0.70711,-0.70711 -2.07613,0.38268 -3,0c-1.30656,-0.54119 -2,-1 -4,-1c-3,0 -6.87856,-2.49346 -10,-3c-2.96126,-0.48055 -6.71201,-0.98162 -12,-2c-2.94586,-0.56732 -5,-1 -9,-1c-3,0 -6,-1 -8,-1c-2,0 -5,-3 -7,-3c-2,0 -5.38687,-0.91761 -8,-2c-0.92388,-0.38268 -3.0535,-0.54049 -5,-1c-2.17625,-0.51374 -4.58578,0.41421 -6,-1c-0.70711,-0.70711 -1,-1 -2,-1c-1,0 -2,0 -3,0c-1,0 -2,0 -4,0c-1,0 -2,0 -3,0c-1,0 -2,0 -4,0c-1,0 -2,0 -3,0c-2,0 -3,0 -5,0c-1,0 -2,0 -3,0c-1,0 -3,0 -4,0c-3,0 -5,0 -7,0c-2,0 -4,0 -6,0c-2,0 -3,0 -5,0c-1,0 -2,0 -3,0c-2,0 -4,0 -5,0c-1,0 -2,0 -4,0c-1,0 -2,0 -3,1l-1,0";

animateLine(canvas, "canvas", "#000", pathString);

);
我从 [d] 属性中获取了您的路径,并将其放入 pathString 变量中。 我还添加了一行来定义 [stroke-width]

我只玩过一次 raphael @,所以我从这个example的页面源中查看了方法。)。


You can find raphael + more info from here.


只是为了好玩,I made my own path...

【讨论】:

我可能晚了,但是如果我希望动画自动开始(而不是显示“悬停”消息),我必须删除什么?跨度> @BetaDecay 我注释掉了悬停线。 Jsfiddle【参考方案3】:

使用 Phrogz 出色的技术,我创建了一个非常基本的 GreenSock 动画,使用 TweenLite 将长度值补间到 getTotalLength() 值。

正如您在演示中所见,将其挂接到补间引擎中可为您提供大量控制,并且只涉及很少的代码。

var orig = document.querySelector('path'), length, timer;

var obj = length:0,
           pathLength:orig.getTotalLength();

orig.style.stroke = '#f60';

var t = TweenMax.to(obj, 10, length:obj.pathLength, onUpdate:drawLine, ease:Linear.easeNone)

function drawLine() 
  orig.style.strokeDasharray = [obj.length,obj.pathLength].join(' ');
  updateSlider();

非常感谢 Phrogz 的出色想法和代码。

http://codepen.io/GreenSock/pen/zLiux

【讨论】:

【参考方案4】:

我去年做了类似的事情来为画布上的绘图设置动画。这些路径都是带有曲线和线条的 SVG 类型路径,因此您可以直接从 SVG 文件中取出它们并将它们放入 Javascript 数组中。

http://www.ashleysheridan.co.uk/coding/javascript/Animated+Glowing+Line+Drawing+in+Canvas

【讨论】:

+1 是一篇不错的文章和技术,但 -1 因为如果您的网站关闭或您更改了 URL,此答案对未来的用户毫无用处。好的答案应该包含足够的信息来解决没有外部资源的问题。 (但拥有演示代码和站点当然也不错。)

以上是关于如何为 svg 路径的渐进式绘制动画?的主要内容,如果未能解决你的问题,请参考以下文章

如何为 SVG 多边形设置动画以进行填充?

如何为 CAShapeLayer 路径和填充颜色设置动画

如何为 svg 动画提供更多空间?

渐进式<;svg>;饼图、甜甜圈图、条形图和折线图

如何为 SVG 笔画设置动画以充当仪表?

svg路径蒙版动画