JAVASCRIPT
const canvas = document.getElementById("theCanvas");
let parameters = {
width: 480,
height: 480,
centerX: 480 / 2,
centerY: 480 / 2,
context: canvas.getContext("2d"),
wipeGlobalAlpha: 0.2,
animate: true,
precision: 50,
sequenceLength: 256, // total number of frames in animation sequence
reciprocalSequenceLength: 1 / 256.0, // reciprocal (for avoiding division)
color0: '#ff0000',
outerWheel0: 96,
innerWheel0: 26,
penPosition0: -2,
radius0: 100,
color1: '#0000ff',
outerWheel1: 96,
innerWheel1: 52,
penPosition1: 2,
radius1: 100
};
let state = {
animationFrame: 0, // animation frames are numbered from 0 to sequenceLength-1 inclusive
animationDirection: 1, // 1: forward, -1: reverse
};
setInterval(mutateShape,16.7);
function mutateShape(){
if(state.animationFrame <= 0)
state.animationDirection =1;
else if(state.animationFrame >= (parameters.sequenceLength-1))
state.animationDirection =-1;
state.animationFrame += state.animationDirection;
updateCanvas();
}
function updateCanvas() {
const context = parameters.context;
let width = parameters.width;
let height = parameters.height;
context.globalAlpha = parameters.wipeGlobalAlpha;
context.fillStyle = '#000000';
context.fillRect(0, 0, width, height);
context.globalAlpha = 1;
let M;
let N;
let penRadius;
let drawingRadius;
if(parameters.animate) {
let p = state.animationFrame * parameters.reciprocalSequenceLength; // position in animation sequence, normalized to range of [0.0,1.0)
// split start and end colors into their component values:
let colorComponents0 = hex2rgb(parameters.color0); // note: r,g,b components returned as array
let colorComponents1 = hex2rgb(parameters.color1);
// calculate current color based on value of p
let currentRed = Math.floor(colorComponents0[0] + p * (colorComponents1[0] - colorComponents0[0]));
let currentGreen = Math.floor(colorComponents0[1] + p * (colorComponents1[1] - colorComponents0[1]));
let currentBlue = Math.floor(colorComponents0[2] + p * (colorComponents1[2] - colorComponents0[2]));
// set color:
context.strokeStyle = rgb2hex(currentRed,currentGreen,currentBlue);
// calculate current params based on p:
M = Math.floor(parameters.outerWheel0 + p * (parameters.outerWheel1 - parameters.outerWheel0));
N = Math.floor(parameters.innerWheel0 + p * (parameters.innerWheel1 - parameters.innerWheel0));
penRadius = parameters.penPosition0 + p * (parameters.penPosition1 - parameters.penPosition0);
drawingRadius = parameters.radius0 + p * (parameters.radius1 - parameters.radius0);
} else {
M = parameters.outerWheel0;
N = parameters.innerWheel0;
penRadius = parameters.penPosition0;
drawingRadius = parameters.radius0;
context.strokeStyle = parameters.color0;
}
let gearRatio = N / M;
let inc = (2*Math.PI / Math.max(N,M) / parameters.precision);
context.beginPath();
// draw first point:
// context.moveTo (centerX + drawingRadius*((1 - gearRatio) + penRadius * gearRatio), centerY);
for (let t = 0; t < 2*Math.PI; t += inc){
let x = (1 - gearRatio) * Math.cos(N * t) + penRadius * (gearRatio) * Math.cos ((M - N) * t);
let y = (1 - gearRatio) * Math.sin(N * t) - penRadius * (gearRatio) * Math.sin ((M - N) * t);
context.lineTo (parameters.centerX + drawingRadius * x, parameters.centerY + drawingRadius * y);
}
context.stroke();
}
// hex2rgb() : converts a html hex color string to separate r,g,b values returned as an array
function hex2rgb(hexString) {
if (hexString.lastIndexOf('#') > -1) {
hexString = hexString.replace(/#/, '0x');
} else {
hexString = '0x' + hexString;
}
var r = hexString >> 16;
var g = (hexString & 0x00FF00) >> 8;
var b = hexString & 0x0000FF;
return [r, g, b];
}
// rgb2hex() : convert separate r,g,b values to html 6-digit hex color string
function rgb2hex(r, g, b) {
return '#'
+ ('0' + r.toString(16)).slice(-2)
+ ('0' + g.toString(16)).slice(-2)
+ ('0' + b.toString(16)).slice(-2);
}
2 Responses
http://spirograph.juddn.com