How to animate a line in canvas

2019-08-13 02:32发布

问题:

I am trying to animate a line using canvas. I want to use TimelineLite to handle the animation. How would I do this? I know that in TimelineLite, the Timelines look like this:

var timeline = new TimelineLite();
timeline.to(target, duration, vars, position);

The points exist in a JSON file, and the file is correctly being brought in with AJAX. I want the line to start at the points x1 & y1, keep x2, as the same value, and animate it to the y2 position. So I basically want it to grow from x1-y1 to x2-y2.

JS

function animateLines(name, stroke, width, x1, y1, x2, y2){
    ctx.beginPath();
    ctx.moveTo(x1, y1);
    ctx.lineWidth = width;
    ctx.strokeStyle = stroke;
    ctx.stroke;
    console.log(x2);
}

for(var i = 0; i < animated_lines.length; i++){
    animateLines(animated_lines[i].name, animated_lines[i].stroke, animated_lines[i].width, animated_lines[i].x1, animated_lines[i].y1, animated_lines[i].x2, animated_lines[i].y2);
}

JSON

"animated_lines": [
    {
        "name": "Test",
        "stroke": "red",
        "width": 3,
        "x1": 0,
        "y1": 0,
        "x2": 0,
        "y2": 50
    }
]

So my question is really a multi-part one. How do I go about animating the line using canvas? How do I animate the line based on the name in the animateLine() function?

回答1:

TimelineLite uses the element to transform the targeted values of that element.

You can watch the update progress of the transform using onUpdate over time and animate your line based on that value.

timeline.eventCallback('onUpdate',function(data){
    var progress = this.progress();
    // Do animation calls here!
});

Here is a working example code snippet

I am transitioning the canvas opacity during the timeline and animating the canvas.

var timeline = new TimelineLite();
var mainCanvas = document.getElementById("ctx");
var ctx = mainCanvas.getContext("2d");
var temp = document.createElement('div');

var animated_lines = [{
  "name": "Red",
  "stroke": "#ff0000",
  "width": 3,
  "x1": 50,
  "y1": 50,
  "x2": 100,
  "y2": 100
},{
  "name": "Green",
  "stroke": "#00ff00",
  "width": 2,
  "x1": 50,
  "y1": 20,
  "x2": 100,
  "y2": 100
}];

function createLine(line, progress) {
  ctx.lineWidth = line.width;
  ctx.strokeStyle = line.stroke;
  ctx.beginPath();
  ctx.moveTo(line.x1, line.y1);
  ctx.lineTo(line.x2, line.y2*progress);
  ctx.stroke();
}
 
console.log('ctx', ctx);
timeline.from('#ctx', 10, { opacity: 0 });

timeline.eventCallback('onUpdate',function(){
  var progress = this.progress();
  //console.log(progress);
  ctx.clearRect ( 0 , 0 , mainCanvas.width, mainCanvas.height );
  for (var i = 0; i < animated_lines.length; i++) {
    createLine(animated_lines[i], progress);
  }
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/gsap/latest/TweenMax.min.js"></script>
<canvas id="ctx" />



回答2:

Is this the kind of effect you were looking to produce or something similar?

JS of which is as belows:

var width,height,centerX,centerY,canvas,context;
var delayFactor=.06,duration=1.2,ease=Elastic.easeOut,destIncrement=200,strokeWidth=2;
var timeline=new TimelineMax({paused:true,repeat:-1,yoyo:true,repeatDelay:duration*.5});
var animatedLines=[
    {name:'Test',stroke:'red',width:strokeWidth,x1:0,y1:0,x2:0,y2:destIncrement},
    {name:'Test',stroke:'green',width:strokeWidth,x1:0,y1:0,x2:destIncrement*.5,y2:destIncrement*.5},
    {name:'Test',stroke:'blue',width:strokeWidth,x1:0,y1:0,x2:destIncrement,y2:0},
    {name:'Test',stroke:'red',width:strokeWidth,x1:0,y1:0,x2:destIncrement*.5,y2:-destIncrement*.5},
    {name:'Test',stroke:'green',width:strokeWidth,x1:0,y1:0,x2:0,y2:-destIncrement},
    {name:'Test',stroke:'blue',width:strokeWidth,x1:0,y1:0,x2:-destIncrement*.5,y2:-destIncrement*.5},
    {name:'Test',stroke:'red',width:strokeWidth,x1:0,y1:0,x2:-destIncrement,y2:0},
    {name:'Test',stroke:'green',width:strokeWidth,x1:0,y1:0,x2:-destIncrement*.5,y2:destIncrement*.5}
];
function init(){
    initCanvas();
    initLines();
    populateTimeline();
    timeline.play();
    TweenLite.ticker.addEventListener('tick',render);
}
function populateTimeline(){
    var length=animatedLines.length,currentLine;
    for(var i=0; i<length; i+=1){
        currentLine=animatedLines[i];
        timeline.to(currentLine,duration,{destX:currentLine.x2,destY:currentLine.y2,ease:ease},i*delayFactor);
    }
}
function initLines(){
    var length=animatedLines.length,currentLine;
    for(var i=0; i<length; i+=1){
        currentLine=animatedLines[i];
        currentLine.destX=currentLine.x1;
        currentLine.destY=currentLine.y1;
    }
}
function initCanvas(){
    canvas=document.querySelector('canvas');
    context=canvas.getContext('2d');
    width=canvas.width=window.innerWidth;
    height=canvas.height=window.innerHeight;
    centerX=width*.5;
    centerY=height*.5;
}
function drawLine(currentLine){
    context.lineWidth=currentLine.width;
    context.strokeStyle=currentLine.stroke;
    context.beginPath();
    context.moveTo(centerX+currentLine.x1,centerY+currentLine.y1);
    context.lineTo(centerX+currentLine.destX,centerY+currentLine.destY);
    context.stroke();
}
function render(){
    var length=animatedLines.length;
    context.clearRect(0,0,width,height);
    for(var i=0; i<length; i+=1){ drawLine(animatedLines[i]); }
}
init();

The trick is to introduce two new variables into each Line of your animatedLines, namely destX and destY, and set their initial values to that of your x1 and y1 respectively, defined under the initLines(); function. And then make them increment towards your x2 and y2 values using TimelineMax which happens in the populateTimeline(); function.

Go ahead and take a look at the jsFiddle to take it further.

Hope this is helpful to you in some way.