I am currently coding a website where, upon clicking a navigation link, HTML canvas draws a line to the relevant content box (I used markE's brilliant answer here https://stackoverflow.com/a/23941786 as a tutorial for the animation). I am in the process of animating these lines and I am running into the following issue / question:
How do I stop the first animation from completing when another link is clicked?
I have tried both creating a flag and cancelAnimationFrame in several iterations, to no joy.
Here's some simplified code, currently utilising a flag:
HTML
<div class="container">
<nav>
<ul>
<li>
<a href="#about" id="about" onclick="draw(this.id);">About</a>
</li>
<li>
<a href="#contact" id="contact" onclick="draw(this.id);">Work</a>
</li>
</ul>
</nav>
</div>
<canvas id="canvas" width="500" height="500"></canvas>
JavaScript
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var isRunning = false;
function draw(contentId) {
if (isRunning) return;
isRunning = true;
var t = 1;
var vertices = [];
function calcWaypoints(vertices) {
var waypoints = [];
for (var i = 1; i < vertices.length; i++) {
var pt0 = vertices[i - 1];
var pt1 = vertices[i];
var dx = pt1.x - pt0.x;
var dy = pt1.y - pt0.y;
for (var j = 0; j < 100; j++) {
var x = pt0.x + dx * j / 100;
var y = pt0.y + dy * j / 100;
waypoints.push({
x: x,
y: y
});
}
}
return (waypoints);
}
function animate() {
if (t < points.length - 1) {
window.requestAnimationFrame(animate);
}
ctx.beginPath();
ctx.moveTo(points[t - 1].x, points[t - 1].y);
ctx.lineTo(points[t].x, points[t].y);
ctx.stroke();
t++;
isRunning = false;
}
// Clear the canvas before drawing
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Assign coordinates
if (contentId == 'about') {
vertices.push({x: 50, y: 0}, {x: 50, y: 50});
vertices.push({x: 40, y: 60}, {x: 0, y: 60});
// Animate lines
ctx.lineCap = "round";
ctx.lineWidth = border;
ctx.strokeStyle = "green";
var points = calcWaypoints(vertices);
animate(points);
}
if (contentId == 'contact') {
vertices.push({x: 150, y: 0}, {x: 150, y: 50});
vertices.push({x: 160, y: 60}, {x: 300, y: 60});
vertices.push({x: 300, y: 70}, {x: 310, y: 70});
ctx.lineCap = "round";
ctx.lineWidth = border;
ctx.strokeStyle = "green";
var points = calcWaypoints(vertices);
animate(points);
}
}
Just to reiterate, in case I was unclear: when I've clicked on 'about' and then straight away click on 'contact', I would like the draw animation that was initialized when I first clicked on 'about' to completely stop (including clearing the canvas) and the 'contact' animation to start instead. At the moment, the 'contact' animation does start, but the 'about' animation insists on finishing as well. The same thing happens when I click on 'about' twice - the animation runs twice, simultaneously...
In case it helps anyone, I managed to solve it (there were some errors in logic and cancelAnimationFrame ended up doing what I needed rather than using a flag):
HTML
<div class="container">
<nav>
<ul>
<li>
<a href="#about" id="about" onclick="checkDraw(this.id);">About</a>
</li>
<li>
<a href="#contact" id="contact" onclick="checkDraw(this.id);">Work</a>
</li>
</ul>
</nav>
</div>
<canvas id="canvas" width="500" height="500"></canvas>
JavaScript
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var requestId;
function checkDraw(contentId) {
stopAnimation();
draw(contentId);
}
function stopAnimation(e) {
cancelAnimationFrame(requestId);
}
function draw(contentId) {
currentID = contentId;
var t = 1;
var vertices = [];
function calcWaypoints(vertices) {
var waypoints = [];
for (var i = 1; i < vertices.length; i++) {
var pt0 = vertices[i - 1];
var pt1 = vertices[i];
var dx = pt1.x - pt0.x;
var dy = pt1.y - pt0.y;
for (var j = 0; j < 100; j++) {
var x = pt0.x + dx * j / 100;
var y = pt0.y + dy * j / 100;
waypoints.push({
x: x,
y: y
});
}
}
return (waypoints);
}
function animate() {
if (t < points.length - 1) {
requestId = requestAnimationFrame(animate);
}
ctx.beginPath();
ctx.moveTo(points[t - 1].x, points[t - 1].y);
ctx.lineTo(points[t].x, points[t].y);
ctx.stroke();
t++;
}
// Clear the canvas before drawing
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Assign coordinates
if (contentId == 'about') {
vertices.push({x: 50, y: 0}, {x: 50, y: 50});
vertices.push({x: 40, y: 60}, {x: 0, y: 60});
// Animate lines
ctx.lineCap = "round";
ctx.lineWidth = border;
ctx.strokeStyle = "green";
var points = calcWaypoints(vertices);
animate(points);
}
if (contentId == 'contact') {
vertices.push({x: 150, y: 0}, {x: 150, y: 50});
vertices.push({x: 160, y: 60}, {x: 300, y: 60});
vertices.push({x: 300, y: 70}, {x: 310, y: 70});
ctx.lineCap = "round";
ctx.lineWidth = border;
ctx.strokeStyle = "green";
var points = calcWaypoints(vertices);
animate(points);
}
}
I hope this makes sense!