├── .eslintignore ├── .eslintrc ├── .gitignore ├── .jshintrc ├── .stylelintrc ├── README.md ├── assets ├── css │ ├── style.css │ ├── styles.css │ └── styles.min.css └── js │ └── script.js ├── gulpfile.js ├── index.html ├── package-lock.json ├── package.json └── src ├── js └── script.js └── scss ├── reset.scss └── styles.scss /.eslintignore: -------------------------------------------------------------------------------- 1 | /build 2 | /assets 3 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "yandex", 3 | "env": { 4 | "browser": true, 5 | "node": true 6 | }, 7 | "parser": "babel-eslint", 8 | "rules": { 9 | "quotes": [2, "single"], 10 | "strict": [2, "never"], 11 | "indent": ["error", 2] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .sass-cache 3 | _temp 4 | 5 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "boss": true, 3 | "node": true, 4 | "eqeqeq": true, 5 | "strict": false, 6 | "newcap": false, 7 | "undef": true, 8 | "unused": true, 9 | "onecase": true, 10 | "lastsemic": true 11 | } -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-standard", 3 | "rules": { 4 | "indentation": 2, 5 | "string-quotes": "double", 6 | "no-duplicate-selectors": true, 7 | "color-hex-case": "upper", 8 | "color-hex-length": "short", 9 | "selector-max-id": 0, 10 | "selector-combinator-space-after": "always", 11 | "selector-attribute-quotes": "always", 12 | "selector-type-case": "upper", 13 | "declaration-block-trailing-semicolon": "always", 14 | "declaration-no-important": true, 15 | "declaration-colon-space-before": "never", 16 | "declaration-colon-space-after": "always", 17 | "property-no-vendor-prefix": true, 18 | "value-no-vendor-prefix": true, 19 | "number-leading-zero": "never", 20 | "function-url-quotes": "always", 21 | "font-family-name-quotes": "always-where-required", 22 | "comment-whitespace-inside": "always", 23 | "comment-empty-line-before": "never", 24 | "at-rule-no-vendor-prefix": true, 25 | "selector-pseudo-element-colon-notation": "double", 26 | "selector-pseudo-class-parentheses-space-inside": "never", 27 | "selector-no-vendor-prefix": true, 28 | "media-feature-range-operator-space-before": "always", 29 | "media-feature-range-operator-space-after": "always", 30 | "media-feature-parentheses-space-inside": "never", 31 | "media-feature-colon-space-before": "never", 32 | "media-feature-colon-space-after": "always" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ShadowPainter 2 | ============= 3 | 4 | Js-creator of box-shadow animation. Just for fun : ) 5 | 6 | Play with it yoksel.github.io/shadowPainter/ 7 | -------------------------------------------------------------------------------- /assets/css/style.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, h1, h2, h3, h4, h5, h6, 2 | p, a, img, b, i, dl, dt, dd, ol, ul, li, 3 | label, article, aside, footer, header, section { 4 | margin: 0; 5 | padding: 0; 6 | border: 0; 7 | font-size: 100%; 8 | font: inherit; 9 | vertical-align: baseline; 10 | } 11 | 12 | body { 13 | line-height: 1; 14 | } 15 | 16 | ol, ul { 17 | list-style: none; 18 | } 19 | 20 | * { 21 | -webkit-box-sizing: border-box; 22 | -moz-box-sizing: border-box; 23 | box-sizing: border-box; 24 | } 25 | 26 | BODY { 27 | background: #FFF; 28 | font: 14px/1.4 'Trebuchet MS', Verdana, sans-serif; 29 | color: #333; 30 | } 31 | 32 | LABEL { 33 | cursor: pointer; 34 | } 35 | 36 | .l-wrapper { 37 | position: absolute; 38 | display: inline-block; 39 | margin: auto; 40 | left: 1em; 41 | right: 1em; 42 | top: 1em; 43 | bottom: 1em; 44 | border: 1px solid #CCC; 45 | white-space: nowrap; 46 | } 47 | .l-wrapper:after { 48 | content: ''; 49 | display: table; 50 | width: 100%; 51 | clear: both; 52 | } 53 | 54 | .b-boxes { 55 | position: relative; 56 | text-align: center; 57 | border: 1px solid #CCC; 58 | border-width: 1px 0; 59 | } 60 | 61 | .l-zero-gravity { 62 | position: absolute; 63 | width: 100%; 64 | top: 0; 65 | bottom: 0; 66 | right: 0; 67 | left: 0; 68 | margin: auto; 69 | } 70 | 71 | .b-box { 72 | position: relative; 73 | display: inline-block; 74 | border: 1px solid #CCC; 75 | } 76 | .b-box:nth-of-type(1) { 77 | margin-right: 20px; 78 | } 79 | 80 | .b-panel { 81 | padding: 10px; 82 | color: #888; 83 | } 84 | .b-panel:before, .b-panel:after { 85 | content: ''; 86 | display: table; 87 | width: 100%; 88 | clear: both; 89 | } 90 | 91 | .b-title { 92 | display: inline-block; 93 | padding-right: 10px; 94 | line-height: 30px; 95 | } 96 | 97 | .b-steps .b-title { 98 | float: none; 99 | display: inline-block; 100 | margin: 0 auto; 101 | padding: 0; 102 | } 103 | 104 | /* Paint Box 105 | ----------------------------- */ 106 | .b-box--paint { 107 | overflow: hidden; 108 | border-width: 0 1px 1px 0; 109 | background-image: -webkit-gradient(linear, left top, right top, from(#CCC), to(transparent)), -webkit-gradient(linear, left top, left bottom, from(#CCC), to(transparent)); 110 | background-image: -webkit-linear-gradient(left, #CCC 1px, transparent 1px), -webkit-linear-gradient(top, #CCC 1px, transparent 1px); 111 | background-image: linear-gradient(to right, #CCC 1px, transparent 1px), linear-gradient(to bottom, #CCC 1px, transparent 1px); 112 | background-position: 0 0; 113 | } 114 | 115 | /* Labels 116 | ----------------------------- */ 117 | .cell { 118 | display: block; 119 | float: left; 120 | } 121 | 122 | .cell__inp { 123 | position: absolute; 124 | display: none; 125 | } 126 | 127 | .cell__lbl { 128 | display: block; 129 | border-radius: 0; 130 | /*box-shadow: 0 0 2px #999 inset;*/ 131 | } 132 | 133 | :checked + .cell__lbl { 134 | position: relative; 135 | } 136 | 137 | .is-colored { 138 | background: orange; 139 | } 140 | 141 | /* Result Box 142 | ----------------------------- */ 143 | .b-box--result { 144 | overflow: hidden; 145 | } 146 | 147 | /* Dotts 148 | ----------------------------- */ 149 | .dot { 150 | display: block; 151 | position: absolute; 152 | border-radius: 0; 153 | } 154 | 155 | .dot--previous { 156 | opacity: .2; 157 | } 158 | 159 | @-webkit-keyframes shadows {} 160 | /* Palette 161 | ----------------------------- */ 162 | .b-palette { 163 | display: inline-block; 164 | } 165 | 166 | .items--colors { 167 | display: inline-block; 168 | vertical-align: middle; 169 | } 170 | 171 | .color { 172 | display: inline-block; 173 | width: 30px; 174 | height: 30px; 175 | } 176 | 177 | .color__inp { 178 | display: none; 179 | } 180 | 181 | .color__lbl { 182 | display: block; 183 | height: 30px; 184 | } 185 | 186 | :checked + .color__lbl { 187 | position: relative; 188 | z-index: 1; 189 | -webkit-box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.3); 190 | box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.3); 191 | } 192 | 193 | /* Color Controls 194 | ----------------------------- */ 195 | .items--colors-controls { 196 | display: inline-block; 197 | position: relative; 198 | height: 30px; 199 | margin-left: 5px; 200 | vertical-align: middle; 201 | } 202 | 203 | .colors-controls__item { 204 | position: absolute; 205 | border: 10px solid transparent; 206 | border-color: #a0a0a0 transparent; 207 | border-width: 10px 4px; 208 | cursor: pointer; 209 | } 210 | .colors-controls__item:hover { 211 | border-color: #878787 transparent; 212 | } 213 | .colors-controls__item:active { 214 | border-color: #545454 transparent; 215 | } 216 | 217 | .colors-controls__item--up { 218 | top: 0; 219 | border-top: 0; 220 | } 221 | .colors-controls__item--up:active { 222 | top: 2px; 223 | } 224 | 225 | .colors-controls__item--down { 226 | bottom: 0; 227 | border-bottom: 0; 228 | } 229 | .colors-controls__item--down:active { 230 | bottom: -2px; 231 | } 232 | 233 | /* Panel 234 | ----------------------------- */ 235 | .b-panel { 236 | position: relative; 237 | } 238 | 239 | .b-panel__item { 240 | float: left; 241 | margin-left: 32px; 242 | } 243 | .b-panel__item:first-child { 244 | margin-left: 0; 245 | } 246 | 247 | .text--value { 248 | width: 3.5em; 249 | height: 30px; 250 | line-height: 30px; 251 | pading: 0; 252 | border: 1px solid #CCC; 253 | border-radius: 5px; 254 | text-align: center; 255 | } 256 | 257 | /* Steps 258 | ----------------------------- */ 259 | .b-steps { 260 | text-align: center; 261 | -webkit-user-select: none; 262 | -moz-user-select: none; 263 | -ms-user-select: none; 264 | user-select: none; 265 | } 266 | 267 | .steps-control { 268 | display: inline-block; 269 | position: relative; 270 | width: 24px; 271 | height: 24px; 272 | line-height: 24px; 273 | margin: 0 6px; 274 | top: 0; 275 | background: lightgray; 276 | font-weight: bold; 277 | color: #444; 278 | cursor: pointer; 279 | -webkit-transition: all .1s; 280 | transition: all .2s; 281 | } 282 | 283 | .steps-control:hover { 284 | top: -1px; 285 | background: #f2f2f2; 286 | color: #555; 287 | } 288 | 289 | .steps-control:active { 290 | top: 1px; 291 | background: #a0a0a0; 292 | color: #FFF; 293 | } 294 | 295 | .is-disabled, 296 | .is-disabled:hover { 297 | top: 0; 298 | background: #f2f2f2; 299 | color: #AAA; 300 | cursor: default; 301 | } 302 | 303 | .items--steps { 304 | display: block; 305 | margin-top: 10px; 306 | } 307 | 308 | .step { 309 | display: inline-block; 310 | width: 30px; 311 | height: 30px; 312 | margin-right: 1px; 313 | line-height: 30px; 314 | background: #f2f2f2; 315 | text-align: center; 316 | } 317 | 318 | .step__inp { 319 | position: absolute; 320 | display: none; 321 | } 322 | 323 | .step__lbl { 324 | display: block; 325 | height: 30px; 326 | } 327 | 328 | .is--filled + .step__lbl { 329 | background: lightgray; 330 | } 331 | 332 | :checked + .step__lbl { 333 | position: relative; 334 | z-index: 1; 335 | background: #a0a0a0; 336 | -webkit-box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.3); 337 | box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.3); 338 | color: #FFF; 339 | } 340 | 341 | .is-hidden { 342 | display: none; 343 | } 344 | 345 | .frames-clear { 346 | position: absolute; 347 | top: 10px; 348 | } 349 | .frames-clear:hover { 350 | padding-top: 1px; 351 | } 352 | 353 | .frames-clear--current { 354 | left: 10px; 355 | border-bottom: 1px dotted; 356 | line-height: 1.2; 357 | color: #555; 358 | cursor: pointer; 359 | } 360 | 361 | .frames-clear--all { 362 | right: 10px; 363 | border-bottom: 1px dotted; 364 | line-height: 1.2; 365 | color: orangered; 366 | cursor: pointer; 367 | } 368 | 369 | .is--sleepy { 370 | color: #777; 371 | } 372 | 373 | /* Codes 374 | ----------------------------- */ 375 | .b-codes { 376 | /*display: none;*/ 377 | position: absolute; 378 | z-index: 10; 379 | top: 0; 380 | right: 0; 381 | left: 0; 382 | background: rgba(240, 240, 240, 0.9); 383 | -webkit-box-shadow: 0 0 20px rgba(0, 0, 0, 0.3); 384 | box-shadow: 0 0 20px rgba(0, 0, 0, 0.3); 385 | } 386 | 387 | .is-open { 388 | right: -.5em; 389 | left: -.5em; 390 | -webkit-box-shadow: 0 0 20px rgba(0, 0, 0, 0.3), 0 0 0 1000px rgba(255, 255, 255, 0.8); 391 | box-shadow: 0 0 20px rgba(0, 0, 0, 0.3), 0 0 0 1000px rgba(255, 255, 255, 0.8); 392 | } 393 | 394 | .b-codes__toggle { 395 | position: absolute; 396 | top: -24px; 397 | right: 10px; 398 | border-bottom: 1px dotted; 399 | line-height: 1.2; 400 | color: #555; 401 | cursor: pointer; 402 | } 403 | .b-codes__toggle:hover { 404 | padding-top: 1px; 405 | } 406 | 407 | .b-codes__wrapper { 408 | display: none; 409 | padding: 20px; 410 | border: 1px solid #BBB; 411 | } 412 | .is-open .b-codes__wrapper { 413 | display: block; 414 | } 415 | 416 | .b-codes--title { 417 | margin-bottom: 6.66667px; 418 | color: #777; 419 | } 420 | 421 | .b-codes__textarea { 422 | display: block; 423 | width: 100%; 424 | height: 120px; 425 | border: 1px solid #CCC; 426 | } 427 | 428 | .textarea--css { 429 | margin-bottom: 20px; 430 | } 431 | 432 | .hello { 433 | display: none; 434 | position: absolute; 435 | z-index: 10; 436 | height: 150px; 437 | width: 100%; 438 | top: -1em; 439 | bottom: -1em; 440 | margin: auto; 441 | background: rgba(0, 0, 0, 0.3); 442 | text-shadow: 0 0 3px rgba(0, 0, 0, 0.5); 443 | text-align: center; 444 | color: #FFF; 445 | } 446 | .hello H2 { 447 | margin-bottom: 10px; 448 | font-size: 4em; 449 | } 450 | .hello SPAN { 451 | font-size: 2em; 452 | } 453 | .hello:before { 454 | content: ""; 455 | display: inline-block; 456 | height: 150px; 457 | vertical-align: middle; 458 | } 459 | 460 | .hello__text { 461 | display: inline-block; 462 | vertical-align: middle; 463 | } 464 | 465 | @media (max-width: 760px) { 466 | .hello { 467 | display: block; 468 | } 469 | } 470 | 471 | /*# sourceMappingURL=style.unprefixed.css.map */ 472 | -------------------------------------------------------------------------------- /assets/css/styles.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, h1, h2, h3, h4, h5, h6, 2 | p, a, img, b, i, dl, dt, dd, ol, ul, li, 3 | label, article, aside, footer, header, section { 4 | margin: 0; 5 | padding: 0; 6 | border: 0; 7 | font-size: 100%; 8 | font: inherit; 9 | vertical-align: baseline; } 10 | 11 | body { 12 | line-height: 1; } 13 | 14 | ol, ul { 15 | list-style: none; } 16 | 17 | * { 18 | box-sizing: border-box; } 19 | 20 | HTML, 21 | BODY { 22 | height: 100%; } 23 | 24 | BODY { 25 | display: flex; 26 | align-items: center; 27 | justify-content: center; 28 | background: #FFF; 29 | font: 14px/1.4 'Trebuchet MS', Verdana, sans-serif; 30 | color: #333; } 31 | 32 | LABEL { 33 | cursor: pointer; } 34 | 35 | .l-wrapper { 36 | position: relative; 37 | border: 1px solid #CCC; 38 | white-space: nowrap; } 39 | .l-wrapper:after { 40 | content: ''; 41 | display: table; 42 | width: 100%; 43 | clear: both; } 44 | 45 | .b-boxes { 46 | position: relative; 47 | text-align: center; 48 | border: 1px solid #CCC; 49 | border-width: 1px 0; } 50 | 51 | .l-zero-gravity { 52 | position: absolute; 53 | width: 100%; 54 | top: 0; 55 | bottom: 0; 56 | right: 0; 57 | left: 0; 58 | margin: auto; } 59 | 60 | .b-box { 61 | position: relative; 62 | display: inline-block; 63 | border: 1px solid #CCC; } 64 | .b-box:nth-of-type(1) { 65 | margin-right: 20px; } 66 | 67 | .b-panel { 68 | padding: 10px; 69 | color: #888; } 70 | .b-panel:before, .b-panel:after { 71 | content: ''; 72 | display: table; 73 | width: 100%; 74 | clear: both; } 75 | 76 | .b-title { 77 | display: inline-block; 78 | padding-right: 10px; 79 | line-height: 30px; } 80 | 81 | .b-steps .b-title { 82 | float: none; 83 | display: inline-block; 84 | margin: 0 auto; 85 | padding: 0; } 86 | 87 | /* Paint Box 88 | ----------------------------- */ 89 | .b-box--paint { 90 | overflow: hidden; 91 | border-width: 0 1px 1px 0; 92 | background-image: linear-gradient(to right, #CCC 1px, transparent 1px), linear-gradient(to bottom, #CCC 1px, transparent 1px); 93 | background-position: 0 0; } 94 | 95 | /* Labels 96 | ----------------------------- */ 97 | .cell { 98 | display: block; 99 | float: left; } 100 | 101 | .cell__inp { 102 | position: absolute; 103 | display: none; } 104 | 105 | .cell__lbl { 106 | display: block; 107 | border-radius: 0; 108 | /*box-shadow: 0 0 2px #999 inset;*/ } 109 | 110 | :checked + .cell__lbl { 111 | position: relative; } 112 | 113 | .is-colored { 114 | background: orange; } 115 | 116 | /* Result Box 117 | ----------------------------- */ 118 | .b-box--result { 119 | overflow: hidden; } 120 | 121 | /* Dotts 122 | ----------------------------- */ 123 | .dot { 124 | display: block; 125 | position: absolute; 126 | border-radius: 0; } 127 | 128 | .dot--previous { 129 | opacity: .2; } 130 | 131 | @keyframes shadows {}/* Palette 132 | ----------------------------- */ 133 | .b-palette { 134 | display: inline-block; } 135 | 136 | .items--colors { 137 | display: inline-block; 138 | vertical-align: middle; } 139 | 140 | .color { 141 | display: inline-block; 142 | width: 30px; 143 | height: 30px; } 144 | 145 | .color__inp { 146 | display: none; } 147 | 148 | .color__lbl { 149 | display: block; 150 | height: 30px; } 151 | 152 | :checked + .color__lbl { 153 | position: relative; 154 | z-index: 1; 155 | box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.3); } 156 | 157 | /* Color Controls 158 | ----------------------------- */ 159 | .items--colors-controls { 160 | display: inline-block; 161 | position: relative; 162 | height: 30px; 163 | margin-left: 5px; 164 | vertical-align: middle; } 165 | 166 | .colors-controls__item { 167 | position: absolute; 168 | border: 10px solid transparent; 169 | border-color: #a0a0a0 transparent; 170 | border-width: 10px 4px; 171 | cursor: pointer; } 172 | .colors-controls__item:hover { 173 | border-color: #878787 transparent; } 174 | .colors-controls__item:active { 175 | border-color: #545454 transparent; } 176 | 177 | .colors-controls__item--up { 178 | top: 0; 179 | border-top: 0; } 180 | .colors-controls__item--up:active { 181 | top: 2px; } 182 | 183 | .colors-controls__item--down { 184 | bottom: 0; 185 | border-bottom: 0; } 186 | .colors-controls__item--down:active { 187 | bottom: -2px; } 188 | 189 | /* Panel 190 | ----------------------------- */ 191 | .b-panel { 192 | position: relative; } 193 | 194 | .b-panel__item { 195 | float: left; 196 | margin-left: 32px; } 197 | .b-panel__item:first-child { 198 | margin-left: 0; } 199 | 200 | .text--value { 201 | width: 3.5em; 202 | height: 30px; 203 | line-height: 30px; 204 | pading: 0; 205 | border: 1px solid #CCC; 206 | border-radius: 5px; 207 | text-align: center; } 208 | 209 | /* Steps 210 | ----------------------------- */ 211 | .b-steps { 212 | text-align: center; 213 | -webkit-user-select: none; 214 | -ms-user-select: none; 215 | user-select: none; } 216 | 217 | .steps-control { 218 | display: inline-block; 219 | position: relative; 220 | width: 24px; 221 | height: 24px; 222 | line-height: 24px; 223 | margin: 0 6px; 224 | top: 0; 225 | background: lightgray; 226 | font-weight: bold; 227 | color: #444; 228 | cursor: pointer; 229 | transition: all .2s; } 230 | 231 | .steps-control:hover { 232 | top: -1px; 233 | background: #f2f2f2; 234 | color: #555; } 235 | 236 | .steps-control:active { 237 | top: 1px; 238 | background: #a0a0a0; 239 | color: #FFF; } 240 | 241 | .is-disabled, 242 | .is-disabled:hover { 243 | top: 0; 244 | background: #f2f2f2; 245 | color: #AAA; 246 | cursor: default; } 247 | 248 | .items--steps { 249 | display: block; 250 | margin-top: 10px; } 251 | 252 | .step { 253 | display: inline-block; 254 | width: 30px; 255 | height: 30px; 256 | margin-right: 1px; 257 | line-height: 30px; 258 | background: #f2f2f2; 259 | text-align: center; } 260 | 261 | .step__inp { 262 | position: absolute; 263 | display: none; } 264 | 265 | .step__lbl { 266 | display: block; 267 | height: 30px; } 268 | 269 | .is--filled + .step__lbl { 270 | background: lightgray; } 271 | 272 | :checked + .step__lbl { 273 | position: relative; 274 | z-index: 1; 275 | background: #a0a0a0; 276 | box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.3); 277 | color: #FFF; } 278 | 279 | .is-hidden { 280 | display: none; } 281 | 282 | .frames-clear { 283 | position: absolute; 284 | top: 10px; } 285 | .frames-clear:hover { 286 | padding-top: 1px; } 287 | 288 | .frames-clear--current { 289 | left: 10px; 290 | border-bottom: 1px dotted; 291 | line-height: 1.2; 292 | color: #555; 293 | cursor: pointer; } 294 | 295 | .frames-clear--all { 296 | right: 10px; 297 | border-bottom: 1px dotted; 298 | line-height: 1.2; 299 | color: orangered; 300 | cursor: pointer; } 301 | 302 | .is--sleepy { 303 | color: #777; } 304 | 305 | /* Codes 306 | ----------------------------- */ 307 | .b-codes { 308 | /*display: none;*/ 309 | position: absolute; 310 | z-index: 10; 311 | top: 0; 312 | right: 0; 313 | left: 0; 314 | background: rgba(240, 240, 240, 0.9); 315 | box-shadow: 0 0 20px rgba(0, 0, 0, 0.3); } 316 | 317 | .is-open { 318 | right: -.5em; 319 | left: -.5em; 320 | box-shadow: 0 0 20px rgba(0, 0, 0, 0.3), 0 0 0 1000px rgba(255, 255, 255, 0.8); } 321 | 322 | .b-codes__toggle { 323 | position: absolute; 324 | top: -24px; 325 | right: 10px; 326 | border-bottom: 1px dotted; 327 | line-height: 1.2; 328 | color: #555; 329 | cursor: pointer; } 330 | .b-codes__toggle:hover { 331 | padding-top: 1px; } 332 | 333 | .b-codes__wrapper { 334 | display: none; 335 | padding: 20px; 336 | border: 1px solid #BBB; } 337 | .is-open .b-codes__wrapper { 338 | display: block; } 339 | 340 | .b-codes--title { 341 | margin-bottom: 6.66667px; 342 | color: #777; } 343 | 344 | .b-codes__textarea { 345 | display: block; 346 | width: 100%; 347 | height: 120px; 348 | border: 1px solid #CCC; } 349 | 350 | .textarea--css { 351 | margin-bottom: 20px; } 352 | 353 | .hello { 354 | display: none; 355 | position: absolute; 356 | z-index: 10; 357 | height: 150px; 358 | width: 100%; 359 | top: -1em; 360 | bottom: -1em; 361 | margin: auto; 362 | background: rgba(0, 0, 0, 0.3); 363 | text-shadow: 0 0 3px rgba(0, 0, 0, 0.5); 364 | text-align: center; 365 | color: #FFF; } 366 | .hello H2 { 367 | margin-bottom: 10px; 368 | font-size: 4em; } 369 | .hello SPAN { 370 | font-size: 2em; } 371 | .hello:before { 372 | content: ""; 373 | display: inline-block; 374 | height: 150px; 375 | vertical-align: middle; } 376 | 377 | .hello__text { 378 | display: inline-block; 379 | vertical-align: middle; } 380 | 381 | @media (max-width: 760px) { 382 | 383 | .hello { 384 | display: block; } } 385 | -------------------------------------------------------------------------------- /assets/css/styles.min.css: -------------------------------------------------------------------------------- 1 | a,article,aside,b,body,dd,div,dl,dt,footer,h1,h2,h3,h4,h5,h6,header,html,i,img,label,li,ol,p,section,span,ul{margin:0;padding:0;border:0;font-size:100%;font:inherit;vertical-align:baseline}body{line-height:1}ol,ul{list-style:none}*{box-sizing:border-box}BODY,HTML{height:100%}BODY{display:flex;align-items:center;justify-content:center;background:#fff;font:14px/1.4 Trebuchet MS,Verdana,sans-serif;color:#333}LABEL{cursor:pointer}.l-wrapper{position:relative;border:1px solid #ccc;white-space:nowrap}.l-wrapper:after{content:"";display:table;width:100%;clear:both}.b-boxes{position:relative;text-align:center;border:1px solid #ccc;border-width:1px 0}.l-zero-gravity{position:absolute;width:100%;top:0;bottom:0;right:0;left:0;margin:auto}.b-box{position:relative;display:inline-block;border:1px solid #ccc}.b-box:first-of-type{margin-right:20px}.b-panel{padding:10px;color:#888}.b-panel:after,.b-panel:before{content:"";display:table;width:100%;clear:both}.b-title{display:inline-block;padding-right:10px;line-height:30px}.b-steps .b-title{float:none;display:inline-block;margin:0 auto;padding:0}.b-box--paint{overflow:hidden;border-width:0 1px 1px 0;background-image:linear-gradient(90deg,#ccc 1px,transparent 0),linear-gradient(180deg,#ccc 1px,transparent 0);background-position:0 0}.cell{display:block;float:left}.cell__inp{position:absolute;display:none}.cell__lbl{display:block;border-radius:0}:checked+.cell__lbl{position:relative}.is-colored{background:orange}.b-box--result{overflow:hidden}.dot{display:block;position:absolute;border-radius:0}.dot--previous{opacity:.2}.b-palette,.items--colors{display:inline-block}.items--colors{vertical-align:middle}.color{display:inline-block;width:30px;height:30px}.color__inp{display:none}.color__lbl{display:block;height:30px}:checked+.color__lbl{position:relative;z-index:1;box-shadow:0 0 0 3px rgba(0,0,0,.3)}.items--colors-controls{display:inline-block;position:relative;height:30px;margin-left:5px;vertical-align:middle}.colors-controls__item{position:absolute;border:10px solid transparent;border-color:#a0a0a0 transparent;border-width:10px 4px;cursor:pointer}.colors-controls__item:hover{border-color:#878787 transparent}.colors-controls__item:active{border-color:#545454 transparent}.colors-controls__item--up{top:0;border-top:0}.colors-controls__item--up:active{top:2px}.colors-controls__item--down{bottom:0;border-bottom:0}.colors-controls__item--down:active{bottom:-2px}.b-panel{position:relative}.b-panel__item{float:left;margin-left:32px}.b-panel__item:first-child{margin-left:0}.text--value{width:3.5em;height:30px;line-height:30px;pading:0;border:1px solid #ccc;border-radius:5px;text-align:center}.b-steps{text-align:center;-webkit-user-select:none;-ms-user-select:none;user-select:none}.steps-control{display:inline-block;position:relative;width:24px;height:24px;line-height:24px;margin:0 6px;top:0;background:#d3d3d3;font-weight:700;color:#444;cursor:pointer;transition:all .2s}.steps-control:hover{top:-1px;background:#f2f2f2;color:#555}.steps-control:active{top:1px;background:#a0a0a0;color:#fff}.is-disabled,.is-disabled:hover{top:0;background:#f2f2f2;color:#aaa;cursor:default}.items--steps{display:block;margin-top:10px}.step{display:inline-block;width:30px;height:30px;margin-right:1px;line-height:30px;background:#f2f2f2;text-align:center}.step__inp{position:absolute;display:none}.step__lbl{display:block;height:30px}.is--filled+.step__lbl{background:#d3d3d3}:checked+.step__lbl{position:relative;z-index:1;background:#a0a0a0;box-shadow:0 0 0 3px rgba(0,0,0,.3);color:#fff}.is-hidden{display:none}.frames-clear{position:absolute;top:10px}.frames-clear:hover{padding-top:1px}.frames-clear--current{left:10px;color:#555}.frames-clear--all,.frames-clear--current{border-bottom:1px dotted;line-height:1.2;cursor:pointer}.frames-clear--all{right:10px;color:#ff4500}.is--sleepy{color:#777}.b-codes{position:absolute;z-index:2;top:0;right:0;left:0;background:hsla(0,0%,94%,.9);box-shadow:0 0 20px rgba(0,0,0,.3)}.is-open{right:-.5em;left:-.5em;box-shadow:0 0 20px rgba(0,0,0,.3),0 0 0 1000px hsla(0,0%,100%,.8)}.b-codes__toggle{position:absolute;top:-24px;right:10px;border-bottom:1px dotted;line-height:1.2;color:#555;cursor:pointer}.b-codes__toggle:hover{padding-top:1px}.b-codes__wrapper{display:none;padding:20px;border:1px solid #bbb}.is-open .b-codes__wrapper{display:block}.b-codes--title{margin-bottom:6.66667px;color:#777}.b-codes__textarea{display:block;width:100%;height:120px;border:1px solid #ccc}.textarea--css{margin-bottom:20px}.hello{display:none;position:absolute;z-index:2;height:150px;width:100%;top:-1em;bottom:-1em;margin:auto;background:rgba(0,0,0,.3);text-shadow:0 0 3px rgba(0,0,0,.5);text-align:center;color:#fff}.hello H2{margin-bottom:10px;font-size:4em}.hello SPAN{font-size:2em}.hello:before{content:"";height:150px}.hello:before,.hello__text{display:inline-block;vertical-align:middle}@media (max-width:760px){.hello{display:block}} -------------------------------------------------------------------------------- /assets/js/script.js: -------------------------------------------------------------------------------- 1 | var line = '------------------------'; 2 | 3 | function ShadowPainter() { 4 | this.timer; 5 | this.isMousePressed = false; 6 | 7 | this.classNames = { 8 | wrapper: '.l-wrapper', 9 | paintBox: '.b-box--paint', 10 | resultBox: '.b-box--result', 11 | resultDot: '.b-box--result .dot', 12 | palette: '.b-palette', 13 | steps: '.b-steps', 14 | 15 | codes: '.b-codes', 16 | codesToggle: '.b-codes__toggle', 17 | codesCSS: '.textarea--css', 18 | codesHtml: '.textarea--html', 19 | 20 | durationInp: '.text--duration', 21 | sizeInp: '.text--size', 22 | dotsInp: '.text--dots', 23 | isRunning: 'is-running', 24 | }; 25 | 26 | this.Elems = {}; 27 | 28 | this.Cell = { 29 | className: 'cell', 30 | inputType: 'checkbox', 31 | inputClass: 'cell__inp', 32 | labelClass: 'cell__lbl', 33 | inputData: 'data-hpos="{hpos}" data-vpos="{vpos}" ', 34 | labelContent: '', 35 | dots: '' 36 | }; 37 | 38 | this.Palettes = [ 39 | ['#3FB8AF','#7FC7AF','#DAD8A7','#FF9E9D','#FF3D7F'], 40 | ['#468966','#FFF0A5','#FFB03B','#B64926','#8E2800'], 41 | ['#004358','#1F8A70','#BEDB39','#FFE11A','#FD7400'], 42 | ['#96CA2D','#B5E655','#EDF7F2','#4BB5C1','#7FC6BC'], 43 | ['#2E0927','#D90000','#FF2D00','#FF8C00','#04756F'], 44 | ['#FCFFF5','#D1DBBD','#91AA9D','#3E606F','#193441'], 45 | ['#332532','#644D52','#F77A52','#FF974F','#A49A87'] 46 | ]; 47 | 48 | this.Colors = this.initColors(); 49 | this.Step = this.initStep(); 50 | 51 | this.stylesClassNames = { 52 | config: 'configBox', 53 | shadows: 'stylesBox', 54 | colors: 'colorsBox' 55 | }; 56 | this.Styles = {}; 57 | 58 | this.Output = { 59 | HTML: '', 60 | CSS: '', 61 | Animation: '', 62 | comment: 'Created in shadowPainter : )' 63 | }; 64 | 65 | this.Scene = { 66 | oneSide: 5,// dottes in line 67 | oneSideMax: 30,// dottes in line 68 | size: 250, 69 | dotSize: 30,// pixels 70 | padding: 20,// pixels. Don't change it 71 | border: 1// pixels 72 | }; 73 | 74 | this.Anim = { 75 | steps: 5, 76 | stepsMax: 20, 77 | duration: '1s', 78 | name: 'shadows', 79 | keyframes: findKeyFrames('shadows') 80 | }; 81 | this.Anim.rules = this.Anim.keyframes.cssRules; 82 | 83 | this.isCodeOpened = false; 84 | 85 | this.Frames = {}; 86 | this.currentFrame = 0; 87 | 88 | this.init = function () { 89 | this.createTemplates(); 90 | this.createStylesBoxes(); 91 | 92 | this.initElements(); 93 | this.addConfig(); 94 | this.createInputsSet(); 95 | this.createFramesSet(); 96 | this.createPalette(); 97 | this.createSteps(); 98 | this.createCodes(); 99 | this.createDurationInp(); 100 | this.createSizeInp(); 101 | this.createDotsInp(); 102 | }; 103 | 104 | } // End ShadowPainter() 105 | 106 | // ----------------------------------------- 107 | 108 | ShadowPainter.prototype.setTimer = function(func, params) { 109 | if (this.timer) { 110 | this.clearTimeout(this.timer); 111 | } 112 | var execFunc = function () { 113 | func(params); 114 | }; 115 | this.timer = this.setTimeout(execFunc , 50); 116 | }; 117 | 118 | // ----------------------------------------- 119 | 120 | ShadowPainter.prototype.getAnimStr = function() { 121 | return `${this.Anim.duration} ${this.Anim.name} linear infinite`; 122 | }; 123 | 124 | // ----------------------------------------- 125 | 126 | ShadowPainter.prototype.initElements = function () { 127 | for (var className in this.classNames) { 128 | this.Elems[className] = document.querySelector(this.classNames[className]); 129 | } 130 | }; 131 | 132 | // ----------------------------------------- 133 | 134 | ShadowPainter.prototype.setParams = function () { 135 | let dotSize = (this.Scene.size / this.Scene.oneSide); 136 | this.Scene.dotSize = dotSize.toFixed(); 137 | this.Scene.size = this.Scene.dotSize * this.Scene.oneSide; 138 | 139 | this.configElemParams = { 140 | '.l-wrapper': { 141 | 'width': 700, // size * 2 + this.Scene.padding * 3 + border*6 142 | 'height': 490, // size + this.Scene.padding * 2 + border*4 143 | }, 144 | '.b-boxes': { 145 | 'height': 340 146 | }, 147 | '.l-zero-gravity': { 148 | 'height': this.Scene.size + this.Scene.border 149 | }, 150 | '.b-box': { 151 | 'width': this.Scene.size + this.Scene.border, 152 | 'height': this.Scene.size 153 | }, 154 | '.cell__lbl': { 155 | 'width': this.Scene.dotSize, 156 | 'height': this.Scene.dotSize 157 | }, 158 | '.dot': { 159 | 'width': this.Scene.dotSize, 160 | 'height': this.Scene.dotSize, 161 | 'top': '-' + this.Scene.dotSize, 162 | 'left': '-' + this.Scene.dotSize 163 | }, 164 | '.is-running': { 165 | 'animation': this.getAnimStr() 166 | }, 167 | '.b-box--paint': { 168 | 'background-size': this.Scene.dotSize + ' ' + this.Scene.dotSize 169 | } 170 | }; 171 | }; 172 | 173 | // ----------------------------------------- 174 | 175 | ShadowPainter.prototype.setOutputParams = function () { 176 | this.outputElemParams = { 177 | '.box': { 178 | 'position': 'absolute', 179 | 'top': '0', 180 | 'bottom': '0', 181 | 'left': '0', 182 | 'right': '0', 183 | 'margin': 'auto', 184 | 'width': this.Scene.size, 185 | 'height': this.Scene.size, 186 | 'overflow': 'hidden', 187 | 'border': '1px solid #DDD' 188 | }, 189 | '.dot': { 190 | 'display': 'block', 191 | 'position': 'absolute', 192 | 'width': this.Scene.dotSize, 193 | 'height': this.Scene.dotSize, 194 | 'top': '-' + this.Scene.dotSize, 195 | 'left': '-' + this.Scene.dotSize, 196 | 'animation': this.getAnimStr() 197 | } 198 | }; 199 | }; 200 | 201 | // ----------------------------------------- 202 | 203 | ShadowPainter.prototype.addConfig = function () { 204 | this.setParams(); 205 | 206 | var styles = ''; 207 | for (var elem in this.configElemParams) { 208 | var className = elem; 209 | var elemStyles = ''; 210 | var params = this.configElemParams[elem]; 211 | for (var item in params) { 212 | var value = addUnits(params[item], item); 213 | elemStyles += item + ': ' + value + ';\n'; 214 | } 215 | styles += className + ' {\n' + elemStyles + '}\n'; 216 | } 217 | 218 | this.Styles.config.innerHTML = styles; 219 | }; 220 | 221 | // ----------------------------------------- 222 | 223 | ShadowPainter.prototype.createStylesBoxes = function () { 224 | for (var styleBox in this.stylesClassNames) { 225 | this.Styles[styleBox] = addStylesElem(this.stylesClassNames[styleBox]); 226 | } 227 | }; 228 | 229 | // ----------------------------------------- 230 | 231 | ShadowPainter.prototype.createTemplates = function () { 232 | var templatesConfig = [this.Cell, this.Colors, this.Step]; 233 | 234 | for (var i = 0; i < templatesConfig.length; i++) { 235 | var item = templatesConfig[i]; 236 | item.template = createTemplate(item); 237 | } 238 | }; 239 | 240 | // ----------------------------------------- 241 | 242 | ShadowPainter.prototype.createControls = function (data) { 243 | var output = ''; 244 | 245 | var counter = 0; 246 | for (var item in data.list) { 247 | data.replacements['{action}'] = item; 248 | data.replacements['{text}'] = data.list[ item ]; 249 | output += fillTemplate(data.template, data.replacements); 250 | 251 | if (counter === 0 && data.insertBetween) { 252 | output += data.insertBetween; 253 | } 254 | counter++; 255 | } 256 | return output; 257 | }; 258 | 259 | // ----------------------------------------- 260 | 261 | ShadowPainter.prototype.addDataDefautlt = function (elem, defaultValue) { 262 | var defData = elem.getAttribute('data-default'); 263 | 264 | if (defData === null) { 265 | elem.setAttribute('data-default', defaultValue); 266 | } 267 | }; 268 | 269 | // ----------------------------------------- 270 | 271 | ShadowPainter.prototype.addEvents = function (itemsClass, func) { 272 | itemsClass = checkClassDot(itemsClass); 273 | var items = document.querySelectorAll(itemsClass); 274 | var parent = this; 275 | 276 | for (var i = 0; i < items.length; i++) { 277 | items[i].onclick = function () { 278 | func.call(parent, this); 279 | }; 280 | } 281 | }; 282 | 283 | // ----------------------------------------- 284 | 285 | ShadowPainter.prototype.addOverEvents = function (itemsClass, func) { 286 | itemsClass = checkClassDot(itemsClass); 287 | var items = document.querySelectorAll(itemsClass); 288 | var parent = this; 289 | 290 | for (var i = 0; i < items.length; i++) { 291 | items[i].onmousedown = function () { 292 | this.isMousePressed = true; 293 | }; 294 | 295 | items[i].onmouseup = function () { 296 | this.isMousePressed = false; 297 | }; 298 | 299 | items[i].onmouseover = function () { 300 | if (this.isMousePressed) { 301 | func.call(parent, this); 302 | } 303 | }; 304 | } 305 | }; 306 | 307 | // ----------------------------------------- 308 | 309 | ShadowPainter.prototype.checkInputValue = function (params) { 310 | var elem = params.elem; 311 | var func = params.func; 312 | var parent = params.parent; 313 | 314 | var minMax = minMaxDef(elem); 315 | var isNan = valueSetDefaultIfNAN(elem); 316 | 317 | if (isNan) { 318 | elem.value = isNan; 319 | } 320 | 321 | if (minMax) { 322 | elem.value = minMax; 323 | } 324 | 325 | func.call(parent, elem); 326 | }; 327 | 328 | // ----------------------------------------- 329 | 330 | ShadowPainter.prototype.addOnChangeEvents = function (itemsClass, func) { 331 | var items = document.querySelectorAll(itemsClass); 332 | var parent = this; 333 | 334 | var params = { 335 | func: func, 336 | parent: parent 337 | }; 338 | 339 | for (var i = 0; i < items.length; i++) { 340 | items[i].onkeyup = function (event) { 341 | params.elem = this; 342 | 343 | if (event.keyCode === 38 || event.keyCode === 40) { 344 | 345 | this.value = getValByKeyCode(this, event.keyCode, event.shiftKey); 346 | func.call(parent, this); 347 | } 348 | else { 349 | this.setTimer(parent.checkInputValue, params); 350 | } 351 | }; 352 | 353 | items[i].onchange = function () { 354 | params.elem = this; 355 | parent.checkInputValue(params); 356 | }; 357 | 358 | items[i].onblur = function () { 359 | params.elem = this; 360 | parent.checkInputValue(params); 361 | }; 362 | } 363 | }; 364 | 365 | // ----------------------------------------- 366 | 367 | ShadowPainter.prototype.createInputsSet = function () { 368 | this.Elems.paintBox.innerHTML = this.Cell.dots; 369 | var output = ''; 370 | 371 | for (var i = 0; i < this.Scene.oneSide * this.Scene.oneSide; i++) { 372 | var hpos = i % this.Scene.oneSide + 1; 373 | var vpos = Math.floor(i / this.Scene.oneSide) + 1; 374 | 375 | var replacements = { 376 | '{i}': i, 377 | '{hpos}': hpos, 378 | '{vpos}': vpos 379 | }; 380 | var checkBox = fillTemplate(this.Cell.template, replacements); 381 | output += checkBox; 382 | } 383 | 384 | this.Elems.paintBox.innerHTML += ``; 385 | 386 | this.addOverEvents(this.Cell.labelClass, this.onOverLabel); 387 | this.addEvents(this.Cell.inputClass, this.onClickCell); 388 | }; 389 | 390 | // ----------------------------------------- 391 | 392 | ShadowPainter.prototype.createFramesSet = function () { 393 | for (var k = 0; k < this.Anim.stepsMax; k++) { 394 | this.Frames[k] = {active: 0}; 395 | 396 | for (var hpos = 0; hpos < this.Scene.oneSideMax; hpos++) { // verticals 397 | this.Frames[k][hpos] = {}; 398 | 399 | for (var vpos = 0; vpos < this.Scene.oneSideMax; vpos++) { // gorizontals 400 | 401 | this.Frames[k][hpos][vpos] = { 402 | 'hpos': hpos, 403 | 'vpos': vpos, 404 | 'color': this.Colors.transparent 405 | }; 406 | } // End gorizontals 407 | } // End verticals 408 | } 409 | }; 410 | 411 | // ----------------------------------------- 412 | 413 | ShadowPainter.prototype.resetCurrentFrame = function () { 414 | var k = this.currentFrame; 415 | this.Frames[k] = {active: 0}; 416 | 417 | for (var hpos = 0; hpos < this.Scene.oneSideMax; hpos++) { // verticals 418 | this.Frames[k][hpos] = {}; 419 | 420 | for (var vpos = 0; vpos < this.Scene.oneSideMax; vpos++) { // gorizontals 421 | this.Frames[k][hpos][vpos] = { 422 | 'color': this.Colors.transparent 423 | }; 424 | } // End gorizontals 425 | } // End verticals 426 | }; 427 | 428 | // ----------------------------------------- 429 | 430 | ShadowPainter.prototype.toggleColorClass = function (elem) { 431 | var findClass = this.Colors.className + '--'; 432 | var classes = elem.classList; 433 | 434 | for (var i = 0; i < classes.length; i++) { 435 | 436 | if (classes[i].indexOf(findClass) >= 0) { 437 | classes.remove(classes[i]); 438 | return; 439 | } 440 | } 441 | 442 | classes.add(findClass + this.Colors.currentNum); 443 | }; 444 | 445 | // ----------------------------------------- 446 | 447 | ShadowPainter.prototype.onClickCell = function (elem) { 448 | this.updateFrames(elem); 449 | }; 450 | 451 | // ----------------------------------------- 452 | 453 | ShadowPainter.prototype.onOverLabel = function (elem) { 454 | var input = elem.previousSibling; 455 | 456 | if (input.checked === true) { 457 | input.checked = false; 458 | } 459 | else { 460 | input.checked = true; 461 | } 462 | 463 | this.updateFrames(input); 464 | }; 465 | 466 | // ----------------------------------------- 467 | 468 | ShadowPainter.prototype.updateFrames = function (elem) { 469 | var hpos = elem.getAttribute('data-hpos'); 470 | var vpos = elem.getAttribute('data-vpos'); 471 | 472 | var place = this.Frames[this.currentFrame]; 473 | var color = this.Colors.transparent; 474 | 475 | if (elem.checked) { 476 | color = this.Colors.current; 477 | place.active++; 478 | } 479 | else { 480 | place.active--; 481 | } 482 | 483 | place[hpos][vpos].color = color; 484 | this.paintShadow(); 485 | }; 486 | 487 | // ----------------------------------------- 488 | 489 | ShadowPainter.prototype.paintShadow = function () { 490 | var styles = ''; 491 | var framesLength = this.Anim.steps; // objLength(this.Frames); 492 | var perc = this.Anim.steps === 1 ? 0 : (100 / framesLength).toFixed(3); 493 | var dottes = this.Frames[this.currentFrame]; 494 | var shadows = this.createShadow(dottes); 495 | 496 | styles = '.b-box--paint .dot {\n ' + shadows + ' \n}\n'; 497 | 498 | dottes = this.Frames[0]; 499 | shadows = this.createShadow(dottes); 500 | styles += this.classNames.resultDot + ' {\n ' + shadows + ' \nanimation-duration: ' + this.Anim.duration + ';\n}\n'; 501 | 502 | if (this.currentFrame > 0) { 503 | dottes = this.Frames[this.currentFrame - 1]; 504 | shadows = this.createShadow(dottes); 505 | 506 | styles += '.b-box--paint .dot--previous {\n ' + shadows + ' \n}\n'; 507 | } 508 | 509 | this.Styles.shadows.innerHTML = styles; 510 | 511 | this.replaceAnimation({perc: perc}); 512 | }; 513 | 514 | // ----------------------------------------- 515 | 516 | ShadowPainter.prototype.createShadow = function (dottes, is_value) { 517 | if (dottes === undefined) { 518 | return; 519 | } 520 | 521 | var shadows = ''; 522 | var if_first = true; 523 | 524 | for (var hpos = 0; hpos < this.Scene.oneSide + 1; hpos++) { 525 | for (var vpos = 0; vpos < this.Scene.oneSide + 1; vpos++) { 526 | var dot = dottes[hpos][vpos]; 527 | 528 | var hpos_px = dot.hpos * this.Scene.dotSize + 'px'; 529 | var vpos_px = dot.vpos * this.Scene.dotSize + 'px'; 530 | var color = this.Colors.transparent; 531 | 532 | 533 | if (dot.color !== this.Colors.transparent) { 534 | color = dot.color; 535 | } 536 | 537 | if (if_first) { 538 | if_first = false; 539 | } 540 | else { 541 | shadows += ', '; 542 | } 543 | 544 | shadows += hpos_px + ' ' + vpos_px + ' 0 0 ' + color; 545 | } 546 | } 547 | 548 | if (!is_value) { 549 | shadows = 'box-shadow: ' + shadows + ';'; 550 | } 551 | 552 | return shadows; 553 | }; 554 | 555 | // ----------------------------------------- 556 | 557 | ShadowPainter.prototype.deleteKeyframes = function () { 558 | var rules = this.Anim.rules; 559 | var keyFrames = this.Anim.keyframes; 560 | const max = 1000; 561 | let counter = 0; 562 | 563 | while (keyFrames.cssRules.length > 0 && counter < max) { 564 | const {keyText} = keyFrames.cssRules[0]; 565 | keyFrames.deleteRule(keyText); 566 | 567 | counter++; 568 | } 569 | }; 570 | 571 | // ----------------------------------------- 572 | 573 | ShadowPainter.prototype.replaceAnimation = function (animation) { 574 | this.Output.Animation = ''; 575 | this.deleteKeyframes(); 576 | 577 | if (this.Anim.steps === 1) { 578 | this.restartAnimation(); 579 | return; 580 | } 581 | 582 | for (var step = 0; step < this.Anim.steps; step++) { 583 | var anim_dottes = this.Frames[step]; 584 | var anim_shadows = this.createShadow(anim_dottes); 585 | var stepPercents = +(animation.perc * step).toFixed(3); 586 | var frameRule = `${stepPercents}% {\n${anim_shadows}\n}`; 587 | 588 | this.Anim.keyframes.appendRule(frameRule); 589 | this.Output.Animation += frameRule + '\n'; 590 | } 591 | 592 | this.restartAnimation(); 593 | }; 594 | 595 | // ----------------------------------------- 596 | 597 | ShadowPainter.prototype.restartAnimation = function () { 598 | var resultDot = document.querySelector(this.classNames.resultDot); 599 | resultDot.classList.remove(this.classNames.isRunning); 600 | resultDot.classList.add(this.classNames.isRunning); 601 | }; 602 | 603 | // ----------------------------------------- 604 | 605 | ShadowPainter.prototype.createPalette = function () { 606 | const paletteControls = this.createControls(this.Colors.upDown); 607 | this.Elems.palette.innerHTML += '

Colors

'; 608 | this.Elems.palette.innerHTML += ''; 609 | this.Elems.palette.innerHTML += ``; 610 | 611 | this.fillPalette(); 612 | this.addEvents(this.Colors.inputClass, this.onClickColor); 613 | 614 | var first = document.querySelector(checkClassDot(this.Colors.inputClass)); 615 | first.checked = true; 616 | 617 | this.addEvents(this.Colors.controlClass, this.onClickColorControl); 618 | }; 619 | 620 | // ----------------------------------------- 621 | 622 | ShadowPainter.prototype.fillPalette = function () { 623 | var output = ''; 624 | var colorsItems = document.querySelector('.items--colors'); 625 | this.Styles.colors.innerHTML = ''; 626 | 627 | for (var i = 0; i < this.Colors.list.length; i++) { 628 | var replacements = { 629 | '{i}': i, 630 | '{color}': this.Colors.list[i] 631 | }; 632 | var colorItem = fillTemplate(this.Colors.template, replacements); 633 | var colorStyle = fillTemplate(this.Colors.StyleTempl, replacements); 634 | this.Styles.colors.innerHTML += colorStyle; 635 | output += colorItem; 636 | } 637 | 638 | colorsItems.innerHTML = output; 639 | }; 640 | 641 | // ----------------------------------------- 642 | 643 | ShadowPainter.prototype.reFillPalette = function () { 644 | var colorsItems = document.querySelectorAll(checkClassDot(this.Colors.inputClass)); 645 | this.Styles.colors.innerHTML = ''; 646 | 647 | for (var i = 0; i < this.Colors.list.length; i++) { 648 | colorsItems[i].setAttribute('data-color', this.Colors.list[i]); 649 | var replacements = { 650 | '{i}': i, 651 | '{color}': this.Colors.list[i] 652 | }; 653 | var colorStyle = fillTemplate(this.Colors.StyleTempl, replacements); 654 | 655 | this.Styles.colors.innerHTML += colorStyle; 656 | } 657 | }; 658 | 659 | // ----------------------------------------- 660 | 661 | ShadowPainter.prototype.onClickColor = function (elem) { 662 | this.Colors.current = elem.getAttribute('data-color'); 663 | this.Colors.currentNum = elem.getAttribute('data-color-num'); 664 | this.Colors.classCurrent = this.Colors.className + '--' + this.Colors.currentNum; 665 | }; 666 | 667 | // ----------------------------------------- 668 | 669 | ShadowPainter.prototype.onClickColorControl = function (elem) { 670 | var direct = elem.getAttribute('data-direction'); 671 | var max = this.Palettes.length - 1; 672 | 673 | if (direct === 'up') { 674 | if (this.Colors.currentListNum < max) { 675 | this.Colors.currentListNum++; 676 | } 677 | else { 678 | this.Colors.currentListNum = 0; 679 | } 680 | } 681 | else { 682 | if (this.Colors.currentListNum > 0) { 683 | this.Colors.currentListNum--; 684 | } 685 | else { 686 | this.Colors.currentListNum = max; 687 | } 688 | } 689 | 690 | this.Colors.list = this.Palettes[this.Colors.currentListNum]; 691 | this.Colors.current = this.Colors.list[this.Colors.currentNum]; 692 | this.Colors.classCurrent = this.Colors.className + '--' + this.Colors.currentNum; 693 | 694 | this.reFillPalette(); 695 | }; 696 | 697 | // ----------------------------------------- 698 | 699 | ShadowPainter.prototype.createSteps = function () { 700 | var output = ''; 701 | const plusMinusControls = this.createControls(this.Step.plusMinus); 702 | this.Elems.steps.innerHTML += `

${plusMinusControls}

`; 703 | 704 | for (var i = 0; i < this.Anim.stepsMax; i++) { 705 | var customClass = i < this.Anim.steps ? '' : ' ' + this.Step.hiddenClass; 706 | 707 | var replacements = { 708 | '{i}': i, 709 | '{i+1}': i+1, 710 | '{customClass}': customClass 711 | }; 712 | 713 | var stepItem = fillTemplate(this.Step.template, replacements); 714 | output += stepItem; 715 | } 716 | 717 | this.Elems.steps.innerHTML += ``; 718 | 719 | this.Elems.steps.innerHTML += this.createControls(this.Step.clearFrames); 720 | 721 | var first = document.querySelector(checkClassDot(this.Step.inputClass)); 722 | first.checked = true; 723 | 724 | this.addEvents(this.Step.inputClass, this.onClickStep); 725 | this.addEvents(this.Step.controlClass, this.onClickStepControl); 726 | this.addEvents(this.Step.clearFramesClass, this.onClickClearFrames); 727 | }; 728 | 729 | // ----------------------------------------- 730 | 731 | ShadowPainter.prototype.onClickStep = function (elem) { 732 | this.currentFrame = elem.getAttribute('data-step-num'); 733 | this.paintShadow(); 734 | this.updateCells(); 735 | }; 736 | 737 | // ----------------------------------------- 738 | 739 | ShadowPainter.prototype.onClickStepControl = function (elem) { 740 | var action = elem.getAttribute('data-action'); 741 | var stepsItems = document.querySelectorAll(checkClassDot(this.Step.className)); 742 | var division = stepsItems[this.Anim.steps - 1]; 743 | 744 | if (action === 'plus' && this.Anim.steps < this.Anim.stepsMax) { 745 | this.Anim.steps++; 746 | 747 | division.nextSibling.classList.remove(this.Step.hiddenClass); 748 | this.paintShadow(); 749 | 750 | if (this.Anim.steps === this.Anim.stepsMax) { 751 | elem.classList.add(this.Step.disabledClass); 752 | } 753 | else if (this.Anim.steps === 2) { 754 | this.enableControls(); 755 | } 756 | } 757 | else if (action === 'minus' && this.Anim.steps > 1) { 758 | this.Anim.steps--; 759 | division.classList.add(this.Step.hiddenClass); 760 | this.paintShadow(); 761 | 762 | if (this.Anim.steps === this.currentFrame) { 763 | this.currentFrame--; 764 | var prevInput = document.querySelectorAll(checkClassDot(this.Step.inputClass))[this.currentFrame]; 765 | prevInput.checked = true; 766 | } 767 | 768 | if (this.Anim.steps === 1) { 769 | elem.classList.add(this.Step.disabledClass); 770 | } 771 | else if (this.Anim.steps === this.Anim.stepsMax - 1) { 772 | this.enableControls(); 773 | } 774 | } 775 | }; 776 | 777 | // ----------------------------------------- 778 | 779 | ShadowPainter.prototype.enableControls = function () { 780 | var disabledItem = document.querySelector(checkClassDot(this.Step.disabledClass)); 781 | if (disabledItem) { 782 | disabledItem.classList.remove(this.Step.disabledClass); 783 | } 784 | }; 785 | 786 | // ----------------------------------------- 787 | 788 | ShadowPainter.prototype.onClickClearFrames = function (elem) { 789 | var action = elem.getAttribute('data-action'); 790 | 791 | if (action === 'all') { 792 | this.createFramesSet(); 793 | this.paintShadow(); 794 | this.updateCells(); 795 | } 796 | else { 797 | this.resetCurrentFrame(); 798 | this.paintShadow(); 799 | this.updateCells(); 800 | } 801 | }; 802 | 803 | // ----------------------------------------- 804 | 805 | ShadowPainter.prototype.updateSteps = function () { 806 | var radio = this.Elems.steps.querySelectorAll('input'); 807 | 808 | for (var i = 0; i < radio.length; i++) { 809 | if (this.Frames[i].active > 0) { 810 | radio[i].classList.add('is--filled'); 811 | } 812 | else { 813 | radio[i].classList.remove('is--filled'); 814 | } 815 | } 816 | }; 817 | 818 | // ----------------------------------------- 819 | 820 | ShadowPainter.prototype.updateCells = function () { 821 | var checkboxes = this.Elems.paintBox.querySelectorAll('input'); 822 | var frameCells = this.Frames[this.currentFrame]; 823 | var colored = 0; 824 | 825 | for (var i = 0; i < checkboxes.length; i++) { 826 | var cell = checkboxes[i]; 827 | var hpos = cell.getAttribute('data-hpos'); 828 | var vpos = cell.getAttribute('data-vpos'); 829 | var color = frameCells[hpos][vpos].color; 830 | 831 | if (color === this.Colors.transparent) { 832 | cell.checked = false; 833 | } 834 | else { 835 | colored++; 836 | cell.checked = true; 837 | } 838 | } 839 | }; 840 | 841 | // ----------------------------------------- 842 | 843 | ShadowPainter.prototype.createCodes = function () { 844 | this.addEvents(this.classNames.codesToggle, this.onClickCodes); 845 | }; 846 | 847 | // ----------------------------------------- 848 | 849 | ShadowPainter.prototype.onClickCodes = function () { 850 | var textInit = this.Elems.codesToggle.getAttribute('data-init'); 851 | var textClose = this.Elems.codesToggle.getAttribute('data-opened'); 852 | var text = textInit; 853 | 854 | if (this.isCodeOpened) { 855 | this.isCodeOpened = false; 856 | } 857 | else { 858 | this.isCodeOpened = true; 859 | text = textClose; 860 | } 861 | 862 | this.Elems.codesToggle.innerHTML = text; 863 | this.Elems.codes.classList.toggle('is-open'); 864 | 865 | this.Output.HTML = `\n
`; 866 | 867 | this.Elems.codesCSS.innerHTML = `/*-- ${this.Output.comment} */\n${this.createOutputCSS()}`; 868 | this.Elems.codesHtml.innerHTML = this.Output.HTML; 869 | }; 870 | 871 | // ----------------------------------------- 872 | 873 | ShadowPainter.prototype.createOutputCSS = function () { 874 | var styles = ''; 875 | var dottes = this.Frames[0]; 876 | var shadows = this.createShadow(dottes, true); 877 | 878 | this.setOutputParams(); 879 | this.outputElemParams['.dot']['box-shadow'] = shadows; 880 | 881 | for (var elem in this.outputElemParams) { 882 | var className = elem; 883 | var elemStyles = ''; 884 | var params = this.outputElemParams[elem]; 885 | for (var item in params) { 886 | var value = addUnits(params[item], item); 887 | elemStyles += item + ': ' + value + ';\n'; 888 | } 889 | styles += `${className} {\n${elemStyles}}\n`; 890 | } 891 | 892 | styles += '\n/* Keyframes */\n'; 893 | 894 | var animation = `@keyframes shadows {\n${this.Output.Animation}\n}\n`; 895 | styles += animation; 896 | 897 | return styles; 898 | }; 899 | 900 | // ----------------------------------------- 901 | 902 | ShadowPainter.prototype.createDurationInp = function () { 903 | var durationInt = this.Anim.duration.split('s').join(''); 904 | this.Elems.durationInp.value = durationInt; 905 | 906 | this.addDataDefautlt(this.Elems.durationInp, durationInt); 907 | this.addOnChangeEvents(this.classNames.durationInp, this.onChangeDuration); 908 | }; 909 | 910 | // ----------------------------------------- 911 | 912 | ShadowPainter.prototype.onChangeDuration = function (elem) { 913 | this.Anim.duration = elem.value + 's'; 914 | this.paintShadow(); 915 | }; 916 | 917 | // ----------------------------------------- 918 | 919 | ShadowPainter.prototype.createSizeInp = function () { 920 | this.Elems.sizeInp.value = this.Scene.size; 921 | 922 | this.addDataDefautlt(this.Elems.sizeInp, this.Scene.size); 923 | this.addOnChangeEvents(this.classNames.sizeInp, this.onChangeSize); 924 | }; 925 | 926 | // ----------------------------------------- 927 | 928 | ShadowPainter.prototype.onChangeSize = function (elem) { 929 | this.Scene.size = Number(elem.value); 930 | 931 | this.addConfig(); 932 | this.paintShadow(); 933 | }; 934 | 935 | ShadowPainter.prototype.onChangeDots = function (elem) { 936 | this.Scene.oneSide = Number(elem.value); 937 | 938 | this.addConfig(); 939 | this.createInputsSet(); 940 | this.paintShadow(); 941 | }; 942 | 943 | // ----------------------------------------- 944 | 945 | ShadowPainter.prototype.createDotsInp = function () { 946 | this.Elems.dotsInp.value = this.Scene.oneSide; 947 | 948 | this.addDataDefautlt(this.Elems.dotsInp, this.Scene.oneSide); 949 | this.addOnChangeEvents(this.classNames.dotsInp, this.onChangeDots); 950 | }; 951 | 952 | // ----------------------------------------- 953 | 954 | ShadowPainter.prototype.initColors = function() { 955 | const colors = { 956 | className: 'color', 957 | inputType: 'radio', 958 | inputClass: 'color__inp', 959 | inputData: 'data-color="{color}" ', 960 | 961 | labelContent: '', 962 | controlClass: 'colors-controls__item', 963 | transparent: 'rgba(255,255,255,0)', 964 | currentNum: 0, 965 | currentListNum: 0 966 | }; 967 | 968 | colors.list = this.Palettes[colors.currentListNum]; 969 | 970 | colors.current = colors.list[0]; 971 | colors.classCurrent = `${colors.className}--${colors.currentNum}`; 972 | 973 | colors.StyleTempl = `.${colors.className}--{i} {background: {color};}\n`; 974 | colors.controlTempl = '
  • '; 975 | 976 | colors.upDown = { 977 | list: { 978 | 'up': '', 979 | 'down': '' 980 | }, 981 | replacements: { 982 | '{itemsClass}': colors.controlClass 983 | }, 984 | template: '
  • ' 985 | }; 986 | 987 | return colors; 988 | }; 989 | 990 | // ----------------------------------------- 991 | 992 | ShadowPainter.prototype.initStep = function() { 993 | const step = { 994 | className: 'step', 995 | inputType: 'radio', 996 | inputClass: 'step__inp', 997 | inputData: '', 998 | labelContent: '{i+1}', 999 | controlClass: 'steps-control', 1000 | clearFramesClass: 'frames-clear', 1001 | 1002 | currentNum: 0, 1003 | customClass: '{customClass}', 1004 | hiddenClass: 'is-hidden', 1005 | disabledClass: 'is-disabled' 1006 | }; 1007 | step.plusMinus = { 1008 | list: { 1009 | 'minus': '–', 1010 | 'plus': '+' 1011 | }, 1012 | replacements: { 1013 | '{itemsClass}': step.controlClass 1014 | }, 1015 | template: '{text}', 1016 | insertBetween: 'this.Frames' 1017 | }; 1018 | 1019 | step.clearFrames = { 1020 | list: { 1021 | 'current': 'Clear current frame', 1022 | 'all': 'Clear all frames' 1023 | }, 1024 | replacements: { 1025 | '{itemsClass}': step.clearFramesClass 1026 | }, 1027 | template: '{text}' 1028 | }; 1029 | 1030 | return step; 1031 | }; 1032 | 1033 | // Helpers 1034 | // ----------------------------------------- 1035 | 1036 | function out(data, is_style, color) { 1037 | var style; 1038 | 1039 | if (is_style) { 1040 | color = color || 'orangered'; 1041 | style = 'color: ' + color + '; padding-left: 20px;'; 1042 | style = `color: ${color}; padding-left: 20px;`; 1043 | data = `%c${data}`; 1044 | console.log(data, style); 1045 | } 1046 | else { 1047 | console.log(data); 1048 | } 1049 | } 1050 | 1051 | // ----------------------------------------- 1052 | 1053 | function addStylesElem(elemClass) { 1054 | var elem = document.createElement('style'); 1055 | elem.classList.add(elemClass); 1056 | var head = document.querySelector('head'); 1057 | head.appendChild(elem); 1058 | return elem; 1059 | } 1060 | 1061 | // ----------------------------------------- 1062 | 1063 | function findKeyFrames(name) { 1064 | var keyFrames; 1065 | var sheets = document.styleSheets; 1066 | 1067 | for (var i = 0; i < sheets.length; i++) { 1068 | try { 1069 | var stylesList = sheets[i].cssRules; 1070 | 1071 | for (var k = 0; k < stylesList.length; k++) { 1072 | if (stylesList[k].name === name) { 1073 | keyFrames = stylesList[k]; 1074 | } 1075 | } 1076 | } 1077 | catch {} 1078 | 1079 | } 1080 | 1081 | return keyFrames; 1082 | } 1083 | 1084 | // ----------------------------------------- 1085 | 1086 | function checkClassDot(className) { 1087 | if (className.indexOf('.') < 0) { 1088 | className = '.' + className; 1089 | } 1090 | return className; 1091 | } 1092 | 1093 | // ----------------------------------------- 1094 | 1095 | function strIsNAN(str) { 1096 | str = str.replace(/-|\./g,''); 1097 | str = str.split(' ').join(''); 1098 | return isNaN(str); 1099 | } 1100 | 1101 | // ----------------------------------------- 1102 | 1103 | function addUnits(str) { 1104 | str = String(str); 1105 | var arr = str.split(' '); 1106 | 1107 | if (strIsNAN(str)) { 1108 | return str; 1109 | } 1110 | 1111 | if (arr.length > 1 && 1112 | arr[0].indexOf('px') < 0 && 1113 | arr[0].indexOf('em') < 0 && 1114 | arr[0].indexOf('%') < 0) { 1115 | str = arr.join('px ') + 'px'; 1116 | return str; 1117 | } 1118 | 1119 | if (str.indexOf('px') < 0 && 1120 | str.indexOf('em') < 0 && 1121 | arr[0].indexOf('%') < 0) { 1122 | str += 'px'; 1123 | } 1124 | 1125 | return str; 1126 | } 1127 | 1128 | // ----------------------------------------- 1129 | 1130 | function getValByKeyCode(elem, key, isShift) { 1131 | var value = Number(elem.value); 1132 | var min = elem.getAttribute('data-min'); 1133 | var max = elem.getAttribute('data-max'); 1134 | 1135 | var step = isShift ? 10 : 1; 1136 | 1137 | if (key === 38) { 1138 | if (value >= 0 && 1139 | value < 1 && 1140 | min < 1) { 1141 | 1142 | step = 0.1; 1143 | } 1144 | value += step; 1145 | } 1146 | else if (key === 40) { 1147 | if (value > 0 && 1148 | value <= 1 && 1149 | min < 1) { 1150 | 1151 | step = 0.1; 1152 | } 1153 | value -= step; 1154 | } 1155 | 1156 | if (value < min) { 1157 | value = min; 1158 | } 1159 | else if (max !== null && value > max) { 1160 | value = max; 1161 | } 1162 | else { 1163 | if (value > 0 && value < 1) { 1164 | value = value.toFixed(1); 1165 | } 1166 | else { 1167 | value = value.toFixed(); 1168 | } 1169 | } 1170 | 1171 | return value; 1172 | } 1173 | 1174 | // ----------------------------------------- 1175 | 1176 | function valueSetDefaultIfNAN(elem) { 1177 | var value = elem.value; 1178 | var defaultValue = elem.getAttribute('data-default'); 1179 | if (isNaN(value)) { 1180 | return defaultValue; 1181 | } 1182 | 1183 | return false; 1184 | } 1185 | 1186 | // ----------------------------------------- 1187 | 1188 | function minMaxDef(elem) { 1189 | var min = elem.getAttribute('data-min'); 1190 | min = min === null ? 0 : Number(min); 1191 | 1192 | var max = elem.getAttribute('data-max'); 1193 | max = max === null ? 100 : Number(max); 1194 | 1195 | var value = elem.value; 1196 | 1197 | var out = value > max ? max : value < min ? min : false; 1198 | return out; 1199 | } 1200 | 1201 | // ----------------------------------------- 1202 | 1203 | function createTemplate(item) { 1204 | var itemType = item.className; 1205 | var inputType = item.inputType; 1206 | var data_attr = item.inputData; 1207 | var lblContent = item.labelContent; 1208 | var itemCustomClass = item.customClass ? item.customClass : ''; 1209 | 1210 | var itemInpClass = itemType + '__inp'; 1211 | var itemLblClass = itemType + '__lbl'; 1212 | 1213 | var replacements = { 1214 | '{inputType}': inputType, 1215 | '{itemType}': itemType, 1216 | '{itemClass}': itemType, 1217 | '{itemLblClass}': itemLblClass, 1218 | '{itemInpClass}': itemInpClass, 1219 | '{lblContent}': lblContent 1220 | }; 1221 | 1222 | var itemInputTempl = ``;// 1223 | var itemLabelTempl = ``; 1224 | var itemTempl = `
  • ${itemInputTempl}${itemLabelTempl}
  • `; 1225 | 1226 | var result = fillTemplate(itemTempl, replacements); 1227 | 1228 | return result; 1229 | } 1230 | 1231 | // ----------------------------------------- 1232 | 1233 | function fillTemplate(dataStr, replacements) { 1234 | for (var key in replacements) { 1235 | var findStr = key; 1236 | var replaceWithStr = replacements[key]; 1237 | 1238 | dataStr = dataStr.split(findStr).join(replaceWithStr); 1239 | } 1240 | return dataStr; 1241 | } 1242 | 1243 | // ----------------------------------------- 1244 | 1245 | function objLength(obj) { 1246 | var count = 0; 1247 | for (var key in obj) { 1248 | count++; 1249 | } 1250 | return count; 1251 | } 1252 | 1253 | // Init 1254 | // ----------------------------------------- 1255 | 1256 | var painter = new ShadowPainter(); 1257 | 1258 | painter.init(); 1259 | 1260 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'); 2 | let sync = require('browser-sync').create(); 3 | var reload = sync.reload; 4 | var include = require('gulp-include'); 5 | var sass = require('gulp-sass'); 6 | var postcss = require('gulp-postcss'); 7 | var autoprefixer = require('autoprefixer'); 8 | var cssnano = require('cssnano'); 9 | var rename = require('gulp-rename'); 10 | var mqpacker = require('css-mqpacker'); 11 | var copy = require('gulp-copy'); 12 | var ghPages = require('gulp-gh-pages'); 13 | var colors = require('colors/safe'); 14 | var del = require('del'); 15 | 16 | sass.compiler = require('node-sass'); 17 | 18 | // SASS, AUTOPREFIXR, MINIMIZE 19 | gulp.task('sass', function () { 20 | var processors = [ 21 | autoprefixer({browsers: [ 22 | 'last 1 version', 23 | 'last 2 Chrome versions', 24 | 'last 2 Firefox versions', 25 | 'last 2 Opera versions', 26 | 'last 2 Edge versions' 27 | ]}), 28 | mqpacker() 29 | ]; 30 | 31 | console.log('⬤ Run ' + colors.yellow('Sass') + 32 | ' + ' + 33 | colors.green('Autoprefixer') + 34 | ' + ' + 35 | colors.cyan('Cssnano') + ' ⬤' 36 | ); 37 | 38 | return gulp.src('src/scss/styles.scss') 39 | .pipe(sass().on('error', sass.logError)) 40 | .pipe(postcss(processors)) 41 | .pipe(gulp.dest('./assets/css')) 42 | .pipe(sync.stream()) 43 | .pipe(postcss([cssnano()])) 44 | .pipe(rename('styles.min.css')) 45 | .pipe(gulp.dest('assets/css')); 46 | }); 47 | 48 | // JS 49 | gulp.task('js', function () { 50 | return gulp.src('src/js/**/*.js') 51 | .pipe(gulp.dest('assets/js/')) 52 | .pipe(reload({stream: true})); 53 | }); 54 | 55 | // INCLUDE BLOCKS IN HTML 56 | gulp.task('include', function () { 57 | console.log(colors.blue('⬤ Include files to HTML... ⬤')); 58 | 59 | gulp.src('src/index.html') 60 | .pipe(include()) 61 | .on('error', console.log) 62 | .pipe(gulp.dest('.')) 63 | .pipe(reload({stream: true})); 64 | }); 65 | 66 | // WATCH SASS, PREPROCESS AND RELOAD 67 | gulp.task('serve', gulp.series('sass', function () { 68 | sync.init({ 69 | ui: false, 70 | notify: false, 71 | port: 3000, 72 | server: { 73 | baseDir: '.' 74 | } 75 | }); 76 | 77 | gulp.watch(['src/**/*.scss'], gulp.series('sass')); 78 | gulp.watch(['src/**/*.html'], gulp.series('include')); 79 | gulp.watch(['src/**/*.js'], gulp.series('js')); 80 | })); 81 | 82 | // CLEAN BUILD 83 | gulp.task('clean', function () { 84 | del(['build/*']).then(paths => { 85 | console.log('⬤ Deleted files and folders:\n', paths.join('\n')); 86 | }); 87 | }); 88 | 89 | // CLEAN BUILD & COPY FILES TO IT 90 | gulp.task('copy', function () { 91 | console.log(colors.magenta('⬤ Clear build/ and copy files to it... ⬤')); 92 | 93 | return gulp.src(['assets/**/*', '*.html']) 94 | .pipe(copy('build/')); 95 | }); 96 | 97 | // PUBLISH TO GITHUB PAGES 98 | gulp.task('ghPages', function () { 99 | console.log(colors.rainbow('⬤ Publish to Github Pages... ⬤')); 100 | 101 | return gulp.src('build/**/*') 102 | .pipe(ghPages()); 103 | }); 104 | 105 | gulp.task('default', function () { 106 | console.log(colors.rainbow('⬤ ================================ ⬤\n')); 107 | console.log(' AVAILABLE COMMANDS:'); 108 | console.log(' ' + colors.cyan('-------------------\n')); 109 | console.log(' ' + colors.yellow('npm start') + 110 | ' — run local server with watcher'); 111 | console.log(' ' + colors.green('npm run build') + 112 | ' — make build of the project'); 113 | console.log(' ' + colors.cyan('npm run deploy') + 114 | ' — make build and publish project to Github Pages'); 115 | console.log(colors.rainbow('\n⬤ ================================ ⬤')); 116 | }); 117 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Create shadow animation 6 | 7 | 8 | 9 |
    10 |
    11 |
    12 |

    Duration

    13 | sec.
    14 |

    Dotts

    15 |
    16 |

    Size

    17 | px
    18 |
    19 |
    20 |
    21 |
    22 |
    23 |
    24 |
    25 |
    26 |
    27 |
    28 |
    29 | Get codes 30 |
    31 |

    CSS

    32 | 33 |

    HTML

    34 | 35 |
    36 |
    37 |
    38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "shadowPainter", 3 | "version": "0.0.1", 4 | "description": "", 5 | "main": "index.html", 6 | "scripts": { 7 | "start": "gulp serve", 8 | "img": "gulp images", 9 | "build": "gulp clean && gulp sass && gulp include && gulp copy", 10 | "deploy": "npm run build && gulp ghPages", 11 | "js:lint": "eslint '**/*.js'", 12 | "js:fix": "eslint '**/*.js' --fix", 13 | "css:lint": "stylelint src/**/*.scss", 14 | "lint": "npm run js:lint && npm run css:lint", 15 | "lint:fix": "npm run js:fix" 16 | }, 17 | "author": "yoksel", 18 | "license": "ISC", 19 | "devDependencies": { 20 | "autoprefixer": "^6.3.5", 21 | "babel-eslint": "^10.0.1", 22 | "browser-sync": "^2.26.3", 23 | "colors": "^1.1.2", 24 | "css-mqpacker": "^4.0.1", 25 | "cssnano": "^4.1.10", 26 | "del": "^2.2.0", 27 | "eslint": "^5.12.1", 28 | "eslint-config-yandex": "^1.0.6", 29 | "eslint-plugin-babel": "^5.3.0", 30 | "gulp": "^4.0.0", 31 | "gulp-copy": "0.0.2", 32 | "gulp-gh-pages": "^0.5.4", 33 | "gulp-include": "^2.1.0", 34 | "gulp-postcss": "^6.1.0", 35 | "gulp-rename": "^1.2.2", 36 | "gulp-sass": "^4.0.2", 37 | "stylelint": "^9.10.1", 38 | "stylelint-config-standard": "^18.2.0" 39 | }, 40 | "dependencies": {} 41 | } 42 | -------------------------------------------------------------------------------- /src/js/script.js: -------------------------------------------------------------------------------- 1 | var line = '------------------------'; 2 | 3 | function ShadowPainter() { 4 | this.timer; 5 | this.isMousePressed = false; 6 | 7 | this.classNames = { 8 | wrapper: '.l-wrapper', 9 | paintBox: '.b-box--paint', 10 | resultBox: '.b-box--result', 11 | resultDot: '.b-box--result .dot', 12 | palette: '.b-palette', 13 | steps: '.b-steps', 14 | 15 | codes: '.b-codes', 16 | codesToggle: '.b-codes__toggle', 17 | codesCSS: '.textarea--css', 18 | codesHtml: '.textarea--html', 19 | 20 | durationInp: '.text--duration', 21 | sizeInp: '.text--size', 22 | dotsInp: '.text--dots', 23 | isRunning: 'is-running', 24 | }; 25 | 26 | this.Elems = {}; 27 | 28 | this.Cell = { 29 | className: 'cell', 30 | inputType: 'checkbox', 31 | inputClass: 'cell__inp', 32 | labelClass: 'cell__lbl', 33 | inputData: 'data-hpos="{hpos}" data-vpos="{vpos}" ', 34 | labelContent: '', 35 | dots: '' 36 | }; 37 | 38 | this.Palettes = [ 39 | ['#3FB8AF','#7FC7AF','#DAD8A7','#FF9E9D','#FF3D7F'], 40 | ['#468966','#FFF0A5','#FFB03B','#B64926','#8E2800'], 41 | ['#004358','#1F8A70','#BEDB39','#FFE11A','#FD7400'], 42 | ['#96CA2D','#B5E655','#EDF7F2','#4BB5C1','#7FC6BC'], 43 | ['#2E0927','#D90000','#FF2D00','#FF8C00','#04756F'], 44 | ['#FCFFF5','#D1DBBD','#91AA9D','#3E606F','#193441'], 45 | ['#332532','#644D52','#F77A52','#FF974F','#A49A87'] 46 | ]; 47 | 48 | this.Colors = this.initColors(); 49 | this.Step = this.initStep(); 50 | 51 | this.stylesClassNames = { 52 | config: 'configBox', 53 | shadows: 'stylesBox', 54 | colors: 'colorsBox' 55 | }; 56 | this.Styles = {}; 57 | 58 | this.Output = { 59 | HTML: '', 60 | CSS: '', 61 | Animation: '', 62 | comment: 'Created in shadowPainter : )' 63 | }; 64 | 65 | this.Scene = { 66 | oneSide: 5,// dottes in line 67 | oneSideMax: 30,// dottes in line 68 | size: 250, 69 | dotSize: 30,// pixels 70 | padding: 20,// pixels. Don't change it 71 | border: 1// pixels 72 | }; 73 | 74 | this.Anim = { 75 | steps: 5, 76 | stepsMax: 20, 77 | duration: '1s', 78 | name: 'shadows', 79 | keyframes: findKeyFrames('shadows') 80 | }; 81 | this.Anim.rules = this.Anim.keyframes.cssRules; 82 | 83 | this.isCodeOpened = false; 84 | 85 | this.Frames = {}; 86 | this.currentFrame = 0; 87 | 88 | this.init = function () { 89 | this.createTemplates(); 90 | this.createStylesBoxes(); 91 | 92 | this.initElements(); 93 | this.addConfig(); 94 | this.createInputsSet(); 95 | this.createFramesSet(); 96 | this.createPalette(); 97 | this.createSteps(); 98 | this.createCodes(); 99 | this.createDurationInp(); 100 | this.createSizeInp(); 101 | this.createDotsInp(); 102 | }; 103 | 104 | } // End ShadowPainter() 105 | 106 | // ----------------------------------------- 107 | 108 | ShadowPainter.prototype.setTimer = function(func, params) { 109 | if (this.timer) { 110 | this.clearTimeout(this.timer); 111 | } 112 | var execFunc = function () { 113 | func(params); 114 | }; 115 | this.timer = this.setTimeout(execFunc , 50); 116 | }; 117 | 118 | // ----------------------------------------- 119 | 120 | ShadowPainter.prototype.getAnimStr = function() { 121 | return `${this.Anim.duration} ${this.Anim.name} linear infinite`; 122 | }; 123 | 124 | // ----------------------------------------- 125 | 126 | ShadowPainter.prototype.initElements = function () { 127 | for (var className in this.classNames) { 128 | this.Elems[className] = document.querySelector(this.classNames[className]); 129 | } 130 | }; 131 | 132 | // ----------------------------------------- 133 | 134 | ShadowPainter.prototype.setParams = function () { 135 | let dotSize = (this.Scene.size / this.Scene.oneSide); 136 | this.Scene.dotSize = dotSize.toFixed(); 137 | this.Scene.size = this.Scene.dotSize * this.Scene.oneSide; 138 | 139 | this.configElemParams = { 140 | '.l-wrapper': { 141 | 'width': 700, // size * 2 + this.Scene.padding * 3 + border*6 142 | 'height': 490, // size + this.Scene.padding * 2 + border*4 143 | }, 144 | '.b-boxes': { 145 | 'height': 340 146 | }, 147 | '.l-zero-gravity': { 148 | 'height': this.Scene.size + this.Scene.border 149 | }, 150 | '.b-box': { 151 | 'width': this.Scene.size + this.Scene.border, 152 | 'height': this.Scene.size 153 | }, 154 | '.cell__lbl': { 155 | 'width': this.Scene.dotSize, 156 | 'height': this.Scene.dotSize 157 | }, 158 | '.dot': { 159 | 'width': this.Scene.dotSize, 160 | 'height': this.Scene.dotSize, 161 | 'top': '-' + this.Scene.dotSize, 162 | 'left': '-' + this.Scene.dotSize 163 | }, 164 | '.is-running': { 165 | 'animation': this.getAnimStr() 166 | }, 167 | '.b-box--paint': { 168 | 'background-size': this.Scene.dotSize + ' ' + this.Scene.dotSize 169 | } 170 | }; 171 | }; 172 | 173 | // ----------------------------------------- 174 | 175 | ShadowPainter.prototype.setOutputParams = function () { 176 | this.outputElemParams = { 177 | '.box': { 178 | 'position': 'absolute', 179 | 'top': '0', 180 | 'bottom': '0', 181 | 'left': '0', 182 | 'right': '0', 183 | 'margin': 'auto', 184 | 'width': this.Scene.size, 185 | 'height': this.Scene.size, 186 | 'overflow': 'hidden', 187 | 'border': '1px solid #DDD' 188 | }, 189 | '.dot': { 190 | 'display': 'block', 191 | 'position': 'absolute', 192 | 'width': this.Scene.dotSize, 193 | 'height': this.Scene.dotSize, 194 | 'top': '-' + this.Scene.dotSize, 195 | 'left': '-' + this.Scene.dotSize, 196 | 'animation': this.getAnimStr() 197 | } 198 | }; 199 | }; 200 | 201 | // ----------------------------------------- 202 | 203 | ShadowPainter.prototype.addConfig = function () { 204 | this.setParams(); 205 | 206 | var styles = ''; 207 | for (var elem in this.configElemParams) { 208 | var className = elem; 209 | var elemStyles = ''; 210 | var params = this.configElemParams[elem]; 211 | for (var item in params) { 212 | var value = addUnits(params[item], item); 213 | elemStyles += item + ': ' + value + ';\n'; 214 | } 215 | styles += className + ' {\n' + elemStyles + '}\n'; 216 | } 217 | 218 | this.Styles.config.innerHTML = styles; 219 | }; 220 | 221 | // ----------------------------------------- 222 | 223 | ShadowPainter.prototype.createStylesBoxes = function () { 224 | for (var styleBox in this.stylesClassNames) { 225 | this.Styles[styleBox] = addStylesElem(this.stylesClassNames[styleBox]); 226 | } 227 | }; 228 | 229 | // ----------------------------------------- 230 | 231 | ShadowPainter.prototype.createTemplates = function () { 232 | var templatesConfig = [this.Cell, this.Colors, this.Step]; 233 | 234 | for (var i = 0; i < templatesConfig.length; i++) { 235 | var item = templatesConfig[i]; 236 | item.template = createTemplate(item); 237 | } 238 | }; 239 | 240 | // ----------------------------------------- 241 | 242 | ShadowPainter.prototype.createControls = function (data) { 243 | var output = ''; 244 | 245 | var counter = 0; 246 | for (var item in data.list) { 247 | data.replacements['{action}'] = item; 248 | data.replacements['{text}'] = data.list[ item ]; 249 | output += fillTemplate(data.template, data.replacements); 250 | 251 | if (counter === 0 && data.insertBetween) { 252 | output += data.insertBetween; 253 | } 254 | counter++; 255 | } 256 | return output; 257 | }; 258 | 259 | // ----------------------------------------- 260 | 261 | ShadowPainter.prototype.addDataDefautlt = function (elem, defaultValue) { 262 | var defData = elem.getAttribute('data-default'); 263 | 264 | if (defData === null) { 265 | elem.setAttribute('data-default', defaultValue); 266 | } 267 | }; 268 | 269 | // ----------------------------------------- 270 | 271 | ShadowPainter.prototype.addEvents = function (itemsClass, func) { 272 | itemsClass = checkClassDot(itemsClass); 273 | var items = document.querySelectorAll(itemsClass); 274 | var parent = this; 275 | 276 | for (var i = 0; i < items.length; i++) { 277 | items[i].onclick = function () { 278 | func.call(parent, this); 279 | }; 280 | } 281 | }; 282 | 283 | // ----------------------------------------- 284 | 285 | ShadowPainter.prototype.addOverEvents = function (itemsClass, func) { 286 | itemsClass = checkClassDot(itemsClass); 287 | var items = document.querySelectorAll(itemsClass); 288 | var parent = this; 289 | 290 | for (var i = 0; i < items.length; i++) { 291 | items[i].onmousedown = function () { 292 | this.isMousePressed = true; 293 | }; 294 | 295 | items[i].onmouseup = function () { 296 | this.isMousePressed = false; 297 | }; 298 | 299 | items[i].onmouseover = function () { 300 | if (this.isMousePressed) { 301 | func.call(parent, this); 302 | } 303 | }; 304 | } 305 | }; 306 | 307 | // ----------------------------------------- 308 | 309 | ShadowPainter.prototype.checkInputValue = function (params) { 310 | var elem = params.elem; 311 | var func = params.func; 312 | var parent = params.parent; 313 | 314 | var minMax = minMaxDef(elem); 315 | var isNan = valueSetDefaultIfNAN(elem); 316 | 317 | if (isNan) { 318 | elem.value = isNan; 319 | } 320 | 321 | if (minMax) { 322 | elem.value = minMax; 323 | } 324 | 325 | func.call(parent, elem); 326 | }; 327 | 328 | // ----------------------------------------- 329 | 330 | ShadowPainter.prototype.addOnChangeEvents = function (itemsClass, func) { 331 | var items = document.querySelectorAll(itemsClass); 332 | var parent = this; 333 | 334 | var params = { 335 | func: func, 336 | parent: parent 337 | }; 338 | 339 | for (var i = 0; i < items.length; i++) { 340 | items[i].onkeyup = function (event) { 341 | params.elem = this; 342 | 343 | if (event.keyCode === 38 || event.keyCode === 40) { 344 | 345 | this.value = getValByKeyCode(this, event.keyCode, event.shiftKey); 346 | func.call(parent, this); 347 | } 348 | else { 349 | this.setTimer(parent.checkInputValue, params); 350 | } 351 | }; 352 | 353 | items[i].onchange = function () { 354 | params.elem = this; 355 | parent.checkInputValue(params); 356 | }; 357 | 358 | items[i].onblur = function () { 359 | params.elem = this; 360 | parent.checkInputValue(params); 361 | }; 362 | } 363 | }; 364 | 365 | // ----------------------------------------- 366 | 367 | ShadowPainter.prototype.createInputsSet = function () { 368 | this.Elems.paintBox.innerHTML = this.Cell.dots; 369 | var output = ''; 370 | 371 | for (var i = 0; i < this.Scene.oneSide * this.Scene.oneSide; i++) { 372 | var hpos = i % this.Scene.oneSide + 1; 373 | var vpos = Math.floor(i / this.Scene.oneSide) + 1; 374 | 375 | var replacements = { 376 | '{i}': i, 377 | '{hpos}': hpos, 378 | '{vpos}': vpos 379 | }; 380 | var checkBox = fillTemplate(this.Cell.template, replacements); 381 | output += checkBox; 382 | } 383 | 384 | this.Elems.paintBox.innerHTML += ``; 385 | 386 | this.addOverEvents(this.Cell.labelClass, this.onOverLabel); 387 | this.addEvents(this.Cell.inputClass, this.onClickCell); 388 | }; 389 | 390 | // ----------------------------------------- 391 | 392 | ShadowPainter.prototype.createFramesSet = function () { 393 | for (var k = 0; k < this.Anim.stepsMax; k++) { 394 | this.Frames[k] = {active: 0}; 395 | 396 | for (var hpos = 0; hpos < this.Scene.oneSideMax; hpos++) { // verticals 397 | this.Frames[k][hpos] = {}; 398 | 399 | for (var vpos = 0; vpos < this.Scene.oneSideMax; vpos++) { // gorizontals 400 | 401 | this.Frames[k][hpos][vpos] = { 402 | 'hpos': hpos, 403 | 'vpos': vpos, 404 | 'color': this.Colors.transparent 405 | }; 406 | } // End gorizontals 407 | } // End verticals 408 | } 409 | }; 410 | 411 | // ----------------------------------------- 412 | 413 | ShadowPainter.prototype.resetCurrentFrame = function () { 414 | var k = this.currentFrame; 415 | this.Frames[k] = {active: 0}; 416 | 417 | for (var hpos = 0; hpos < this.Scene.oneSideMax; hpos++) { // verticals 418 | this.Frames[k][hpos] = {}; 419 | 420 | for (var vpos = 0; vpos < this.Scene.oneSideMax; vpos++) { // gorizontals 421 | this.Frames[k][hpos][vpos] = { 422 | 'color': this.Colors.transparent 423 | }; 424 | } // End gorizontals 425 | } // End verticals 426 | }; 427 | 428 | // ----------------------------------------- 429 | 430 | ShadowPainter.prototype.toggleColorClass = function (elem) { 431 | var findClass = this.Colors.className + '--'; 432 | var classes = elem.classList; 433 | 434 | for (var i = 0; i < classes.length; i++) { 435 | 436 | if (classes[i].indexOf(findClass) >= 0) { 437 | classes.remove(classes[i]); 438 | return; 439 | } 440 | } 441 | 442 | classes.add(findClass + this.Colors.currentNum); 443 | }; 444 | 445 | // ----------------------------------------- 446 | 447 | ShadowPainter.prototype.onClickCell = function (elem) { 448 | this.updateFrames(elem); 449 | }; 450 | 451 | // ----------------------------------------- 452 | 453 | ShadowPainter.prototype.onOverLabel = function (elem) { 454 | var input = elem.previousSibling; 455 | 456 | if (input.checked === true) { 457 | input.checked = false; 458 | } 459 | else { 460 | input.checked = true; 461 | } 462 | 463 | this.updateFrames(input); 464 | }; 465 | 466 | // ----------------------------------------- 467 | 468 | ShadowPainter.prototype.updateFrames = function (elem) { 469 | var hpos = elem.getAttribute('data-hpos'); 470 | var vpos = elem.getAttribute('data-vpos'); 471 | 472 | var place = this.Frames[this.currentFrame]; 473 | var color = this.Colors.transparent; 474 | 475 | if (elem.checked) { 476 | color = this.Colors.current; 477 | place.active++; 478 | } 479 | else { 480 | place.active--; 481 | } 482 | 483 | place[hpos][vpos].color = color; 484 | this.paintShadow(); 485 | }; 486 | 487 | // ----------------------------------------- 488 | 489 | ShadowPainter.prototype.paintShadow = function () { 490 | var styles = ''; 491 | var framesLength = this.Anim.steps; // objLength(this.Frames); 492 | var perc = this.Anim.steps === 1 ? 0 : (100 / framesLength).toFixed(3); 493 | var dottes = this.Frames[this.currentFrame]; 494 | var shadows = this.createShadow(dottes); 495 | 496 | styles = '.b-box--paint .dot {\n ' + shadows + ' \n}\n'; 497 | 498 | dottes = this.Frames[0]; 499 | shadows = this.createShadow(dottes); 500 | styles += this.classNames.resultDot + ' {\n ' + shadows + ' \nanimation-duration: ' + this.Anim.duration + ';\n}\n'; 501 | 502 | if (this.currentFrame > 0) { 503 | dottes = this.Frames[this.currentFrame - 1]; 504 | shadows = this.createShadow(dottes); 505 | 506 | styles += '.b-box--paint .dot--previous {\n ' + shadows + ' \n}\n'; 507 | } 508 | 509 | this.Styles.shadows.innerHTML = styles; 510 | 511 | this.replaceAnimation({perc: perc}); 512 | }; 513 | 514 | // ----------------------------------------- 515 | 516 | ShadowPainter.prototype.createShadow = function (dottes, is_value) { 517 | if (dottes === undefined) { 518 | return; 519 | } 520 | 521 | var shadows = ''; 522 | var if_first = true; 523 | 524 | for (var hpos = 0; hpos < this.Scene.oneSide + 1; hpos++) { 525 | for (var vpos = 0; vpos < this.Scene.oneSide + 1; vpos++) { 526 | var dot = dottes[hpos][vpos]; 527 | 528 | var hpos_px = dot.hpos * this.Scene.dotSize + 'px'; 529 | var vpos_px = dot.vpos * this.Scene.dotSize + 'px'; 530 | var color = this.Colors.transparent; 531 | 532 | 533 | if (dot.color !== this.Colors.transparent) { 534 | color = dot.color; 535 | } 536 | 537 | if (if_first) { 538 | if_first = false; 539 | } 540 | else { 541 | shadows += ', '; 542 | } 543 | 544 | shadows += hpos_px + ' ' + vpos_px + ' 0 0 ' + color; 545 | } 546 | } 547 | 548 | if (!is_value) { 549 | shadows = 'box-shadow: ' + shadows + ';'; 550 | } 551 | 552 | return shadows; 553 | }; 554 | 555 | // ----------------------------------------- 556 | 557 | ShadowPainter.prototype.deleteKeyframes = function () { 558 | var rules = this.Anim.rules; 559 | var keyFrames = this.Anim.keyframes; 560 | const max = 1000; 561 | let counter = 0; 562 | 563 | while (keyFrames.cssRules.length > 0 && counter < max) { 564 | const {keyText} = keyFrames.cssRules[0]; 565 | keyFrames.deleteRule(keyText); 566 | 567 | counter++; 568 | } 569 | }; 570 | 571 | // ----------------------------------------- 572 | 573 | ShadowPainter.prototype.replaceAnimation = function (animation) { 574 | this.Output.Animation = ''; 575 | this.deleteKeyframes(); 576 | 577 | if (this.Anim.steps === 1) { 578 | this.restartAnimation(); 579 | return; 580 | } 581 | 582 | for (var step = 0; step < this.Anim.steps; step++) { 583 | var anim_dottes = this.Frames[step]; 584 | var anim_shadows = this.createShadow(anim_dottes); 585 | var stepPercents = +(animation.perc * step).toFixed(3); 586 | var frameRule = `${stepPercents}% {\n${anim_shadows}\n}`; 587 | 588 | this.Anim.keyframes.appendRule(frameRule); 589 | this.Output.Animation += frameRule + '\n'; 590 | } 591 | 592 | this.restartAnimation(); 593 | }; 594 | 595 | // ----------------------------------------- 596 | 597 | ShadowPainter.prototype.restartAnimation = function () { 598 | var resultDot = document.querySelector(this.classNames.resultDot); 599 | resultDot.classList.remove(this.classNames.isRunning); 600 | resultDot.classList.add(this.classNames.isRunning); 601 | }; 602 | 603 | // ----------------------------------------- 604 | 605 | ShadowPainter.prototype.createPalette = function () { 606 | const paletteControls = this.createControls(this.Colors.upDown); 607 | this.Elems.palette.innerHTML += '

    Colors

    '; 608 | this.Elems.palette.innerHTML += ''; 609 | this.Elems.palette.innerHTML += ``; 610 | 611 | this.fillPalette(); 612 | this.addEvents(this.Colors.inputClass, this.onClickColor); 613 | 614 | var first = document.querySelector(checkClassDot(this.Colors.inputClass)); 615 | first.checked = true; 616 | 617 | this.addEvents(this.Colors.controlClass, this.onClickColorControl); 618 | }; 619 | 620 | // ----------------------------------------- 621 | 622 | ShadowPainter.prototype.fillPalette = function () { 623 | var output = ''; 624 | var colorsItems = document.querySelector('.items--colors'); 625 | this.Styles.colors.innerHTML = ''; 626 | 627 | for (var i = 0; i < this.Colors.list.length; i++) { 628 | var replacements = { 629 | '{i}': i, 630 | '{color}': this.Colors.list[i] 631 | }; 632 | var colorItem = fillTemplate(this.Colors.template, replacements); 633 | var colorStyle = fillTemplate(this.Colors.StyleTempl, replacements); 634 | this.Styles.colors.innerHTML += colorStyle; 635 | output += colorItem; 636 | } 637 | 638 | colorsItems.innerHTML = output; 639 | }; 640 | 641 | // ----------------------------------------- 642 | 643 | ShadowPainter.prototype.reFillPalette = function () { 644 | var colorsItems = document.querySelectorAll(checkClassDot(this.Colors.inputClass)); 645 | this.Styles.colors.innerHTML = ''; 646 | 647 | for (var i = 0; i < this.Colors.list.length; i++) { 648 | colorsItems[i].setAttribute('data-color', this.Colors.list[i]); 649 | var replacements = { 650 | '{i}': i, 651 | '{color}': this.Colors.list[i] 652 | }; 653 | var colorStyle = fillTemplate(this.Colors.StyleTempl, replacements); 654 | 655 | this.Styles.colors.innerHTML += colorStyle; 656 | } 657 | }; 658 | 659 | // ----------------------------------------- 660 | 661 | ShadowPainter.prototype.onClickColor = function (elem) { 662 | this.Colors.current = elem.getAttribute('data-color'); 663 | this.Colors.currentNum = elem.getAttribute('data-color-num'); 664 | this.Colors.classCurrent = this.Colors.className + '--' + this.Colors.currentNum; 665 | }; 666 | 667 | // ----------------------------------------- 668 | 669 | ShadowPainter.prototype.onClickColorControl = function (elem) { 670 | var direct = elem.getAttribute('data-direction'); 671 | var max = this.Palettes.length - 1; 672 | 673 | if (direct === 'up') { 674 | if (this.Colors.currentListNum < max) { 675 | this.Colors.currentListNum++; 676 | } 677 | else { 678 | this.Colors.currentListNum = 0; 679 | } 680 | } 681 | else { 682 | if (this.Colors.currentListNum > 0) { 683 | this.Colors.currentListNum--; 684 | } 685 | else { 686 | this.Colors.currentListNum = max; 687 | } 688 | } 689 | 690 | this.Colors.list = this.Palettes[this.Colors.currentListNum]; 691 | this.Colors.current = this.Colors.list[this.Colors.currentNum]; 692 | this.Colors.classCurrent = this.Colors.className + '--' + this.Colors.currentNum; 693 | 694 | this.reFillPalette(); 695 | }; 696 | 697 | // ----------------------------------------- 698 | 699 | ShadowPainter.prototype.createSteps = function () { 700 | var output = ''; 701 | const plusMinusControls = this.createControls(this.Step.plusMinus); 702 | this.Elems.steps.innerHTML += `

    ${plusMinusControls}

    `; 703 | 704 | for (var i = 0; i < this.Anim.stepsMax; i++) { 705 | var customClass = i < this.Anim.steps ? '' : ' ' + this.Step.hiddenClass; 706 | 707 | var replacements = { 708 | '{i}': i, 709 | '{i+1}': i+1, 710 | '{customClass}': customClass 711 | }; 712 | 713 | var stepItem = fillTemplate(this.Step.template, replacements); 714 | output += stepItem; 715 | } 716 | 717 | this.Elems.steps.innerHTML += ``; 718 | 719 | this.Elems.steps.innerHTML += this.createControls(this.Step.clearFrames); 720 | 721 | var first = document.querySelector(checkClassDot(this.Step.inputClass)); 722 | first.checked = true; 723 | 724 | this.addEvents(this.Step.inputClass, this.onClickStep); 725 | this.addEvents(this.Step.controlClass, this.onClickStepControl); 726 | this.addEvents(this.Step.clearFramesClass, this.onClickClearFrames); 727 | }; 728 | 729 | // ----------------------------------------- 730 | 731 | ShadowPainter.prototype.onClickStep = function (elem) { 732 | this.currentFrame = elem.getAttribute('data-step-num'); 733 | this.paintShadow(); 734 | this.updateCells(); 735 | }; 736 | 737 | // ----------------------------------------- 738 | 739 | ShadowPainter.prototype.onClickStepControl = function (elem) { 740 | var action = elem.getAttribute('data-action'); 741 | var stepsItems = document.querySelectorAll(checkClassDot(this.Step.className)); 742 | var division = stepsItems[this.Anim.steps - 1]; 743 | 744 | if (action === 'plus' && this.Anim.steps < this.Anim.stepsMax) { 745 | this.Anim.steps++; 746 | 747 | division.nextSibling.classList.remove(this.Step.hiddenClass); 748 | this.paintShadow(); 749 | 750 | if (this.Anim.steps === this.Anim.stepsMax) { 751 | elem.classList.add(this.Step.disabledClass); 752 | } 753 | else if (this.Anim.steps === 2) { 754 | this.enableControls(); 755 | } 756 | } 757 | else if (action === 'minus' && this.Anim.steps > 1) { 758 | this.Anim.steps--; 759 | division.classList.add(this.Step.hiddenClass); 760 | this.paintShadow(); 761 | 762 | if (this.Anim.steps === this.currentFrame) { 763 | this.currentFrame--; 764 | var prevInput = document.querySelectorAll(checkClassDot(this.Step.inputClass))[this.currentFrame]; 765 | prevInput.checked = true; 766 | } 767 | 768 | if (this.Anim.steps === 1) { 769 | elem.classList.add(this.Step.disabledClass); 770 | } 771 | else if (this.Anim.steps === this.Anim.stepsMax - 1) { 772 | this.enableControls(); 773 | } 774 | } 775 | }; 776 | 777 | // ----------------------------------------- 778 | 779 | ShadowPainter.prototype.enableControls = function () { 780 | var disabledItem = document.querySelector(checkClassDot(this.Step.disabledClass)); 781 | if (disabledItem) { 782 | disabledItem.classList.remove(this.Step.disabledClass); 783 | } 784 | }; 785 | 786 | // ----------------------------------------- 787 | 788 | ShadowPainter.prototype.onClickClearFrames = function (elem) { 789 | var action = elem.getAttribute('data-action'); 790 | 791 | if (action === 'all') { 792 | this.createFramesSet(); 793 | this.paintShadow(); 794 | this.updateCells(); 795 | } 796 | else { 797 | this.resetCurrentFrame(); 798 | this.paintShadow(); 799 | this.updateCells(); 800 | } 801 | }; 802 | 803 | // ----------------------------------------- 804 | 805 | ShadowPainter.prototype.updateSteps = function () { 806 | var radio = this.Elems.steps.querySelectorAll('input'); 807 | 808 | for (var i = 0; i < radio.length; i++) { 809 | if (this.Frames[i].active > 0) { 810 | radio[i].classList.add('is--filled'); 811 | } 812 | else { 813 | radio[i].classList.remove('is--filled'); 814 | } 815 | } 816 | }; 817 | 818 | // ----------------------------------------- 819 | 820 | ShadowPainter.prototype.updateCells = function () { 821 | var checkboxes = this.Elems.paintBox.querySelectorAll('input'); 822 | var frameCells = this.Frames[this.currentFrame]; 823 | var colored = 0; 824 | 825 | for (var i = 0; i < checkboxes.length; i++) { 826 | var cell = checkboxes[i]; 827 | var hpos = cell.getAttribute('data-hpos'); 828 | var vpos = cell.getAttribute('data-vpos'); 829 | var color = frameCells[hpos][vpos].color; 830 | 831 | if (color === this.Colors.transparent) { 832 | cell.checked = false; 833 | } 834 | else { 835 | colored++; 836 | cell.checked = true; 837 | } 838 | } 839 | }; 840 | 841 | // ----------------------------------------- 842 | 843 | ShadowPainter.prototype.createCodes = function () { 844 | this.addEvents(this.classNames.codesToggle, this.onClickCodes); 845 | }; 846 | 847 | // ----------------------------------------- 848 | 849 | ShadowPainter.prototype.onClickCodes = function () { 850 | var textInit = this.Elems.codesToggle.getAttribute('data-init'); 851 | var textClose = this.Elems.codesToggle.getAttribute('data-opened'); 852 | var text = textInit; 853 | 854 | if (this.isCodeOpened) { 855 | this.isCodeOpened = false; 856 | } 857 | else { 858 | this.isCodeOpened = true; 859 | text = textClose; 860 | } 861 | 862 | this.Elems.codesToggle.innerHTML = text; 863 | this.Elems.codes.classList.toggle('is-open'); 864 | 865 | this.Output.HTML = `\n
    `; 866 | 867 | this.Elems.codesCSS.innerHTML = `/*-- ${this.Output.comment} */\n${this.createOutputCSS()}`; 868 | this.Elems.codesHtml.innerHTML = this.Output.HTML; 869 | }; 870 | 871 | // ----------------------------------------- 872 | 873 | ShadowPainter.prototype.createOutputCSS = function () { 874 | var styles = ''; 875 | var dottes = this.Frames[0]; 876 | var shadows = this.createShadow(dottes, true); 877 | 878 | this.setOutputParams(); 879 | this.outputElemParams['.dot']['box-shadow'] = shadows; 880 | 881 | for (var elem in this.outputElemParams) { 882 | var className = elem; 883 | var elemStyles = ''; 884 | var params = this.outputElemParams[elem]; 885 | for (var item in params) { 886 | var value = addUnits(params[item], item); 887 | elemStyles += item + ': ' + value + ';\n'; 888 | } 889 | styles += `${className} {\n${elemStyles}}\n`; 890 | } 891 | 892 | styles += '\n/* Keyframes */\n'; 893 | 894 | var animation = `@keyframes shadows {\n${this.Output.Animation}\n}\n`; 895 | styles += animation; 896 | 897 | return styles; 898 | }; 899 | 900 | // ----------------------------------------- 901 | 902 | ShadowPainter.prototype.createDurationInp = function () { 903 | var durationInt = this.Anim.duration.split('s').join(''); 904 | this.Elems.durationInp.value = durationInt; 905 | 906 | this.addDataDefautlt(this.Elems.durationInp, durationInt); 907 | this.addOnChangeEvents(this.classNames.durationInp, this.onChangeDuration); 908 | }; 909 | 910 | // ----------------------------------------- 911 | 912 | ShadowPainter.prototype.onChangeDuration = function (elem) { 913 | this.Anim.duration = elem.value + 's'; 914 | this.paintShadow(); 915 | }; 916 | 917 | // ----------------------------------------- 918 | 919 | ShadowPainter.prototype.createSizeInp = function () { 920 | this.Elems.sizeInp.value = this.Scene.size; 921 | 922 | this.addDataDefautlt(this.Elems.sizeInp, this.Scene.size); 923 | this.addOnChangeEvents(this.classNames.sizeInp, this.onChangeSize); 924 | }; 925 | 926 | // ----------------------------------------- 927 | 928 | ShadowPainter.prototype.onChangeSize = function (elem) { 929 | this.Scene.size = Number(elem.value); 930 | 931 | this.addConfig(); 932 | this.paintShadow(); 933 | }; 934 | 935 | ShadowPainter.prototype.onChangeDots = function (elem) { 936 | this.Scene.oneSide = Number(elem.value); 937 | 938 | this.addConfig(); 939 | this.createInputsSet(); 940 | this.paintShadow(); 941 | }; 942 | 943 | // ----------------------------------------- 944 | 945 | ShadowPainter.prototype.createDotsInp = function () { 946 | this.Elems.dotsInp.value = this.Scene.oneSide; 947 | 948 | this.addDataDefautlt(this.Elems.dotsInp, this.Scene.oneSide); 949 | this.addOnChangeEvents(this.classNames.dotsInp, this.onChangeDots); 950 | }; 951 | 952 | // ----------------------------------------- 953 | 954 | ShadowPainter.prototype.initColors = function() { 955 | const colors = { 956 | className: 'color', 957 | inputType: 'radio', 958 | inputClass: 'color__inp', 959 | inputData: 'data-color="{color}" ', 960 | 961 | labelContent: '', 962 | controlClass: 'colors-controls__item', 963 | transparent: 'rgba(255,255,255,0)', 964 | currentNum: 0, 965 | currentListNum: 0 966 | }; 967 | 968 | colors.list = this.Palettes[colors.currentListNum]; 969 | 970 | colors.current = colors.list[0]; 971 | colors.classCurrent = `${colors.className}--${colors.currentNum}`; 972 | 973 | colors.StyleTempl = `.${colors.className}--{i} {background: {color};}\n`; 974 | colors.controlTempl = '
  • '; 975 | 976 | colors.upDown = { 977 | list: { 978 | 'up': '', 979 | 'down': '' 980 | }, 981 | replacements: { 982 | '{itemsClass}': colors.controlClass 983 | }, 984 | template: '
  • ' 985 | }; 986 | 987 | return colors; 988 | }; 989 | 990 | // ----------------------------------------- 991 | 992 | ShadowPainter.prototype.initStep = function() { 993 | const step = { 994 | className: 'step', 995 | inputType: 'radio', 996 | inputClass: 'step__inp', 997 | inputData: '', 998 | labelContent: '{i+1}', 999 | controlClass: 'steps-control', 1000 | clearFramesClass: 'frames-clear', 1001 | 1002 | currentNum: 0, 1003 | customClass: '{customClass}', 1004 | hiddenClass: 'is-hidden', 1005 | disabledClass: 'is-disabled' 1006 | }; 1007 | step.plusMinus = { 1008 | list: { 1009 | 'minus': '–', 1010 | 'plus': '+' 1011 | }, 1012 | replacements: { 1013 | '{itemsClass}': step.controlClass 1014 | }, 1015 | template: '{text}', 1016 | insertBetween: 'this.Frames' 1017 | }; 1018 | 1019 | step.clearFrames = { 1020 | list: { 1021 | 'current': 'Clear current frame', 1022 | 'all': 'Clear all frames' 1023 | }, 1024 | replacements: { 1025 | '{itemsClass}': step.clearFramesClass 1026 | }, 1027 | template: '{text}' 1028 | }; 1029 | 1030 | return step; 1031 | }; 1032 | 1033 | // Helpers 1034 | // ----------------------------------------- 1035 | 1036 | function out(data, is_style, color) { 1037 | var style; 1038 | 1039 | if (is_style) { 1040 | color = color || 'orangered'; 1041 | style = 'color: ' + color + '; padding-left: 20px;'; 1042 | style = `color: ${color}; padding-left: 20px;`; 1043 | data = `%c${data}`; 1044 | console.log(data, style); 1045 | } 1046 | else { 1047 | console.log(data); 1048 | } 1049 | } 1050 | 1051 | // ----------------------------------------- 1052 | 1053 | function addStylesElem(elemClass) { 1054 | var elem = document.createElement('style'); 1055 | elem.classList.add(elemClass); 1056 | var head = document.querySelector('head'); 1057 | head.appendChild(elem); 1058 | return elem; 1059 | } 1060 | 1061 | // ----------------------------------------- 1062 | 1063 | function findKeyFrames(name) { 1064 | var keyFrames; 1065 | var sheets = document.styleSheets; 1066 | 1067 | for (var i = 0; i < sheets.length; i++) { 1068 | try { 1069 | var stylesList = sheets[i].cssRules; 1070 | 1071 | for (var k = 0; k < stylesList.length; k++) { 1072 | if (stylesList[k].name === name) { 1073 | keyFrames = stylesList[k]; 1074 | } 1075 | } 1076 | } 1077 | catch {} 1078 | 1079 | } 1080 | 1081 | return keyFrames; 1082 | } 1083 | 1084 | // ----------------------------------------- 1085 | 1086 | function checkClassDot(className) { 1087 | if (className.indexOf('.') < 0) { 1088 | className = '.' + className; 1089 | } 1090 | return className; 1091 | } 1092 | 1093 | // ----------------------------------------- 1094 | 1095 | function strIsNAN(str) { 1096 | str = str.replace(/-|\./g,''); 1097 | str = str.split(' ').join(''); 1098 | return isNaN(str); 1099 | } 1100 | 1101 | // ----------------------------------------- 1102 | 1103 | function addUnits(str) { 1104 | str = String(str); 1105 | var arr = str.split(' '); 1106 | 1107 | if (strIsNAN(str)) { 1108 | return str; 1109 | } 1110 | 1111 | if (arr.length > 1 && 1112 | arr[0].indexOf('px') < 0 && 1113 | arr[0].indexOf('em') < 0 && 1114 | arr[0].indexOf('%') < 0) { 1115 | str = arr.join('px ') + 'px'; 1116 | return str; 1117 | } 1118 | 1119 | if (str.indexOf('px') < 0 && 1120 | str.indexOf('em') < 0 && 1121 | arr[0].indexOf('%') < 0) { 1122 | str += 'px'; 1123 | } 1124 | 1125 | return str; 1126 | } 1127 | 1128 | // ----------------------------------------- 1129 | 1130 | function getValByKeyCode(elem, key, isShift) { 1131 | var value = Number(elem.value); 1132 | var min = elem.getAttribute('data-min'); 1133 | var max = elem.getAttribute('data-max'); 1134 | 1135 | var step = isShift ? 10 : 1; 1136 | 1137 | if (key === 38) { 1138 | if (value >= 0 && 1139 | value < 1 && 1140 | min < 1) { 1141 | 1142 | step = 0.1; 1143 | } 1144 | value += step; 1145 | } 1146 | else if (key === 40) { 1147 | if (value > 0 && 1148 | value <= 1 && 1149 | min < 1) { 1150 | 1151 | step = 0.1; 1152 | } 1153 | value -= step; 1154 | } 1155 | 1156 | if (value < min) { 1157 | value = min; 1158 | } 1159 | else if (max !== null && value > max) { 1160 | value = max; 1161 | } 1162 | else { 1163 | if (value > 0 && value < 1) { 1164 | value = value.toFixed(1); 1165 | } 1166 | else { 1167 | value = value.toFixed(); 1168 | } 1169 | } 1170 | 1171 | return value; 1172 | } 1173 | 1174 | // ----------------------------------------- 1175 | 1176 | function valueSetDefaultIfNAN(elem) { 1177 | var value = elem.value; 1178 | var defaultValue = elem.getAttribute('data-default'); 1179 | if (isNaN(value)) { 1180 | return defaultValue; 1181 | } 1182 | 1183 | return false; 1184 | } 1185 | 1186 | // ----------------------------------------- 1187 | 1188 | function minMaxDef(elem) { 1189 | var min = elem.getAttribute('data-min'); 1190 | min = min === null ? 0 : Number(min); 1191 | 1192 | var max = elem.getAttribute('data-max'); 1193 | max = max === null ? 100 : Number(max); 1194 | 1195 | var value = elem.value; 1196 | 1197 | var out = value > max ? max : value < min ? min : false; 1198 | return out; 1199 | } 1200 | 1201 | // ----------------------------------------- 1202 | 1203 | function createTemplate(item) { 1204 | var itemType = item.className; 1205 | var inputType = item.inputType; 1206 | var data_attr = item.inputData; 1207 | var lblContent = item.labelContent; 1208 | var itemCustomClass = item.customClass ? item.customClass : ''; 1209 | 1210 | var itemInpClass = itemType + '__inp'; 1211 | var itemLblClass = itemType + '__lbl'; 1212 | 1213 | var replacements = { 1214 | '{inputType}': inputType, 1215 | '{itemType}': itemType, 1216 | '{itemClass}': itemType, 1217 | '{itemLblClass}': itemLblClass, 1218 | '{itemInpClass}': itemInpClass, 1219 | '{lblContent}': lblContent 1220 | }; 1221 | 1222 | var itemInputTempl = ``;// 1223 | var itemLabelTempl = ``; 1224 | var itemTempl = `
  • ${itemInputTempl}${itemLabelTempl}
  • `; 1225 | 1226 | var result = fillTemplate(itemTempl, replacements); 1227 | 1228 | return result; 1229 | } 1230 | 1231 | // ----------------------------------------- 1232 | 1233 | function fillTemplate(dataStr, replacements) { 1234 | for (var key in replacements) { 1235 | var findStr = key; 1236 | var replaceWithStr = replacements[key]; 1237 | 1238 | dataStr = dataStr.split(findStr).join(replaceWithStr); 1239 | } 1240 | return dataStr; 1241 | } 1242 | 1243 | // ----------------------------------------- 1244 | 1245 | function objLength(obj) { 1246 | var count = 0; 1247 | for (var key in obj) { 1248 | count++; 1249 | } 1250 | return count; 1251 | } 1252 | 1253 | // Init 1254 | // ----------------------------------------- 1255 | 1256 | var painter = new ShadowPainter(); 1257 | 1258 | painter.init(); 1259 | 1260 | -------------------------------------------------------------------------------- /src/scss/reset.scss: -------------------------------------------------------------------------------- 1 | html, body, div, span, h1, h2, h3, h4, h5, h6, 2 | p, a, img, b, i, dl, dt, dd, ol, ul, li, 3 | label, article, aside, footer, header, section { 4 | margin: 0; 5 | padding: 0; 6 | border: 0; 7 | font-size: 100%; 8 | font: inherit; 9 | vertical-align: baseline; 10 | } 11 | body { 12 | line-height: 1; 13 | } 14 | ol, ul { 15 | list-style: none; 16 | } 17 | -------------------------------------------------------------------------------- /src/scss/styles.scss: -------------------------------------------------------------------------------- 1 | $sideCells: 10; 2 | $dotSize: 30px; 3 | $box-size: $dotSize * $sideCells; 4 | $radius: 0; 5 | 6 | $padding: 20px; 7 | 8 | $hello-height: 150px; 9 | 10 | $color-border: #CCC; 11 | $color-active: orange; 12 | $color-step: lightgray; 13 | $color-step-l: lighten($color-step, 12); 14 | $color-step-d: darken($color-step, 20); 15 | 16 | @mixin clear { 17 | content: ''; 18 | display: table; 19 | width: 100%; 20 | clear: both; 21 | } 22 | 23 | @mixin dotted-text($color: #555) { 24 | border-bottom: 1px dotted; 25 | line-height: 1.2; 26 | color: $color; 27 | cursor: pointer; 28 | } 29 | 30 | $time: 1s; 31 | 32 | @import "reset"; 33 | 34 | * { 35 | box-sizing: border-box; 36 | } 37 | 38 | HTML, 39 | BODY { 40 | height: 100%; 41 | } 42 | 43 | BODY { 44 | display: flex; 45 | align-items: center; 46 | justify-content: center; 47 | background: #FFF; 48 | font: 14px/1.4 'Trebuchet MS', Verdana, sans-serif; 49 | color: #333; 50 | } 51 | 52 | LABEL { 53 | cursor: pointer; 54 | } 55 | 56 | .l-wrapper { 57 | position: relative; 58 | border: 1px solid $color-border; 59 | white-space: nowrap; 60 | 61 | &:after { 62 | @include clear; 63 | } 64 | } 65 | 66 | .b-boxes { 67 | position: relative; 68 | text-align: center; 69 | border: 1px solid $color-border; 70 | border-width: 1px 0; 71 | } 72 | .l-zero-gravity { 73 | position: absolute; 74 | width: 100%; 75 | top: 0; 76 | bottom: 0; 77 | right: 0; 78 | left: 0; 79 | margin: auto; 80 | } 81 | .b-box { 82 | position: relative; 83 | display: inline-block; 84 | border: 1px solid $color-border; 85 | &:nth-of-type(1){ 86 | margin-right: $padding; 87 | } 88 | } 89 | .b-panel { 90 | padding: $padding/2; 91 | color: #888; 92 | &:before, 93 | &:after { 94 | @include clear; 95 | } 96 | } 97 | 98 | 99 | .b-panel--top { 100 | } 101 | 102 | .b-panel--bottom { 103 | } 104 | 105 | 106 | .b-title { 107 | display: inline-block; 108 | padding-right: $padding/2; 109 | line-height: $dotSize; 110 | } 111 | 112 | .b-steps .b-title { 113 | float: none; 114 | display: inline-block; 115 | margin: 0 auto; 116 | padding: 0; 117 | } 118 | 119 | /* Paint Box 120 | ----------------------------- */ 121 | .b-box--paint { 122 | overflow: hidden; 123 | border-width: 0 1px 1px 0; 124 | background-image: 125 | linear-gradient( to right, 126 | $color-border 1px, 127 | transparent 1px 128 | ), 129 | linear-gradient( to bottom, 130 | $color-border 1px, 131 | transparent 1px 132 | ); 133 | background-position: 0 0; 134 | } 135 | 136 | /* Labels 137 | ----------------------------- */ 138 | .cell { 139 | display: block; 140 | float: left; 141 | } 142 | .cell__inp { 143 | position: absolute; 144 | display: none; 145 | } 146 | .cell__lbl { 147 | display: block; 148 | border-radius: $radius; 149 | /*box-shadow: 0 0 2px #999 inset;*/ 150 | } 151 | 152 | :checked + .cell__lbl { 153 | position: relative; 154 | } 155 | .is-colored { 156 | background: $color-active; 157 | } 158 | 159 | /* Result Box 160 | ----------------------------- */ 161 | .b-box--result { 162 | overflow: hidden; 163 | } 164 | 165 | /* Dotts 166 | ----------------------------- */ 167 | 168 | .dot { 169 | display: block; 170 | position: absolute; 171 | border-radius: $radius; 172 | } 173 | .dot--previous { 174 | opacity: .2; 175 | } 176 | 177 | .is-running{ 178 | } 179 | 180 | @keyframes shadows {} 181 | 182 | /* Palette 183 | ----------------------------- */ 184 | .b-palette { 185 | display: inline-block; 186 | } 187 | .items--colors { 188 | display: inline-block; 189 | vertical-align: middle; 190 | } 191 | .color { 192 | display: inline-block; 193 | width: $dotSize; 194 | height: $dotSize; 195 | } 196 | .color__inp { 197 | display: none; 198 | } 199 | 200 | .color__lbl { 201 | display: block; 202 | height: $dotSize; 203 | } 204 | :checked + .color__lbl { 205 | position: relative; 206 | z-index: 1; 207 | box-shadow: 0 0 0 3px rgba(0, 0, 0, .3); 208 | } 209 | 210 | /* Color Controls 211 | ----------------------------- */ 212 | .items--colors-controls { 213 | display: inline-block; 214 | position: relative; 215 | height: $dotSize; 216 | margin-left: 5px; 217 | vertical-align: middle; 218 | } 219 | .colors-controls__item { 220 | position: absolute; 221 | border: 10px solid transparent; 222 | border-color: $color-step-d transparent; 223 | border-width: 10px 4px; 224 | cursor: pointer; 225 | &:hover { 226 | border-color: darken($color-step-d, 10) transparent; 227 | } 228 | &:active { 229 | border-color: darken($color-step-d, 30) transparent; 230 | } 231 | } 232 | .colors-controls__item--up { 233 | top: 0; 234 | border-top: 0; 235 | &:active { 236 | top: 2px; 237 | } 238 | } 239 | .colors-controls__item--down { 240 | bottom: 0; 241 | border-bottom: 0; 242 | &:active { 243 | bottom: -2px; 244 | } 245 | } 246 | /* Panel 247 | ----------------------------- */ 248 | .b-panel { 249 | position: relative; 250 | } 251 | .b-panel__item { 252 | float: left; 253 | margin-left: $padding*1.6; 254 | &:first-child { 255 | margin-left: 0; 256 | } 257 | } 258 | .text--value { 259 | width: 3.5em; 260 | height: $dotSize; 261 | line-height: $dotSize; 262 | pading: 0; 263 | border: 1px solid $color-border; 264 | border-radius: 5px; 265 | text-align: center; 266 | } 267 | 268 | /* Steps 269 | ----------------------------- */ 270 | .b-steps { 271 | text-align: center; 272 | -webkit-user-select: none; 273 | -moz-user-select: none; 274 | -ms-user-select: none; 275 | user-select: none; 276 | } 277 | .steps-control { 278 | display: inline-block; 279 | position: relative; 280 | width: $dotSize*.8; 281 | height: $dotSize*.8; 282 | line-height: $dotSize*.8; 283 | margin: 0 6px; 284 | top: 0; 285 | background: $color-step; 286 | font-weight: bold; 287 | color: #444; 288 | cursor: pointer; 289 | -webkit-transition: all .1s; 290 | transition: all .2s; 291 | } 292 | .steps-control:hover { 293 | top: -1px; 294 | background: $color-step-l; 295 | color: #555; 296 | } 297 | .steps-control:active { 298 | top: 1px; 299 | background: $color-step-d; 300 | color: #FFF; 301 | } 302 | 303 | .is-disabled, 304 | .is-disabled:hover { 305 | top: 0; 306 | background: $color-step-l; 307 | color: #AAA; 308 | cursor: default; 309 | } 310 | 311 | .items--steps { 312 | display: block; 313 | margin-top: 10px; 314 | } 315 | .step { 316 | display: inline-block; 317 | width: $dotSize; 318 | height: $dotSize; 319 | margin-right: 1px; 320 | line-height: $dotSize; 321 | background: $color-step-l; 322 | text-align: center; 323 | } 324 | .step__inp { 325 | position: absolute; 326 | display: none; 327 | } 328 | .step__lbl { 329 | display: block; 330 | height: $dotSize; 331 | } 332 | .is--filled + .step__lbl { 333 | background: $color-step; 334 | } 335 | 336 | :checked + .step__lbl { 337 | position: relative; 338 | z-index: 1; 339 | background: $color-step-d; 340 | box-shadow: 0 0 0 3px rgba(0, 0, 0, .3); 341 | color: #FFF; 342 | } 343 | 344 | .is-hidden { 345 | display: none; 346 | } 347 | 348 | .frames-clear { 349 | position: absolute; 350 | top: $padding/2; 351 | &:hover { 352 | padding-top: 1px; 353 | } 354 | } 355 | 356 | .frames-clear--current { 357 | left: $padding/2; 358 | @include dotted-text; 359 | } 360 | .frames-clear--all { 361 | right: $padding/2; 362 | @include dotted-text(orangered); 363 | } 364 | 365 | .is--sleepy { 366 | color: #777 367 | } 368 | /* Codes 369 | ----------------------------- */ 370 | .b-codes { 371 | /*display: none;*/ 372 | 373 | position: absolute; 374 | z-index: 10; 375 | top: 0; 376 | right: 0; 377 | left: 0; 378 | background: rgba(240, 240, 240, .9); 379 | box-shadow: 0 0 20px rgba(0, 0, 0, .3); 380 | 381 | } 382 | .is-open { 383 | right: -.5em; 384 | left: -.5em; 385 | box-shadow: 0 0 20px rgba(0, 0, 0, .3), 386 | 0 0 0 1000px rgba(255, 255, 255, .8); 387 | } 388 | 389 | .b-codes__toggle { 390 | position: absolute; 391 | top: -$padding*1.2; 392 | right: $padding/2; 393 | @include dotted-text; 394 | &:hover { 395 | padding-top: 1px; 396 | } 397 | } 398 | .b-codes__wrapper { 399 | display: none; 400 | padding: $padding; 401 | border: 1px solid #BBB; 402 | .is-open & { 403 | display: block; 404 | } 405 | } 406 | 407 | .b-codes--title { 408 | margin-bottom: $padding/3; 409 | color: #777; 410 | } 411 | .b-codes__textarea { 412 | display: block; 413 | width: 100%; 414 | height: $dotSize*4; 415 | border: 1px solid $color-border; 416 | } 417 | .textarea--css { 418 | margin-bottom: $padding; 419 | } 420 | 421 | 422 | .hello { 423 | display: none; 424 | position: absolute; 425 | z-index: 10; 426 | height: $hello-height; 427 | width: 100%; 428 | top: -1em; 429 | bottom: -1em; 430 | margin: auto; 431 | background: rgba(0,0,0,.3); 432 | text-shadow: 0 0 3px rgba(0,0,0,.5); 433 | text-align: center; 434 | color: #FFF; 435 | H2 { 436 | margin-bottom: 10px; 437 | font-size: 4em; 438 | } 439 | SPAN { 440 | font-size: 2em; 441 | } 442 | &:before { 443 | content: ""; 444 | display: inline-block; 445 | height: $hello-height; 446 | vertical-align: middle; 447 | } 448 | } 449 | .hello__text { 450 | display: inline-block; 451 | vertical-align: middle; 452 | } 453 | @media (max-width: 760px){ 454 | .hello { 455 | display: block; 456 | } 457 | } 458 | --------------------------------------------------------------------------------