Playful Palette is a color selection interface for digital painting similar to oil and watercolor palettes. The palette contains a set of color blobs that combine to create gradients and color palettes. They can be manipulated directly to explore arrangements and harmonies.
Last active
February 10, 2019 19:40
-
-
Save pastuh/385dc70adc9cf3c514c1d899bea3553a to your computer and use it in GitHub Desktop.
Playful Palette - Color mixing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function(){ | |
//DOM Library V0.0.1 | |
var fn = {}; | |
$js.libraryAdd('hasClass', function(a, b){ | |
var c = (a.getAttribute('class') || '').split(/\s+/g); | |
for(var d in c){ | |
if(c[d] == b){ | |
return true; | |
} | |
} | |
return false; | |
}, 'static'); | |
$js.libraryAdd('addClass', function(a, b){ | |
if(this.hasClass(a, b) == false){ | |
var c = (a.getAttribute('class') || '').split(/\s+/g); | |
c.push(b); | |
a.setAttribute('class', c.join(' ')); | |
} | |
return false; | |
}, 'static'); | |
$js.libraryAdd('removeClass', function(a, b){ | |
if(this.hasClass(a, b) == true){ | |
var c = (a.getAttribute('class') || '').split(/\s+/g), | |
d = new Array(); | |
for(var e in c){ | |
if(c[e] != b){ | |
d.push(c[e]); | |
} | |
} | |
a.setAttribute('class', d.join(' ')); | |
} | |
return false; | |
}, 'static'); | |
$js.libraryAdd('matches', function(a, b){ | |
if(a.nodeType != 1){ | |
console.log('ok', a.nodeType); | |
return false; | |
}else if(!b){ | |
return true; | |
} | |
var c = b.split(/[,\s]+/g); | |
for(var d = 0; d < c.length; d += 1){ | |
var e = c[d]; | |
if(e.substring(0, 1) == '#'){ | |
throw 'not supported:' + e; | |
}else if(e.substring(0, 1) == '.'){ | |
if(this.hasClass(a, e.substring(1))){ | |
return true; | |
} | |
}else{ | |
if(a.tagName.toUpperCase() == e.toUpperCase()){ | |
return true; | |
} | |
} | |
} | |
return false; | |
}, 'static'); | |
$js.libraryAdd('html', function(a){ | |
var b = this.parser.parseFromString('<div xmlns="http://www.w3.org/1999/xhtml">'+a+'</div>','text/xml').firstChild, | |
c = []; | |
while(b.firstChild){ | |
c.push(b.firstChild); | |
b.removeChild(b.firstChild); | |
} | |
c.__proto__ = fn; | |
return c; | |
}, 'static'); | |
$js.libraryAdd('pxToNum', function(a){ | |
if(!a || typeof a != 'string' || a.length <= 2 || a.charAt(a.length - 2) != 'p' || a.charAt(a.length - 1) != 'x'){ | |
return 0; | |
} | |
return +a.substring(0, a.length - 2); | |
}, 'static'); | |
fn.attr = function(a){ | |
if(arguments.length == 1){ | |
if(typeof a == 'string'){ | |
return this.getAttribute(a); | |
} | |
for(var b in a){ | |
this.setAttribute(b, a[b]); | |
} | |
}else if(arguments.length == 2){ | |
this.setAttribute(a, arguments[1]); | |
} | |
return this; | |
} | |
fn.prop = function(a){ | |
if(arguments.length == 1){ | |
if(typeof a == 'string'){ | |
return this[a]; | |
} | |
for(var b in a){ | |
this[b] = a[b]; | |
} | |
}else if(arguments.length == 2){ | |
this[a] = arguments[1]; | |
} | |
return this; | |
} | |
fn.css = function(a){ | |
if(arguments.length == 1){ | |
if(typeof a == 'string'){ | |
return this.style[a]; | |
} | |
for(var b in a){ | |
this.style[b] = a[b]; | |
} | |
}else if(arguments.length == 2){ | |
this.style[a] = arguments[1]; | |
} | |
return this; | |
} | |
fn.val = function(){ | |
if(arguments.length == 0){ | |
return this.value || ''; | |
}else if(arguments.length == 1){ | |
this.value = arguments[0]; | |
} | |
return this; | |
} | |
fn.on = function(a, b){ | |
var c = a.split(/\s+/g); | |
for(var d = 0; d < c.length; d += 1){ | |
this.addEventListener(c[d], b); | |
} | |
return this; | |
} | |
fn.off = function(a, b){ | |
var c = a.split(/\s+/g); | |
for(var d = 0; d < c.length; d += 1){ | |
this.removeEventListener(c[d], b); | |
} | |
return this; | |
} | |
fn.offset = function(){ | |
var a = {left:0, top:0}, | |
b = null; | |
for(var c = this; c.parentNode != null; c = c.parentNode){ | |
if(c.offsetParent != null){ | |
b = c; break; | |
} | |
} | |
if(b != null){ | |
for(var c = b; c.offsetParent != null; c = c.offsetParent){ | |
a.left += c.offsetLeft; | |
a.top += c.offsetTop; | |
} | |
} | |
for(var c = this; c.parentNode != null && c != document.body; c = c.parentNode){ | |
a.left -= c.scrollLeft; | |
a.top -= c.scrollTop; | |
} | |
return a; | |
} | |
fn.append = function(a){ | |
if(typeof a == 'string'){ | |
a = $js.html(a); | |
} | |
for(var b = 0; b < a.length; b += 1){ | |
this.appendChild(a[b]); | |
} | |
return this; | |
} | |
fn.prepend = function(a){ | |
if(typeof a == 'string'){ | |
a = $js.html(a); | |
} | |
for(var b = 0; b < a.length; b += 1){ | |
if(this.firstChild){ | |
this.insertBefore(a[b], this.firstChild); | |
}else{ | |
this.appendChild(a[b]); | |
} | |
} | |
return this; | |
} | |
fn.insertBefore = function(a){ | |
var b = a[0]; | |
b.parentNode.insertBefore(this, b); | |
return this; | |
} | |
fn.insertAfter = function(a){ | |
var b = a[0]; | |
if(b.nextSibling){ | |
b.parentNode.insertBefore(this, b.nextSibling); | |
}else{ | |
b.parentNode.appendChild(this); | |
} | |
return this; | |
} | |
fn.remove = function(){ | |
if(this.parentNode){ | |
this.parentNode.removeChild(this); | |
} | |
return this; | |
} | |
fn.detach = function(){ | |
if(this.parentNode){ | |
this.parentNode.removeChild(this); | |
} | |
return this; | |
} | |
fn.parent = function(){ | |
return DOM(this.parentNode); | |
} | |
fn.closest = function(a){ | |
for(var b = this; b != null; b = b.parentNode){ | |
if($js.matches(b, a)){ | |
return $DOM(b); | |
} | |
} | |
return DOM(); | |
} | |
fn.children = function(a){ | |
var b = [], | |
c = this.childNodes; | |
for(var d = 0; d < c.length; d += 1){ | |
if($js.matches(c.item(d), a)){ | |
b.push(c.item(d)); | |
} | |
} | |
b.__proto__ = fn; | |
return b; | |
} | |
fn.index = function(a){ | |
return Array.prototype.indexOf.call(DOM(this).parent().children(a), this); | |
} | |
fn.find = function(a){ | |
var b = [], | |
c = this.querySelectorAll(a); | |
for(var d = 0; d < c.length; d += 1){ | |
b.push(c.item(d)); | |
} | |
b.__proto__ = fn; | |
return b; | |
} | |
fn.clone = function(){return DOM(this.cloneNode(true));} | |
fn.focus = function(){this.focus(); return this;} | |
fn.select = function(){this.select(); return this;} | |
fn.submit = function(){this.submit(); return this;} | |
fn.scrollLeft = function(){ | |
if(arguments.length == 0){ | |
return this.scrollLeft; | |
} | |
this.scrollLeft = arguments[0]; | |
return this; | |
} | |
fn.scrollTop = function(){ | |
if(arguments.length == 0){ | |
return this.scrollTop; | |
} | |
this.scrollTop = arguments[0]; | |
return this; | |
} | |
fn.html = function(){ | |
if(arguments.length == 0){ | |
return this.innerHTML; | |
} | |
this.innerHTML = arguments[0]; | |
return this; | |
} | |
fn.text = function(){ | |
if(typeof this.textContent != 'undefined'){ | |
if(arguments.length == 0){ | |
return this.textContent; | |
} | |
this.textContent = arguments[0]; | |
return this; | |
}else{ | |
if(arguments.length == 0){ | |
return this.innerText; | |
} | |
this.innerText = arguments[0]; | |
return this; | |
} | |
} | |
fn.outerWidth = function(a){ | |
var b = this.offsetWidth; | |
if(a){ | |
var c = window.getComputedStyle(this, null); | |
return b + $js.pxToNum(c.marginLeft) + $js.pxToNum(c.marginRight); | |
} | |
return b; | |
} | |
fn.innerWidth = function(){ | |
var a = window.getComputedStyle(this, null); | |
return this.offsetWidth - $js.pxToNum(a.borderLeftWidth) - $js.pxToNum(a.borderRightWidth); | |
} | |
fn.width = function(){ | |
if(this == window){ | |
return this.innerWidth; | |
} | |
var a = window.getComputedStyle(this, null); | |
return this.offsetWidth - $js.pxToNum(a.borderLeftWidth) - $js.pxToNum(a.borderRightWidth) - $js.pxToNum(a.paddingLeft) - $js.pxToNum(a.paddingRight); | |
} | |
fn.outerHeight = function(a){ | |
var b = this.offsetHeight; | |
if(a){ | |
var c = window.getComputedStyle(this, null); | |
return b + $js.pxToNum(c.marginTop) + $js.pxToNum(c.marginBottom); | |
} | |
return b; | |
} | |
fn.innerHeight = function(){ | |
var a = window.getComputedStyle(this, null); | |
return this.offsetHeight - $js.pxToNum(a.borderTopWidth) - $js.pxToNum(a.borderBottomWidth); | |
} | |
fn.height = function(){ | |
if(this == window){ | |
return this.innerHeight; | |
} | |
var a = window.getComputedStyle(this, null); | |
return this.offsetHeight - $js.pxToNum(a.borderTopWidth) - $js.pxToNum(a.borderBottomWidth) - $js().pxToNum(a.paddingTop) - $js.pxToNum(a.paddingBottom); | |
} | |
fn.getSize = function(){ | |
var a = (this.width || this.innerWidth || this.clientWidth), | |
b = (this.height || this.innerHeight || this.clientHeight); | |
return {width: a, height: b} | |
} | |
fn.addClass = function(a){ | |
$js.addClass(this, a); | |
return this; | |
} | |
fn.removeClass = function(a){ | |
$js.removeClass(this, a); | |
return this; | |
} | |
fn.hasClass = function(a){ | |
return $js.hasClass(this, a); | |
} | |
fn.getPath = function(){ | |
var a = String(this.tagName).toLocaleLowerCase(); | |
if(this.id){ | |
a += '#'+this.id; | |
}else if(this.classList.length > 0){ | |
for(var b = 0; b<this.classList.length; b++){ | |
a += '.'+this.classList[b]; | |
} | |
} | |
return a; | |
} | |
fn.getAllPath = function(){ | |
var a = [], b = this.parentNode; | |
a.push(DOM(this).getPath()); | |
while(b){ | |
if(!b.tagName || b.tagName == 'BODY' || b.tagName == 'HTML'){break;} | |
var c = String(b.tagName).toLocaleLowerCase(); | |
if(b.id){ | |
c += '#'+b.id; | |
b=b.parentNode; | |
}else if(b.classList && b.classList.length > 0){ | |
for(var d = 0; d<b.classList.length; d++){ | |
c += '.'+b.classList[d]; | |
} | |
} | |
a.unshift(c); | |
b=b.parentNode; | |
} | |
return a.join(" > "); | |
} | |
$js.each(fn, function(a, b){ | |
fn[a] = function(){ | |
var c = null; | |
for(var d = 0; d < this.length; d += 1){ | |
var e = this[d], | |
g = b.apply(e, arguments); | |
if(e !== g){ | |
if(g != null && g.__proto__ == fn){ | |
if(c == null){c = [];} | |
c = c.concat(g); | |
}else{ | |
return g; | |
} | |
} | |
} | |
if(c != null){ | |
c.__proto__ = fn; | |
return c; | |
} | |
return this; | |
}; | |
}); | |
fn = $js.extend(fn, { | |
each : function(a){ | |
for(var b = 0; b < this.length; b += 1){ | |
a.call(this[b], b); | |
} | |
return this; | |
}, | |
getDom : function(){ | |
return this.length > 0? this[0] : null; | |
}, | |
first : function(){ | |
return DOM(this.length > 0? this[0] : null); | |
}, | |
last : function(){ | |
return DOM(this.length > 0? this[this.length - 1] : null); | |
} | |
}); | |
$js.libraryAdd('DOM', function(a){ | |
if(typeof a == 'string'){ | |
if(a.charAt(0) == '<'){ | |
return $js.html(a); | |
}else{ | |
var b = document.querySelectorAll(a), | |
c = []; | |
for(var d = 0; d < b.length; d += 1){ | |
c.push(b.item(d)); | |
} | |
c.__proto__ = fn; | |
return c; | |
} | |
}else if(typeof a == 'object' && a != null){ | |
if(a.__proto__ == fn){ | |
return a; | |
}else{ | |
var c = []; | |
c.push(a); | |
c.__proto__ = fn; | |
return c; | |
} | |
}else{ | |
var c = []; | |
c.__proto__ = fn; | |
return c; | |
} | |
}); | |
}()); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div id="thisCan"></div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
window.$js = (function(){ | |
var init = {}; | |
init.libraryAdd = function(name, calback, type){ | |
var fun, method; | |
if(type === 'static'){ | |
fun = init[name] = calback; | |
}else{ | |
fun = window[name] = calback; | |
} | |
method = function(fun){ | |
fun.extend = function(a, b){ | |
if(a instanceof Object){ | |
for(var c in a){ | |
this.extend(c, a[c]); | |
} | |
return this; | |
}else if(typeof a == 'string' && typeof b == 'function'){ | |
var c = fun.prototype[a] = b; | |
return method(c); | |
} | |
} | |
return fun; | |
} | |
return method(fun); | |
} | |
init.libraryAdd('extend', function(a, b){ | |
for(var c in b){ | |
if(typeof a[c] == 'object' || a[c] instanceof Array){ | |
a[c] = this.extend(a[c], b[c]); | |
}else{ | |
a[c] = b[c]; | |
} | |
} | |
return a; | |
}, 'static'); | |
init.libraryAdd('each', function(a, b){ | |
if(typeof a.splice == 'function'){ | |
for(var c = 0; c < a.length; c += 1){ | |
b(c, a[c]); | |
} | |
}else{ | |
for(var d in a){ | |
b(d, a[d]); | |
} | |
} | |
}, 'static'); | |
init.libraryAdd('grep', function(a, b){ | |
var c = []; | |
for(var d = 0; d < a.length; d += 1){ | |
var e = a[d]; | |
if(b(e)){c.push(e);} | |
} | |
return c; | |
}, 'static'); | |
init.libraryAdd('parser', (function(){return new window.DOMParser()}()), 'static'); | |
return init; | |
}()); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function () { | |
function bubble(x, y, color, r) { | |
this.x = x; | |
this.y = y; | |
this.color = Array.isArray(color) ? color : [Math.round(Math.random() * 255), Math.round(Math.random() * 255), Math.round(Math.random() * 255)]; | |
this.r = typeof r == "number" ? r : 300; | |
} | |
bubble.prototype.move = function (x, y) { | |
this.x = x; | |
this.y = y; | |
return this; | |
}; | |
bubble.prototype.ray = function (r) { | |
this.r = r; | |
return this; | |
}; | |
bubble.prototype.containThisPosition = function (x, y) { | |
return this.x - this.r < x && this.x + this.r > x && this.y - this.y < y && this.y + this.r > y; | |
} | |
function blend() { | |
this.paints = []; | |
} | |
blend.prototype.add = function (color) { | |
var a = new bubble(10, 10, color); | |
this.paints.push(a); | |
return a; | |
} | |
function getFragColor(x, y, blend) { | |
blend = !blend.paints || !(blend.paints instanceof Array) ? [] : blend.paints; | |
if (blend.length == 0) { return [255, 255, 255]; } | |
var b2 = 0.44, b3 = b2 * b2, b4 = b3 * b2, f = 0, p = 1, c = [0, 0, 0]; | |
for (var i = 0; i < blend.length; i++) { | |
if (!blend[i].containThisPosition(x, y)) { continue; } | |
var bub = blend[i], r = bub.r, | |
dx = (bub.x - x), dy = (bub.y - y), | |
d2 = (dx * dx + dy * dy) / r / r; | |
if (d2 <= b2) { | |
var d4 = d2 * d2, d5 = 1 - (4 * d4 * d2 / b4 - 17 * d4 / b3 + 22 * d2 / b2) / 9; | |
c[0] = (c[0] + bub.color[0] * d5); | |
c[1] = (c[1] + bub.color[1] * d5); | |
c[2] = (c[2] + bub.color[2] * d5); | |
f += d5; | |
} | |
} | |
var max = 0.5, border = 0.02; | |
if (f < max) { | |
return [255, 255, 255]; | |
} else { | |
p = f < (max + border) ? Math.min(Math.max(((f - max) / (border)), 0), 1) : 1; | |
c[0] = Math.round((1 - p) * 255 + p * (c[0] / f)); | |
c[1] = Math.round((1 - p) * 255 + p * (c[1] / f)); | |
c[2] = Math.round((1 - p) * 255 + p * (c[2] / f)); | |
return c; | |
} | |
} | |
var pp = window.Palette = function () { | |
this.paints = new blend(); | |
} | |
pp.prototype.addPaint = function (c) { | |
var a = this.paints.add(c); | |
return a; | |
} | |
pp.prototype.getPick = function (x, y) { | |
return getFragColor(x, y, this.paints); | |
} | |
pp.prototype.render = function (w, h) { | |
var p = this.paints, x = 0, y = 0, img = document.createElement('canvas').getContext('2d').createImageData(w, h), d = img.data; | |
for (var i = 0; i < d.length; i += 4) { | |
var x = (i / 4) % w; | |
y = x == 0 ? y + 1 : y; | |
var c = getFragColor(x, y, p); | |
d[i] = c[0]; d[i + 1] = c[1]; d[i + 2] = c[2]; d[i + 3] = 255; | |
} | |
return img; | |
} | |
pp.prototype.selectPaint = function (x, y) { | |
var p = this.paints.paints, | |
dist = function (a, b, r) { | |
var r = typeof r == "number" ? r : 50, | |
diff = function (a, b) { if (a > b) { return (a - b); } else { return (b - a); } }, | |
dx = diff(a.x, b.x), | |
dy = diff(a.y, b.y); | |
return (dx * dx + dy * dy) / r / r; | |
}; | |
p.sort(function (a, b) { | |
var pt = { x: x, y: y }; | |
return dist(pt, a, a.r) - dist(pt, b, b.r); | |
}); | |
if (dist({ x: x, y: y }, p[0], p[0].r) <= 0.25) { | |
return p[0]; | |
} | |
return false; | |
} | |
}()); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function () { | |
var can_ = DOM('<canvas></canvas>'); | |
DOM("#thisCan").prepend(can_); | |
can_.css({ | |
"border": "1px solid #f3f3f3" | |
}); | |
var can = can_.getDom(), | |
ctx = can.getContext('2d'), | |
width = can.width = 800, | |
height = can.height = 480, | |
pp = new Palette(); | |
var yellowColor = function () { | |
return [238, 217, 3]; //Cadmium Yellow Lemon ( Talens Rembrandt Extra Fine Artist' Oil colors ) | |
}; | |
var redColor = function () { | |
return [185, 5, 32]; //C.P. Cadmium Red Medium ( Golden Heavy Body Acrylics ) | |
}; | |
var blueColor = function () { | |
return [0, 85, 152]; //Cerulean Blue, Chromium ( Golden Heavy Body Acrylics ) | |
}; | |
var blackColor = function () { | |
return [40, 39, 42]; //Ivory Black ( Talens Rembrandt Extra Fine Artist' Oil colors ) | |
}; | |
var whiteColor = function () { | |
return [251, 241, 244]; //Titanium White ( Talens Rembrandt Extra Fine Artist' Oil colors ) | |
}; | |
pp.addPaint(yellowColor()).move(100, 100).ray(250); | |
pp.addPaint(redColor()).move(250, 100).ray(250); | |
pp.addPaint(blueColor()).move(170, 240).ray(250); | |
pp.addPaint(blackColor()).move(600, 100).ray(300); | |
pp.addPaint(whiteColor()).move(600, 240).ray(300); | |
pp.addPaint(yellowColor()).move(500, 420).ray(150); | |
pp.addPaint(redColor()).move(580, 420).ray(150); | |
pp.addPaint(blueColor()).move(660, 420).ray(150); | |
function update() { | |
var imageData = pp.render(width, height); | |
ctx.putImageData(imageData, 0, 0); | |
} | |
update(); | |
var isMove = false, | |
objSelect = null; | |
can_.on('mousemove', function (a) { | |
if (isMove == true && objSelect.move) { | |
objSelect.move(a.offsetX, a.offsetY); | |
update(); | |
} | |
}); | |
can_.on('mousedown', function (a) { | |
objSelect = pp.selectPaint(a.offsetX, a.offsetY); | |
isMove = true; | |
}); | |
can_.on('mouseup', function (a) { | |
objSelect = null; | |
isMove = false; | |
}); | |
can_.on('mouseout', function (a) { | |
objSelect = null; | |
isMove = false; | |
}); | |
}()); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script src="https://rawgit.com/ismael1361/-JS/master/library/%24JS.js"></script> | |
<script src="https://rawgit.com/ismael1361/-JS/master/library/%24JS.DOM.js"></script> | |
<script src="https://rawgit.com/ismael1361/-JS/master/playfulPalette/playfulPalette.js"></script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body{ | |
background: #efeee7; | |
user-select: none; | |
font-family: Arial, Helvetica, sans-serif; | |
color: #263238; | |
text-align: center; | |
margin-top:25px; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment