Style
Design points
You can use Object.assign to assign properties to any object including elements and the element's style
You don't need the ctx.beginPath call before you clear the canvas.
Same with ctx.closePath
BTW closePath
is not related to beginPath
. Close path creates a path (line) from the last pointed added to the start of the current path.
For a 360 (2 PI) ctx.arc the closePath
call does nothing apart from use up CPU cycles..
(ctx) refers CanvasRenderingContext2D
Animation
setInterval is a dangerous, unreliable, useless excuse for a memory leak. It really should be removed from the language, but its too late we can't break the web can we :(. Don't use it... ever!
Case in point. As you have used it setInterval(animate_circle, 10);
you have assigned memory and CPU time that can not be removed as you neglected to store the interval's handle.
The only way to stop the interval and free up the closure it holds is to
- Refresh the page
- Or try to guess the handle for it.
rAF
To time animations use window.requestAnimationFrame to ensure that the animation is in sync with the display hardware and behaves in a predictable way in regards to power, resource consumption, and presentation.
Example Rewrite
I Have completely rewritten your code.
Key points.
Use rAF to time animation frames.
Use a CSS rule to style the canvas.
Defined an object to hold the circles state.
Use a function to render circles if given a circle like object.(Any object with the properties, x
, y
, r
).
Also used functions to separate the code into clear and distinct behaviors/roles.
Linked the animation to the time as given by the rAF callback. Same as performance.now (time since page load).
Note that I store the time of the first frame of the animation to make sure the animation starts when the page can render the animation. (eg if the page loads and the page is not visible the animation will start when it becomes visible)
Created utility functions to move common code (cyclic waveform and unit scale) out of the main loop
Use constants for any value that does not need to change.
Put all the code inside a single self evoking function to keep the global scope clean and code state safe from 3rd party content.
Used strict mode as part of a good habit and to ensure code runs at optimal speed.
;(()=>{
"use strict";
requestAnimationFrame(mainLoop); // will only start when all code below is ready
const TAU = Math.PI * 2;
const unitScale = (min, max, u) => (max - min) * u + min;
/* waveform sawtooth wave freq 1 amp 0.5 (dc)offset 0.5 min val 0 max val 1 */
const sawtooth = u => (u %= 1, (u < 0.5 ? u : (1 - u)) * 2);
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
Object.assign(canvas, {width: innerWidth - 40, height: innerHeight - 40});
document.body.appendChild(canvas);
var startTime;
const minSize = Math.min(canvas.width, canvas.height);
const circle = { // all coords and sizes in pixels.
x: canvas.width * 0.5,
y: canvas.height * 0.5,
time: 1, // in seconds.
min: minSize * 0.5 * 0.1,
max: minSize * 0.5 * 0.9,
r: 0,
col: "black", // CSS color value
};
function drawCircle(circle) {
ctx.fillStyle = circle.col ?? ctx.fillStyle;
ctx.beginPath();
ctx.arc(circle.x, circle.y, circle.r, 0, TAU);
ctx.fill();
}
function updateAnimation(time) {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
circle.r = unitScale(
circle.min, circle.max, sawtooth(time / (circle.time * 1000))
);
drawCircle(circle);
}
function mainLoop(time) {
startTime = startTime ?? time;
updateAnimation(time - startTime);
requestAnimationFrame(mainLoop);
}
})();
canvas {
border: 2px solid black;
position: absolute;
top: 0px;
left: 0px;
}