简体   繁体   中英

Drawing animated curves in canvas

A few days ago I came to stackoverflow asking about how to draw an arrow slowly into a canvas. No one was able to give me the correct answer... So I hope this helps someone.

Basically, i wanted to animate the progress of an invasion from one country to another country in a map. To do that I should use canvas and draw an arrow that moved from country A to country B, but not a fixed arrow... An arrow that grows progressively.

The code below draws an arrow, but not progressively. So, I needed to draw this curve like a CSS animation with a 5s transition.

 function drawCurve (ctx, x0, y0, x1, y1, x2, y2){ ctx.beginPath(); ctx.moveTo( x0, y0 ); ctx.quadraticCurveTo( x1, y1, x2, y2 ); ctx.stroke(); ctx.closePath(); } var docCanvas = document.getElementById('canvas'); var ctx = docCanvas.getContext('2d'); drawCurve(ctx, 0, 100, 150, -50, 300, 100);
 <canvas id="canvas" width="480" height="320"></canvas>

After some digging I came to this solution that gives me all I wanted.

Basically drawBezierSplit() allow you to draw a section of a quadratic bezier curve.

All the credit to Patrick Galbraith .

 /** * Animates bezier-curve * * @param ctx The canvas context to draw to * @param x0 The x-coord of the start point * @param y0 The y-coord of the start point * @param x1 The x-coord of the control point * @param y1 The y-coord of the control point * @param x2 The x-coord of the end point * @param y2 The y-coord of the end point * @param duration The duration in milliseconds */ function animatePathDrawing(ctx, x0, y0, x1, y1, x2, y2, duration) { var start = null; var step = function animatePathDrawingStep(timestamp) { if (start === null) start = timestamp; var delta = timestamp - start, progress = Math.min(delta / duration, 1); // Clear canvas ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); // Draw curve drawBezierSplit(ctx, x0, y0, x1, y1, x2, y2, 0, progress); if (progress < 1) { window.requestAnimationFrame(step); } }; window.requestAnimationFrame(step); } /** * Draws a splitted bezier-curve * * @param ctx The canvas context to draw to * @param x0 The x-coord of the start point * @param y0 The y-coord of the start point * @param x1 The x-coord of the control point * @param y1 The y-coord of the control point * @param x2 The x-coord of the end point * @param y2 The y-coord of the end point * @param t0 The start ratio of the splitted bezier from 0.0 to 1.0 * @param t1 The start ratio of the splitted bezier from 0.0 to 1.0 */ function drawBezierSplit(ctx, x0, y0, x1, y1, x2, y2, t0, t1) { ctx.beginPath(); if( 0.0 == t0 && t1 == 1.0 ) { ctx.moveTo( x0, y0 ); ctx.quadraticCurveTo( x1, y1, x2, y2 ); } else if( t0 != t1 ) { var t00 = t0 * t0, t01 = 1.0 - t0, t02 = t01 * t01, t03 = 2.0 * t0 * t01; var nx0 = t02 * x0 + t03 * x1 + t00 * x2, ny0 = t02 * y0 + t03 * y1 + t00 * y2; t00 = t1 * t1; t01 = 1.0 - t1; t02 = t01 * t01; t03 = 2.0 * t1 * t01; var nx2 = t02 * x0 + t03 * x1 + t00 * x2, ny2 = t02 * y0 + t03 * y1 + t00 * y2; var nx1 = lerp ( lerp ( x0 , x1 , t0 ) , lerp ( x1 , x2 , t0 ) , t1 ), ny1 = lerp ( lerp ( y0 , y1 , t0 ) , lerp ( y1 , y2 , t0 ) , t1 ); ctx.moveTo( nx0, ny0 ); ctx.quadraticCurveTo( nx1, ny1, nx2, ny2 ); } ctx.stroke(); ctx.closePath(); } /** * Linearly interpolates between two numbers */ function lerp(v0, v1, t) { return ( 1.0 - t ) * v0 + t * v1; } var docCanvas = document.getElementById('canvas'); var ctx = docCanvas.getContext('2d'); animatePathDrawing(ctx, 0, 100, 150, -50, 300, 100, 5000);
 <canvas id="canvas" width="480" height="320"></canvas>

EDIT :

And if you need a polyfill, you can use this code:

(function() {
    var lastTime = 0;
    var vendors = ['webkit', 'moz'];
    for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
        window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
        window.cancelAnimationFrame =
          window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
    }

    if (!window.requestAnimationFrame)
        window.requestAnimationFrame = function(callback, element) {
            var currTime = new Date().getTime();
            var timeToCall = Math.max(0, 16 - (currTime - lastTime));
            var id = window.setTimeout(function() { callback(currTime + timeToCall); },
              timeToCall);
            lastTime = currTime + timeToCall;
            return id;
        };

    if (!window.cancelAnimationFrame)
        window.cancelAnimationFrame = function(id) {
            clearTimeout(id);
        };
}());

Link : http://www.pjgalbraith.com/drawing-animated-curves-javascript/

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM