Skip to content

Instantly share code, notes, and snippets.

@rileyjshaw
Created August 31, 2014 19:35
Show Gist options
  • Save rileyjshaw/b2f11f3ff39f669b2ef1 to your computer and use it in GitHub Desktop.
Save rileyjshaw/b2f11f3ff39f669b2ef1 to your computer and use it in GitHub Desktop.
A Pen by Riley Shaw.
<p id="tip">Click and drag to draw. You can draw anywhere.</p>
<div id="toggles">
<p id="cycling">T: Cycling off</p>
<p id="watercolor">W: Watercolor off</p>
<p>S: Save image</p>
</div>
function createCanvasElement (width, height, ratio, id, insertAfter) {
// Creates a scaled-up canvas based on the device's
// resolution, then displays it properly using styles
function createHDCanvas (ratio) {
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
// Creates a dummy canvas to test device's pixel ratio
ratio = ratio || (function () {
var context = document.createElement('canvas').getContext('2d');
var dpr = window.devicePixelRatio || 1;
var bsr = context.webkitBackingStorePixelRatio ||
context.mozBackingStorePixelRatio ||
context.msBackingStorePixelRatio ||
context.oBackingStorePixelRatio ||
context.backingStorePixelRatio || 1;
return dpr / bsr;
})();
canvas.width = width * ratio;
canvas.height = height * ratio;
canvas.style.width = width + 'px';
canvas.style.height = height + 'px';
context.setTransform(ratio, 0, 0, ratio, 0, 0);
if (id) canvas.id = id;
return canvas;
}
var canvas = createHDCanvas(ratio);
canvas.relativeMouseCoords = function (event) {
var totalOffsetX = 0, totalOffsetY = 0, canvasX = 0, canvasY = 0, currentElement = this;
do {
totalOffsetX += currentElement.offsetLeft - currentElement.scrollLeft;
totalOffsetY += currentElement.offsetTop - currentElement.scrollTop;
currentElement = currentElement.offsetParent;
} while (currentElement);
return {
x: event.pageX - totalOffsetX,
y: event.pageY - totalOffsetY
};
};
canvas.download = function () {
var imgLink = document.createElement('a');
var img = new Image();
img.src = canvas.toDataURL('image/png');
context.fillStyle = '#fffbf8';
context.fillRect(0, 0, width, height);
context.drawImage(img, 0, 0);
img = canvas.toDataURL('image/png');
imgLink.href = img;
imgLink.download = 'watercolors';
imgLink.click();
};
if (insertAfter) insertAfter.parentNode.insertBefore(canvas, insertAfter.nextSibling);
else body.appendChild(canvas);
return canvas;
};
function hslToRgb (h, s, l) {
function hueToRgb (p, q, t) {
if (t < 0) t += 1;
if (t > 1) t -= 1;
if (t < 1/6) return p + (q - p) * 6 * t;
if (t < 1/2) return q;
if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
return p;
}
var r, g, b, q, p;
if (s === 0) {
r = g = b = l; // achromatic
} else {
q = l < 0.5 ? l * (1 + s) : l + s - l * s;
p = 2 * l - q;
r = hueToRgb(p, q, h + 1/3);
g = hueToRgb(p, q, h);
b = hueToRgb(p, q, h - 1/3);
}
return [
Math.round(r * 255),
Math.round(g * 255),
Math.round(b * 255)
];
}
function Spot (x, y, radius, hueOffset) {
this.x = x;
this.y = y;
this.radius = radius;
this.hueOffset = hueOffset;
}
function drawSpot (spot) {
var hue = (hueBase + spot.hueOffset) % 360 / 360;
var gradient = context.createLinearGradient(spot.x - spot.radius, spot.y - spot.radius, spot.x + spot.radius, spot.y + spot.radius);
gradient.addColorStop(0, 'rgba(' + hslToRgb(hue, 0.75, 1) + ', ' + opacity + ')');
gradient.addColorStop(1, 'rgba(' + hslToRgb(hue, 0.4, 0.85) + ', ' + opacity + ')');
context.beginPath();
context.arc(spot.x, spot.y, spot.radius, 0, PI2);
context.fillStyle = gradient;
context.fill();
context.closePath();
}
function draw (x, y) {
var spot;
hueOffsetBase = (hueOffsetBase + hueDelta) % 360;
radius = radius + radiusDelta;
if (radius <= 0) {
init();
return true;
} else {
spot = new Spot(x, y, radius, hueOffsetBase);
drawSpot(spot);
spots.push(spot);
}
}
function drawBetween (x1, y1, x2, y2) {
var xDiff, yDiff, maxDiff, xDelta, yDelta, x, y;
xDiff = x1 - x2;
yDiff = y1 - y2;
maxDiff = Math.max(Math.abs(xDiff), Math.abs(yDiff));
xDelta = xDiff / maxDiff;
yDelta = yDiff / maxDiff;
for (var i = 0; i < maxDiff; i++) {
x = x2 + i * xDelta;
y = y2 + i * yDelta;
if (draw(x, y)) return;
}
lastCoords = {x: x1, y: y1};
}
function init () {
drawMode = false;
radius = 60;
lastCoords = {};
setTimeout(function () { body.className = ''; }, 1000);
}
function tick () {
hueBase = (360 + hueBase - 3) % 360;
context.clearRect(0, 0, width, height);
spots.forEach(drawSpot);
if (cycling) requestAnimationFrame(tick);
}
var PI2 = Math.PI * 2;
var body = document.body;
var cyclingToggle = document.getElementById('cycling');
var watercolorToggle = document.getElementById('watercolor');
var width = document.documentElement.clientWidth;
var height = window.innerHeight;
var canvas = createCanvasElement(width, height, 1, 'worm', document.getElementById('toggles'));
var context = canvas.getContext('2d');
var x1 = 300;
var y1 = 300;
var hueBase = 240;
var hueOffsetBase = 0;
var hueDelta = 0.4;
var drawMode, radius, lastCoords;
var radiusDelta = -0.02;
var spots = [];
var cycling = true;
var opacity = 0.01;
init();
canvas.addEventListener('mousedown', function (event) {
var coords = canvas.relativeMouseCoords(event);
draw(coords.x, coords.y);
lastCoords = coords;
drawMode = true;
body.className = 'fadeOut';
}, false);
canvas.addEventListener('mouseup', init, false);
canvas.addEventListener('mousemove', function (event) {
if (drawMode) {
var coords = canvas.relativeMouseCoords(event);
drawBetween(coords.x, coords.y, lastCoords.x, lastCoords.y);
lastCoords = coords;
}
}, false);
document.addEventListener('keydown', function (event) {
var keyCode = event.keyCode;
if (keyCode === 83) canvas.download();
else if (keyCode === 84) {
if (cycling) cyclingToggle.textContent = 'T: Cycling on';
else {
cyclingToggle.textContent = 'T: Cycling off';
requestAnimationFrame(tick);
}
cycling = !cycling;
} else if (keyCode === 87) {
if (opacity === 0.01) {
watercolor.textContent = 'W: Watercolor on';
opacity = 1;
} else {
watercolor.textContent = 'W: Watercolor off';
opacity = 0.01;
}
}
}, false);
requestAnimationFrame(tick);
body {
overflow: hidden;
margin: 0px;
font: bold 24px/1.5em sans-serif;
color: #c8c4b9;
background: #fffbf8;
cursor: crosshair;
}
#tip, #toggles {
position: fixed;
left: 1em;
transition: opacity 2s 20s;
pointer-events: none;
}
#tip {
top: 1em;
}
#toggles {
bottom: 1em;
}
p {
width: 10em;
margin: 0;
}
.fadeOut #tip, .fadeOut #toggles {
opacity: 0;
transition: opacity 2s;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment