How can I animate a progressive drawing of svg path?

前端 未结 4 1327
忘掉有多难
忘掉有多难 2020-11-29 21:37

I want to animate a progressive drawing of a line using only css with svg/canvas and js maximum. An idea of the line I want to draw can be found here



        
相关标签:
4条回答
  • 2020-11-29 21:39

    Using Phrogz excellent technique I created a very basic GreenSock animation using TweenLite to tween the length value to getTotalLength() value.

    As you can see in the demo, hooking this into a tween engine gives you tons of control and it involves very little code.

    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();
    }
    

    Much respect to Phrogz for the awesome idea and code.

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

    0 讨论(0)
  • 2020-11-29 21:42

    So there's this similar question with this answer.


    I took your path and put it into the code in that 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 ( Note that Jquery 1.8.3 and Raphael 1.5.2 was used in the example ):

    $(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);
    
    });
    
    • I took your path from the [d] attribute and put it inside the pathString variable.
    • I also added a line to define the [stroke-width]

    I've only played around with raphael quite briefly once, but checking the examples and looking at how the code is constructed ( from page source ) gets you quite far ( I couldn't remember how/where to put the stroke-width, so I checked the method from the page source of this example. ).


    You can find raphael + more info from here.


    Just for fun, I made my own path...

    0 讨论(0)
  • 2020-11-29 21:43

    There are three techniques listed in this answer:


    There is an all-SVG solution that involves progressively modifying the stroke-dasharray for the shape to draw a longer and longer 'dash' followed by an enormous gap.

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

    Relevant code:

    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 = '';
    }
    

    Alternatively, you can still use all SVG and choose to build the SVG path one command at a time:

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

    Relevant code:

    // 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;
      }
    }
    

    Finally, you may choose to draw your path to an HTML5 canvas by sampling the SVG path periodically and drawing to the canvas. (Note that the SVG path does not need to be displayed for this to happen; you can build an SVG path element entirely in JavaScript and sample it):

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

    Relevant code:

    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();
    }
    
    0 讨论(0)
  • 2020-11-29 21:46

    I did something similar last year to animate a drawing in canvas. The paths are all SVG-type paths with curves and lines, so you could take those straight out of your SVG file and put them into the Javascript array.

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

    0 讨论(0)
提交回复
热议问题