patternjavascriptMinor
Canvas animation
Viewed 0 times
animationcanvasstackoverflow
Problem
Just started learning how to use the canvas today as I wanted to copy an animated GIF.
The GIF I tried to copy is below.
The animation does work but I've hacked it left and right. There's obviously alot of refactoring to do but am I going about it the right way?
```
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var segments = {};
segments['0'] = {
last: 0,
height: 15,
distanceToOuter: 30,
broke: false,
outerAnimStarted: false,
outerAnimThickness: 3,
outerAnimDistance: 5,
incValue: 0.5,
colour: 'green',
outerColour: 'green'
};
var percentage = 0;
var lastPercentage = 0;
var maxPercentage = 100;
//simulate download
(function loop() {
setTimeout(function () {
//set segment start size
lastPercentage = percentage;
//set percentage
percentage += randomNumber(1, 20);
//if percentage goes over 100, reduce back to 100
if (percentage > 100) {
percentage = 100;
}
//push segment into object stack
segments[percentage] = {
last: lastPercentage,
height: 15,
distanceToOuter: 30,
broke: false,
outerAnimStarted: false,
outerAnimThickness: 3,
outerAnimDistance: 5,
incValue: 0.5,
colour: 'green',
outerColour: 'green'
};
//if percentage is under 100, continue to loop
if (percentage === 100) {
segments[100].broke = true;
segments[segments[100].last].broke = true;
} else if (percentage === 0) {
segments[0].broke = true;
} else {
segments[lastPercentage].broke = true;
}
if (percentage = 120) {
segments[key].height = 15;
segments[key].distanceToOuter = 120;
if (!segments[key].outerAnimStarted)
The GIF I tried to copy is below.
The animation does work but I've hacked it left and right. There's obviously alot of refactoring to do but am I going about it the right way?
html, body {
background-color: lightgrey;
}```
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
var segments = {};
segments['0'] = {
last: 0,
height: 15,
distanceToOuter: 30,
broke: false,
outerAnimStarted: false,
outerAnimThickness: 3,
outerAnimDistance: 5,
incValue: 0.5,
colour: 'green',
outerColour: 'green'
};
var percentage = 0;
var lastPercentage = 0;
var maxPercentage = 100;
//simulate download
(function loop() {
setTimeout(function () {
//set segment start size
lastPercentage = percentage;
//set percentage
percentage += randomNumber(1, 20);
//if percentage goes over 100, reduce back to 100
if (percentage > 100) {
percentage = 100;
}
//push segment into object stack
segments[percentage] = {
last: lastPercentage,
height: 15,
distanceToOuter: 30,
broke: false,
outerAnimStarted: false,
outerAnimThickness: 3,
outerAnimDistance: 5,
incValue: 0.5,
colour: 'green',
outerColour: 'green'
};
//if percentage is under 100, continue to loop
if (percentage === 100) {
segments[100].broke = true;
segments[segments[100].last].broke = true;
} else if (percentage === 0) {
segments[0].broke = true;
} else {
segments[lastPercentage].broke = true;
}
if (percentage = 120) {
segments[key].height = 15;
segments[key].distanceToOuter = 120;
if (!segments[key].outerAnimStarted)
Solution
The programme is rather mixed up at the moment although I can't see any obvious 'hacks' as such. You are calling
-
a
-
a
-
a
-
an overall controller function which sets off
A few other comments:
-
it would help to have a comment line at the start of the long functions explaining what they do
-
a matter of taste, but when you have a complicated set of
EDIT - additional point on updating the segments:
draw and requestAnimationFrame from multiple places, sometimes unnecessarily. I would rewrite it to separate the actual drawing from the underlying logic of the changing characteristics of the segments. It would include the following components:-
a
view module containing the canvas and context variables, the functions that do all the actual drawing, and a method render which renders the whole canvas. render will be run upon requestAnimationFrame. Within view there should also be some generic drawing functions like circle and arc, and a specific function renderSegment which takes a segment object as its argument.-
a
Segment object constructor which stores all the information about each segment. The Segment prototype would have a method update which does the work currently done by increaseDistanceToOuter. These methods would be run on a separate setTimeout timer.-
a
newSegment function that is run on a setTimeout similar to the function you have called loop, and which creates the new segments-
an overall controller function which sets off
newSegment and starts the view rendering the objects on the screen.A few other comments:
-
it would help to have a comment line at the start of the long functions explaining what they do
-
a matter of taste, but when you have a complicated set of
if ... else statements I always find it less confusing to start with the positive condition, e.g. if (broke) ... else ... rather than if (!broke)... EDIT - additional point on updating the segments:
- As you can't always rely on setTimeout firing at the right times, it will be better to keep track of the time elapsed since each segment has been created, and use that to determine its position.
Context
StackExchange Code Review Q#58788, answer score: 4
Revisions (0)
No revisions yet.