Reputation: 27041
I know how to draw a line with lineTo():
var c = document.getElementById("myCanvas");
var ctx = c.getContext("2d");
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(300, 150);
ctx.stroke();
Is there a way to apply animations (for example with anime.js) to the line?
Upvotes: 2
Views: 680
Reputation: 792
Yes! Yes, there is. If you don't want to get your hands messy by using a js based script to perform animation, let me introduce you to this CSS library that I've been using to add simplistic animations to my tags.
Its called Animate.css
If you want to apply your own custom animations then I'd suggest you try out the @keyframes and animation tags of CSS (for simple animations)
Upvotes: 1
Reputation: 54099
To animate you need to draw your graphic slightly differently at a speed that is higher than human eye can see distinct frames (from about 20 frames per second).
To do that on the canvas you need to first clear the display, then draw the scene.
For example lets animate a line as being the line between a points moving in two circles around the center of the canvas. A variable time
is used to determine where to draw the line. if we add to time
the line is animated forward, if subtract from time the line moves in the opposite direction. The bigger the change of the variable time the faster it will move the line.
// time determines where the line is drawn
function drawLine(time) {
// get size of the canvas
const w = ctx.canvas.width;
const h = ctx.canvas.height;
// get the radius of circle that fits canvas
const radius = Math.min(w, h) / 2;
// get the center of the canvas
const cx = w / 2;
const cy = h / 2;
// start of line outer radius
const x1 = Math.cos(time) * radius * 0.9;
const y1 = Math.sin(time) * radius * 0.9;
// end of line, offset time
const x2 = Math.cos(time * 0.707) * radius * 0.4;
const y2 = Math.sin(time * 0.707) * radius * 0.4;
ctx.moveTo(x1 + cx, y1 + cy); // Add to path
ctx.lineTo(x2 + cx, y2 + cy);
}
Now that you have a function that draws a line at different times you need to repeat that at a rate that looks animated. Modern browsers via the DOM provides a callback function that is designed to present animate content via the DOM. It is called 60 times a second (as requested, per request) which is the fastest that the DOM will present animated content. To set the callback function use requestAnimationFrame
(see example)
We generally use one update function that handles the whole process of clearing and drawing the frame. Below is an example of an update function that animates the line function above.
var time = 0; // position of line as abstracted time
const rate = 0.01; // Movement per frame in radians
requestAnimationFrame(updateFrame); // request the first frame
function updateFrame() {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); // clear the canvas
time += rate;
// set the line with color and end cap
ctx.lineWidth = 10;
ctx.lineCap = "round";
ctx.strokeStyle = "black";
// create a path
ctx.beginPath();
drawLine(time);
// now draw it
ctx.stroke();
requestAnimationFrame(updateFrame);
}
Putting that together we get the following.
I have added some stuff to make it more interesting. Click the canvas to add lines and restart the time variable. Add lines to get a feel of how many lines can be drawn before the device can not draw lines at a rate that fools the eye. For average devices that will be a lot of lines. (There are ways to greatly improve that rate but that is a little more advanced)
Adding a lot of lines will also show some interesting aliasing FX that can create patterns of movement that do not exist but are due to tiny imperfections of how the DOM renders to the canvas and how the eye deduces movement.
Also checks the size of the canvas compared to the page and resizes the canvas if needed. It is better to resize this way rather than using the resize event as resize events are not in sync with the display.
// time determines where the line is drawn
function drawLine(time) {
// get size of the canvas
const w = ctx.canvas.width;
const h = ctx.canvas.height;
// get the radius of circle that fits canvas
const radius = Math.min(w, h) / 2;
// get the center of the canvas
const cx = w / 2;
const cy = h / 2;
// start of line outer radius
const x1 = Math.cos(time) * radius * 0.95;
const y1 = Math.sin(time*1.04) * radius * 0.95;
// end of line, offset time
const x2 = Math.cos(time * 0.9) * radius * 0.82;
const y2 = Math.sin(time * 0.902) * radius * 0.82;
// Offset inner circle a little for interesting FX
const ox = Math.cos(time * 0.7) * radius * 0.12;
const oy = Math.sin(time * 0.703) * radius * 0.12;
ctx.moveTo(x1 + cx, y1 + cy);
ctx.lineTo(x2 + cx + ox, y2 + cy + oy);
}
const ctx = canvas.getContext("2d");
canvas.addEventListener("click",() => {
timeOffsets.push(timeOffsets[timeOffsets.length -1] * 1.002);
timeOffsets.push(timeOffsets[timeOffsets.length -1] * 1.002);
timeOffsets.push(timeOffsets[timeOffsets.length -1] * 1.002);
timeOffsets.push(timeOffsets[timeOffsets.length -1] * 1.002);
timeOffsets.push(timeOffsets[timeOffsets.length -1] * 1.002);
timeOffsets.push(timeOffsets[timeOffsets.length -1] * 1.002);
timeOffsets.push(timeOffsets[timeOffsets.length -1] * 1.002);
timeOffsets.push(timeOffsets[timeOffsets.length -1] * 1.002);
time = 2;
});
const timeOffsets = [1];
var time = 0; // position of line as abstracted time
const rate = 0.01; // Movement per frame in radians
requestAnimationFrame(updateFrame); // request the first frame
function updateFrame() {
// check if the canvas size needs to change to fit the page
if (innerWidth !== ctx.canvas.width || innerHeight !== ctx.canvas.height) {
// changing the canvas resolution also clears the canvas so dont need to clear
ctx.canvas.width = innerWidth;
ctx.canvas.height = innerHeight;
timeOffsets.length = 1;
} else {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height); // clear the canvas
}
time += rate;
var separate = 0;
const angularSep = 8 / Math.min(ctx.canvas.width, ctx.canvas.height);
// set the line with color and end cap
ctx.lineWidth = 2;
ctx.lineCap = "round";
ctx.strokeStyle = "black";
ctx.beginPath();
for(const t of timeOffsets) {
drawLine(time * t + separate);
separate += angularSep;
}
ctx.stroke();
requestAnimationFrame(updateFrame);
}
body: {
padding: 0px;
margin: 0px;
}
canvas {
position: absolute;
top: 0px;
left: 0px;
}
<canvas id="canvas"></canvas>
Upvotes: 11
Reputation: 494
Just like @Blindman67 said, it works by frames and you need to clear the canvas every frame and update the drawing. You can also use libraries to develop drawings with animation just a little bit easier, and make games as well.
I recommend you looking into p5.js and watching videos from Coding Train Channel. It will help you a lot. Good Luck
Upvotes: 2