Last active
July 14, 2019 20:44
-
-
Save ugate/88f9791107ec81964eb87d09826591f6 to your computer and use it in GitHub Desktop.
Pixel Editor Using Canvas https://gistpreview.github.io/?88f9791107ec81964eb87d09826591f6
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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