MAAAJOOOOR
This commit is contained in:
parent
e7d1c2399f
commit
cfe3bb7e99
90
css/color-picker.css
Normal file
90
css/color-picker.css
Normal file
@ -0,0 +1,90 @@
|
||||
/* Purty Picker Copyright 2013 Jayden Seric (MIT license): https://github.com/jaydenseric/Purty-Picker */
|
||||
/* Core: No touchy! */
|
||||
.color-picker .spectrum {
|
||||
position: relative;
|
||||
/* To position pin, luminosity filter */
|
||||
background: linear-gradient(gray, transparent), linear-gradient(90deg, red, #ff2b00, #ff5500, #ff8000, #ffaa00, #ffd500, yellow, #d4ff00, #aaff00, #80ff00, #55ff00, #2bff00, lime, #00ff2b, #00ff55, #00ff80, #00ffaa, #00ffd5, cyan, #00d4ff, #00aaff, #007fff, #0055ff, #002bff, blue, #2a00ff, #5500ff, #7f00ff, #aa00ff, #d400ff, magenta, #ff00d4, #ff00aa, #ff0080, #ff0055, #ff002b, red);
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
/* Prevent pin interaction causing content selection */
|
||||
cursor: crosshair;
|
||||
}
|
||||
.color-picker .spectrum.active {
|
||||
cursor: none;
|
||||
}
|
||||
.color-picker .spectrum.active .pin {
|
||||
cursor: none;
|
||||
}
|
||||
.color-picker .spectrum > div {
|
||||
/* Luminosity filter */
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.color-picker .spectrum .pin {
|
||||
position: absolute;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
/* Customization: Default skin */
|
||||
.color-picker {
|
||||
margin: 20px;
|
||||
padding: 11px;
|
||||
border: 1px solid #e3e3e3;
|
||||
border-radius: 4px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.color-picker .color,
|
||||
.color-picker .luminosity {
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
display: block;
|
||||
width: 100%;
|
||||
}
|
||||
.color-picker .format {
|
||||
display: block;
|
||||
margin: 0 auto 10px auto;
|
||||
}
|
||||
.color-picker .color {
|
||||
-webkit-appearance: none;
|
||||
border: 0;
|
||||
border-radius: 2px;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
font-size: 11px;
|
||||
letter-spacing: 1px;
|
||||
font-family: Consolas, Monaco, 'Andale Mono', monospace;
|
||||
color: rgba(0, 0, 0, 0.6);
|
||||
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2);
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.color-picker .color.dark {
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
.color-picker .spectrum {
|
||||
height: 150px;
|
||||
/* Arbitary but required */
|
||||
overflow: hidden;
|
||||
/* Prevent pin overflowing container */
|
||||
border-radius: 2px;
|
||||
margin: 10px 0;
|
||||
}
|
||||
.color-picker .spectrum > div {
|
||||
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.color-picker .spectrum .pin {
|
||||
margin-left: -4px;
|
||||
margin-top: -4px;
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
border: 2px solid white;
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.4);
|
||||
border-radius: 100%;
|
||||
}
|
||||
.color-picker .luminosity {
|
||||
margin: 0;
|
||||
}
|
BIN
css/imgs/clear.png
Normal file
BIN
css/imgs/clear.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.7 KiB |
BIN
css/imgs/redo.png
Normal file
BIN
css/imgs/redo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.3 KiB |
BIN
css/imgs/undo.png
Normal file
BIN
css/imgs/undo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
@ -65,7 +65,25 @@ button {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.picker {
|
||||
width: 30rem;
|
||||
height: 30rem;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin-top: -15rem;
|
||||
margin-left: -15rem;
|
||||
|
||||
#closePicker {
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
border: 1px solid rgb(227, 227, 227);
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
top: 1rem;
|
||||
left: 27rem;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
header {
|
||||
@ -117,12 +135,14 @@ header {
|
||||
color: white;
|
||||
font-family: 'MozTT-Light';
|
||||
font-size: 8pt;
|
||||
transition: left 0.2s ease-out;
|
||||
|
||||
&.pulled {
|
||||
left: 0;
|
||||
transition: left 0.2s ease-out;
|
||||
}
|
||||
|
||||
button[id^='set'], p {
|
||||
button[id^='set'], p, .bottom button {
|
||||
background: none;
|
||||
display: block;
|
||||
width: 75%;
|
||||
@ -132,6 +152,7 @@ header {
|
||||
font-family: 'MozTT-Light';
|
||||
font-size: 8pt;
|
||||
padding: 0 0.6rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
span {
|
||||
float: right;
|
||||
@ -150,13 +171,22 @@ header {
|
||||
}
|
||||
}
|
||||
}
|
||||
.icon-settings {
|
||||
hr {
|
||||
clear: both;
|
||||
padding: 0.7rem 0;
|
||||
margin-bottom: 0.7rem;
|
||||
border: none;
|
||||
border-bottom: 1px solid rgba(255,255,255,0.3);
|
||||
}
|
||||
*[class^='icon'] {
|
||||
display: block;
|
||||
margin: 1rem 0.5rem;
|
||||
|
||||
&:nth-of-type(2) {
|
||||
padding-top: 0.5rem;
|
||||
}
|
||||
&:before {
|
||||
content: '';
|
||||
background: url('imgs/settings.png') center center no-repeat;
|
||||
background-size: 2rem;
|
||||
width: 2rem;
|
||||
height: 2rem;
|
||||
@ -166,10 +196,30 @@ header {
|
||||
}
|
||||
}
|
||||
|
||||
.icon-settings:before {
|
||||
background-image: url('imgs/settings.png');
|
||||
}
|
||||
.icon-clear:before {
|
||||
background-image: url('imgs/clear.png');
|
||||
}
|
||||
.icon-undo:before {
|
||||
background-image: url('imgs/undo.png');
|
||||
}
|
||||
.icon-redo:before {
|
||||
background-image: url('imgs/redo.png');
|
||||
}
|
||||
|
||||
.options {
|
||||
display: block;
|
||||
margin-top: 1rem;
|
||||
}
|
||||
.bottom {
|
||||
position: absolute;
|
||||
bottom: 5rem;
|
||||
button {
|
||||
margin-left: 3.5rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -114,7 +114,7 @@ form[role="dialog"][data-type="value-selector"] [role="listbox"] li:active label
|
||||
}
|
||||
|
||||
/* Checked status */
|
||||
form[role="dialog"][data-type="value-selector"] [role="listbox"] li[aria-selected="true"] span {
|
||||
form[role="dialog"][data-type="value-selector"] [role="listbox"] li[aria-selected="true"]:not([data-input]) span {
|
||||
padding-right: 2.6rem;
|
||||
margin-right: 1.2rem;
|
||||
color: #00abcd;
|
||||
|
105
index.html
105
index.html
@ -8,6 +8,7 @@
|
||||
<link rel='stylesheet' href='css/switches.css'>
|
||||
<link rel='stylesheet' href='css/value_selector.css'>
|
||||
<link rel='stylesheet' href='css/seekbars.css'>
|
||||
<link rel='stylesheet' href='css/color-picker.css'>
|
||||
<script src='js/less-1.5.0.min.js'></script>
|
||||
<script src='js/libs/mobilebrowsers.js'></script>
|
||||
</head>
|
||||
@ -36,24 +37,59 @@
|
||||
<button id='setlineJoin'>Line Join<span>Round</span></button>
|
||||
<button id='setlineCap'>Line Cap<span>Round</span></button>
|
||||
<p id='lineWidth'>Line Width <span>2</span></p>
|
||||
<div role="slider" aria-valuemin="0" aria-valuenow="80" aria-valuemax="100" aria-valuetext="slider description">
|
||||
<div role="slider" class='lineWidth'>
|
||||
<div>
|
||||
<progress value="2" max="100"></progress>
|
||||
<button>handler</button>
|
||||
<progress value="2" min="1" max="100"></progress>
|
||||
<button style='left: 2%'>handler</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br />
|
||||
<hr>
|
||||
<div class='sketch'>
|
||||
<p class='icon-settings'>Sketch</p>
|
||||
<p id='connectTelorance'>Connet Telorance<span>40</span></p>
|
||||
<div role="slider" class='connectTelorance'>
|
||||
<div>
|
||||
<progress value="40" min="1" max="100"></progress>
|
||||
<button style='left: 40%'>handler</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='fur hidden'>
|
||||
<p class='icon-settings'>Fur</p>
|
||||
<p id='connectTelorance'>Connet Telorance<span>40</span></p>
|
||||
<div role="slider" class='connectTelorance'>
|
||||
<div>
|
||||
<progress value="40" min="10" max="100"></progress>
|
||||
<button style='left: 40%'>handler</button>
|
||||
</div>
|
||||
</div>
|
||||
<p id='furLength'>Fur Length<span>20</span></p>
|
||||
<div role="slider" class='furLength'>
|
||||
<div>
|
||||
<progress value="20" min="1" max="100"></progress>
|
||||
<button style='left: 20%'>handler</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class='bottom'>
|
||||
<button id='clear' class='icon-clear'>Clear</button>
|
||||
<button id='undo' class='icon-undo'>Undo</button>
|
||||
<button id='redo' class='icon-redo'>Redo</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form role='dialog' data-type='value-selector' id='type' class='hidden'>
|
||||
<form role='dialog' data-type='value-selector' id='type' class='hidden single'>
|
||||
<section class='scrollable'>
|
||||
<h1>Brush Type</h1>
|
||||
<ol role='listbox'>
|
||||
<li role='option' aria-selected='true'><label role='presentation'><span>Sketch</span></label></li>
|
||||
<li role='option'><label role='presentation'><span>Fur</span></label></li>
|
||||
<li role='option'><label role='presentation'><span>Pen</span></label></li>
|
||||
<li role='option'><label role='presentation'><span>Line</span></label></li>
|
||||
<li aria-selected='true'><label><span>Sketch</span></label></li>
|
||||
<li><label><span>Fur</span></label></li>
|
||||
<li><label><span>Pen</span></label></li>
|
||||
<li><label><span>Line</span></label></li>
|
||||
</ol>
|
||||
</section>
|
||||
<menu>
|
||||
@ -61,13 +97,13 @@
|
||||
</menu>
|
||||
</form>
|
||||
|
||||
<form role='dialog' data-type='value-selector' id='lineJoin' class='hidden'>
|
||||
<form role='dialog' data-type='value-selector' id='lineJoin' class='hidden single'>
|
||||
<section class='scrollable'>
|
||||
<h1>Line Join</h1>
|
||||
<ol role='listbox'>
|
||||
<li role='option' aria-selected='true'><label role='presentation'><span>Round</span></label></li>
|
||||
<li role='option'><label role='presentation'><span>Bevel</span></label></li>
|
||||
<li role='option'><label role='presentation'><span>Miter</span></label></li>
|
||||
<li aria-selected='true'><label><span>Round</span></label></li>
|
||||
<li><label><span>Bevel</span></label></li>
|
||||
<li><label><span>Miter</span></label></li>
|
||||
</ol>
|
||||
</section>
|
||||
<menu>
|
||||
@ -75,13 +111,13 @@
|
||||
</menu>
|
||||
</form>
|
||||
|
||||
<form role='dialog' data-type='value-selector' id='lineCap' class='hidden'>
|
||||
<form role='dialog' data-type='value-selector' id='lineCap' class='hidden single'>
|
||||
<section class='scrollable'>
|
||||
<h1>Line Cap</h1>
|
||||
<ol role='listbox'>
|
||||
<li role='option' aria-selected='true'><label role='presentation'><span>Round</span></label></li>
|
||||
<li role='option'><label role='presentation'><span>Square</span></label></li>
|
||||
<li role='option'><label role='presentation'><span>Butt</span></label></li>
|
||||
<li aria-selected='true'><label><span>Round</span></label></li>
|
||||
<li><label><span>Square</span></label></li>
|
||||
<li><label><span>Butt</span></label></li>
|
||||
</ol>
|
||||
</section>
|
||||
<menu>
|
||||
@ -89,8 +125,45 @@
|
||||
</menu>
|
||||
</form>
|
||||
|
||||
<form role='dialog' data-type='value-selector' id='save' class='hidden confirm'>
|
||||
<section class='scrollable'>
|
||||
<h1>Save</h1>
|
||||
<h1>Background</h1>
|
||||
<ol role='listbox'>
|
||||
<li aria-selected='true'><label><span>White</span></label></li>
|
||||
<li><label><span>Current Color</span></label></li>
|
||||
<li><label><span>Transparent</span></label></li>
|
||||
</ol>
|
||||
<h1>File Name</h1>
|
||||
<ol role='listbox'>
|
||||
<li aria-selected='true' data-input><label><span contenteditable>My Sketch</span></label></li>
|
||||
</ol>
|
||||
</section>
|
||||
<menu>
|
||||
<button class='affirmative' type='button'>Cancel</button>
|
||||
<button class='affirmative' type='button'>Save</button>
|
||||
</menu>
|
||||
</form>
|
||||
|
||||
|
||||
<!-- COLOR PICKER -->
|
||||
<div class='picker overlay hidden'>
|
||||
<div class='color-picker'>
|
||||
<button id='closePicker'>×</button>
|
||||
<input value='Hex' class='format' type='hidden' selected>
|
||||
<input class='color' val='#000000'>
|
||||
<div class="spectrum">
|
||||
<div>
|
||||
<div class="pin"></div>
|
||||
</div>
|
||||
</div>
|
||||
<input class="luminosity" type="range" min="0" max="100" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src='js/libs/yepnope.min.js'></script>
|
||||
<script src='js/libs/zepto.min.js'></script>
|
||||
<script src='js/libs/touch.js'></script>
|
||||
<script src='js/functions.js'></script>
|
||||
<script src='js/main.js'></script>
|
||||
|
||||
|
145
js/events.js
145
js/events.js
@ -1,22 +1,56 @@
|
||||
"use strict";
|
||||
|
||||
function save() {
|
||||
switch(save.background) {
|
||||
case 'white': {
|
||||
var cache = {
|
||||
color: c.color,
|
||||
composite: c.globalCompositeOperation
|
||||
}
|
||||
c.fillStyle = 'white';
|
||||
c.globalCompositeOperation = 'destination-over';
|
||||
c.fillRect(0, 0, $c.width(), $c.height());
|
||||
c.fillStyle = cache.fillStyle;
|
||||
c.globalCompositeOperation = cache.composite;
|
||||
break;
|
||||
}
|
||||
case 'current color': {
|
||||
var cache = {
|
||||
fillStyle: c.color,
|
||||
composite: c.globalCompositeOperation
|
||||
}
|
||||
c.fillStyle = settings.strokeStyle;
|
||||
c.globalCompositeOperation = 'destination-over';
|
||||
c.fillRect(0, 0, $c.width(), $c.height());
|
||||
c.fillStyle = cache.fillStyle;
|
||||
c.globalCompositeOperation = cache.composite;
|
||||
break;
|
||||
}
|
||||
}
|
||||
var data = $c[0].toDataURL();
|
||||
window.open(data, save['file name']);
|
||||
|
||||
c.putImageData(window.points.history[window.points.history.length-1].data, 0, 0);
|
||||
|
||||
}
|
||||
|
||||
$('.menu').click(function() {
|
||||
$('#menu').toggleClass('pulled');
|
||||
})
|
||||
$('.save').click(function() {
|
||||
var data = $c[0].toDataURL();
|
||||
var img = $('<img src="' + data + '" width="' + $c.width() + '" height="' + $c.height() + '" class="overlay"/>');
|
||||
$('body').append(img);
|
||||
$('#save').removeClass('hidden');
|
||||
})
|
||||
$c.last().bind('mousedown', function(e) {
|
||||
$c.last().on('mousedown', function(e) {
|
||||
e.preventDefault();
|
||||
var xy = relative(e.pageX, e.pageY);
|
||||
startPoint(xy.x, xy.y);
|
||||
window.active = true;
|
||||
}).bind('mousemove', function(e) {
|
||||
}).on('mousemove', function(e) {
|
||||
e.preventDefault();
|
||||
if (!window.active || settings.type == 'line') return;
|
||||
var xy = relative(e.pageX, e.pageY);
|
||||
drawPoint(xy.x, xy.y);
|
||||
}).bind('mouseup mouseout', function(e) {
|
||||
}).on('mouseup', function(e) {
|
||||
e.preventDefault();
|
||||
window.active = false;
|
||||
|
||||
@ -33,32 +67,68 @@
|
||||
|
||||
// Value Selector
|
||||
|
||||
var $selector = $('form[data-type="value-selector"]');
|
||||
// Single
|
||||
|
||||
$selector.find('li').on('mouseup', function(e) {
|
||||
alert(e.type);
|
||||
$(this).parent().find('li').removeAttr('aria-selected');
|
||||
var $single = $('form[data-type="value-selector"].single');
|
||||
|
||||
$single.find('li').click(function(e) {
|
||||
$(this).parent().find('li[aria-selected]').removeAttr('aria-selected');
|
||||
$(this).attr('aria-selected', 'true');
|
||||
var prop = $(this).parents('form').attr('id'),
|
||||
key = $(this).find('label span').html().toLowerCase();
|
||||
window.settings[prop] = key;
|
||||
var key = $(this).parents('form').attr('id'),
|
||||
value = $(this).find('label span').html().toLowerCase();
|
||||
window.settings[key] = value;
|
||||
|
||||
$('button[id="set' + prop + '"] span').html(key[0].toUpperCase() + key.substr(1));
|
||||
$('button[id="set' + key + '"] span').html(value[0].toUpperCase() + value.substr(1));
|
||||
$('#menu div.options > div').addClass('hidden');
|
||||
$('#menu div.options > .general, #menu div.options > .'+value).removeClass('hidden');
|
||||
|
||||
$(this).parents('form').addClass('hidden');
|
||||
})
|
||||
|
||||
$selector.submit(function(e) {
|
||||
$single.submit(function(e) {
|
||||
e.preventDefault();
|
||||
$(this).addClass('hidden');
|
||||
})
|
||||
|
||||
// Confirm
|
||||
|
||||
var $confirm = $('form[data-type="value-selector"].confirm');
|
||||
|
||||
$confirm.find('li').click(function(e) {
|
||||
$(this).parent().find('li[aria-selected]').removeAttr('aria-selected');
|
||||
$(this).attr('aria-selected', 'true');
|
||||
})
|
||||
$confirm.find('button').last().click(function(e) {
|
||||
e.preventDefault();
|
||||
var v = $(this).parents('form').attr('id');
|
||||
$(this).parents('form').find('h1').each(function(i) {
|
||||
if( i > 0 ) {
|
||||
var key = $(this).html().toLowerCase();
|
||||
var value = $(this).parent().find('ol:nth-of-type('+i+') li[aria-selected] span').html();
|
||||
if( key !== 'file name' ) value = key.toLowerCase();
|
||||
|
||||
window[v][key] = value;
|
||||
}
|
||||
})
|
||||
$(this).parents('form').addClass('hidden');
|
||||
window[v]();
|
||||
})
|
||||
$confirm.find('button').first().click(function(e) {
|
||||
e.preventDefault();
|
||||
$(this).parents('form').addClass('hidden');
|
||||
})
|
||||
|
||||
// Value Selector Callers
|
||||
|
||||
var $btn = $('button[id^="set"]');
|
||||
$btn.each(function() {
|
||||
var target = /set(.*)/.exec($(this).attr('id'))[1];
|
||||
$(this).on('mouseup', function(e) {
|
||||
// Exception for Color
|
||||
if( target == 'color' ) {
|
||||
return $(this).click(function() {
|
||||
$('.picker').removeClass('hidden');
|
||||
})
|
||||
}
|
||||
$(this).click(function(e) {
|
||||
e.preventDefault();
|
||||
$('form[id="' + target + '"]').removeClass('hidden');
|
||||
})
|
||||
@ -66,19 +136,50 @@
|
||||
|
||||
// Seekbar
|
||||
|
||||
var sliderLeft = $('div[role="slider"] button').offset().left;
|
||||
var sliderLeft;
|
||||
$('div[role="slider"] button').on('mousedown', function() {
|
||||
$(this).attr('data-moving','true');
|
||||
if( !sliderLeft ) sliderLeft = $('div[role="slider"] button').offset().left;
|
||||
}).on('mousemove', function(e) {
|
||||
if( $(this).attr('data-moving') ) {
|
||||
var x = e.pageX - sliderLeft - 15;
|
||||
if( x <= 100 && x > 0 ) {
|
||||
var x = parseInt(e.pageX - sliderLeft - 15);
|
||||
var progress = $(this).parent().children().first();
|
||||
var max = +progress.attr('max');
|
||||
var min = +progress.attr('min');
|
||||
if( x <= max && x >= min ) {
|
||||
$(this).css('left', x+'%');
|
||||
$(this).parent().find('progress').attr('value', x);
|
||||
settings.lineWidth = x / 10;
|
||||
$('#lineWidth span').html(x);
|
||||
var key = $(this).parents('div[role="slider"]').attr('class');
|
||||
settings[key] = x;
|
||||
$('#'+ key +' span').html(x);
|
||||
}
|
||||
}
|
||||
}).on('mouseup mouseleave', function() {
|
||||
$(this).removeAttr('data-moving');
|
||||
})
|
||||
|
||||
|
||||
// Color Picker
|
||||
|
||||
$('#closePicker').click(function() {
|
||||
$('.picker').addClass('hidden');
|
||||
})
|
||||
|
||||
// Bottom
|
||||
|
||||
$('#clear').click(function() {
|
||||
c.clearRect(0, 0, $c.width(), $c.height());
|
||||
if(window.points.history.last < window.points.history.length-1) {
|
||||
window.points.history.splice(window.points.history.last+1);
|
||||
}
|
||||
|
||||
window.points.history.push({
|
||||
data: c.getImageData(0, 0, $c.width(), $c.height()),
|
||||
points: []
|
||||
})
|
||||
window.points.history.last = window.points.history.length-1;
|
||||
})
|
||||
|
||||
$('#undo').click(undo);
|
||||
$('#redo').click(redo);
|
||||
|
||||
|
@ -6,16 +6,20 @@ function sizeAndPos() {
|
||||
var data = c.getImageData(0,0, $c.width(), $c.height());
|
||||
var w = $(window).width(),
|
||||
h = $(window).height();
|
||||
$c.attr('width', w);
|
||||
$c.attr('height',h - 53);
|
||||
$c.attr('width', w * window.devicePixelRatio);
|
||||
$c.attr('height',h * window.devicePixelRatio - 53 * window.devicePixelRatio);
|
||||
$c.css({
|
||||
'width' : w,
|
||||
'height' : h - 53
|
||||
});
|
||||
c.clearRect(0,0, $c.width(), $c.height());
|
||||
c.putImageData(data, 0, 0);
|
||||
}
|
||||
|
||||
function relative(x,y) {
|
||||
return {
|
||||
x : x - $c.offset().left,
|
||||
y : y - $c.offset().top
|
||||
x : x*window.devicePixelRatio,
|
||||
y : y*window.devicePixelRatio - 53 * window.devicePixelRatio
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,11 +36,13 @@ function line(x1, y1, x2, y2, opts, overlay) {
|
||||
c.beginPath();
|
||||
c.lineCap = opts.lineCap || settings.lineCap;
|
||||
c.lineJoin = opts.lineJoin || settings.lineJoin;
|
||||
c.strokeStyle = opts.strokeStyle || settings.strokeStyle;
|
||||
c.lineWidth = opts.lineWidth || settings.lineWidth;
|
||||
c.strokeStyle = opts.color || settings.color;
|
||||
c.fillStyle = opts.color || settings.color;
|
||||
c.lineWidth = ( opts.lineWidth || settings.lineWidth ) / 10;
|
||||
c.moveTo(x1, y1);
|
||||
c.lineTo(x2, y2);
|
||||
c.stroke();
|
||||
if( !opts.noStroke ) c.stroke();
|
||||
if( opts.fill ) c.fill();
|
||||
}
|
||||
|
||||
function erase(x1, y1, x2, y2, overlay) {
|
||||
@ -73,6 +79,13 @@ function redo() {
|
||||
}
|
||||
}
|
||||
|
||||
function dataToBlob(data) {
|
||||
var binary = atob(data.split(',')[1]), array = [];
|
||||
var type = data.split(',')[0].split(':')[1].split(';')[0];
|
||||
for(var i = 0; i < binary.length; i++) array.push(binary.charCodeAt(i));
|
||||
return new Blob([new Uint8Array(array)], {type: type});
|
||||
}
|
||||
|
||||
|
||||
/*** END ***/
|
||||
|
||||
@ -90,7 +103,10 @@ function startPoint(x, y) {
|
||||
type : settings.type
|
||||
}
|
||||
if( old.type !== 'line' && current.type == 'line' ) {
|
||||
line(x,y,x,y, {lineWidth: 5, strokeStyle: 'red'}, true);
|
||||
window.o.beginPath();
|
||||
window.o.fillStyle = 'red';
|
||||
window.o.arc(x,y, 3, 0, 2*Math.PI);
|
||||
window.o.fill();
|
||||
}
|
||||
|
||||
if( old.type == 'line' ) {
|
||||
@ -135,11 +151,11 @@ function drawPoint(x,y) {
|
||||
points.push(current);
|
||||
|
||||
for( var i = 0, len = points.length-1; i < len; i++ ) {
|
||||
if(threshold(points[i].x, points[i].y, current.x, current.y, 40)) {
|
||||
if(threshold(points[i].x, points[i].y, current.x, current.y, settings.connectTelorance)) {
|
||||
var x = points[i].x - current.x,
|
||||
y = points[i].y - current.y;
|
||||
|
||||
line(points[i].x - x*0.2, points[i].y - y*0.2, current.x + x*0.2, current.y + y*0.2, {strokeStyle: 'rgba(0,0,0,0.4)', lineWidth: settings.lineWidth/2})
|
||||
line(points[i].x - x*0.2, points[i].y - y*0.2, current.x + x*0.2, current.y + y*0.2, {strokeStyle: 'rgba(0,0,0,0.4)', lineWidth: settings.lineWidth/20})
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -155,10 +171,10 @@ function drawPoint(x,y) {
|
||||
points.push(current);
|
||||
|
||||
for( var i = 0, len = points.length-1; i < len; i++ ) {
|
||||
if(threshold(points[i].x, points[i].y, current.x, current.y, settings.lineWidth*20)) {
|
||||
if(threshold(points[i].x, points[i].y, current.x, current.y, settings.connectTelorance)) {
|
||||
var x = points[i].x - current.x,
|
||||
y = points[i].y - current.y;
|
||||
var l = settings.furLength || 0.2;
|
||||
var l = settings.furLength / 100 || 0.2;
|
||||
line(points[i].x + x*l, points[i].y + y*l, current.x - x*l, current.y - y*l, {strokeStyle: 'rgba(0,0,0,0.4)', lineWidth: settings.lineWidth/2})
|
||||
}
|
||||
}
|
||||
|
290
js/libs/color-picker-touch.js
Normal file
290
js/libs/color-picker-touch.js
Normal file
@ -0,0 +1,290 @@
|
||||
/*
|
||||
|
||||
Purty Picker Copyright 2013 Jayden Seric (MIT license).
|
||||
|
||||
A super lightweight visual HSL, RGB and hex color picker with a responsive, touch-friendly and customizable UI.
|
||||
|
||||
Requires jQuery or Zepto with core and event modules.
|
||||
|
||||
https://github.com/jaydenseric/Purty-Picker
|
||||
|
||||
*/
|
||||
|
||||
// DOM ready
|
||||
$(function() {
|
||||
'use strict';
|
||||
|
||||
//-------------------------------------------- Color conversions
|
||||
|
||||
//---------------------- Convert HSL to RGB
|
||||
|
||||
// Source: http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
|
||||
|
||||
function HSLToRGB(h, s, l) {
|
||||
h /= 360;
|
||||
s /= 100;
|
||||
l /= 100;
|
||||
var r, g, b;
|
||||
if(s == 0) {
|
||||
r = g = b = l; // Achromatic
|
||||
} else {
|
||||
var hueToRGB = function(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 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 {
|
||||
red: Math.round(r * 255),
|
||||
green: Math.round(g * 255),
|
||||
blue: Math.round(b * 255)
|
||||
};
|
||||
}
|
||||
|
||||
//---------------------- Convert RGB to HSL
|
||||
|
||||
// Source: http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
|
||||
|
||||
function RGBToHSL(r, g, b) {
|
||||
r /= 255,
|
||||
g /= 255,
|
||||
b /= 255;
|
||||
var max = Math.max(r, g, b),
|
||||
min = Math.min(r, g, b);
|
||||
var h, s, l = (max + min) / 2;
|
||||
if (max == min) {
|
||||
h = s = 0; // Achromatic
|
||||
} else {
|
||||
var d = max - min;
|
||||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||||
switch(max) {
|
||||
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
||||
case g: h = (b - r) / d + 2; break;
|
||||
case b: h = (r - g) / d + 4; break;
|
||||
}
|
||||
h /= 6;
|
||||
}
|
||||
return {
|
||||
hue: Math.round(h * 360),
|
||||
saturation: Math.round(s * 100),
|
||||
luminosity: Math.round(l * 100)
|
||||
};
|
||||
}
|
||||
|
||||
//---------------------- Convert RGB to Hex
|
||||
|
||||
// Source: http://stackoverflow.com/a/5624139
|
||||
|
||||
function RGBToHex(r, g, b) {
|
||||
return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
|
||||
}
|
||||
|
||||
//---------------------- Convert hex to RGB
|
||||
|
||||
// Source: http://stackoverflow.com/a/11508164
|
||||
|
||||
function hexToRGB(hex) {
|
||||
var bigInt = parseInt(hex.replace('#', ''), 16),
|
||||
r = (bigInt >> 16) & 255,
|
||||
g = (bigInt >> 8) & 255,
|
||||
b = bigInt & 255;
|
||||
return {
|
||||
red: r,
|
||||
green: g,
|
||||
blue: b
|
||||
};
|
||||
}
|
||||
|
||||
//---------------------- Convert hex to HSL
|
||||
|
||||
function hexToHSL(hex) {
|
||||
var RGB = hexToRGB(hex);
|
||||
return RGBToHSL(RGB.red, RGB.green, RGB.blue);
|
||||
}
|
||||
|
||||
//---------------------- Convert HSL to hex
|
||||
|
||||
function HSLToHex(h, s, l) {
|
||||
var RGB = HSLToRGB(h, s, l);
|
||||
return RGBToHex(RGB.red, RGB.green, RGB.blue);
|
||||
}
|
||||
|
||||
//-------------------------------------------- Setup each color picker
|
||||
|
||||
$.each($('.color-picker'), function() {
|
||||
|
||||
//---------------------- Find componenets
|
||||
|
||||
var picker = $(this),
|
||||
formatInput = picker.find('.format'),
|
||||
colorInput = picker.find('.color'),
|
||||
luminosityInput = picker.find('input[type=range]'),
|
||||
spectrum = picker.find('.spectrum'),
|
||||
pin = picker.find('.pin');
|
||||
|
||||
//---------------------- Get current color in HSL
|
||||
|
||||
function getHSL() {
|
||||
var position = picker.find('.pin').position(),
|
||||
width = spectrum.width(),
|
||||
height = spectrum.height();
|
||||
return {
|
||||
hue: Math.round(position.left / width * 360),
|
||||
saturation: Math.round(position.top / height * 100),
|
||||
luminosity: luminosityInput.val()
|
||||
};
|
||||
}
|
||||
|
||||
//---------------------- Output color in desired format
|
||||
|
||||
function updateColorInput() {
|
||||
var HSL = getHSL();
|
||||
switch (formatInput.val()) {
|
||||
case 'HSL':
|
||||
colorInput.val('hsl(' + HSL.hue + ', ' + HSL.saturation + '%, ' + HSL.luminosity + '%)');
|
||||
break;
|
||||
case 'RGB':
|
||||
var RGB = HSLToRGB(HSL.hue, HSL.saturation, HSL.luminosity);
|
||||
colorInput.val('rgb(' + RGB.red + ', ' + RGB.green + ', ' + RGB.blue + ')');
|
||||
break;
|
||||
case 'Hex':
|
||||
colorInput.val(HSLToHex(HSL.hue, HSL.saturation, HSL.luminosity));
|
||||
break;
|
||||
}
|
||||
// Trigger color picker change event for custom callbacks
|
||||
picker.trigger('change');
|
||||
}
|
||||
|
||||
//---------------------- Set color format
|
||||
|
||||
formatInput.on('change', function() {
|
||||
updateColorInput();
|
||||
});
|
||||
|
||||
//---------------------- Set color
|
||||
|
||||
colorInput.on('change', function() {
|
||||
// Get the color values in HSL format
|
||||
var HSL;
|
||||
switch (formatInput.val()) {
|
||||
case 'HSL':
|
||||
var values = $(this).val().match(/\d+/g);
|
||||
HSL = {
|
||||
hue: values[0],
|
||||
saturation: values[1],
|
||||
luminosity: values[2]
|
||||
};
|
||||
break;
|
||||
case 'RGB':
|
||||
var values = $(this).val().match(/\d+/g);
|
||||
HSL = RGBToHSL(values[0], values[1], values[2]);
|
||||
break;
|
||||
case 'Hex':
|
||||
HSL = hexToHSL($(this).val());
|
||||
break;
|
||||
}
|
||||
// Set the luminosity
|
||||
luminosityInput.val(HSL.luminosity);
|
||||
setLuminosity(HSL.luminosity);
|
||||
// Place the pin
|
||||
pin.css({
|
||||
left: HSL.hue / 360 * 100 + '%',
|
||||
top: HSL.saturation + '%'
|
||||
});
|
||||
// Trigger color picker change event for custom callbacks
|
||||
picker.trigger('change');
|
||||
});
|
||||
|
||||
//---------------------- Set luminosity
|
||||
|
||||
//---------- Set the luminosity spectrum overlay
|
||||
|
||||
function setLuminosity(luminosity) {
|
||||
var color,
|
||||
alpha;
|
||||
if (luminosity <= 50) {
|
||||
color = '0, 0, 0';
|
||||
alpha = 1 - luminosity / 100 * 2;
|
||||
} else {
|
||||
color = '255, 255, 255';
|
||||
alpha = luminosity / 100 * 2 - 1;
|
||||
}
|
||||
// Apply luminosity to the spectrum
|
||||
spectrum.children().css('background-color', 'rgba(' + color + ', ' + alpha + ')');
|
||||
}
|
||||
|
||||
//---------- Luminosity input interaction
|
||||
|
||||
luminosityInput.on('change', function() {
|
||||
setLuminosity($(this).val());
|
||||
updateColorInput();
|
||||
});
|
||||
|
||||
//---------------------- Set hue, saturation via pin
|
||||
|
||||
//---------- Move the pin
|
||||
|
||||
var movePin = function(event) {
|
||||
var offset = spectrum.offset(),
|
||||
width = spectrum.width(),
|
||||
height = spectrum.height(),
|
||||
x = event.changedTouches[0].clientX - offset.left,
|
||||
y = event.changedTouches[0].clientY - offset.top;
|
||||
// Account for pin being dragged outside the spectrum area
|
||||
// Sanatize x
|
||||
if (x < 0) {
|
||||
x = 0;
|
||||
} else if (x >= width) {
|
||||
x = width;
|
||||
}
|
||||
// Sanatize y
|
||||
if (y < 0) {
|
||||
y = 0;
|
||||
} else if (y >= height) {
|
||||
y = height;
|
||||
}
|
||||
// Place the pin
|
||||
pin.css({
|
||||
left: x / width * 100 + '%',
|
||||
top: y / height * 100 + '%'
|
||||
});
|
||||
// Output new color value
|
||||
updateColorInput();
|
||||
};
|
||||
|
||||
//---------- Pin interaction
|
||||
|
||||
spectrum.on('touchstart', function(event) {
|
||||
event.preventDefault();
|
||||
movePin(event);
|
||||
spectrum.addClass('active');
|
||||
$(document).on('touchmove', movePin);
|
||||
});
|
||||
|
||||
$(document).on('touchend', function() {
|
||||
spectrum.removeClass('active');
|
||||
$(document).off('touchmove', movePin);
|
||||
});
|
||||
|
||||
spectrum.on('touchmove touchstart', movePin);
|
||||
|
||||
//---------------------- Output color preview
|
||||
|
||||
picker.on('change', function() {
|
||||
colorInput.css('background-color', colorInput.val()).toggleClass('dark', luminosityInput.val() <= 50);
|
||||
});
|
||||
|
||||
//---------------------- Initialize this color picker
|
||||
|
||||
colorInput.trigger('change');
|
||||
|
||||
});
|
||||
});
|
289
js/libs/color-picker.js
Normal file
289
js/libs/color-picker.js
Normal file
@ -0,0 +1,289 @@
|
||||
/*
|
||||
|
||||
Purty Picker Copyright 2013 Jayden Seric (MIT license).
|
||||
|
||||
A super lightweight visual HSL, RGB and hex color picker with a responsive, touch-friendly and customizable UI.
|
||||
|
||||
Requires jQuery or Zepto with core and event modules.
|
||||
|
||||
https://github.com/jaydenseric/Purty-Picker
|
||||
|
||||
*/
|
||||
|
||||
// DOM ready
|
||||
$(function() {
|
||||
'use strict';
|
||||
|
||||
//-------------------------------------------- Color conversions
|
||||
|
||||
//---------------------- Convert HSL to RGB
|
||||
|
||||
// Source: http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
|
||||
|
||||
function HSLToRGB(h, s, l) {
|
||||
h /= 360;
|
||||
s /= 100;
|
||||
l /= 100;
|
||||
var r, g, b;
|
||||
if(s == 0) {
|
||||
r = g = b = l; // Achromatic
|
||||
} else {
|
||||
var hueToRGB = function(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 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 {
|
||||
red: Math.round(r * 255),
|
||||
green: Math.round(g * 255),
|
||||
blue: Math.round(b * 255)
|
||||
};
|
||||
}
|
||||
|
||||
//---------------------- Convert RGB to HSL
|
||||
|
||||
// Source: http://axonflux.com/handy-rgb-to-hsl-and-rgb-to-hsv-color-model-c
|
||||
|
||||
function RGBToHSL(r, g, b) {
|
||||
r /= 255,
|
||||
g /= 255,
|
||||
b /= 255;
|
||||
var max = Math.max(r, g, b),
|
||||
min = Math.min(r, g, b);
|
||||
var h, s, l = (max + min) / 2;
|
||||
if (max == min) {
|
||||
h = s = 0; // Achromatic
|
||||
} else {
|
||||
var d = max - min;
|
||||
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
|
||||
switch(max) {
|
||||
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
|
||||
case g: h = (b - r) / d + 2; break;
|
||||
case b: h = (r - g) / d + 4; break;
|
||||
}
|
||||
h /= 6;
|
||||
}
|
||||
return {
|
||||
hue: Math.round(h * 360),
|
||||
saturation: Math.round(s * 100),
|
||||
luminosity: Math.round(l * 100)
|
||||
};
|
||||
}
|
||||
|
||||
//---------------------- Convert RGB to Hex
|
||||
|
||||
// Source: http://stackoverflow.com/a/5624139
|
||||
|
||||
function RGBToHex(r, g, b) {
|
||||
return '#' + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
|
||||
}
|
||||
|
||||
//---------------------- Convert hex to RGB
|
||||
|
||||
// Source: http://stackoverflow.com/a/11508164
|
||||
|
||||
function hexToRGB(hex) {
|
||||
var bigInt = parseInt(hex.replace('#', ''), 16),
|
||||
r = (bigInt >> 16) & 255,
|
||||
g = (bigInt >> 8) & 255,
|
||||
b = bigInt & 255;
|
||||
return {
|
||||
red: r,
|
||||
green: g,
|
||||
blue: b
|
||||
};
|
||||
}
|
||||
|
||||
//---------------------- Convert hex to HSL
|
||||
|
||||
function hexToHSL(hex) {
|
||||
var RGB = hexToRGB(hex);
|
||||
return RGBToHSL(RGB.red, RGB.green, RGB.blue);
|
||||
}
|
||||
|
||||
//---------------------- Convert HSL to hex
|
||||
|
||||
function HSLToHex(h, s, l) {
|
||||
var RGB = HSLToRGB(h, s, l);
|
||||
return RGBToHex(RGB.red, RGB.green, RGB.blue);
|
||||
}
|
||||
|
||||
//-------------------------------------------- Setup each color picker
|
||||
|
||||
$.each($('.color-picker'), function() {
|
||||
|
||||
//---------------------- Find componenets
|
||||
|
||||
var picker = $(this),
|
||||
formatInput = picker.find('.format'),
|
||||
colorInput = picker.find('.color'),
|
||||
luminosityInput = picker.find('input[type=range]'),
|
||||
spectrum = picker.find('.spectrum'),
|
||||
pin = picker.find('.pin');
|
||||
|
||||
//---------------------- Get current color in HSL
|
||||
|
||||
function getHSL() {
|
||||
var position = picker.find('.pin').position(),
|
||||
width = spectrum.width(),
|
||||
height = spectrum.height();
|
||||
return {
|
||||
hue: Math.round(position.left / width * 360),
|
||||
saturation: Math.round(position.top / height * 100),
|
||||
luminosity: luminosityInput.val()
|
||||
};
|
||||
}
|
||||
|
||||
//---------------------- Output color in desired format
|
||||
|
||||
function updateColorInput() {
|
||||
var HSL = getHSL();
|
||||
switch (formatInput.val()) {
|
||||
case 'HSL':
|
||||
colorInput.val('hsl(' + HSL.hue + ', ' + HSL.saturation + '%, ' + HSL.luminosity + '%)');
|
||||
break;
|
||||
case 'RGB':
|
||||
var RGB = HSLToRGB(HSL.hue, HSL.saturation, HSL.luminosity);
|
||||
colorInput.val('rgb(' + RGB.red + ', ' + RGB.green + ', ' + RGB.blue + ')');
|
||||
break;
|
||||
case 'Hex':
|
||||
colorInput.val(HSLToHex(HSL.hue, HSL.saturation, HSL.luminosity));
|
||||
break;
|
||||
}
|
||||
// Trigger color picker change event for custom callbacks
|
||||
picker.trigger('change');
|
||||
}
|
||||
|
||||
//---------------------- Set color format
|
||||
|
||||
formatInput.on('change', function() {
|
||||
updateColorInput();
|
||||
});
|
||||
|
||||
//---------------------- Set color
|
||||
|
||||
colorInput.on('change', function() {
|
||||
// Get the color values in HSL format
|
||||
var HSL;
|
||||
switch (formatInput.val()) {
|
||||
case 'HSL':
|
||||
var values = $(this).val().match(/\d+/g);
|
||||
HSL = {
|
||||
hue: values[0],
|
||||
saturation: values[1],
|
||||
luminosity: values[2]
|
||||
};
|
||||
break;
|
||||
case 'RGB':
|
||||
var values = $(this).val().match(/\d+/g);
|
||||
HSL = RGBToHSL(values[0], values[1], values[2]);
|
||||
break;
|
||||
case 'Hex':
|
||||
HSL = hexToHSL($(this).val());
|
||||
break;
|
||||
}
|
||||
// Set the luminosity
|
||||
luminosityInput.val(HSL.luminosity);
|
||||
setLuminosity(HSL.luminosity);
|
||||
// Place the pin
|
||||
pin.css({
|
||||
left: HSL.hue / 360 * 100 + '%',
|
||||
top: HSL.saturation + '%'
|
||||
});
|
||||
// Trigger color picker change event for custom callbacks
|
||||
picker.trigger('change');
|
||||
});
|
||||
|
||||
//---------------------- Set luminosity
|
||||
|
||||
//---------- Set the luminosity spectrum overlay
|
||||
|
||||
function setLuminosity(luminosity) {
|
||||
var color,
|
||||
alpha;
|
||||
if (luminosity <= 50) {
|
||||
color = '0, 0, 0';
|
||||
alpha = 1 - luminosity / 100 * 2;
|
||||
} else {
|
||||
color = '255, 255, 255';
|
||||
alpha = luminosity / 100 * 2 - 1;
|
||||
}
|
||||
// Apply luminosity to the spectrum
|
||||
spectrum.children().css('background-color', 'rgba(' + color + ', ' + alpha + ')');
|
||||
}
|
||||
|
||||
//---------- Luminosity input interaction
|
||||
|
||||
luminosityInput.on('change', function() {
|
||||
setLuminosity($(this).val());
|
||||
updateColorInput();
|
||||
});
|
||||
|
||||
//---------------------- Set hue, saturation via pin
|
||||
|
||||
//---------- Move the pin
|
||||
|
||||
var movePin = function(event) {
|
||||
var offset = spectrum.offset(),
|
||||
width = spectrum.width(),
|
||||
height = spectrum.height(),
|
||||
x = event.clientX - offset.left,
|
||||
y = event.clientY - offset.top;
|
||||
// Account for pin being dragged outside the spectrum area
|
||||
// Sanatize x
|
||||
if (x < 0) {
|
||||
x = 0;
|
||||
} else if (x >= width) {
|
||||
x = width;
|
||||
}
|
||||
// Sanatize y
|
||||
if (y < 0) {
|
||||
y = 0;
|
||||
} else if (y >= height) {
|
||||
y = height;
|
||||
}
|
||||
// Place the pin
|
||||
pin.css({
|
||||
left: x / width * 100 + '%',
|
||||
top: y / height * 100 + '%'
|
||||
});
|
||||
// Output new color value
|
||||
updateColorInput();
|
||||
};
|
||||
|
||||
//---------- Pin interaction
|
||||
|
||||
spectrum.on('mousedown', function(event) {
|
||||
event.preventDefault();
|
||||
movePin(event);
|
||||
spectrum.addClass('active');
|
||||
$(document).on('mousemove', movePin);
|
||||
});
|
||||
|
||||
$(document).on('mouseup', function() {
|
||||
spectrum.removeClass('active');
|
||||
$(document).off('mousemove', movePin);
|
||||
});
|
||||
|
||||
spectrum.on('touchmove touchstart', movePin);
|
||||
|
||||
//---------------------- Output color preview
|
||||
|
||||
picker.on('change', function() {
|
||||
colorInput.css('background-color', colorInput.val()).toggleClass('dark', luminosityInput.val() <= 50);
|
||||
});
|
||||
|
||||
//---------------------- Initialize this color picker
|
||||
|
||||
colorInput.trigger('change');
|
||||
});
|
||||
});
|
22
js/libs/stack.js
Normal file
22
js/libs/stack.js
Normal file
@ -0,0 +1,22 @@
|
||||
// Zepto.js
|
||||
// (c) 2010-2014 Thomas Fuchs
|
||||
// Zepto.js may be freely distributed under the MIT license.
|
||||
|
||||
;(function($){
|
||||
$.fn.end = function(){
|
||||
return this.prevObject || $()
|
||||
}
|
||||
|
||||
$.fn.andSelf = function(){
|
||||
return this.add(this.prevObject || $())
|
||||
}
|
||||
|
||||
'filter,add,not,eq,first,last,find,closest,parents,parent,children,siblings'.split(',').forEach(function(property){
|
||||
var fn = $.fn[property]
|
||||
$.fn[property] = function(){
|
||||
var ret = fn.apply(this, arguments)
|
||||
ret.prevObject = this
|
||||
return ret
|
||||
}
|
||||
})
|
||||
})(Zepto)
|
165
js/libs/touch.js
Normal file
165
js/libs/touch.js
Normal file
@ -0,0 +1,165 @@
|
||||
// Zepto.js
|
||||
// (c) 2010-2014 Thomas Fuchs
|
||||
// Zepto.js may be freely distributed under the MIT license.
|
||||
|
||||
;(function($){
|
||||
var touch = {},
|
||||
touchTimeout, tapTimeout, swipeTimeout, longTapTimeout,
|
||||
longTapDelay = 750,
|
||||
gesture
|
||||
|
||||
function swipeDirection(x1, x2, y1, y2) {
|
||||
return Math.abs(x1 - x2) >=
|
||||
Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down')
|
||||
}
|
||||
|
||||
function longTap() {
|
||||
longTapTimeout = null
|
||||
if (touch.last) {
|
||||
touch.el.trigger('longTap')
|
||||
touch = {}
|
||||
}
|
||||
}
|
||||
|
||||
function cancelLongTap() {
|
||||
if (longTapTimeout) clearTimeout(longTapTimeout)
|
||||
longTapTimeout = null
|
||||
}
|
||||
|
||||
function cancelAll() {
|
||||
if (touchTimeout) clearTimeout(touchTimeout)
|
||||
if (tapTimeout) clearTimeout(tapTimeout)
|
||||
if (swipeTimeout) clearTimeout(swipeTimeout)
|
||||
if (longTapTimeout) clearTimeout(longTapTimeout)
|
||||
touchTimeout = tapTimeout = swipeTimeout = longTapTimeout = null
|
||||
touch = {}
|
||||
}
|
||||
|
||||
function isPrimaryTouch(event){
|
||||
return (event.pointerType == 'touch' ||
|
||||
event.pointerType == event.MSPOINTER_TYPE_TOUCH)
|
||||
&& event.isPrimary
|
||||
}
|
||||
|
||||
function isPointerEventType(e, type){
|
||||
return (e.type == 'pointer'+type ||
|
||||
e.type.toLowerCase() == 'mspointer'+type)
|
||||
}
|
||||
|
||||
$(document).ready(function(){
|
||||
var now, delta, deltaX = 0, deltaY = 0, firstTouch, _isPointerType
|
||||
|
||||
if ('MSGesture' in window) {
|
||||
gesture = new MSGesture()
|
||||
gesture.target = document.body
|
||||
}
|
||||
|
||||
$(document)
|
||||
.bind('MSGestureEnd', function(e){
|
||||
var swipeDirectionFromVelocity =
|
||||
e.velocityX > 1 ? 'Right' : e.velocityX < -1 ? 'Left' : e.velocityY > 1 ? 'Down' : e.velocityY < -1 ? 'Up' : null;
|
||||
if (swipeDirectionFromVelocity) {
|
||||
touch.el.trigger('swipe')
|
||||
touch.el.trigger('swipe'+ swipeDirectionFromVelocity)
|
||||
}
|
||||
})
|
||||
.on('touchstart MSPointerDown pointerdown', function(e){
|
||||
if((_isPointerType = isPointerEventType(e, 'down')) &&
|
||||
!isPrimaryTouch(e)) return
|
||||
firstTouch = _isPointerType ? e : e.touches[0]
|
||||
if (e.touches && e.touches.length === 1 && touch.x2) {
|
||||
// Clear out touch movement data if we have it sticking around
|
||||
// This can occur if touchcancel doesn't fire due to preventDefault, etc.
|
||||
touch.x2 = undefined
|
||||
touch.y2 = undefined
|
||||
}
|
||||
now = Date.now()
|
||||
delta = now - (touch.last || now)
|
||||
touch.el = $('tagName' in firstTouch.target ?
|
||||
firstTouch.target : firstTouch.target.parentNode)
|
||||
touchTimeout && clearTimeout(touchTimeout)
|
||||
touch.x1 = firstTouch.pageX
|
||||
touch.y1 = firstTouch.pageY
|
||||
if (delta > 0 && delta <= 250) touch.isDoubleTap = true
|
||||
touch.last = now
|
||||
longTapTimeout = setTimeout(longTap, longTapDelay)
|
||||
// adds the current touch contact for IE gesture recognition
|
||||
if (gesture && _isPointerType) gesture.addPointer(e.pointerId);
|
||||
})
|
||||
.on('touchmove MSPointerMove pointermove', function(e){
|
||||
if((_isPointerType = isPointerEventType(e, 'move')) &&
|
||||
!isPrimaryTouch(e)) return
|
||||
firstTouch = _isPointerType ? e : e.touches[0]
|
||||
cancelLongTap()
|
||||
touch.x2 = firstTouch.pageX
|
||||
touch.y2 = firstTouch.pageY
|
||||
|
||||
deltaX += Math.abs(touch.x1 - touch.x2)
|
||||
deltaY += Math.abs(touch.y1 - touch.y2)
|
||||
})
|
||||
.on('touchend MSPointerUp pointerup', function(e){
|
||||
if((_isPointerType = isPointerEventType(e, 'up')) &&
|
||||
!isPrimaryTouch(e)) return
|
||||
cancelLongTap()
|
||||
|
||||
// swipe
|
||||
if ((touch.x2 && Math.abs(touch.x1 - touch.x2) > 30) ||
|
||||
(touch.y2 && Math.abs(touch.y1 - touch.y2) > 30))
|
||||
|
||||
swipeTimeout = setTimeout(function() {
|
||||
touch.el.trigger('swipe')
|
||||
touch.el.trigger('swipe' + (swipeDirection(touch.x1, touch.x2, touch.y1, touch.y2)))
|
||||
touch = {}
|
||||
}, 0)
|
||||
|
||||
// normal tap
|
||||
else if ('last' in touch)
|
||||
// don't fire tap when delta position changed by more than 30 pixels,
|
||||
// for instance when moving to a point and back to origin
|
||||
if (deltaX < 30 && deltaY < 30) {
|
||||
// delay by one tick so we can cancel the 'tap' event if 'scroll' fires
|
||||
// ('tap' fires before 'scroll')
|
||||
tapTimeout = setTimeout(function() {
|
||||
|
||||
// trigger universal 'tap' with the option to cancelTouch()
|
||||
// (cancelTouch cancels processing of single vs double taps for faster 'tap' response)
|
||||
var event = $.Event('tap')
|
||||
event.cancelTouch = cancelAll
|
||||
touch.el.trigger(event)
|
||||
|
||||
// trigger double tap immediately
|
||||
if (touch.isDoubleTap) {
|
||||
if (touch.el) touch.el.trigger('doubleTap')
|
||||
touch = {}
|
||||
}
|
||||
|
||||
// trigger single tap after 250ms of inactivity
|
||||
else {
|
||||
touchTimeout = setTimeout(function(){
|
||||
touchTimeout = null
|
||||
if (touch.el) touch.el.trigger('singleTap')
|
||||
touch = {}
|
||||
}, 250)
|
||||
}
|
||||
}, 0)
|
||||
} else {
|
||||
touch = {}
|
||||
}
|
||||
deltaX = deltaY = 0
|
||||
|
||||
})
|
||||
// when the browser window loses focus,
|
||||
// for example when a modal dialog is shown,
|
||||
// cancel all ongoing events
|
||||
.on('touchcancel MSPointerCancel pointercancel', cancelAll)
|
||||
|
||||
// scrolling the window indicates intention of the user
|
||||
// to scroll, not tap or swipe, so cancel all ongoing events
|
||||
$(window).on('scroll', cancelAll)
|
||||
})
|
||||
|
||||
;['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown',
|
||||
'doubleTap', 'tap', 'singleTap', 'longTap'].forEach(function(eventName){
|
||||
$.fn[eventName] = function(callback){ return this.on(eventName, callback) }
|
||||
})
|
||||
})(Zepto)
|
4
js/libs/zepto.min.js
vendored
4
js/libs/zepto.min.js
vendored
File diff suppressed because one or more lines are too long
26
js/main.js
26
js/main.js
@ -1,22 +1,17 @@
|
||||
"use strict";
|
||||
|
||||
yepnope({
|
||||
test: window.mobile,
|
||||
yep : 'js/mobile-events.js',
|
||||
nope: 'js/events.js'
|
||||
})
|
||||
|
||||
$(document).ready(function() {
|
||||
window.c = $('canvas')[0].getContext('2d');
|
||||
window.o = $('canvas')[1].getContext('2d');
|
||||
|
||||
window.settings = {
|
||||
lineWidth : 0.2,
|
||||
strokeStyle : 'black',
|
||||
lineWidth : 2,
|
||||
color : 'black',
|
||||
type: 'sketch',
|
||||
lineCap: 'round',
|
||||
lineJoin: 'round',
|
||||
furLength: 5
|
||||
furLength: 5,
|
||||
connectTelorance: 40
|
||||
};
|
||||
window.points = [];
|
||||
window.$c = $('canvas');
|
||||
@ -25,4 +20,17 @@ $(document).ready(function() {
|
||||
sizeAndPos();
|
||||
$(window).resize(sizeAndPos);
|
||||
|
||||
$('.color-picker').change(function() {
|
||||
var c = $(this).find('.color').val();
|
||||
settings.color = c;
|
||||
$('#setcolor span').html(c);
|
||||
})
|
||||
$('.color').val('#000000');
|
||||
|
||||
yepnope({
|
||||
test: window.mobile,
|
||||
yep : ['js/mobile-events.js', 'js/libs/color-picker-touch.js'],
|
||||
nope: ['js/events.js', 'js/libs/color-picker.js']
|
||||
})
|
||||
|
||||
})
|
||||
|
@ -1,28 +1,63 @@
|
||||
$('.menu').bind('touchend', function() {
|
||||
"use strict";
|
||||
|
||||
function save() {
|
||||
switch(save.background) {
|
||||
case 'white': {
|
||||
var cache = {
|
||||
fillStyle: c.color,
|
||||
composite: c.globalCompositeOperation
|
||||
}
|
||||
c.fillStyle = 'white';
|
||||
c.globalCompositeOperation = 'destination-over';
|
||||
c.fillRect(0, 0, $c.width(), $c.height());
|
||||
c.fillStyle = cache.fillStyle;
|
||||
c.globalCompositeOperation = cache.composite;
|
||||
break;
|
||||
}
|
||||
case 'current color': {
|
||||
var cache = {
|
||||
fillStyle: c.color,
|
||||
composite: c.globalCompositeOperation
|
||||
}
|
||||
c.fillStyle = settings.strokeStyle;
|
||||
c.globalCompositeOperation = 'destination-over';
|
||||
c.fillRect(0, 0, $c.width(), $c.height());
|
||||
c.fillStyle = cache.fillStyle;
|
||||
c.globalCompositeOperation = cache.composite;
|
||||
break;
|
||||
}
|
||||
}
|
||||
var data = $c[0].toDataURL();
|
||||
var file = dataToBlob($c[0].toDataURL());
|
||||
var pics = navigator.getDeviceStorage('pictures');
|
||||
var r = pics.addNamed(file, save['file name'] + '.png');
|
||||
r.onsuccess = function() {
|
||||
alert('Your sketch was successfuly saved to pictures/' + this.result.name);
|
||||
}
|
||||
r.onerror = function() {
|
||||
alert('Something bad happened when we tried to save your file\n Possible problems: \n Duplicate name \n Permission problems')
|
||||
console.warn(this.error);
|
||||
}
|
||||
c.putImageData(window.points.history[window.points.history.length-1].data, 0, 0);
|
||||
}
|
||||
|
||||
$('.menu').on('tap', function() {
|
||||
$('#menu').toggleClass('pulled');
|
||||
})
|
||||
$('.save').bind('touchend', function() {
|
||||
var data = $c[0].toDataURL();
|
||||
var img = $('<img src="' + data + '" width="' + $c.width() + '" height="' + $c.height() + '" class="overlay"/>');
|
||||
$('body').append(img);
|
||||
var share = new MozActivity({
|
||||
name: 'share',
|
||||
data: {
|
||||
type: 'image/*'
|
||||
}
|
||||
})
|
||||
$('.save').on('tap', function() {
|
||||
$('#save').removeClass('hidden');
|
||||
})
|
||||
$c.last().bind('touchstart', function(e) {
|
||||
$c.last().on('touchstart', function(e) {
|
||||
e.preventDefault();
|
||||
var xy = relative(e.changedTouches[0].pageX, e.changedTouches[0].pageY);
|
||||
startPoint(xy.x, xy.y);
|
||||
window.active = true;
|
||||
}).bind('touchmove', function(e) {
|
||||
}).on('touchmove', function(e) {
|
||||
e.preventDefault();
|
||||
if (!window.active || settings.type == 'line') return;
|
||||
var xy = relative(e.changedTouches[0].pageX, e.changedTouches[0].pageY);
|
||||
drawPoint(xy.x, xy.y);
|
||||
}).bind('touchend', function(e) {
|
||||
}).on('touchend', function(e) {
|
||||
e.preventDefault();
|
||||
window.active = false;
|
||||
|
||||
@ -39,31 +74,67 @@
|
||||
|
||||
// Value Selector
|
||||
|
||||
var $selector = $('form[data-type="value-selector"]');
|
||||
var $single = $('form[data-type="value-selector"].single');
|
||||
|
||||
$selector.find('li').on('touchend', function(e) {
|
||||
$(this).parent().find('li').removeAttr('aria-selected');
|
||||
$single.find('li').on('tap', function(e) {
|
||||
e.preventDefault();
|
||||
$(this).parent().find('li[aria-selected]').removeAttr('aria-selected');
|
||||
$(this).attr('aria-selected', 'true');
|
||||
var prop = $(this).parents('form').attr('id'),
|
||||
key = $(this).find('label span').html().toLowerCase();
|
||||
window.settings[prop] = key;
|
||||
var key = $(this).parents('form').attr('id'),
|
||||
value = $(this).find('label span').html().toLowerCase();
|
||||
window.settings[key] = value;
|
||||
|
||||
$('button[id="set' + prop + '"] span').html(key[0].toUpperCase() + key.substr(1));
|
||||
$('button[id="set' + key + '"] span').html(value[0].toUpperCase() + value.substr(1));
|
||||
$('#menu div.options > div').addClass('hidden');
|
||||
$('#menu div.options > .general, #menu div.options > .'+value).removeClass('hidden');
|
||||
|
||||
$(this).parents('form').addClass('hidden');
|
||||
})
|
||||
|
||||
$selector.submit(function(e) {
|
||||
$single.submit(function(e) {
|
||||
e.preventDefault();
|
||||
$(this).addClass('hidden');
|
||||
})
|
||||
|
||||
// Confirm
|
||||
|
||||
var $confirm = $('form[data-type="value-selector"].confirm');
|
||||
|
||||
$confirm.find('li').on('tap', function(e) {
|
||||
$(this).parent().find('li[aria-selected]').removeAttr('aria-selected');
|
||||
$(this).attr('aria-selected', 'true');
|
||||
})
|
||||
$confirm.find('button').last().on('tap', function(e) {
|
||||
e.preventDefault();
|
||||
var v = $(this).parents('form').attr('id');
|
||||
$(this).parents('form').find('h1').each(function(i) {
|
||||
if( i > 0 ) {
|
||||
var key = $(this).html().toLowerCase();
|
||||
var value = $(this).parent().find('ol:nth-of-type('+i+') li[aria-selected] span').html();
|
||||
if( key !== 'file name' ) value = key.toLowerCase();
|
||||
|
||||
window[v][key] = value;
|
||||
}
|
||||
})
|
||||
$(this).parents('form').addClass('hidden');
|
||||
window[v]();
|
||||
})
|
||||
$confirm.find('button').first().on('tap', function(e) {
|
||||
e.preventDefault();
|
||||
$(this).parents('form').addClass('hidden');
|
||||
})
|
||||
|
||||
// Value Selector Callers
|
||||
|
||||
var $btn = $('button[id^="set"]');
|
||||
$btn.each(function() {
|
||||
var target = /set(.*)/.exec($(this).attr('id'))[1];
|
||||
$(this).on('touchend', function(e) {
|
||||
if( target == 'color' ) {
|
||||
return $(this).on('tap', function() {
|
||||
$('.picker').removeClass('hidden');
|
||||
})
|
||||
}
|
||||
$(this).on('tap', function(e) {
|
||||
e.preventDefault();
|
||||
$('form[id="' + target + '"]').removeClass('hidden');
|
||||
})
|
||||
@ -71,19 +142,49 @@
|
||||
|
||||
// Seekbar
|
||||
|
||||
var sliderLeft = $('div[role="slider"] button').offset().left;
|
||||
var sliderLeft;
|
||||
$('div[role="slider"] button').on('touchstart', function() {
|
||||
$(this).attr('data-moving','true');
|
||||
if( !sliderLeft ) sliderLeft = $('div[role="slider"] button').offset().left;
|
||||
}).on('touchmove', function(e) {
|
||||
if( $(this).attr('data-moving') ) {
|
||||
var x = e.changedTouches[0].pageX - sliderLeft - 15;
|
||||
if( x <= 100 && x > 0 ) {
|
||||
if( $(this).attr('data-moving') ) {
|
||||
var x = parseInt(e.changedTouches[0].pageX - sliderLeft - 15);
|
||||
var progress = $(this).parent().children().first();
|
||||
var max = +progress.attr('max');
|
||||
var min = +progress.attr('min');
|
||||
if( x <= max && x >= min ) {
|
||||
$(this).css('left', x+'%');
|
||||
$(this).parent().find('progress').attr('value', x);
|
||||
settings.lineWidth = x / 10;
|
||||
$('#lineWidth span').html(x);
|
||||
var key = $(this).parents('div[role="slider"]').attr('class');
|
||||
settings[key] = x;
|
||||
$('#'+ key +' span').html(x);
|
||||
}
|
||||
}
|
||||
}).on('touchend', function() {
|
||||
$(this).removeAttr('data-moving');
|
||||
})
|
||||
|
||||
// Color Picker
|
||||
|
||||
$('#closePicker').on('tap', function() {
|
||||
$('.picker').addClass('hidden');
|
||||
})
|
||||
|
||||
// Bottom
|
||||
|
||||
$('#clear').on('tap', function() {
|
||||
c.clearRect(0, 0, $c.width(), $c.height());
|
||||
if(window.points.history.last < window.points.history.length-1) {
|
||||
window.points.history.splice(window.points.history.last+1);
|
||||
}
|
||||
|
||||
window.points.history.push({
|
||||
data: c.getImageData(0, 0, $c.width(), $c.height()),
|
||||
points: []
|
||||
})
|
||||
window.points.history.last = window.points.history.length-1;
|
||||
})
|
||||
|
||||
$('#undo').on('tap', undo);
|
||||
$('#redo').on('tap', redo);
|
||||
|
||||
|
@ -2,6 +2,8 @@
|
||||
"name": "Sketchy",
|
||||
"description": "Free Sketch/Paint app",
|
||||
"version": 1,
|
||||
"fullscreen": true,
|
||||
"type": "privileged",
|
||||
"launch_path": "/index.html",
|
||||
"icons": {
|
||||
"16": "/img/icons/icon16.png",
|
||||
@ -22,5 +24,11 @@
|
||||
"name": "Sketchy",
|
||||
"description": "برنامهی رایگان طراحی/نقاشی"
|
||||
}
|
||||
},
|
||||
"permissions": {
|
||||
"device-storage:pictures": {
|
||||
"description": "Required to save sketched pics",
|
||||
"access": "readwrite"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user