var c = document.getElementById('c'); var context = c.getContext("2d"); var centerx = context.canvas.width / 2; var centery = context.canvas.height / 2; // shim layer with setTimeout fallback window.requestAnimFrame = (function() { return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function(callback) { throw new Error("WTF? why no annimation?"); }; })(); // usage: // instead of setInterval(render, 16) .... running = false; $('#toggle').click(function() { i = 500; running = !running; $('#toggle').val(running); animloop(); }); i = 500; function animloop() { if (!running) return; requestAnimFrame(animloop); advance(); if (i-- < 0) running = false; }; // place the rAF *before* the render() to assure as close to // 60fps with the setTimeout fallback. function advance() { Z = incrementVal("Z", 0.01); b = incrementVal("b", 0.01); e = incrementVal("e", 0.01); renderSpiral({ a: 1, //1, b: b, e: e, //1.5, Z: Z, tail: 10 }); } function incrementVal(valName, step) { percentVal = parseFloat($('#percent' + valName).val()); diff = parseFloat($('#max' + valName).val()) - parseFloat($('#min' + valName).val()); val = parseFloat($('#min' + valName).val()) + diff * percentVal / 100; $('#percent' + valName).val(percentVal + step); $('#val' + valName).val(val); return val; } function renderSpiral(params) { a = params.a; b = params.b; e = params.e; Z = params.Z; tail = params.tail; context.clearRect(0, 0, 400, 400); targetX = a * Math.pow(e, b * Z * .1) * Math.cos(Z * .1); targetY = a * Math.pow(e, b * Z * .1) * Math.sin(Z * .1); theta = Math.atan(targetX / targetY); y = targetX * Math.sin(theta) + targetY * Math.cos(theta); if (y < 0) theta = theta + Math.PI; endZ = Z + tail; endX = a * Math.pow(e, b * endZ * .1) * Math.cos(endZ * .1); endY = a * Math.pow(e, b * endZ * .1) * Math.sin(endZ * .1); y = endX * Math.sin(theta) + endY * Math.cos(theta); x = endX * Math.cos(theta) - endY * Math.sin(theta); context.save(); max = Math.max(Math.abs(endX), Math.max(Math.abs(endY), 1)); scale = 50 / max; x = x * scale; y = y * scale; context.translate(centerx - x, centery - y); context.rotate(theta); context.lineWidth = 1 / scale; context.scale(scale, scale); $('#scale').text(scale); context.beginPath(); context.moveTo(0, 0); context.lineTo(0, 100); context.strokeStyle = "green"; context.stroke(); // a = scale; context.beginPath(); context.rect(-max, -max, max * 2, max * 2); context.strokeStyle = 'blue'; context.stroke(); context.beginPath(); for (i = 0; i < Z + 1 + tail; i++) { angle = 0.1 * i; x = a * Math.pow(e, b * angle) * Math.cos(angle); y = a * Math.pow(e, b * angle) * Math.sin(angle); if (i == 0) context.moveTo(x, y); context.lineTo(x, y); } context.strokeStyle = "#F00"; context.stroke(); context.restore(); context.beginPath(); context.moveTo(0, 0); context.rect(-100 + centerx, -100 + centery, 200, 200); context.strokeStyle = 'black'; context.stroke(); context.beginPath(); context.rect(-10 + centerx, -10 + centery, 20, 20); context.fill(); }
<canvas id="c" width="220" height="250"></canvas><input type="button" value="Start" id="toggle"/> <br/> Z: <input type="value" id="minZ" class="val" value="50"/> <input type="range" min="0" max="100" step="0.001" value="50" id="percentZ"/> <input type="value" id="maxZ" class="val" value="300"/> : <input type="value" id="valZ" class="val" value=""/> <br/> b: <input type="value" id="minb" class="val" value="2"/> <input type="range" min="0" max="100" step="0.001" value="50" id="percentb"/> <input type="value" id="maxb" class="val" value=".5"/> : <input type="value" id="valb" class="val" value=""/> <br/> e: <input type="value" id="mine" class="val" value="3"/> <input type="range" min="0" max="100" step="0.001" value="50" id="percente"/> <input type="value" id="maxe" class="val" value="1.01"/> : <input type="value" id="vale" class="val" value=""/> <br/> Scale: <span id="scale"></span>
#c { border: 1px solid #000; } .val { width: 30px; }