const ctx = document.getElementById('poly').getContext('2d');
let state = {
magnitude: 1,
origin: { x: 0, y: 0 },
points: 4,
radius: 60,
rotation: Math.PI / 4,
nodeInset: 16,
nodeSize: 8,
nodeRotation: Math.PI / 4,
};
const main = () => {
init();
requestAnimationFrame(update);
};
const init = () => {
Object.assign(ctx.canvas, { width: 160, height: 160 });
Object.assign(ctx, { strokeStyle: 'green', lineWidth: 1 });
Object.assign(state, {
origin: {
x: ctx.canvas.width / 2,
y: ctx.canvas.height / 2
}
});
};
// https://stackoverflow.com/a/65829199/1762224
const FPS = 30;
let lastTimestamp = 0;
const update = (timestamp) => {
let inset = state.nodeInset + state.magnitude;
if (inset > state.radius) {
inset = state.radius;
state.magnitude *= -1;
} else if (inset < state.nodeSize) {
inset = state.nodeSize;
state.magnitude *= -1;
}
state.nodeInset = inset;
state.nodeRotation += (Math.PI / 36);
requestAnimationFrame(update);
if (timestamp - lastTimestamp < 1000 / FPS) return;
redraw();
lastTimestamp = timestamp;
};
const redraw = () => {
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
drawPolygon(ctx, state.origin.x, state.origin.y, state.points, state.radius, {
nodeInset: state.nodeInset,
nodeSize: state.nodeSize,
nodeRotation: state.nodeRotation,
rotation: state.rotation
});
}
const defaultPolygonOptions = {
nodeInset: 0,
nodeSize: 0,
rotation: 0,
};
/**
* @param {CanvasRenderingContext2D} ctx - Canvas 2D context
* @param {Number} x - origin x-position
* @param {Number} y - origin y-position
* @param {Number} n - number of points
* @param {Number} radius - radius of polygon
* @param {Object} [options] - configuration options
* @param {Number} [options.nodeInset] - insets for nodes
* @param {Number} [options.nodeSize] - size of node
* @param {Number} [options.nodeRotation] - rotation of nodes
* @param {Number} [options.rotation] - polygon rotation
*/
const drawPolygon = (ctx, x, y, n, radius, options = {}) => {
const opts = { ...defaultPolygonOptions, ...options };
ctx.beginPath();
ctx.moveTo(
x + radius * Math.cos(opts.rotation),
y + radius * Math.sin(opts.rotation)
);
for (let i = 1; i <= n; i += 1) {
const angle = (i * (2 * Math.PI / n)) + opts.rotation;
ctx.lineTo(
x + radius * Math.cos(angle),
y + radius * Math.sin(angle)
);
}
ctx.stroke();
if (!opts.nodeSize) return;
const dist = radius - opts.nodeInset;
for (let i = 1; i <= n; i += 1) {
const angle = (i * (2 * Math.PI / n)) + opts.nodeRotation;
ctx.beginPath();
ctx.arc(
x + dist * Math.cos(angle),
y + dist * Math.sin(angle),
opts.nodeSize,
0,
2 * Math.PI
);
ctx.stroke();
}
};
main();
canvas { background: #222; }
<canvas id="poly"></canvas>