Skip to content

Instantly share code, notes, and snippets.

@xav76
Created October 23, 2012 18:26
Show Gist options
  • Select an option

  • Save xav76/3940562 to your computer and use it in GitHub Desktop.

Select an option

Save xav76/3940562 to your computer and use it in GitHub Desktop.
A CodePen by Neil Carpenter. Spirograph in canvas - Canvas for spirograph-y effects, works best on a large screen.
<body>
<canvas id="canvas"></canvas>
<div id="controls">
<span>
<h1>spiro</h1>
<p>Click and drag to draw. Move sliders to adjust effects. Double click to clear screen. (No sliders in Firefox, sorry!)</p>
<p>
<label>Size</label>
<input class="controller" name="maxSize" type="range" min="50" max="1000" value="500">
</p>
<p>
<label>Strength</label>
<input class="controller" name="lineWidth" type="range" min="0.1" max="40" step="0.5" value="0.5">
</p>
<p>
<label>Speed</label>
<input class="controller" name="speed" type="range" min="1" max="20" value="5">
</p>
<p>
<label>Colour</label>
<input class="controller" name="hue" type="range" min="0" max="360" value="0">
</p>
<p>
<label>Mirrors</label>
<input class="controller" name="mirrors" type="range" min="0" max="2" step="1" value="2">
</p>
<p>
<label>Background</label>
<input class="bg-slider" type="range" min="0" max="1" step="1" value="0">
</p>
<p>
<label>RAINBOWFY</label>
<input class="controller" name="rainbowfy" type="range" min="0" max="1" step="1" value="0">
</p>
</span>
<a href="#" class="toggle-controls" id="open">Open controls</a>
<a href="#" class="toggle-controls" id="close">Close controls</a>
</div>
</body>
// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
// http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
// requestAnimationFrame polyfill by Erik Möller
// fixes from Paul Irish and Tino Zijdel
(function() {
var lastTime = 0;
var vendors = ['ms', 'moz', 'webkit', 'o'];
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
|| window[vendors[x]+'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame)
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function() { callback(currTime + timeToCall); },
timeToCall);
lastTime = currTime + timeToCall;
return id;
};
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}());
var CANVASINGZ = {
options: {
maxSize: 500,
lineWidth: 0.5,
speed: 5,
hue: 0,
mirrors: 2,
rainbowfy: 0
},
animation: null,
canvas: null,
context: null,
winWidth: window.innerWidth,
winHeight: window.innerHeight,
mouse: { x: -1000, y: -1000, down: false },
mousePrev: { x: -1000, y: -1000 },
degrees: 45,
init: function () {
this.canvas = document.getElementById( 'canvas' );
this.context = canvas.getContext( '2d' );
this.canvas.width = this.winWidth;
this.canvas.height = this.winHeight;
this.canvas.addEventListener('mousemove', this.mouseMove, false);
this.canvas.addEventListener('mousedown', this.mouseDown, false);
this.canvas.addEventListener('mouseup', this.mouseUp, false);
this.canvas.addEventListener('mouseout', this.mouseOut, false);
this.canvas.addEventListener('dblclick', this.mouseDbl, false);
this.draw();
window.onresize = function () {
CANVASINGZ.winWidth = window.innerWidth;
CANVASINGZ.winHeight = window.innerHeight;
CANVASINGZ.canvas.width = CANVASINGZ.winWidth;
CANVASINGZ.canvas.height = CANVASINGZ.winHeight;
};
},
drawLines: function () {
if (!this.mouse.down) {
return;
}
var distance = Math.sqrt(Math.pow(this.mousePrev.x - this.mouse.x, 2) + Math.pow(this.mousePrev.y - this.mouse.y, 2)),
r = ((distance / this.winWidth) * 1000) + this.options.maxSize,
alpha = ((distance / this.winWidth) * 10) + 0.1,
radians = this.degrees * (Math.PI/180),
radians2 = (this.degrees + 180) * (Math.PI/180);
var x1, y1, x2, y2, x3, y3, x4, y4;
x1 = this.mouse.x + (Math.sin(radians) * r);
y1 = this.mouse.y + (Math.cos(radians) * r);
x2 = this.mouse.x + (Math.sin(radians2) * r);
y2 = this.mouse.y + (Math.cos(radians2) * r);
x3 = this.winWidth - x1;
y3 = this.winHeight - y1;
x4 = this.winWidth - x2;
y4 = this.winHeight - y2;
this.context.strokeStyle = 'hsla(' + this.options.hue + ', 100%, 50%, ' + alpha + ')';
this.context.lineWidth = this.options.lineWidth;
this.context.beginPath();
this.context.moveTo(x1,y1);
this.context.lineTo(x2,y2);
if (this.options.mirrors > 0) {
this.context.moveTo(x3,y1);
this.context.lineTo(x4,y2);
if (this.options.mirrors > 1) {
this.context.moveTo(x1,y3);
this.context.lineTo(x2,y4);
this.context.moveTo(x3,y3);
this.context.lineTo(x4,y4);
}
}
this.context.stroke();
this.degrees += this.options.speed;
// rainbow
if (this.options.rainbowfy > 0) {
this.options.hue += 10;
if (this.options.hue > 360) {
this.options.hue = 0;
}
}
},
draw: function () {
this.animation = requestAnimationFrame( function(){ CANVASINGZ.draw(); } );
this.drawLines();
this.mousePrev.x = this.mouse.x;
this.mousePrev.y = this.mouse.y;
},
clear: function() {
this.canvas.width = this.canvas.width;
},
mouseMove: function (e) {
CANVASINGZ.mouse.x = e.offsetX || (e.layerX - CANVASINGZ.canvas.offsetLeft);
CANVASINGZ.mouse.y = e.offsetY || (e.layerY - CANVASINGZ.canvas.offsetTop);
},
mouseDown: function () {
CANVASINGZ.mouse.down = true;
},
mouseUp: function () {
CANVASINGZ.mouse.down = false;
},
mouseOut: function () {
CANVASINGZ.mouse.down = false;
},
mouseDbl: function () {
CANVASINGZ.clear();
}
};
function eventListenerz() {
var inputs = document.getElementsByClassName('controller');
function onChange() {
var name = this.name,
value = this.value,
max = this.getAttribute('max');
value = +value;
if (value > max) {
value = max;
this.value = max;
}
CANVASINGZ.options[name] = value;
}
for (var i = 0; i < inputs.length; i++) {
inputs[i].addEventListener('change', onChange, false);
}
var controlToggles = document.getElementsByClassName('toggle-controls'),
controls = document.getElementById('controls');
function toggleControls(e) {
e.preventDefault();
controls.className = controls.className === 'closed' ? '' : 'closed';
}
for (var j = 0; j < 2; j++) {
controlToggles[j].addEventListener('click', toggleControls, false);
}
var bgToggle = document.getElementsByClassName('bg-slider')[0];
bgToggle.addEventListener('change', changeBG, false);
function changeBG() {
var value = this.value;
document.body.style.backgroundColor = 'rgba(0, 0, 0, ' + value + ')';
}
}
window.onload = function() {
CANVASINGZ.init();
eventListenerz();
}
@import url("http://fonts.googleapis.com/css?family=Carrois+Gothic|Russo+One");
html, body {
-webkit-font-smoothing: antialiased;
font: normal 12px/14px "Carrois Gothic", sans-serif;
width: 100%;
height: 100%;
margin: 0;
overflow: hidden;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
body {
-webkit-transition: background-color .5s ease-in-out;
-moz-transition: background-color .5s ease-in-out;
-o-transition: background-color .5s ease-in-out;
transition: background-color .5s ease-in-out;
}
span {
display: inline-block;
color: black;
background: url(http://neilcarpenter.com/demos/canvas/spiro/spiro-bg.jpg) repeat;
-webkit-background-clip: text;
background-clip: text;
-webkit-text-fill-color: transparent;
text-fill-color: transparent;
}
h1 {
font: normal 50px/50px "Russo One", Helvetica, Arial;
text-transform: uppercase;
margin: 0;
text-align: center;
}
#controls {
background: rgba(0, 0, 0, 0.8);
position: fixed;
top: 0;
left: 20px;
width: 250px;
padding: 20px;
-webkit-transform-origin: top center;
-moz-transform-origin: top center;
-o-transform-origin: top center;
transform-origin: top center;
-webkit-transform: rotate(0deg);
-moz-transform: rotate(0deg);
-o-transform: rotate(0deg);
transform: rotate(0deg);
-webkit-transition: -webkit-transform .5s ease-in-out;
-moz-transition: -moz-transform .5s ease-in-out;
-o-transition: -o-transform .5s ease-in-out;
transition: transform .5s ease-in-out;
}
#controls.closed {
-webkit-transform: rotate(-180deg);
-moz-transform: rotate(-180deg);
-o-transform: rotate(-180deg);
transform: rotate(-180deg);
}
.controller,
.bg-slider {
float: right;
width: 150px;
}
.toggle-controls {
position: absolute;
display: block;
height: 10px;
background: rgba(0, 0, 0, 0.9);
width: 290px;
left: 0;
text-align: center;
padding: 3px 0 7px;
text-decoration: none;
color: white;
}
#close {
bottom: -20px;
}
#open {
top: -20px;
-webkit-transform: rotate(180deg);
-moz-transform: rotate(180deg);
-o-transform: rotate(180deg);
transform: rotate(180deg);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment