Spiral Of Death

HTML
<!DOCTYPE html> <html> <body> <a href="https://github.com/jniemann66/react-spirograph" target="_blank"> <canvas id="theCanvas" width="480" height="480"> </canvas> </a> </body> </html>
CSS
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); }
Expand for more options Login