Skip to content

Instantly share code, notes, and snippets.

@ugate
Last active July 14, 2019 20:44
Show Gist options
  • Save ugate/88f9791107ec81964eb87d09826591f6 to your computer and use it in GitHub Desktop.
Save ugate/88f9791107ec81964eb87d09826591f6 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html lang="en">
<head>
<title>Pixel Editor</title>
<style>
.row {margin-left: auto;margin-right: auto;margin-bottom: 20px;} .row:after {content: "";display: table;clear: both;}
.row .col {float: left;-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;padding: 0 0.75rem;text-align: left;}
.row .col.s1 {width: 8.33333%;margin-left: 0;} .row .col.s2 {width: 16.66667%;margin-left: 0;} .row .col.s3 {width: 25%;margin-left: 0;} .row .col.s4 {width: 33.33333%;margin-left: 0;} .row .col.s5 {width: 41.66667%;margin-left: 0;}
.row .col.s6 {width: 50%;margin-left: 0;} .row .col.s7 {width: 58.33333%;margin-left: 0;} .row .col.s8 {width: 66.66667%;margin-left: 0;} .row .col.s9 {width: 75%;margin-left: 0;} .row .col.s10 {width: 83.33333%;margin-left: 0;}
.row .col.s11 {width: 91.66667%;margin-left: 0;} .row .col.s12 {width: 100%;margin-left: 0;} .row .col.offset-s1 {margin-left: 8.33333%;} .row .col.offset-s2 {margin-left: 16.66667%;} .row .col.offset-s3 {margin-left: 25%;}
.row .col.offset-s4 {margin-left: 33.33333%;} .row .col.offset-s5 {margin-left: 41.66667%;} .row .col.offset-s6 {margin-left: 50%;} .row .col.offset-s7 {margin-left: 58.33333%;} .row .col.offset-s8 {margin-left: 66.66667%;}
.row .col.offset-s9 {margin-left: 75%;} .row .col.offset-s10 {margin-left: 83.33333%;} .row .col.offset-s11 {margin-left: 91.66667%;} .row .col.offset-s12 {margin-left: 100%;}
@media only screen and (min-width : 601px) {
.row .col.m1 {width: 8.33333%;margin-left: 0;} .row .col.m2 {width: 16.66667%;margin-left: 0;} .row .col.m3 {width: 25%;margin-left: 0;} .row .col.m4 {width: 33.33333%;margin-left: 0;} .row .col.m5 {width: 41.66667%;margin-left: 0;}
.row .col.m6 {width: 50%;margin-left: 0;} .row .col.m7 {width: 58.33333%;margin-left: 0;} .row .col.m8 {width: 66.66667%;margin-left: 0;} .row .col.m9 {width: 75%;margin-left: 0;}.row .col.m10 {width: 83.33333%;margin-left: 0;}
.row .col.m11 {width: 91.66667%;margin-left: 0;} .row .col.m12 {width: 100%;margin-left: 0;} .row .col.offset-m1 {margin-left: 8.33333%;} .row .col.offset-m2 {margin-left: 16.66667%;} .row .col.offset-m3 {margin-left: 25%;}
.row .col.offset-m4 {margin-left: 33.33333%;} .row .col.offset-m5 {margin-left: 41.66667%;} .row .col.offset-m6 {margin-left: 50%;} .row .col.offset-m7 {margin-left: 58.33333%;} .row .col.offset-m8 {margin-left: 66.66667%;}
.row .col.offset-m9 {margin-left: 75%;} .row .col.offset-m10 {margin-left: 83.33333%;} .row .col.offset-m11 {margin-left: 91.66667%;} .row .col.offset-m12 {margin-left: 100%;}
}
@media only screen and (min-width : 993px) {
.row .col.l1 {width: 8.33333%;margin-left: 0;} .row .col.l2 {width: 16.66667%;margin-left: 0;} .row .col.l3 {width: 25%;margin-left: 0;} .row .col.l4 {width: 33.33333%;margin-left: 0;} .row .col.l5 {width: 41.66667%;margin-left: 0;}
.row .col.l6 {width: 50%;margin-left: 0;} .row .col.l7 {width: 58.33333%;margin-left: 0;} .row .col.l8 {width: 66.66667%;margin-left: 0;} .row .col.l9 {width: 75%;margin-left: 0;} .row .col.l10 {width: 83.33333%;margin-left: 0;}
.row .col.l11 {width: 91.66667%;margin-left: 0;} .row .col.l12 {width: 100%;margin-left: 0;} .row .col.offset-l1 {margin-left: 8.33333%;} .row .col.offset-l2 {margin-left: 16.66667%;} .row .col.offset-l3 {margin-left: 25%;}
.row .col.offset-l4 {margin-left: 33.33333%;} .row .col.offset-l5 {margin-left: 41.66667%;} .row .col.offset-l6 {margin-left: 50%;} .row .col.offset-l7 {margin-left: 58.33333%;} .row .col.offset-l8 {margin-left: 66.66667%;}
.row .col.offset-l9 {margin-left: 75%;} .row .col.offset-l10 {margin-left: 83.33333%;}.row .col.offset-l11 {margin-left: 91.66667%;} .row .col.offset-l12 {margin-left: 100%;}
.content {width: calc(100% - 16px);}
}
html, body {font-family:'Roboto', sans-serif;font-size:14px;font-weight:400;line-height:20px;}
html {width:100%;height:100%;color:rgba(0,0,0,.87);-ms-touch-action:manipulation;touch-action:manipulation;}
body {margin:0;width:100%;min-height:100%;background-color:#f5f5f5 !important;} canvas {border: 1px solid #263238;} ::selection {background:#b3d4fc;text-shadow:none;} .hide {display:none;}
.red-text {color: #ff0266 !important;} .green-text {color: #00C853 !important;} .blue-text {color: #2962FF !important;} .slate-text {color: #455A64 !important;} .brown-text {color: #3E2723 !important;}
.white-text, .white-text path {fill: #fff !important;color: #fff !important;} .grey-text {color:#424242 !important;}
.bg-green {background-color: #4CAF50 !important;} .bg-teal {background-color: #009688 !important;} .bg-indigo {background-color: #3F51B5 !important;} .bg-purple {background-color: #673AB7 !important;}
.bg-pink {background-color: #E91E63 !important;} .bg-blue {background-color: #03A9F4 !important;} .bg-grey {background-color:#424242 !important;} .bg-blue-grey {background-color:#263238 !important;}
.bg-white {background-color:#fff !important;} .bg-slate {background-color: #607D8B !important;} .bg-yellow {background-color: #FFEB3B !important;} .bg-dark-grey {background-color: #212121 !important;}
.logo {width:5rem;height:5em;top:-3em;left:calc((100vw / 2) - 2.5rem);position:absolute;z-index:1;}
label.logo {top:-2em;left: calc((100vw / 2) - 3.5rem);text-align:center;width:25em;height:3em;word-spacing:7em;transform:translateX(-35%);}
label.logo a {text-decoration:none;transform:perspective(2.5em) rotateX(15deg) scaleY(0.8);transition:all 0.5s;
display:inline-block;text-align:center;font-size:4em;font-weight:700;line-height:0.5;color:#08e3ff;
text-shadow:0 -1px 15px rgba(0, 0, 0, 0.9), 0 1px 0 #005d64, 0 3px 0 #006269, 0 5px 0 #00676e, 0 7px 0 #006c73, 0 9px 0 #007178, 0 6px 50px rgba(59, 233, 255, 0.8);}
label.logo a:first-line {font-size:0.8em;} label.logo a:hover {transform:perspective(8em) rotateX(11deg) scale(1.2);
text-shadow:0 -1px 15px black, 0 1px 0 #005d64, 0 2px 0 #006269, 0 0px 0 #00676e, 0 1px 0 #006c73, 0 2px 0 #007178, 0 2px 30px rgba(59, 233, 255, 0.6);}
.shadow {box-shadow:0 4px 5px 0 rgba(0,0,0,.14), 0 1px 10px 0 rgba(0,0,0,.12), 0 2px 4px -1px rgba(0,0,0,.2);}
.ribbon {position:fixed;width:100%;height:40vh;flex-shrink:0;} .overlay {top:0;left:0;position:fixed;width:100vw;height:200vh;background:rgba(0,0,0,.5);transform:translateY(-50%);}
.loader {position:relative;margin:0 auto;width:100px;height:100%;} .loader:before {content:'';display:block;padding-top:100%;}
.loader-circular {width: 90px;height:100%;top: 70px;left: 0px;animation:rotate 2s linear infinite;transform-origin:center center;position:absolute;bottom:0;right:0;margin:auto;}
.loader-path {stroke-dasharray:1, 200;stroke-linecap: round;stroke-dashoffset:0;animation:dash 1.5s ease-in-out infinite, color 6s ease-in-out infinite;}
@keyframes rotate { 100% {transform:rotate(360deg);}}@keyframes dash { 0% {stroke-dasharray:1, 200;stroke-dashoffset:0;} 50% {stroke-dasharray:89, 200;stroke-dashoffset:-35px;}
100% {stroke-dasharray:89, 200;stroke-dashoffset:-124px;}}
@keyframes color { 100%, 0% {stroke:#FF1744;} 40% {stroke:#D500F9;} 66% {stroke:#76FF03;} 80%, 90% {stroke:#FFA700;}}
.no-select {-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;}
#snackbar { border: none;position: fixed;right: 0;left: 50%;z-index: 2000;bottom: -112px;min-width: 50vw;min-height: 48px;max-height: 112px;max-width: 1235px;overflow: hidden;
padding: 14px 24px;margin: 0;border-radius: 2px;font-size: 1rem;color: white;background-color: #006064;background-clip: padding-box;box-sizing: border-box;transition: all 200ms ease-out;
transform: translateX(-50%); cursor: pointer;} #snackbar.active {bottom: 0;} #snackbar.snackbar-error {background-color: #B71C1C;} #snackbar .snackbar-message {overflow: auto;}
#snackbar.snackbar-header {overflow: visible;} #snackbar.snackbar-header .snackbar-head {position: absolute;top: -1.45em;left: 0;opacity: 0;font-size: 1.5em;
background: rgba(66,66,66,1); border: none;text-align: center;padding: 0 .5em;width: 100%;transition: opacity 300ms;} #snackbar.snackbar-header.active .snackbar-head {opacity: 1;}
@media only screen and (max-width: 600px) { #snackbar {width: 100%;margin: 0;border-radius: 0px;}} #snackbar:last-child {padding: 0 0 0 24px;float: right;color: #ffeb3b;
text-decoration: none;text-transform: uppercase;} .snackbar-msg {display: none;}
.unload-bar {position: absolute;background-color: black;width: 100%;height: 3px;top: 0;left: 0;animation: unloadbar 2s linear 1 forwards;}
#snackbar:hover .unload-bar {animation: none;} @keyframes unloadbar { 0% {width: 100%;} 100% {width: 0px;}}
.switch {position:relative;display:inline-block;width:60px;height:34px;} .switch input:not([type=range]):not([type=color]) {display:none;}
.slider:not([type=range]) {position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background-color:#B71C1C;-webkit-transition:.2s;transition:.2s;}
.slider:not([type=range]):before {position:absolute;content:"";height:26px;width:26px;left:4px;bottom:4px;background-color:white;-webkit-transition:.2s;transition:.2s;}
input:not([type=range]):not([type=color]):checked + .slider:not([type=range]) {background-color:#00C853;} input:not([type=range]):not([type=color]):focus + .slider:not([type=range]) {box-shadow:0 0 1px #00C853;}
input:not([type=range]):not([type=color]):checked + .slider:not([type=range]):before {-webkit-transform:translateX(26px);-ms-transform:translateX(26px);transform:translateX(26px);}
.slider:not([type=range]).round {border-radius:34px;} .slider:not([type=range]).round:before {border-radius:50%;} input[type=color] {margin: 1em 0;padding: 0 !important;} input[type=range]:focus {outline: none;}
input[type=range].slider {-webkit-appearance: none;appearance: none;background: none;width: 100%;display: inline-block;padding: 1em 0;}
input[type=range]::-webkit-slider-runnable-track {height: 10px;background: #ddd;border: none;border-radius: 3px;} input[type=range]::-ms-track {height: 10px;background: #ddd;border: none;border-radius: 3px;}
input[type=range]::-moz-range-track {height: 10px;background: #ddd;border: none;border-radius: 3px;}
input[type=range]::-webkit-slider-thumb {-webkit-appearance: none;appearance: none;border: none;height: 16px;width: 16px;border-radius: 50%;background: #555;margin-top: -3px;position: relative;}
input[type=range].red::-webkit-slider-thumb {background: #ff0266;} input[type=range].green::-webkit-slider-thumb {background: #00C853;} input[type=range].blue::-webkit-slider-thumb {background: #2962FF;}
input[type=range].hue::-webkit-slider-thumb {background: linear-gradient(90deg, #ff0266 0%, #00C853 40%, #2962FF 90%);}
input[type=range].saturation::-webkit-slider-thumb {background: linear-gradient(90deg, rgba(255,255,255,.2) 0%, #555000 100%);}
input[type=range].luminance::-webkit-slider-thumb {background: linear-gradient(90deg, rgba(0,0,0,1) 0%, rgba(255,255,255,1) 100%);}
input[type=range]::-ms-thumb {-webkit-appearance: none;appearance: none;border: none;height: 16px;width: 16px;border-radius: 50%;background: #555;margin-top: 0px;position: relative;}
input[type=range].red::-ms-thumb/*, input[type="range"].red::-ms-fill-lower*/ {background: #ff0266;} input[type=range].green::-ms-thumb/*, input[type="range"].green::-ms-fill-lower*/ {background: #00C853;}
input[type=range].blue::-ms-thumb/*, input[type="range"].blue::-ms-fill-lower*/ {background: #2962FF;} input[type=range].hue::-ms-thumb {background: linear-gradient(90deg, #ff0266 0%, #00C853 40%, #2962FF 90%);}
input[type=range].saturation::-ms-thumb {background: linear-gradient(90deg, rgba(255,255,255,.2) 0%, #555000 100%);} input[type=range].luminance::-ms-thumb {background: linear-gradient(90deg, rgba(0,0,0,1) 0%, rgba(255,255,255,1) 100%);}
input[type=range]::-moz-range-thumb {-webkit-appearance: none;appearance: none;border: none;height: 16px;width: 16px;border-radius: 50%;background: #555;margin-top: -5px;position: relative;}
input[type=range].red::-moz-range-thumb/*, input[type="range"].red::-moz-range-progress*/ {background: #ff0266;} input[type=range].green::-moz-range-thumb/*, input[type="range"].green::-moz-range-progress*/ {background: #00C853;}
input[type=range].blue::-moz-range-thumb/*, input[type="range"].blue::-moz-range-progress*/ {background: #2962FF;} input[type=range].hue::-moz-range-thumb {background: linear-gradient(90deg, #ff0266 0%, #00C853 40%, #2962FF 90%);}
input[type=range].saturation::-moz-range-thumb {background: linear-gradient(90deg, rgba(255,255,255,.2) 0%, #555000 100%);}
input[type=range].luminance::-moz-range-thumb {background: linear-gradient(90deg, rgba(0,0,0,1) 0%, rgba(255,255,255,1) 100%);} input[type=range]:focus::-webkit-slider-runnable-track {background: #ccc;}
input[type=range]:focus::-ms-track {background: #ccc;} input[type=range]:focus::-moz-range-track {background: #ccc;} input.label-value {display: inline-block !important;width: auto !important;}
input[type=range].slate::-webkit-slider-runnable-track {background: #B0BEC5;} input[type=range].slate::-ms-track {background: #B0BEC5;} input[type=range].brown::-moz-range-track {background: #B0BEC5;}
input[type=range].slate::-webkit-slider-thumb {background: #263238;} input[type=range].slate::-ms-thumb {background: #263238;} input[type=range].slate::-moz-range-thumb {background: #263238;}
input[type=range].brown::-webkit-slider-runnable-track {background: #BCAAA4;} input[type=range].brown::-ms-track {background: #BCAAA4;} input[type=range].brown::-moz-range-track {background: #BCAAA4;}
input[type=range].brown::-webkit-slider-thumb {background: #3E2723;} input[type=range].brown::-ms-thumb {background: #3E2723;} input[type=range].brown::-moz-range-thumb {background: #3E2723;}
output.range-tooltip {display: inline-block;position: relative;left: 10px;top: calc(-1em - var(--height));padding: 3px;border-radius: 3px;background: #95a;color: #eee;transform: translate(calc((var(--val) - var(--min))/(var(--max) - var(--min)) * var(--width) - 50%));}
.dimensions > input {width: 3em !important;-webkit-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select: none;} .dimensions > input:first-of-type {text-align: right;} .dimensions > input:nth-of-type(2) {width: 1em !important;}
.canvas-editor, .canvas-row {display: flex;justify-content: center;align-items: center;} .canvas-editor {overflow: auto;padding-bottom: .1em;} .canvas-editor canvas {cursor: crosshair;} .canvas-row {flex-basis: 100%;}
.color-toolbar {display: flex;flex-direction: column;} .color-toolbar .btn {margin: .1em;}
.color-toolbar input.active ~ label {box-shadow: 0px .4em 0px -.2em #FFFFFF, 0px -.4em 0px -.2em #FFFFFF, .4em 0px 0px -.2em #FFFFFF, -.4em 0px 0px -.2em #FFFFFF, 0px 0px 0px .2em #B71C1C, .1em .1em .4em .1em rgba(0,0,0,0);}
main {position:relative;top:3em;flex-shrink:0;flex-grow:1;-webkit-overflow-scrolling:touch;} main > div {max-width:1600px;width:calc(100% - 16px);margin:0px auto;display:flex;flex-flow:row wrap;align-items:stretch;}
@media (max-width: 479px) {main > div {padding:8px 0;margin:0;width:100% !important;}}
@media (min-width: 840px) {main > div {padding:8px;}}
@media (max-width: 839px) and (min-width: 480px) {main > div {padding:8px;}}
@media (max-width: 479px) {.margin {width:calc(50% - 16px);display:none !important;margin:8px;box-sizing:border-box;}}
@media (max-width: 839px) and (min-width: 480px) {.margin {width:calc(25% - 16px);display:none !important;margin:8px;box-sizing:border-box;}}
@media (min-width: 840px) {.margin {width:calc(16.6666666667% - 16px);margin:8px;box-sizing:border-box;}}
.content {min-height:calc(100vh - 15vh);margin-bottom:80px;margin-top:8px;border-radius:2px;box-sizing:border-box;}
.menu {position:relative;width:100%;text-align:center;padding:1.5em 0 1.2em 0;background:#03A9F4;}
.btn-icon {cursor: pointer;font-size:1.3em;padding:1em .2em .3em .3em;color:white;fill:white;line-height:1.25em;vertical-align:bottom;border:4px solid transparent;border-radius:50%;user-select:none;}
.btn-icon.active {border:4px solid #00E5FF;}
@media (min-width: 993px) {.content {width: calc(100% - 16px);}}
@media (max-width: 992px) and (min-width: 840px) {}
@media (max-width: 839px) and (min-width: 480px) {.content {width:calc(100% - 16px);margin:8px;} .instruct {font-size:1.5em !important;}.canvas-editor{flex-wrap: wrap;flex-direction: column;}.color-toolbar {flex-direction: row;}}
@media (max-width: 479px) {.content {width:100%;margin:8px 0;} .container {padding:0 !important;} .instruct {font-size:1em !important;}.canvas-editor{flex-wrap: wrap;flex-direction: column;}.color-toolbar {flex-direction: row;}}
ul, ol {font-size:14px;line-height:24px;font-weight:400;letter-spacing:0;} .instruct {text-align:center;width:100%;background:#3F51B5;color:#fff;font-size:1.7em;padding:1em 0;}
a {color:rgb(255,82,82);font-weight: 500;}
a, button, checkbox, .icon-palette, radio, .slider, .switch {-webkit-tap-highlight-color:transparent;-webkit-tap-highlight-color:rgba(255,255,255,0);}
button, input:not([type=range]):not([type=color]) {overflow:visible;} button, input:not([type=range]):not([type=color]), optgroup, select, textarea {font-family:sans-serif;font-size:100%;line-height:1.15;margin:0;}
.center {display:flex;flex-flow:row nowrap;align-items:center;justify-content:center;} .container {padding:0 2rem;font-size:1.5em;text-align:center;}
input:not([type=range]):not([type=color]), select, textarea, body *,input:not([type=range]):not([type=color])::after,input:not([type=range]):not([type=color])::before,select::after,select::before,textarea::after,textarea::before {box-sizing:border-box;}
.vcol {margin:1em 0 2em 0;} .vcol > * {padding:0 0 1em 0;} .no-top-btm-margin {margin-top:0 !important;margin-bottom:0 !important;}
.form-radio, .md-grp {position:relative;margin-top:2.25em;margin-bottom:2.25em;} .md-grp input:not([type=range]):not([type=color]) {height:1.9rem;} .md-grp textarea {resize:none;}
.md-grp .bar {position:relative;border-bottom:0.0625em solid #999;display:block;}
.md-grp .bar::before {content:'';height:0.125em;width:0;left:50%;bottom:-0.0625em;position:absolute;background:#03A9F4;transition:left 0.28s ease, width 0.28s ease;}
.md-grp select {width:100%;font-size:1em;height:1.6em;padding:0.125em 0.125em 0.0625em;background:none;border:none;line-height:1.6;box-shadow:none;}
.md-grp label:not(.switch) {position:absolute;top:0.25em;left:0;width:100%;padding-left:0.125em;z-index:1;color:#b3b3b3;font-size:1em;font-weight:normal;transition:all 180ms cubic-bezier(0.4, 0, 0.2, 1);}
.md-grp input:not([type=range]):not([type=color]), .md-grp textarea {display:block;background:none;padding:0.125em 0.125em 0.0625em;font-size:1em;border-width:0;border-color:transparent;line-height:1.9;
width:100%;transition:all 0.28s ease;box-shadow:none;margin-bottom:.5em;} .md-grp input:not([type=range]):not([type=color]):invalid:not(:focus), .md-grp textarea:invalid:not(:focus) {color:transparent;}
.md-grp select, .md-grp input:not([type=range]):not([type=color]):focus, .md-grp input:not([type=range]):not([type=color]):valid, .md-grp input:not([type=range]):not([type=color]).has-value, .md-grp textarea:focus, .md-grp textarea:valid, .md-grp textarea.has-value {color:#333;}
.md-grp select ~ label, .md-grp input:not([type=range]):not([type=color]):focus ~ label, .md-grp input:not([type=range]):not([type=color]):valid ~ label, .md-grp input:not([type=range]):not([type=color]).has-value ~ label, .md-grp textarea:focus ~ label,
.md-grp textarea:valid ~ label, .md-grp textarea.has-value ~ label {font-size:0.8em;color:grey;top:-1.5em;left:0;}
.md-grp input:not([type=range]):not([type=color]):placeholder-shown ~ label, .md-grp textarea:placeholder-shown ~ label {top:0.25em !important;}
.md-grp input:not([type=range]):not([type=color]):invalid ~ label, .md-grp textarea:invalid ~ label {color:#E91E63 !important;}
.md-grp select:invalid ~ .bar::before, .md-grp input:not([type=range]):not([type=color]):invalid ~ .bar::before, .md-grp textarea:invalid ~ .bar::before {width:100%;left:0;background:#E91E63;}
.md-grp select:focus, .md-grp input:not([type=range]):not([type=color]):focus, .md-grp textarea:focus {outline:none;} .md-grp select:focus ~ label, .md-grp input:not([type=range]):not([type=color]):focus ~ label, .md-grp textarea:focus ~ label {color:#03A9F4;}
.md-grp select:focus ~ .bar::before, .md-grp input:not([type=range]):not([type=color]):focus ~ .bar::before, .md-grp textarea:focus ~ .bar::before {width:100%;left:0;}
button {border:none;cursor:pointer;color:white;border-radius:3px;box-shadow:3px 3px 4px rgba(0, 0, 0, .4);background:#03A9F4;position:relative;overflow:hidden;} button.large {padding:15px 40px;}
button:after {content:'';position:absolute;top:50%;left:50%;width:5px;height:5px;background:rgba(255, 255, 255, .5);opacity:0;border-radius:100%;transform:scale(1, 1) translate(-50%);transform-origin:50% 50%;}
button:focus:not(:active)::after {animation:ripple 500ms ease-out;} @keyframes ripple { 0% {transform:scale(0, 0);opacity:1;} 20% {transform:scale(25, 25);opacity:1;} 100% {opacity:0;transform:scale(40, 40);}}
.btn {text-decoration: none;text-align: center;letter-spacing: .5px;transition: .2s ease-out;cursor: pointer;border: none;border-radius: 2px;display: inline-block;height: 36px;line-height: 36px;outline: 0;padding: 0 2rem;
vertical-align: middle;-webkit-tap-highlight-color: transparent;box-shadow: 0 2px 5px 0 rgba(0,0,0,0.16), 0 2px 10px 0 rgba(0,0,0,0.12);}
.btn [type="checkbox"]:not(:checked), .btn [type="checkbox"]:checked, .btn [type="radio"]:not(:checked), .btn [type="radio"]:checked {position: absolute;left: -9999px;visibility: hidden;-webkit-tap-highlight-color: transparent;}
.btn input[type="checkbox"], .btn input[type="radio"] {box-sizing: border-box;padding: 0;}
.button-toggle {position: relative;display: block;padding: 0 0 0 .5rem !important;margin: 0;top: 0 !important;left: 0 !important;width: 3rem;font-size: 2rem !important;transition: .2s ease !important;}
.button-toggle [type="checkbox"] ~ label, .button-toggle [type="radio"] ~ label {position: absolute;padding: .2em .5em !important;margin: 0;top: 0 !important;left: 0 !important;height: 100% !important;overflow: hidden;font-size: 1rem !important;cursor: pointer;}
.button-toggle [type="checkbox"] + label, .button-toggle [type="radio"] + label {width: 100%;}
.button-toggle [type="checkbox"] + label + label, .button-toggle [type="radio"] + label + label {left: 3em !important;margin-top: .8em !important;height: calc(100% - .8em) !important;border-radius: 0 .4em .4em 0;}
.button-toggle [type="checkbox"] ~ label svg, .button-toggle [type="checkbox"] ~ label b, .button-toggle [type="radio"] ~ label svg, .button-toggle [type="radio"] ~ label b {font-size: 2em !important;position: relative;top: 0 !important;line-height: 1em !important;
height: 100% !important;display: block;transition: top .2s ease-in-out;}
.button-toggle [type="checkbox"] ~ label svg + svg, .button-toggle [type="checkbox"] ~ label b + b, .button-toggle [type="radio"] ~ label svg + svg, .button-toggle [type="radio"] ~ label b + b {margin-top: .2em !important;}
.button-toggle [type="checkbox"]:checked ~ label svg, .button-toggle [type="checkbox"]:checked ~ label b, .button-toggle [type="radio"]:checked ~ label svg, .button-toggle [type="radio"]:checked ~ label b {top: -1.25em !important;}
.button-toggle [type="checkbox"] ~ label:before, .button-toggle [type="radio"] ~ label:before {display: none;} .button-toggle [type="checkbox"] ~ label b:first-child, .button-toggle [type="radio"] ~ label b:first-child {padding-top: .2em !important;}
.button-toggle [type="checkbox"] ~ label b, .button-toggle [type="radio"] ~ label b {font-size: 1em !important;line-height: 1em !important;white-space: nowrap;cursor: auto;text-align: center;}
</style>
</head>
<body class="bg-grey" onload="mm.loaded()">
<div class="row color-select">
<div class="col s12 m12 l12">
<div class="canvas-row dimensions">
<input class="canvas-editor-cols white-text" type="number" min="16" max="144" step="1" value="16"/>
<input class="white-text" type="text" disabled="disabled" value="X"/>
<input class="canvas-editor-rows white-text" type="number" min="16" max="144" step="1" value="16"/>
<!-- <input class="canvas-editor-blocks white-text" type="number" min="10" max="40" step="1" value="20"/> -->
</div>
<div class="canvas-editor">
<div class="color-toolbar brown-text">
<div class="btn button-toggle">
<input class="canvas-editor-pen active" type="checkbox" checked="checked" id="pixPen" />
<label for="pixPen" class="hex-value-bg">
<svg viewBox="0 0 24 24" title="Pixel Eraser">
<path d="M16.24,3.56L21.19,8.5C21.97,9.29 21.97,10.55 21.19,11.34L12,20.53C10.44,22.09 7.91,22.09 6.34,20.53L2.81,17C2.03,16.21 2.03,14.95 2.81,14.16L13.41,3.56C14.2,2.78 15.46,2.78 16.24,3.56M4.22,15.58L7.76,19.11C8.54,19.9 9.8,19.9 10.59,19.11L14.12,15.58L9.17,10.63L4.22,15.58Z" />
</svg>
<svg viewBox="0 0 24 24" title="Pixel Pen">
<path d="M20.71,7.04C21.1,6.65 21.1,6 20.71,5.63L18.37,3.29C18,2.9 17.35,2.9 16.96,3.29L15.12,5.12L18.87,8.87M3,17.25V21H6.75L17.81,9.93L14.06,6.18L3,17.25Z" />
</svg>
</label>
</div>
<div class="btn button-toggle">
<input class="canvas-editor-fill" type="checkbox" checked="checked" id="pixFill" />
<label for="pixFill" class="hex-value-bg">
<svg viewBox="0 0 24 24" title="Fill Eraser">
<path d="M15.14,3C14.63,3 14.12,3.2 13.73,3.59L2.59,14.73C1.81,15.5 1.81,16.77 2.59,17.56L5.03,20H12.69L21.41,11.27C22.2,10.5 22.2,9.23 21.41,8.44L16.56,3.59C16.17,3.2 15.65,3 15.14,3M17,18L15,20H22V18" />
</svg>
<svg viewBox="0 0 24 24" title="Fill Bucket">
<path d="M19,11.5C19,11.5 17,13.67 17,15A2,2 0 0,0 19,17A2,2 0 0,0 21,15C21,13.67 19,11.5 19,11.5M5.21,10L10,5.21L14.79,10M16.56,8.94L7.62,0L6.21,1.41L8.59,3.79L3.44,8.94C2.85,9.5 2.85,10.47 3.44,11.06L8.94,16.56C9.23,16.85 9.62,17 10,17C10.38,17 10.77,16.85 11.06,16.56L16.56,11.06C17.15,10.47 17.15,9.5 16.56,8.94Z" />
</svg>
</label>
</div>
<div class="btn button-toggle">
<input class="canvas-editor-spray-or-eyedrop" type="checkbox" checked="checked" id="pixSprayOrEydrop" />
<label for="pixSprayOrEydrop" class="hex-value-bg">
<svg viewBox="0 0 24 24" title="Eyedrop Select Color">
<path d="M6.92,19L5,17.08L13.06,9L15,10.94M20.71,5.63L18.37,3.29C18,2.9 17.35,2.9 16.96,3.29L13.84,6.41L11.91,4.5L10.5,5.91L11.92,7.33L3,16.25V21H7.75L16.67,12.08L18.09,13.5L19.5,12.09L17.58,10.17L20.7,7.05C21.1,6.65 21.1,6 20.71,5.63Z" />
</svg>
<svg viewBox="0 0 24 24" title="Spray Color">
<path d="M10,4H12V6H10V4M7,3H9V5H7V3M7,6H9V8H7V6M6,8V10H4V8H6M6,5V7H4V5H6M6,2V4H4V2H6M13,22A2,2 0 0,1 11,20V10A2,2 0 0,1 13,8V7H14V4H17V7H18V8A2,2 0 0,1 20,10V20A2,2 0 0,1 18,22H13M13,10V20H18V10H13Z" />
</svg>
</label>
</div>
<div class="btn button-toggle canvas-editor-download">
<input type="checkbox" disabled="disabled" id="pixDownload" />
<label for="pixDownload" class="white-text bg-slate">
<svg viewBox="0 0 24 24" title="Download Bitmap">
<path d="M5,20H19V18H5M19,9H15V3H9V9H5L12,16L19,9Z" />
</svg>
</label>
</div>
<div class="btn button-toggle canvas-editor-upload">
<input type="checkbox" disabled="disabled" data-req-method="POST" data-req-action="/bitmap" id="pixUpload" />
<label for="pixUpload" class="white-text bg-green">
<svg style="width:24px;height:24px" viewBox="0 0 24 24">
<path d="M20,12A8,8 0 0,1 12,20A8,8 0 0,1 4,12A8,8 0 0,1 12,4C12.76,4 13.5,4.11 14.2,4.31L15.77,2.74C14.61,2.26 13.34,2 12,2A10,10 0 0,0 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12M7.91,10.08L6.5,11.5L11,16L21,6L19.59,4.58L11,13.17L7.91,10.08Z" />
</svg>
</label>
</div>
</div>
<div>
<canvas class="editor">Bitmap pixel editor canvas not supported</canvas>
</div>
</div>
</div>
<input class="hsl-text" type="hidden" value="0,0%,0%"/>
<input class="rgb-text" type="hidden" value="0,0,0" id="rgb"/>
<input class="col s12 m12 l12 hex" type="color" value="#03A9F4" name="color" id="color"/>
<div class="col s12 m12 l4">
<div>Hue: <output class="hue-out">0</output></div>
<input class="slider hue" type="range" min="0" max="1" step="0.01" value="0"/>
</div>
<div class="col s12 m12 l4">
<div>Saturation: <output class="saturation-out">0</output></div>
<input class="slider saturation" type="range" min="0" max="1" step="0.01" value="0"/>
</div>
<div class="col s12 m12 l4">
<div>Luminance: <output class="luminance-out">0</output></div>
<input class="slider luminance" type="range" min="0" max="1" step="0.01" value="0"/>
</div>
<div class="col s12 m12 l4">
<div class="red-text">Red: <output class="red-out">0</output></div>
<input class="slider red" type="range" min="0" max="255" step="1" value="0"/>
</div>
<div class="col s12 m12 l4">
<div class="green-text">Green: <output class="green-out">0</output></div>
<input class="slider green" type="range" min="0" max="255" step="1" value="0"/>
</div>
<div class="col s12 m12 l4">
<div class="blue-text">Blue: <output class="blue-out">0</output></div>
<input class="slider blue" type="range" min="0" max="255" step="1" value="0"/>
</div>
</div>
<script>
var mm = new MM();
function MM() {};
MM.prototype.loaded = function loaded() {
var el = document.body.querySelector('.color-select');
var clr = el && MM.Colorize.init({ parent: el, backgroundsSelect: '.hex-value-bg' });
if (clr) MM.Canvas.init({ parent: el, inputColor: clr, upload: function(formData, input){
alert('Complete upload to server using passed FormData');
}});
};
MM.clazzList = function clazzList(el) {
//if (el.classList) return el.classList;
var clo = {};
clo.clz = function(clzs, rplr) {
var arr = typeof rplr === 'boolean' ? Array.prototype.slice.call(clzs) : null, rm = rplr;
if (arr) rplr = function addrm(mtch, cn) {
arr.splice(arr.indexOf(cn), 1);
return rm ? '' : mtch;
};
for (var i = 0, l = clzs.length, isStr, val; i < l; ++i) {
clo.setClz(el, clzs[i], rplr);
}
return arr;
};
clo.setClz = function(el, cl, rplr) {
for (var i = 0, els = el instanceof Element ? [el] : el, ncl; i < els.length; ++i) {
var isStr = typeof els[i].className === 'string', rtyp = typeof rplr; // some elements don't support className the same way (e.g. svg)
var val = isStr ? els[i].className : els[i].getAttribute('class') || '';
ncl = cl && Array.isArray(cl) ? cl[i] || cl.join(' ') : cl; // try to match the element index with the passed class index or use all of the classes in combo
if (rtyp === 'string' || rtyp === 'function') val = val.replace(new RegExp('\\s*(' + ncl + ')\\s*', 'gi'), rplr);
else if (ncl) val += ' ' + ncl + ' ';
if (isStr) els[i].className = val;
else els[i].setAttribute('class', val);
}
};
return {
has: function hasClass(clz, all) {
if (!clz) return false;
for (var i = 0, els = el instanceof Element ? [el] : el, match = ' ' + clz + ' ', isMatch, mcnt = 0; i < els.length; ++i) {
isMatch = (' ' + els[i].className + ' ').replace(/[\n\t]/g, ' ').indexOf(match) > -1;
if ((!all && isMatch) || (all && !isMatch)) return isMatch;
if (isMatch) ++mcnt;
}
return mcnt === els.length;
},
remove: function rmer() {
clo.clz(arguments, ' ');
},
add: function addClass() {
for (var i = 0, clzs = clo.clz(arguments, false), l = clzs.length; i < l; ++i) {
clo.setClz(el, clzs[i]);
}
},
toggle: function tger() {
for (var i = 0, clzs = clo.clz(arguments, true), l = clzs.length; i < l; ++i) {
clo.setClz(el, clzs[i]);
}
}
};
};
MM.Color = {
blend: function(percentage, fromColor, toColor) {
var p = percentage, from = fromColor, to = toColor;
if (typeof(p) != 'number' || p <- 1 || p > 1 || typeof(from) != 'string' || (from[0] != 'r' && from[0] != '#') || (to && typeof(to) != 'string')) return null;
var h = typeof(to) === 'string' ? to.length > 9 ? true : to === 'c' ? from.length <= 9 : false : from.length > 9;
var b = p < 0, p = b ? p * -1 : p, to = to && to != 'c' ? to : b ? '#000000' : '#FFFFFF';
var f = from && typeof(from) === 'object' ? from : MM.Color.propertyToRGB(from), t = to && typeof(to) === 'object' ? to : MM.Color.propertyToRGB(to);
if (!f || !t) return null;
if (h) return 'rgb' + (f.a > -1 || t.a >- 1 ? 'a(' : '(') + Math.round((t.r - f.r) * p + f.r) + ',' + Math.round((t.g - f.g) * p + f.g)
+ ',' + Math.round((t.b - f.b) * p + f.b) + (f.a < 0 && t.a < 0 ? ')' : ','
+ (f.a > -1 && t.a > -1 ? Math.round(((t.a - f.a) * p + f.a) * 10000) / 10000 : t.a < 0 ? f.a : t.a) + ')');
else return '#' + (0x100000000 + Math.round((t.r - f.r) * p + f.r) * 0x1000000 + Math.round((t.g - f.g) * p + f.g) * 0x10000
+ Math.round((t.b - f.b) * p + f.b) * 0x100 + (f.a > -1 && t.a > -1 ? Math.round(((t.a - f.a) * p + f.a) *255) : t.a > -1
? Math.round(t.a * 255) : f.a > -1 ? Math.round(f.a * 255) : 255)).toString(16).slice(1, f.a > -1 || t.a > -1 ? undefined : -2);
},
contrast: function(rgb) {
var lum = 0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2];
return lum > 150 ? '000000' : 'FFFFFF';
},
brightness: function(hsl, percentage) {
var lum = hsl[2] + hsl[2] * (percentage / 100);
return [hsl[0], hsl[1], lum];
},
propertyToRGB: function(color) {
var d = color, l = d.length, RGB = {};
if (l > 9) {
d = d.split(',');
if (d.length < 3 || d.length > 4) return null;
RGB.r = parseInt(d[0].split('(')[1]),
RGB.g = parseInt(d[1]);
RGB.b = parseInt(d[2]);
RGB.a = d[3] ? parseFloat(d[3]) : -1;
RGB.isHex = false;
} else {
if (l == 8 || l == 6 || l < 4) return null;
if (l < 6) d = '#' + d[1] + d[1] + d[2] + d[2] + d[3] + d[3] + (l > 4 ? d[4] + '' + d[4] : ''); // allow for 3 or 4 digit hex values
d = parseInt(d.slice(1), 16);
RGB.r = d >> 16 & 255;
RGB.g = d >> 8 & 255;
RGB.b = d & 255;
RGB.a = -1;
RGB.isHex = true;
if (l == 9 || l == 5) RGB.a = Math.round((RGB.b / 255) * 10000) / 10000;
RGB.b = RGB.g;
RGB.g = RGB.r;
RGB.r = d >> 24 & 255;
}
return RGB;
},
convert360: function(n) {
return Math.round(n * 360);
},
convert100: function(n) {
return Math.round(n * 100);
},
rgbToHSL: function(r, g, b) {
if (arguments.length === 1 && Array.isArray(r)) { // hexToHSL
g = r[1] || 0;
b = r[2] || 0;
r = r[0] || 0;
}
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 [h, s, l];
},
rgbToHex: function(r, g, b) {
var hex = '';
if (r == null || g == null || b == null) return hex;
for (var i = 0, cls = [r, g, b], comp; i < cls.length; i++) {
comp = parseInt(cls[i]).toString(16);
hex += comp.length == 1 ? '0' + comp : comp;
}
return hex;
},
hexToRGB: function(hex) {
var r, g, b;
hex = hex.replace('#', '').match(/.{2}/g),
r = hex[0], g = hex[1], b = hex[2];
if (r == null || g == null || b == null) return false;
return [parseInt(r, 16), parseInt(g, 16), parseInt(b, 16)];
},
hueToRGB(v1, v2, vh) {
if (vh < 0) vh += 1;
if (vh > 1) vh -= 1;
if (6 * vh < 1) return v1 + (v2 - v1) * 6 * vh;
if (2 * vh < 1) return v2;
if (3 * vh < 2) return v1 + (v2 - v1) * (2 / 3 - vh) * 6;
return v1;
},
hslToRGB(h, s, l) {
var r, g, b, temp_1, temp_2;
if (s === 0) {
r = Math.round(l * 255);
g = Math.round(l * 255);
b = Math.round(l * 255);
} else {
temp_2 = l < .5 ? l * (1 + s) : l + s - s * l;
temp_1 = 2 * l - temp_2;
r = Math.round(255 * this.hueToRGB(temp_1, temp_2, h + 1 / 3));
g = Math.round(255 * this.hueToRGB(temp_1, temp_2, h));
b = Math.round(255 * this.hueToRGB(temp_1, temp_2, h - 1 / 3));
}
return [r, g, b];
},
hslToHex: function(h, s, l) {
var rgb = MM.Color.hslToRGB(h, s, l);
return MM.Color.rgbToHex(rgb[0], rgb[1], rgb[2]);
}
};
MM.Colorize = {
controls: function(options) {
var opts = options || {}, prnt = opts.parent || document;
return {
hue: prnt.querySelector('.slider.hue'),
sat: prnt.querySelector('.slider.saturation'),
lum: prnt.querySelector('.slider.luminance'),
red: prnt.querySelector('.slider.red'),
green: prnt.querySelector('.slider.green'),
blue: prnt.querySelector('.slider.blue'),
hsl: prnt.querySelector('.hsl-text'),
rgb: prnt.querySelector('.rgb-text'),
hex: prnt.querySelector('.hex'),
backgrounds: opts.backgroundsSelect && prnt.querySelectorAll(opts.backgroundsSelect),
hueOut: prnt.querySelector('output.hue-out'),
satOut: prnt.querySelector('output.saturation-out'),
lumOut: prnt.querySelector('output.luminance-out'),
redOut: prnt.querySelector('output.red-out'),
greenOut: prnt.querySelector('output.green-out'),
blueOut: prnt.querySelector('output.blue-out')
};
},
setRGB: function(r, g, b, els) {
els.red.value = r;
els.green.value = g;
els.blue.value = b;
if (els.rgb) els.rgb.value = r + ',' + g + ',' + b;
if (els.redOut) els.redOut.value = els.red.value;
if (els.greenOut) els.greenOut.value = els.green.value;
if (els.blueOut) els.blueOut.value = els.blue.value;
},
setTooltip: function(inp, out, fixed) {
out.value = fixed >= 0 ? parseFloat(inp.value || 0).toFixed(fixed) : inp.value;
out.style.setProperty('--val', inp.value);
var inpStyle = getComputedStyle(inp);
out.style.setProperty('--width', Math.round(inpStyle.width.replace(/[^\d\.]/g, '') || 0) + 'px');
out.style.setProperty('--height', Math.round(inpStyle.height.replace(/[^\d\.]/g, '') || 0) + 'px');
},
setHSL: function(h, s, l, els) {
els.hue.value = h;
els.sat.value = s;
els.lum.value = l;
var h2 = els.hsl || els.hueOut ? MM.Color.convert360(h) : null, s2 = els.hsl || els.satOut ? MM.Color.convert100(s) : null;
var l2 = els.hsl || els.lumOut ? MM.Color.convert100(s) : null;
if (els.hsl) els.hsl.value = h2 + ',' + s2 + '%,' + l2 + '%';
if (els.hueOut) els.hueOut.value = MM.Color.convert360(els.hue.value);
if (els.satOut) els.satOut.value = MM.Color.convert100(els.sat.value) + '%';
if (els.lumOut) els.lumOut.value = MM.Color.convert100(els.lum.value) + '%';
},
setBgs: function(els) {
if (els.hex && els.backgrounds) {
var tcolor = '#' + MM.Color.contrast(MM.Color.hexToRGB(els.hex.value));
for (var i = 0, chld; i < els.backgrounds.length; i++) {
els.backgrounds[i].style.backgroundColor = els.hex.value;
els.backgrounds[i].style.color = tcolor;
chld = els.backgrounds[i].querySelectorAll('path');
for (var j = 0; j < chld.length; j++) {
chld[j].setAttribute('fill', tcolor);
}
}
}
},
setHEX: function(hex, els) {
if (els.hex) els.hex.value = '#' + hex;
MM.Colorize.setBgs(els);
},
getHEX: function(hex) {
return hex.indexOf('#') !== -1 ? hex.substring(1) : hex;
},
hslChange: function(evt, els) {
var h = parseFloat(els.hue.value), s = parseFloat(els.sat.value), l = parseFloat(els.lum.value);
var rgb = MM.Color.hslToRGB(h, s, l);
var hex = MM.Color.rgbToHex(rgb[0], rgb[1], rgb[2]);
MM.Colorize.setRGB(rgb[0], rgb[1], rgb[2], els);
MM.Colorize.setHSL(h, s, l, els);
MM.Colorize.setHEX(hex, els);
},
rgbChange: function(evt, els) {
var r = parseFloat(els.red.value), g = parseFloat(els.green.value), b = parseFloat(els.blue.value);
var hsl = MM.Color.rgbToHSL(r, g, b);
var hex = MM.Color.rgbToHex(r, g, b);
MM.Colorize.setHSL(hsl[0], hsl[1], hsl[2], els);
MM.Colorize.setRGB(r, g, b, els);
MM.Colorize.setHEX(hex, els);
},
hexChange: function(evt, els) {
var hex = MM.Colorize.getHEX(els.hex.value);
if (hex.length === 3) hex = hex.repeat(2);
if (hex.length !== 6) return false;
var rgb = MM.Color.hexToRGB(hex);
var hsl = MM.Color.rgbToHSL(rgb[0], rgb[1], rgb[2]);
MM.Colorize.setRGB(rgb[0], rgb[1], rgb[2], els);
MM.Colorize.setHSL(hsl[0], hsl[1], hsl[2], els);
MM.Colorize.setBgs(els);
},
init: function(options) {
var els = MM.Colorize.controls(options);
if (els.hex) els.hex.addEventListener('input', function onHexInput(evt) {
requestAnimationFrame(function colorizeRequest() {
return MM.Colorize.hexChange(evt, els);
});
});
var hslListener = function(evt) {
requestAnimationFrame(function colorizeRequest() {
MM.Colorize.hslChange(evt, els);
})
};
els.hue.addEventListener('input', hslListener);
els.sat.addEventListener('input', hslListener);
els.lum.addEventListener('input', hslListener);
var rgbListener = function(evt) {
requestAnimationFrame(function colorizeRequest() {
MM.Colorize.rgbChange(evt, els);
});
};
els.red.addEventListener('input', rgbListener);
els.green.addEventListener('input', rgbListener);
els.blue.addEventListener('input', rgbListener);
if (els.hex) MM.Colorize.hexChange(null, els);
return els.hex;
}
};
MM.Canvas = {
createGraph: function(opts) {
opts = opts || {};
opts.parent = opts.parent || document;
var graph = {
parent: opts.parent,
canvas: opts.parent.querySelector('canvas.editor'),
inputCols: opts.parent.querySelector('.canvas-editor-cols'),
inputRows: opts.parent.querySelector('.canvas-editor-rows'),
inputBlocks: opts.parent.querySelector('.canvas-editor-blocks'),
inputPen: opts.parent.querySelector('.canvas-editor-pen'),
inputFill: opts.parent.querySelector('.canvas-editor-fill'),
inputSprayOrEyeDrop: opts.parent.querySelector('.canvas-editor-spray-or-eyedrop'),
inputDownload: opts.parent.querySelector('.canvas-editor-download'),
inputUpload: opts.parent.querySelector('.canvas-editor-upload'),
inputColor: opts.inputColor,
upload: opts.upload,
pixels: {},
timestamps: {},
threshold: opts.threshold || 50,
blankColor: opts.blankColor || '#000000',
bgColor: opts.bgColor || '#BBBBBB',
highlightColor: opts.highlightColor || 'rgba(221,221,221,0.5)',
blockSize: opts.blockSize,
x: 0, y: 0, ox: opts.offsetX || 0, oy: opts.offsetY || 0
};
graph.inputSel = graph.inputPen || graph.inputFill;
if (!graph.canvas) return false;
graph.context = graph.canvas.getContext('2d');
graph.gridCanvas = document.createElement('canvas');
graph.gridContext = graph.gridCanvas.getContext('2d');
MM.Canvas.resize(graph);
return graph;
},
resize: function(graph, rows, cols, blockSize) {
var oldw, oldh, oldb;
if (!graph.blockSize || !blockSize || blockSize !== graph.blockSize) {
oldb = graph.blockSize || 20;
if (graph.inputBlocks) graph.inputBlocks.value = blockSize || oldb;
graph.blockSize = (graph.inputBlocks && parseInt(graph.inputBlocks.value)) || blockSize || oldb;
}
if (!graph.gridColCount || !cols || cols !== graph.gridColCount || graph.blockSize !== oldb) {
oldw = graph.gridColCount || 16;
if (graph.inputCols) graph.inputCols.value = cols || oldw;
graph.gridColCount = (graph.inputCols && parseInt(graph.inputCols.value)) || cols || oldw;
graph.gridCanvasWidth = (graph.gridColCount * graph.blockSize) + 1;
graph.gridCanvas.width = graph.gridCanvasWidth;
graph.canvas.width = graph.gridCanvas.width + (graph.ox * 2);
}
if (!graph.gridRowCount || !rows || rows !== graph.gridRowCount || graph.blockSize !== oldb) {
oldh = graph.gridRowCount || 16;
if (graph.inputRows) graph.inputRows.value = rows || oldh;
graph.gridRowCount = (graph.inputRows && parseInt(graph.inputRows.value)) || rows || oldh;
graph.gridCanvasHeight = (graph.gridRowCount * graph.blockSize) + 1;
graph.gridCanvas.height = graph.gridCanvasHeight;
graph.canvas.height = graph.gridCanvas.height + (graph.oy * 2);
}
graph.gridContext.fillStyle = graph.bgColor;
graph.gridContext.fillRect(0, 0, graph.gridCanvasWidth, graph.gridCanvasHeight);
graph.grid = graph.gridContext.getImageData(0, 0, graph.gridCanvasWidth, graph.gridCanvasHeight);
MM.Canvas.paint(graph);
},
pixel: function(graph, row, col, val) {
var id = 'pixel_col' + col + '_row' + row;
if (val === true) val = graph.pixels[id] || {
id: id,
on: false,
color: graph.blankColor,
col: col,
row: row,
x: (col - 1) * graph.blockSize,
y: (row - 1) * graph.blockSize,
history: []
};
if (val) graph.pixels[id] = val;
else if (val === false) return graph.pixels[id] && delete graph.pixels[id];
return graph.pixels[id];
},
inRange: function(graph) {
return graph.x > 0 && graph.y > 0 && graph.x <= graph.ox + graph.gridCanvasWidth && graph.y <= graph.oy + graph.gridCanvasHeight;
},
inPoint: function(graph, pix) {
return graph.x >= (graph.ox + pix.x) && graph.x <= (graph.ox + pix.x + graph.blockSize - 1)
&& graph.y >= (graph.oy + pix.y) && graph.y <= (graph.oy + pix.y + graph.blockSize - 1);
},
paint: function(graph, setPix, highlight, onlyGet) {
var inRange = MM.Canvas.inRange(graph);
if (!inRange && onlyGet) return;
var forFill = !forDrop && graph.inputSel && graph.inputSel === graph.inputFill;
var forDrop = !forFill && graph.inputSel && graph.inputSel === graph.inputSprayOrEyeDrop;
var usePix = forFill && !onlyGet && inRange;
var tpix = usePix && MM.Canvas.paint(graph, false, false, true);
tpix = tpix ? { on: tpix.on, color: tpix.color, pix: tpix } : tpix;
for (var col = 1, pix, fx, fy, fw, fh, hlt, chg, ids = forDrop && graph.inputSel.checked && []; col <= graph.gridColCount; col++) {
for (var row = 1; row <= graph.gridRowCount; row++) {
pix = MM.Canvas.pixel(graph, row, col, true);
if (onlyGet) {
if (MM.Canvas.inPoint(graph, pix)) return pix;
continue;
}
hlt = false;
fx = graph.ox + pix.x + 1;
fy = graph.oy + pix.y + 1;
fw = graph.blockSize - 1;
fh = graph.blockSize - 1;
if (forFill && usePix) { // fill
hlt = highlight && tpix && tpix.color === pix.color;
if (setPix && tpix && tpix.color === pix.color) {
pix.on = graph.inputSel.checked;
pix.color = (pix.on && graph.inputColor.value) || graph.blankColor;
}
} else if (forDrop) {
if (graph.inputSel.checked) { // sprayer
if ((highlight || setPix) && MM.Canvas.inPoint(graph, pix)) {
hlt = highlight;
MM.Canvas.perimeter(graph, pix, setPix, graph.inputColor.value || graph.blankColor, ids);
if (setPix) {
pix.on = true;
pix.color = graph.inputColor.value || graph.blankColor;
}
}
} else if (setPix && MM.Canvas.inPoint(graph, pix)) { // dropper
hlt = highlight;
chg = graph.inputColor.value !== pix.color;
graph.inputColor.value = pix.color;
graph.inputColor.dispatchEvent(new Event('input'));
if (chg) graph.inputColor.dispatchEvent(new Event('change'));
}
} else if ((!forFill && !forDrop) && inRange && MM.Canvas.inPoint(graph, pix)) { // pen/eraser
hlt = highlight;
if (setPix) {
pix.on = graph.inputPen ? graph.inputPen.checked : pix.color;
pix.color = (pix.on && graph.inputColor.value) || graph.blankColor;
}
}
if (!ids || ids.indexOf(pix.id) < 0) {
graph.context.fillStyle = graph.blankColor;
graph.context.fillRect(fx, fy, fw, fh);
if (pix.on) {
graph.context.fillStyle = pix.color;
graph.context.fillRect(fx, fy, fw, fh);
}
}
if (hlt) {
graph.context.fillStyle = graph.inputSel && !graph.inputSel.checked ? graph.blankColor : graph.inputColor.value;
graph.context.fillRect(fx, fy, fw, fh);
}
}
}
},
perimeter: function(graph, pix, setPix, altColor, ids, reach) {
reach = reach || 1;
var fw = graph.blockWidth - 1, fh = graph.blockSize - 1;
for (var r = pix.row - reach, pixc, prcnt, hsl, hex; r <= pix.row + reach; r++) {
if (r < 1) continue;
else if (r > graph.gridRowCount) break;
for (var c = pix.col - reach; c <= pix.col + reach; c++) {
if (c < 1) continue;
else if (c > graph.gridColCount) break;
if (r === pix.row && c === pix.col) continue;
if (pixc = MM.Canvas.pixel(graph, r, c)) {
prcnt = pix.row === pixc.row || pix.col === pixc.col ? 50 : 80;
hsl = MM.Color.brightness(MM.Color.rgbToHSL(MM.Color.hexToRGB(altColor || pix.color)), prcnt);
hex = '#' + MM.Color.hslToHex(hsl[0], hsl[1], hsl[2]);
fx = graph.ox + pixc.x + 1;
fy = graph.oy + pixc.y + 1;
graph.context.fillStyle = hex;
graph.context.fillRect(fx, fy, fw, fh);
if (setPix) {
pixc.on = true;
pixc.color = hex;
}
if (ids) ids.push(pixc.id);
}
}
}
},
export: function(graph, transparent) {
var canvas = document.createElement('canvas'), context = canvas.getContext('2d');
var blankColor = transparent ? null : graph.blankColor;
canvas.width = graph.gridColCount;
canvas.height = graph.gridRowCount;
for (var y = 1, pix; y <= graph.gridRowCount; y++) {
for (var x = 1; x <= graph.gridColCount; x++) {
pix = MM.Canvas.pixel(graph, y, x);
if (pix.on || blankColor) {
context.fillStyle = pix.on ? pix.color : blankColor;
context.fillRect(x - 1, y - 1, 1, 1);
}
}
}
return canvas;
},
download: function(graph, transparent) {
var canvas = MM.Canvas.export(graph), link = document.createElement('a');
link.download = 'image.bmp';
link.href = canvas.toDataURL('image/bmp');
link.click();
},
upload: function(grpah, transparent) {
var canvas = MM.Canvas.export(graph);
canvas.toBlob(function blobed(blob) {
var formData = new FormData();
formData.append('bitmap', blob);
graph.upload(formData, graph.inputUpload);
});
},
init: function(options) {
var graph = MM.Canvas.createGraph(options);
if (!graph || !graph.canvas) return false;
var listener = function(evt) {
if (evt.type.indexOf('touch') === 0 && evt.type !== 'touchstart') evt.preventDefault();
var el = this, canvasFill, typ = evt.type;
var move = typ === 'touchmove' || typ === 'mousemove', up = typ === 'touchend' || typ === 'mouseup';
var down = typ === 'touchstart' || (typ === 'mousedown' && evt.button === 0);
var evp = (evt.changedTouches && evt.changedTouches[0]) || evt;
if (move || typ === 'mouseover') {
var rect = el.getBoundingClientRect();
graph.x = Math.floor(evp.clientX - (rect.left + document.body.scrollLeft));
graph.y = Math.floor(evp.clientY - (rect.top + document.body.scrollTop));
} else if (typ === 'touchend' || typ === 'mouseout') {
graph.x = 0;
graph.y = 0;
}
canvasFill = function(ts) {
var lts = graph.timestamps[typ];
if (!lts) lts = graph.timestamps[typ] = ts;
if (!typ !== 'init' && ts - lts < graph.threshold) return requestAnimationFrame(canvasFill);
if (typ === 'init') graph.context.putImageData(graph.grid, graph.ox + graph.x, graph.oy + graph.y);
graph.drawing = up ? false : (move && graph.drawing) || down;
MM.Canvas.paint(graph, graph.drawing, move || typ === 'mouseover', typ === 'mouseout');
};
requestAnimationFrame(canvasFill);
};
graph.canvas.addEventListener('touchstart', listener);
graph.canvas.addEventListener('touchmove', listener);
graph.canvas.addEventListener('touchend', listener);
graph.canvas.addEventListener('mouseover', listener);
graph.canvas.addEventListener('mouseout', listener);
graph.canvas.addEventListener('mousemove', listener);
graph.canvas.addEventListener('mousedown', listener);
graph.canvas.addEventListener('mouseup', listener);
if (graph.inputCols && graph.inputRows) {
var resizeListener = function resizeWidth(evt) {
var rows = parseInt((graph.inputRows && graph.inputRows.value) || 0);
var cols = parseInt((graph.inputCols && graph.inputCols.value) || 0);
var blks = parseInt((graph.inputBlocks && graph.inputBlocks.value) || 0);
MM.Canvas.resize(graph, rows, cols, blks);
};
graph.inputCols.addEventListener('change', resizeListener);
graph.inputRows.addEventListener('change', resizeListener);
}
if (graph.inputBlocks) graph.inputBlocks.addEventListener('change', function blockSize(evt) {
var rows = parseInt((graph.inputRows && graph.inputRows.value) || 0);
var cols = parseInt((graph.inputCols && graph.inputCols.value) || 0);
var blks = parseInt((graph.inputBlocks && graph.inputBlocks.value) || 0);
MM.Canvas.resize(graph, rows, cols, blks);
});
var toolbarListener = function(evt) {
if (graph.inputSel) MM.clazzList(graph.inputSel).remove('active');
graph.inputSel = evt.currentTarget;
MM.clazzList(graph.inputSel).add('active');
};
if (graph.inputPen) graph.inputPen.addEventListener('click', toolbarListener);
if (graph.inputFill) graph.inputFill.addEventListener('click', toolbarListener);
if (graph.inputSprayOrEyeDrop) graph.inputSprayOrEyeDrop.addEventListener('click', toolbarListener);
if (graph.inputDownload) graph.inputDownload.addEventListener('click', function download() {
MM.Canvas.download(graph);
});
if (graph.inputUpload) graph.inputUpload.addEventListener('click', function upload() {
MM.Canvas.upload(graph);
});
listener.call(graph.canvas, { type: 'init' });
}
};
MM.Rangify = {
getInputs: function(parent) {
return {
start: parent.querySelector('.slider.rangify-start'),
stop: parent.querySelector('.slider.rangify-stop'),
startOut: parent.querySelector('.rangify-start-out'),
stopOut: parent.querySelector('.rangify-stop-out'),
};
},
constrain: function(evt, els) {
var frmOut = evt && ((els.startOut && evt.target === els.startOut) || (els.stopOut && evt.target === els.stopOut));
var start = parseFloat(els[frmOut ? 'startOut' : 'start'].value);
var stop = els.stop ? parseFloat(els[frmOut ? 'stopOut' : 'stop'].value) : start;
els.start.value = Math.min(start, stop);
if (els.stop) els.stop.value = Math.max(start, stop);
if (els.startOut) {
els.startOut.value = els.start.value;
els.startOut.min = els.start.min;
els.startOut.max = els.start.max;
els.startOut.step = els.start.step;
}
if (els.stop && els.stopOut) {
els.stopOut.value = els.stop.value;
els.stopOut.min = els.stop.min;
els.stopOut.max = els.stop.max;
els.stopOut.step = els.stop.step;
}
},
init: function(parent) {
var els = MM.Rangify.getInputs(parent);
var constrainer = function(evt) {
requestAnimationFrame(function rangeRequest() {
MM.Rangify.constrain(evt, els);
});
};
els.start.addEventListener('input', constrainer);
if (els.stop) els.stop.addEventListener('input', constrainer);
if (els.startOut) els.startOut.addEventListener('change', constrainer);
if (els.stopOut) els.stopOut.addEventListener('change', constrainer);
MM.Rangify.constrain(null, els);
}
};
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment