add the cube master
|
After Width: | Height: | Size: 16 KiB |
@@ -0,0 +1,6 @@
|
||||
.DS_Store
|
||||
node_modules/
|
||||
|
||||
*.sublime-workspace
|
||||
|
||||
export/
|
||||
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"build_systems":
|
||||
[
|
||||
{
|
||||
"file_patterns":
|
||||
[
|
||||
"*.js"
|
||||
],
|
||||
"name": "Rollup",
|
||||
"shell_cmd": "rollup -c rollup.config.dev.js",
|
||||
"working_dir": "${project_path}"
|
||||
},
|
||||
{
|
||||
"cmd": "$file",
|
||||
"file_patterns":
|
||||
[
|
||||
"*.scss"
|
||||
],
|
||||
"name": "Libsass",
|
||||
"target": "compile_sass",
|
||||
"working_dir": "${project_path}"
|
||||
}
|
||||
],
|
||||
"folders":
|
||||
[
|
||||
{
|
||||
"path": "."
|
||||
},
|
||||
{
|
||||
"path": "/Users/boris/Desktop/the-cube-codepen"
|
||||
}
|
||||
],
|
||||
"settings":
|
||||
{
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 32 KiB |
|
After Width: | Height: | Size: 101 KiB |
|
After Width: | Height: | Size: 31 KiB |
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<browserconfig>
|
||||
<msapplication>
|
||||
<tile>
|
||||
<square150x150logo src="/assets/icons/mstile-150x150.png"/>
|
||||
<TileColor>#da532c</TileColor>
|
||||
</tile>
|
||||
</msapplication>
|
||||
</browserconfig>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 35 KiB |
|
After Width: | Height: | Size: 16 KiB |
@@ -0,0 +1,39 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
|
||||
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
|
||||
width="580.000000pt" height="580.000000pt" viewBox="0 0 580.000000 580.000000"
|
||||
preserveAspectRatio="xMidYMid meet">
|
||||
<metadata>
|
||||
Created by potrace 1.11, written by Peter Selinger 2001-2013
|
||||
</metadata>
|
||||
<g transform="translate(0.000000,580.000000) scale(0.100000,-0.100000)"
|
||||
fill="#000000" stroke="none">
|
||||
<path d="M2815 5744 c-108 -61 -195 -109 -285 -157 -41 -22 -86 -47 -100 -56
|
||||
-14 -9 -51 -30 -83 -46 -32 -17 -70 -40 -84 -53 -14 -13 -44 -35 -66 -49 -49
|
||||
-31 -76 -61 -77 -86 0 -11 -5 -15 -11 -11 -6 3 -34 6 -62 5 -39 0 -71 -11
|
||||
-132 -42 -44 -23 -92 -50 -107 -60 -14 -11 -29 -19 -32 -19 -4 0 -58 -29 -121
|
||||
-65 -63 -36 -117 -65 -119 -65 -12 0 -133 -75 -146 -90 -8 -10 -35 -29 -60
|
||||
-43 -26 -14 -53 -39 -64 -58 -18 -33 -21 -34 -80 -34 -60 0 -65 -2 -366 -168
|
||||
-168 -93 -309 -174 -315 -181 -5 -6 -26 -21 -45 -33 -68 -42 -82 -65 -93 -155
|
||||
-12 -90 -16 -600 -6 -713 7 -76 17 -105 51 -148 15 -18 19 -30 12 -35 -41 -27
|
||||
-47 -84 -49 -442 -1 -386 4 -439 41 -493 l31 -43 -20 -26 c-30 -41 -39 -178
|
||||
-34 -525 6 -349 11 -375 78 -425 62 -46 520 -320 552 -330 18 -6 39 -15 47
|
||||
-19 73 -42 96 -50 126 -47 30 3 34 0 50 -34 21 -47 56 -82 116 -116 26 -15
|
||||
138 -83 250 -151 112 -68 214 -130 228 -137 96 -50 170 -79 203 -79 34 0 39
|
||||
-4 58 -43 18 -36 85 -102 103 -102 4 0 34 -18 67 -40 33 -22 62 -40 63 -40 2
|
||||
0 88 -52 192 -115 103 -63 196 -115 207 -115 10 0 38 -11 62 -25 60 -36 117
|
||||
-40 169 -13 22 12 64 32 91 43 51 22 566 326 597 353 10 8 27 36 39 62 20 45
|
||||
23 47 49 37 23 -9 35 -6 76 17 27 15 69 35 94 43 36 12 330 182 557 322 30 18
|
||||
51 42 69 79 24 48 29 52 56 48 19 -3 48 5 86 25 31 16 63 29 70 29 17 0 81 32
|
||||
167 85 39 23 144 86 235 140 91 54 175 106 187 117 39 36 63 110 68 210 10
|
||||
185 13 530 5 558 -5 17 -9 53 -11 82 -1 30 -10 68 -21 86 l-18 34 30 62 c29
|
||||
59 32 73 36 197 9 257 10 508 1 531 -5 13 -10 51 -12 86 -2 37 -10 74 -20 89
|
||||
-16 24 -16 27 14 87 28 55 32 72 36 171 10 259 9 550 -2 571 -7 11 -12 44 -11
|
||||
72 0 79 -15 113 -68 146 -26 17 -58 40 -72 53 -20 19 -245 147 -563 321 -60
|
||||
34 -100 43 -140 32 -22 -7 -28 -3 -43 24 -9 17 -36 44 -60 59 -24 15 -54 37
|
||||
-68 50 -14 13 -157 97 -319 187 -288 161 -294 164 -349 162 -51 -2 -56 0 -72
|
||||
27 -9 17 -36 42 -60 57 -23 14 -52 37 -63 49 -16 18 -195 121 -565 323 -25 13
|
||||
-49 28 -54 33 -17 16 -39 9 -121 -37z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "The Cube",
|
||||
"short_name": "The Cube",
|
||||
"icons": [
|
||||
{
|
||||
"src": "android-chrome-192x192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "android-chrome-512x512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
],
|
||||
"theme_color": "#ffffff",
|
||||
"background_color": "#ffffff",
|
||||
"display": "standalone"
|
||||
}
|
||||
|
After Width: | Height: | Size: 71 KiB |
@@ -0,0 +1,154 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
|
||||
<meta charset="UTF-8">
|
||||
<meta name="author" content="Boris Sehovac">
|
||||
<meta name="description" content="See if you can solve this classic puzzle game.">
|
||||
<meta name="viewport" content="width=device-width,height=device-height,user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0">
|
||||
|
||||
<title>The Cube - A Rubik's Cube Game</title>
|
||||
<meta property="og:description" content="See if you can solve this classic puzzle game."/>
|
||||
<meta property="og:image" content="assets/icons/meta-image.png"/>
|
||||
<meta property="og:site_name" content="The Cube - A Rubik's Cube Game"/>
|
||||
<meta property="og:title" content="The Cube - A Rubik's Cube Game"/>
|
||||
<meta property="og:type" content="website"/>
|
||||
<meta property="og:url" content="https://bsehovac.github.io"/>
|
||||
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:creator" content="@BorisSehovac">
|
||||
<meta name="twitter:title" content="The Cube - A Rubik's Cube Game">
|
||||
<meta name="twitter:image" content="https://bsehovac.github.io/the-cube/assets/icons/twitter-card.png">
|
||||
<meta name="twitter:description" content="See if you can solve this classic puzzle game.">
|
||||
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-title" content="The Cube">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="translucent-black">
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="assets/icons/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="assets/icons/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="assets/icons/favicon-16x16.png">
|
||||
<link rel="manifest" href="assets/icons/site.webmanifest">
|
||||
<link rel="mask-icon" href="assets/icons/safari-pinned-tab.svg" color="#5bbad5">
|
||||
<link rel="shortcut icon" href="assets/icons/favicon.ico">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="assets/css/styles.css">
|
||||
|
||||
<!-- Global site tag (gtag.js) - Google Analytics -->
|
||||
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-128627708-1"></script>
|
||||
<script>
|
||||
window.dataLayer = window.dataLayer || [];
|
||||
function gtag(){dataLayer.push(arguments);}
|
||||
gtag('js', new Date());
|
||||
gtag('config', 'UA-128627708-1');
|
||||
</script>
|
||||
|
||||
<script>
|
||||
window.gameVersion = '0.99.2';
|
||||
</script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="ui">
|
||||
|
||||
<div class="ui__background"></div>
|
||||
|
||||
<div class="ui__game"></div>
|
||||
|
||||
<div class="ui__texts">
|
||||
<h1 class="text text--title">
|
||||
<span>THE</span>
|
||||
<span>CUBE</span>
|
||||
</h1>
|
||||
<div class="text text--note">
|
||||
Double tap to start
|
||||
</div>
|
||||
<div class="text text--timer">
|
||||
0:00
|
||||
</div>
|
||||
<div class="text text--complete">
|
||||
<span>Complete!</span>
|
||||
</div>
|
||||
<div class="text text--best-time">
|
||||
<icon trophy></icon>
|
||||
<span>Best Time!</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ui__prefs">
|
||||
<range name="size" title="Cube Size" list="2,3,4,5"></range>
|
||||
<range name="flip" title="Flip Type" list="Swift ,Smooth,Bounce"></range>
|
||||
<range name="scramble" title="Scramble Length" list="20,25,30"></range>
|
||||
<range name="fov" title="Camera Angle" list="Ortographic,Perspective"></range>
|
||||
<range name="theme" title="Color Scheme" list="Cube,Erno,Dust,Camo,Rain"></range>
|
||||
</div>
|
||||
|
||||
<div class="ui__theme">
|
||||
<range name="hue" title="Hue" color></range>
|
||||
<range name="saturation" title="Saturation" color></range>
|
||||
<range name="lightness" title="Lightness" color></range>
|
||||
</div>
|
||||
|
||||
<div class="ui__stats">
|
||||
<div class="stats" name="cube-size">
|
||||
<i>Cube:</i><b>3x3x3</b>
|
||||
</div>
|
||||
<div class="stats" name="total-solves">
|
||||
<i>Total solves:</i><b>-</b>
|
||||
</div>
|
||||
<div class="stats" name="best-time">
|
||||
<i>Best time:</i><b>-</b>
|
||||
</div>
|
||||
<div class="stats" name="worst-time">
|
||||
<i>Worst time:</i><b>-</b>
|
||||
</div>
|
||||
<div class="stats" name="average-5">
|
||||
<i>Average of 5:</i><b>-</b>
|
||||
</div>
|
||||
<div class="stats" name="average-12">
|
||||
<i>Average of 12:</i><b>-</b>
|
||||
</div>
|
||||
<div class="stats" name="average-25">
|
||||
<i>Average of 25:</i><b>-</b>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ui__buttons">
|
||||
<button class="btn btn--bl btn--stats">
|
||||
<icon trophy></icon>
|
||||
</button>
|
||||
<button class="btn btn--br btn--prefs">
|
||||
<icon settings></icon>
|
||||
</button>
|
||||
<button class="btn btn--bl btn--back">
|
||||
<icon back></icon>
|
||||
</button>
|
||||
<button class="btn btn--br btn--theme">
|
||||
<icon theme></icon>
|
||||
</button>
|
||||
<button class="btn btn--br btn--reset">
|
||||
<icon reset></icon>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script src="assets/js/three.js"></script>
|
||||
<script src="assets/js/cube.js"></script>
|
||||
|
||||
<script src="upup.min.js"></script>
|
||||
<script>
|
||||
if (UpUp !== null) {
|
||||
UpUp.start({
|
||||
'cache-version': window.gameVersion,
|
||||
'content-url': 'index.html',
|
||||
'assets': [ 'assets/css/styles.css', 'assets/js/three.js', 'assets/js/cube.js' ]
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,727 @@
|
||||
{
|
||||
"name": "bsehovac.github.io",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@comandeer/babel-plugin-banner": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@comandeer/babel-plugin-banner/-/babel-plugin-banner-2.0.2.tgz",
|
||||
"integrity": "sha512-5/GMOcgqBy/+cMYfOTFhqrioolOP+g7ADjl4jo1nYONwVGvMpzKPdweuVaJsC/Ol2PH+GmwlF2WrNP3xjWoSiw==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz",
|
||||
"integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=",
|
||||
"dev": true
|
||||
},
|
||||
"babel-code-frame": {
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz",
|
||||
"integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"chalk": "1.1.3",
|
||||
"esutils": "2.0.2",
|
||||
"js-tokens": "3.0.2"
|
||||
}
|
||||
},
|
||||
"babel-core": {
|
||||
"version": "6.26.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz",
|
||||
"integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-code-frame": "6.26.0",
|
||||
"babel-generator": "6.26.1",
|
||||
"babel-helpers": "6.24.1",
|
||||
"babel-messages": "6.23.0",
|
||||
"babel-register": "6.26.0",
|
||||
"babel-runtime": "6.26.0",
|
||||
"babel-template": "6.26.0",
|
||||
"babel-traverse": "6.26.0",
|
||||
"babel-types": "6.26.0",
|
||||
"babylon": "6.18.0",
|
||||
"convert-source-map": "1.5.1",
|
||||
"debug": "2.6.9",
|
||||
"json5": "0.5.1",
|
||||
"lodash": "4.17.10",
|
||||
"minimatch": "3.0.4",
|
||||
"path-is-absolute": "1.0.1",
|
||||
"private": "0.1.8",
|
||||
"slash": "1.0.0",
|
||||
"source-map": "0.5.7"
|
||||
}
|
||||
},
|
||||
"babel-generator": {
|
||||
"version": "6.26.1",
|
||||
"resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz",
|
||||
"integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-messages": "6.23.0",
|
||||
"babel-runtime": "6.26.0",
|
||||
"babel-types": "6.26.0",
|
||||
"detect-indent": "4.0.0",
|
||||
"jsesc": "1.3.0",
|
||||
"lodash": "4.17.10",
|
||||
"source-map": "0.5.7",
|
||||
"trim-right": "1.0.1"
|
||||
}
|
||||
},
|
||||
"babel-helper-evaluate-path": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-helper-evaluate-path/-/babel-helper-evaluate-path-0.4.3.tgz",
|
||||
"integrity": "sha1-ComvcCwGshcCf6NxkI3UmJ0+Yz8=",
|
||||
"dev": true
|
||||
},
|
||||
"babel-helper-flip-expressions": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-helper-flip-expressions/-/babel-helper-flip-expressions-0.4.3.tgz",
|
||||
"integrity": "sha1-NpZzahKKwYvCUlS19AoizrPB0/0=",
|
||||
"dev": true
|
||||
},
|
||||
"babel-helper-is-nodes-equiv": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/babel-helper-is-nodes-equiv/-/babel-helper-is-nodes-equiv-0.0.1.tgz",
|
||||
"integrity": "sha1-NOmzALFHnd2Y7HfqC76TQt/jloQ=",
|
||||
"dev": true
|
||||
},
|
||||
"babel-helper-is-void-0": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-helper-is-void-0/-/babel-helper-is-void-0-0.4.3.tgz",
|
||||
"integrity": "sha1-fZwBtFYee5Xb2g9u7kj1tg5nMT4=",
|
||||
"dev": true
|
||||
},
|
||||
"babel-helper-mark-eval-scopes": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-helper-mark-eval-scopes/-/babel-helper-mark-eval-scopes-0.4.3.tgz",
|
||||
"integrity": "sha1-0kSjvvmESHJgP/tG4izorN9VFWI=",
|
||||
"dev": true
|
||||
},
|
||||
"babel-helper-remove-or-void": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-helper-remove-or-void/-/babel-helper-remove-or-void-0.4.3.tgz",
|
||||
"integrity": "sha1-pPA7QAd6D/6I5F0HAQ3uJB/1rmA=",
|
||||
"dev": true
|
||||
},
|
||||
"babel-helper-to-multiple-sequence-expressions": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-helper-to-multiple-sequence-expressions/-/babel-helper-to-multiple-sequence-expressions-0.4.3.tgz",
|
||||
"integrity": "sha1-W1GLESf0ezA4dzOGoVYaK0jmMrY=",
|
||||
"dev": true
|
||||
},
|
||||
"babel-helpers": {
|
||||
"version": "6.24.1",
|
||||
"resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz",
|
||||
"integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-runtime": "6.26.0",
|
||||
"babel-template": "6.26.0"
|
||||
}
|
||||
},
|
||||
"babel-messages": {
|
||||
"version": "6.23.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz",
|
||||
"integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-runtime": "6.26.0"
|
||||
}
|
||||
},
|
||||
"babel-plugin-minify-builtins": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-minify-builtins/-/babel-plugin-minify-builtins-0.4.3.tgz",
|
||||
"integrity": "sha1-nqPVn0rEp7uVjXEtKVVqH4b3+B4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-helper-evaluate-path": "0.4.3"
|
||||
}
|
||||
},
|
||||
"babel-plugin-minify-constant-folding": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-minify-constant-folding/-/babel-plugin-minify-constant-folding-0.4.3.tgz",
|
||||
"integrity": "sha1-MA+d6N2ghEoXaxk2U5YOJK0z4ZE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-helper-evaluate-path": "0.4.3"
|
||||
}
|
||||
},
|
||||
"babel-plugin-minify-dead-code-elimination": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-minify-dead-code-elimination/-/babel-plugin-minify-dead-code-elimination-0.4.3.tgz",
|
||||
"integrity": "sha1-c2KCZYZPkAjQAnUG9Yq+s8HQLZg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-helper-evaluate-path": "0.4.3",
|
||||
"babel-helper-mark-eval-scopes": "0.4.3",
|
||||
"babel-helper-remove-or-void": "0.4.3",
|
||||
"lodash.some": "4.6.0"
|
||||
}
|
||||
},
|
||||
"babel-plugin-minify-flip-comparisons": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-minify-flip-comparisons/-/babel-plugin-minify-flip-comparisons-0.4.3.tgz",
|
||||
"integrity": "sha1-AMqHDLjxO0XAOLPB68DyJyk8llo=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-helper-is-void-0": "0.4.3"
|
||||
}
|
||||
},
|
||||
"babel-plugin-minify-guarded-expressions": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-minify-guarded-expressions/-/babel-plugin-minify-guarded-expressions-0.4.3.tgz",
|
||||
"integrity": "sha1-zHCbRFP9IbHzAod0RMifiEJ845c=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-helper-flip-expressions": "0.4.3"
|
||||
}
|
||||
},
|
||||
"babel-plugin-minify-infinity": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-minify-infinity/-/babel-plugin-minify-infinity-0.4.3.tgz",
|
||||
"integrity": "sha1-37h2obCKBldjhO8/kuZTumB7Oco=",
|
||||
"dev": true
|
||||
},
|
||||
"babel-plugin-minify-mangle-names": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-minify-mangle-names/-/babel-plugin-minify-mangle-names-0.4.3.tgz",
|
||||
"integrity": "sha1-FvG/90t6fJPfwkHngx3V+0sCPvc=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-helper-mark-eval-scopes": "0.4.3"
|
||||
}
|
||||
},
|
||||
"babel-plugin-minify-numeric-literals": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-minify-numeric-literals/-/babel-plugin-minify-numeric-literals-0.4.3.tgz",
|
||||
"integrity": "sha1-jk/VYcefeAEob/YOjF/Z3u6TwLw=",
|
||||
"dev": true
|
||||
},
|
||||
"babel-plugin-minify-replace": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-minify-replace/-/babel-plugin-minify-replace-0.4.3.tgz",
|
||||
"integrity": "sha1-nSifS6FdTmAR6HmfpfG6d+yBIZ0=",
|
||||
"dev": true
|
||||
},
|
||||
"babel-plugin-minify-simplify": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-minify-simplify/-/babel-plugin-minify-simplify-0.4.3.tgz",
|
||||
"integrity": "sha1-N3VthcYURktLCSfytOQXGR1Vc4o=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-helper-flip-expressions": "0.4.3",
|
||||
"babel-helper-is-nodes-equiv": "0.0.1",
|
||||
"babel-helper-to-multiple-sequence-expressions": "0.4.3"
|
||||
}
|
||||
},
|
||||
"babel-plugin-minify-type-constructors": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-minify-type-constructors/-/babel-plugin-minify-type-constructors-0.4.3.tgz",
|
||||
"integrity": "sha1-G8bxW4f3qxCF1CszC3F2V6IVZQA=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-helper-is-void-0": "0.4.3"
|
||||
}
|
||||
},
|
||||
"babel-plugin-transform-inline-consecutive-adds": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-transform-inline-consecutive-adds/-/babel-plugin-transform-inline-consecutive-adds-0.4.3.tgz",
|
||||
"integrity": "sha1-Mj1Ho+pjqDp6w8gRro5pQfrysNE=",
|
||||
"dev": true
|
||||
},
|
||||
"babel-plugin-transform-member-expression-literals": {
|
||||
"version": "6.9.4",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-transform-member-expression-literals/-/babel-plugin-transform-member-expression-literals-6.9.4.tgz",
|
||||
"integrity": "sha1-NwOcmgwzE6OUlfqsL/OmtbnQOL8=",
|
||||
"dev": true
|
||||
},
|
||||
"babel-plugin-transform-merge-sibling-variables": {
|
||||
"version": "6.9.4",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-transform-merge-sibling-variables/-/babel-plugin-transform-merge-sibling-variables-6.9.4.tgz",
|
||||
"integrity": "sha1-hbQi/DN3tEnJ0c3kQIcgNTJAHa4=",
|
||||
"dev": true
|
||||
},
|
||||
"babel-plugin-transform-minify-booleans": {
|
||||
"version": "6.9.4",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-transform-minify-booleans/-/babel-plugin-transform-minify-booleans-6.9.4.tgz",
|
||||
"integrity": "sha1-rLs+VqNVXdI5KOS1gtKFFi3SsZg=",
|
||||
"dev": true
|
||||
},
|
||||
"babel-plugin-transform-property-literals": {
|
||||
"version": "6.9.4",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-transform-property-literals/-/babel-plugin-transform-property-literals-6.9.4.tgz",
|
||||
"integrity": "sha1-mMHSHiVXNlc/k+zlRFn2ziSYXTk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"esutils": "2.0.2"
|
||||
}
|
||||
},
|
||||
"babel-plugin-transform-regexp-constructors": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-transform-regexp-constructors/-/babel-plugin-transform-regexp-constructors-0.4.3.tgz",
|
||||
"integrity": "sha1-WLd3W2OvzzMyj66aX4j71PsLSWU=",
|
||||
"dev": true
|
||||
},
|
||||
"babel-plugin-transform-remove-console": {
|
||||
"version": "6.9.4",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-console/-/babel-plugin-transform-remove-console-6.9.4.tgz",
|
||||
"integrity": "sha1-uYA2DAZzhOJLNXpYjYB9PINSd4A=",
|
||||
"dev": true
|
||||
},
|
||||
"babel-plugin-transform-remove-debugger": {
|
||||
"version": "6.9.4",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-debugger/-/babel-plugin-transform-remove-debugger-6.9.4.tgz",
|
||||
"integrity": "sha1-QrcnYxyXl44estGZp67IShgznvI=",
|
||||
"dev": true
|
||||
},
|
||||
"babel-plugin-transform-remove-undefined": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-transform-remove-undefined/-/babel-plugin-transform-remove-undefined-0.4.3.tgz",
|
||||
"integrity": "sha1-1AsNp/kcCMBsxyt2dHTAHEiU3gI=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-helper-evaluate-path": "0.4.3"
|
||||
}
|
||||
},
|
||||
"babel-plugin-transform-simplify-comparison-operators": {
|
||||
"version": "6.9.4",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-transform-simplify-comparison-operators/-/babel-plugin-transform-simplify-comparison-operators-6.9.4.tgz",
|
||||
"integrity": "sha1-9ir+CWyrDh9ootdT/fKDiIRxzrk=",
|
||||
"dev": true
|
||||
},
|
||||
"babel-plugin-transform-undefined-to-void": {
|
||||
"version": "6.9.4",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-transform-undefined-to-void/-/babel-plugin-transform-undefined-to-void-6.9.4.tgz",
|
||||
"integrity": "sha1-viQcqBQEAwZ4t0hxcyK4nQyP4oA=",
|
||||
"dev": true
|
||||
},
|
||||
"babel-preset-minify": {
|
||||
"version": "0.4.3",
|
||||
"resolved": "https://registry.npmjs.org/babel-preset-minify/-/babel-preset-minify-0.4.3.tgz",
|
||||
"integrity": "sha1-spw91pGJBThFmPCSuVUVLiah/g8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-plugin-minify-builtins": "0.4.3",
|
||||
"babel-plugin-minify-constant-folding": "0.4.3",
|
||||
"babel-plugin-minify-dead-code-elimination": "0.4.3",
|
||||
"babel-plugin-minify-flip-comparisons": "0.4.3",
|
||||
"babel-plugin-minify-guarded-expressions": "0.4.3",
|
||||
"babel-plugin-minify-infinity": "0.4.3",
|
||||
"babel-plugin-minify-mangle-names": "0.4.3",
|
||||
"babel-plugin-minify-numeric-literals": "0.4.3",
|
||||
"babel-plugin-minify-replace": "0.4.3",
|
||||
"babel-plugin-minify-simplify": "0.4.3",
|
||||
"babel-plugin-minify-type-constructors": "0.4.3",
|
||||
"babel-plugin-transform-inline-consecutive-adds": "0.4.3",
|
||||
"babel-plugin-transform-member-expression-literals": "6.9.4",
|
||||
"babel-plugin-transform-merge-sibling-variables": "6.9.4",
|
||||
"babel-plugin-transform-minify-booleans": "6.9.4",
|
||||
"babel-plugin-transform-property-literals": "6.9.4",
|
||||
"babel-plugin-transform-regexp-constructors": "0.4.3",
|
||||
"babel-plugin-transform-remove-console": "6.9.4",
|
||||
"babel-plugin-transform-remove-debugger": "6.9.4",
|
||||
"babel-plugin-transform-remove-undefined": "0.4.3",
|
||||
"babel-plugin-transform-simplify-comparison-operators": "6.9.4",
|
||||
"babel-plugin-transform-undefined-to-void": "6.9.4",
|
||||
"lodash.isplainobject": "4.0.6"
|
||||
}
|
||||
},
|
||||
"babel-register": {
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz",
|
||||
"integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-core": "6.26.3",
|
||||
"babel-runtime": "6.26.0",
|
||||
"core-js": "2.5.7",
|
||||
"home-or-tmp": "2.0.0",
|
||||
"lodash": "4.17.10",
|
||||
"mkdirp": "0.5.1",
|
||||
"source-map-support": "0.4.18"
|
||||
}
|
||||
},
|
||||
"babel-runtime": {
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz",
|
||||
"integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"core-js": "2.5.7",
|
||||
"regenerator-runtime": "0.11.1"
|
||||
}
|
||||
},
|
||||
"babel-template": {
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz",
|
||||
"integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-runtime": "6.26.0",
|
||||
"babel-traverse": "6.26.0",
|
||||
"babel-types": "6.26.0",
|
||||
"babylon": "6.18.0",
|
||||
"lodash": "4.17.10"
|
||||
}
|
||||
},
|
||||
"babel-traverse": {
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz",
|
||||
"integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-code-frame": "6.26.0",
|
||||
"babel-messages": "6.23.0",
|
||||
"babel-runtime": "6.26.0",
|
||||
"babel-types": "6.26.0",
|
||||
"babylon": "6.18.0",
|
||||
"debug": "2.6.9",
|
||||
"globals": "9.18.0",
|
||||
"invariant": "2.2.4",
|
||||
"lodash": "4.17.10"
|
||||
}
|
||||
},
|
||||
"babel-types": {
|
||||
"version": "6.26.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz",
|
||||
"integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"babel-runtime": "6.26.0",
|
||||
"esutils": "2.0.2",
|
||||
"lodash": "4.17.10",
|
||||
"to-fast-properties": "1.0.3"
|
||||
}
|
||||
},
|
||||
"babylon": {
|
||||
"version": "6.18.0",
|
||||
"resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz",
|
||||
"integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==",
|
||||
"dev": true
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
|
||||
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
|
||||
"dev": true
|
||||
},
|
||||
"brace-expansion": {
|
||||
"version": "1.1.11",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"balanced-match": "1.0.0",
|
||||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz",
|
||||
"integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "2.2.1",
|
||||
"escape-string-regexp": "1.0.5",
|
||||
"has-ansi": "2.0.0",
|
||||
"strip-ansi": "3.0.1",
|
||||
"supports-color": "2.0.0"
|
||||
}
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||
"dev": true
|
||||
},
|
||||
"convert-source-map": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.5.1.tgz",
|
||||
"integrity": "sha1-uCeAl7m8IpNl3lxiz1/K7YtVmeU=",
|
||||
"dev": true
|
||||
},
|
||||
"core-js": {
|
||||
"version": "2.5.7",
|
||||
"resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz",
|
||||
"integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==",
|
||||
"dev": true
|
||||
},
|
||||
"debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"detect-indent": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz",
|
||||
"integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"repeating": "2.0.1"
|
||||
}
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
|
||||
"dev": true
|
||||
},
|
||||
"esutils": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
|
||||
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
|
||||
"dev": true
|
||||
},
|
||||
"globals": {
|
||||
"version": "9.18.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz",
|
||||
"integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==",
|
||||
"dev": true
|
||||
},
|
||||
"has-ansi": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz",
|
||||
"integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "2.1.1"
|
||||
}
|
||||
},
|
||||
"home-or-tmp": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
|
||||
"integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"os-homedir": "1.0.2",
|
||||
"os-tmpdir": "1.0.2"
|
||||
}
|
||||
},
|
||||
"invariant": {
|
||||
"version": "2.2.4",
|
||||
"resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz",
|
||||
"integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loose-envify": "1.4.0"
|
||||
}
|
||||
},
|
||||
"is-finite": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz",
|
||||
"integrity": "sha1-zGZ3aVYCvlUO8R6LSqYwU0K20Ko=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"number-is-nan": "1.0.1"
|
||||
}
|
||||
},
|
||||
"js-tokens": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz",
|
||||
"integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=",
|
||||
"dev": true
|
||||
},
|
||||
"jsesc": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz",
|
||||
"integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=",
|
||||
"dev": true
|
||||
},
|
||||
"json5": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz",
|
||||
"integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash": {
|
||||
"version": "4.17.10",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.10.tgz",
|
||||
"integrity": "sha512-UejweD1pDoXu+AD825lWwp4ZGtSwgnpZxb3JDViD7StjQz+Nb/6l093lx4OQ0foGWNRoc19mWy7BzL+UAK2iVg==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.isplainobject": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
|
||||
"integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.some": {
|
||||
"version": "4.6.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz",
|
||||
"integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0=",
|
||||
"dev": true
|
||||
},
|
||||
"loose-envify": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"js-tokens": "3.0.2"
|
||||
}
|
||||
},
|
||||
"magic-string": {
|
||||
"version": "0.24.1",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.24.1.tgz",
|
||||
"integrity": "sha512-YBfNxbJiixMzxW40XqJEIldzHyh5f7CZKalo1uZffevyrPEX8Qgo9s0dmcORLHdV47UyvJg8/zD+6hQG3qvJrA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"sourcemap-codec": "1.4.1"
|
||||
}
|
||||
},
|
||||
"minimatch": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
|
||||
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"brace-expansion": "1.1.11"
|
||||
}
|
||||
},
|
||||
"minimist": {
|
||||
"version": "0.0.8",
|
||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
|
||||
"dev": true
|
||||
},
|
||||
"mkdirp": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"minimist": "0.0.8"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
|
||||
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
|
||||
"dev": true
|
||||
},
|
||||
"number-is-nan": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
|
||||
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=",
|
||||
"dev": true
|
||||
},
|
||||
"os-homedir": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
|
||||
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
|
||||
"dev": true
|
||||
},
|
||||
"os-tmpdir": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
||||
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
|
||||
"dev": true
|
||||
},
|
||||
"path-is-absolute": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
|
||||
"dev": true
|
||||
},
|
||||
"private": {
|
||||
"version": "0.1.8",
|
||||
"resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz",
|
||||
"integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==",
|
||||
"dev": true
|
||||
},
|
||||
"regenerator-runtime": {
|
||||
"version": "0.11.1",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
|
||||
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
|
||||
"dev": true
|
||||
},
|
||||
"repeating": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz",
|
||||
"integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-finite": "1.0.2"
|
||||
}
|
||||
},
|
||||
"rollup-plugin-babel-minify": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/rollup-plugin-babel-minify/-/rollup-plugin-babel-minify-5.0.0.tgz",
|
||||
"integrity": "sha512-/XMozvFf1wm0O04FEybfEGl05jq9RQ408Xx/GjCfCau9ETzKYlEkfdi19pj2EvmpbSO/r0yspMKmwnUHNycdsw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@comandeer/babel-plugin-banner": "2.0.2",
|
||||
"babel-core": "6.26.3",
|
||||
"babel-preset-minify": "0.4.3",
|
||||
"magic-string": "0.24.1"
|
||||
}
|
||||
},
|
||||
"slash": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
|
||||
"integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
|
||||
"dev": true
|
||||
},
|
||||
"source-map": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||
"integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=",
|
||||
"dev": true
|
||||
},
|
||||
"source-map-support": {
|
||||
"version": "0.4.18",
|
||||
"resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz",
|
||||
"integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"source-map": "0.5.7"
|
||||
}
|
||||
},
|
||||
"sourcemap-codec": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.1.tgz",
|
||||
"integrity": "sha512-hX1eNBNuilj8yfFnECh0DzLgwKpBLMIvmhgEhixXNui8lMLBInTI8Kyxt++RwJnMNu7cAUo635L2+N1TxMJCzA==",
|
||||
"dev": true
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "2.1.1"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
|
||||
"integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
|
||||
"dev": true
|
||||
},
|
||||
"to-fast-properties": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz",
|
||||
"integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=",
|
||||
"dev": true
|
||||
},
|
||||
"trim-right": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz",
|
||||
"integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"name": "bsehovac.github.io",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"rollup-plugin-babel-minify": "^5.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "rollup -c rollup.config.build.js && cp upup.min.js export/ && cp upup.sw.min.js export/ && cp index.html export/ && cp -R assets export/"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+ssh://git@github.com/bsehovac/bsehovac.github.io.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"bugs": {
|
||||
"url": "https://github.com/bsehovac/bsehovac.github.io/issues"
|
||||
},
|
||||
"homepage": "https://github.com/bsehovac/bsehovac.github.io#readme"
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import minify from 'rollup-plugin-babel-minify';
|
||||
|
||||
export default {
|
||||
input: './src/js/Game.js',
|
||||
plugins: [
|
||||
minify({ comments: false, sourceMap: false }),
|
||||
],
|
||||
output: {
|
||||
format: 'iife',
|
||||
file: './assets/js/cube.js',
|
||||
indent: '\t',
|
||||
sourceMap: false,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
export default {
|
||||
input: './src/js/Game.js',
|
||||
plugins: [],
|
||||
output: {
|
||||
format: 'iife',
|
||||
file: './assets/js/cube.js',
|
||||
indent: '\t',
|
||||
sourceMap: false,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,88 @@
|
||||
const animationEngine = ( () => {
|
||||
|
||||
let uniqueID = 0;
|
||||
|
||||
class AnimationEngine {
|
||||
|
||||
constructor() {
|
||||
|
||||
this.ids = [];
|
||||
this.animations = {};
|
||||
this.update = this.update.bind( this );
|
||||
this.raf = 0;
|
||||
this.time = 0;
|
||||
|
||||
}
|
||||
|
||||
update() {
|
||||
|
||||
const now = performance.now();
|
||||
const delta = now - this.time
|
||||
this.time = now;
|
||||
|
||||
let i = this.ids.length;
|
||||
|
||||
this.raf = i ? requestAnimationFrame( this.update ) : 0;
|
||||
|
||||
while ( i-- )
|
||||
this.animations[ this.ids[ i ] ] && this.animations[ this.ids[ i ] ].update( delta );
|
||||
|
||||
}
|
||||
|
||||
add( animation ) {
|
||||
|
||||
animation.id = uniqueID ++;
|
||||
|
||||
this.ids.push( animation.id );
|
||||
this.animations[ animation.id ] = animation;
|
||||
|
||||
if ( this.raf !== 0 ) return;
|
||||
|
||||
this.time = performance.now();
|
||||
this.raf = requestAnimationFrame( this.update );
|
||||
|
||||
}
|
||||
|
||||
remove( animation ) {
|
||||
|
||||
const index = this.ids.indexOf( animation.id );
|
||||
|
||||
if ( index < 0 ) return;
|
||||
|
||||
this.ids.splice( index, 1 );
|
||||
delete this.animations[ animation.id ];
|
||||
animation = null;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return new AnimationEngine();
|
||||
|
||||
} )();
|
||||
|
||||
class Animation {
|
||||
|
||||
constructor( start ) {
|
||||
|
||||
if ( start === true ) this.start();
|
||||
|
||||
}
|
||||
|
||||
start() {
|
||||
|
||||
animationEngine.add( this );
|
||||
|
||||
}
|
||||
|
||||
stop() {
|
||||
|
||||
animationEngine.remove( this );
|
||||
|
||||
}
|
||||
|
||||
update( delta ) {}
|
||||
|
||||
}
|
||||
|
||||
export { Animation };
|
||||
@@ -0,0 +1 @@
|
||||
Background
|
||||
@@ -0,0 +1,228 @@
|
||||
import { Animation } from './Animation.js';
|
||||
|
||||
class Confetti {
|
||||
|
||||
constructor( game ) {
|
||||
|
||||
this.game = game;
|
||||
this.started = 0;
|
||||
|
||||
this.options = {
|
||||
speed: { min: 0.0011, max: 0.0022 },
|
||||
revolution: { min: 0.01, max: 0.05 },
|
||||
size: { min: 0.1, max: 0.15 },
|
||||
colors: [ 0x41aac8, 0x82ca38, 0xffef48, 0xef3923, 0xff8c0a ],
|
||||
}
|
||||
|
||||
this.geometry = new THREE.PlaneGeometry( 1, 1 );
|
||||
this.material = new THREE.MeshLambertMaterial( { side: THREE.DoubleSide } );
|
||||
|
||||
this.holders = [
|
||||
new ConfettiStage( this.game, this, 1, 20 ),
|
||||
new ConfettiStage( this.game, this, -1, 30 ),
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
start() {
|
||||
|
||||
if ( this.started > 0 ) return;
|
||||
|
||||
this.holders.forEach( holder => {
|
||||
|
||||
this.game.world.scene.add( holder.holder );
|
||||
holder.start();
|
||||
this.started ++;
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
stop() {
|
||||
|
||||
if ( this.started == 0 ) return;
|
||||
|
||||
this.holders.forEach( holder => {
|
||||
|
||||
holder.stop( () => {
|
||||
|
||||
this.game.world.scene.remove( holder.holder );
|
||||
this.started --;
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
updateColors( colors ) {
|
||||
|
||||
this.holders.forEach( holder => {
|
||||
|
||||
holder.options.colors.forEach( ( color, index ) => {
|
||||
|
||||
holder.options.colors[ index ] = colors[ [ 'D', 'F', 'R', 'B', 'L' ][ index ] ];
|
||||
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ConfettiStage extends Animation {
|
||||
|
||||
constructor( game, parent, distance, count ) {
|
||||
|
||||
super( false );
|
||||
|
||||
this.game = game;
|
||||
this.parent = parent;
|
||||
|
||||
this.distanceFromCube = distance;
|
||||
|
||||
this.count = count;
|
||||
this.particles = [];
|
||||
|
||||
this.holder = new THREE.Object3D();
|
||||
this.holder.rotation.copy( this.game.world.camera.rotation );
|
||||
|
||||
this.object = new THREE.Object3D();
|
||||
this.holder.add( this.object );
|
||||
|
||||
this.resizeViewport = this.resizeViewport.bind( this );
|
||||
this.game.world.onResize.push( this.resizeViewport )
|
||||
this.resizeViewport();
|
||||
|
||||
this.geometry = this.parent.geometry;
|
||||
this.material = this.parent.material;
|
||||
|
||||
this.options = this.parent.options;
|
||||
|
||||
let i = this.count;
|
||||
while ( i-- ) this.particles.push( new Particle( this ) );
|
||||
|
||||
}
|
||||
|
||||
start() {
|
||||
|
||||
this.time = performance.now();
|
||||
this.playing = true;
|
||||
|
||||
let i = this.count;
|
||||
while ( i-- ) this.particles[ i ].reset();
|
||||
|
||||
super.start();
|
||||
|
||||
}
|
||||
|
||||
stop( callback ) {
|
||||
|
||||
this.playing = false;
|
||||
this.completed = 0;
|
||||
this.callback = callback;
|
||||
|
||||
}
|
||||
|
||||
reset() {
|
||||
|
||||
super.stop();
|
||||
|
||||
this.callback();
|
||||
|
||||
}
|
||||
|
||||
update() {
|
||||
|
||||
const now = performance.now();
|
||||
const delta = now - this.time;
|
||||
this.time = now;
|
||||
|
||||
let i = this.count;
|
||||
|
||||
while ( i-- )
|
||||
if ( ! this.particles[ i ].completed ) this.particles[ i ].update( delta );
|
||||
|
||||
if ( ! this.playing && this.completed == this.count ) this.reset();
|
||||
|
||||
}
|
||||
|
||||
resizeViewport() {
|
||||
|
||||
const fovRad = this.game.world.camera.fov * THREE.Math.DEG2RAD;
|
||||
|
||||
this.height = 2 * Math.tan( fovRad / 2 ) * ( this.game.world.camera.position.length() - this.distanceFromCube );
|
||||
this.width = this.height * this.game.world.camera.aspect;
|
||||
|
||||
const scale = 1 / this.game.transition.data.cameraZoom;
|
||||
|
||||
this.width *= scale;
|
||||
this.height *= scale;
|
||||
|
||||
this.object.position.z = this.distanceFromCube;
|
||||
this.object.position.y = this.height / 2;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Particle {
|
||||
|
||||
constructor( confetti ) {
|
||||
|
||||
this.confetti = confetti;
|
||||
this.options = this.confetti.options;
|
||||
|
||||
this.velocity = new THREE.Vector3();
|
||||
this.force = new THREE.Vector3();
|
||||
|
||||
this.mesh = new THREE.Mesh( this.confetti.geometry, this.confetti.material.clone() );
|
||||
this.confetti.object.add( this.mesh );
|
||||
|
||||
this.size = THREE.Math.randFloat( this.options.size.min, this.options.size.max );
|
||||
this.mesh.scale.set( this.size, this.size, this.size );
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
reset( randomHeight = true ) {
|
||||
|
||||
this.completed = false;
|
||||
|
||||
this.color = new THREE.Color( this.options.colors[ Math.floor( Math.random() * this.options.colors.length ) ] );
|
||||
this.mesh.material.color.set( this.color );
|
||||
|
||||
this.speed = THREE.Math.randFloat( this.options.speed.min, this.options.speed.max ) * - 1;
|
||||
this.mesh.position.x = THREE.Math.randFloat( - this.confetti.width / 2, this.confetti.width / 2 );
|
||||
this.mesh.position.y = ( randomHeight )
|
||||
? THREE.Math.randFloat( this.size, this.confetti.height + this.size )
|
||||
: this.size;
|
||||
|
||||
this.revolutionSpeed = THREE.Math.randFloat( this.options.revolution.min, this.options.revolution.max );
|
||||
this.revolutionAxis = [ 'x', 'y', 'z' ][ Math.floor( Math.random() * 3 ) ];
|
||||
this.mesh.rotation.set( Math.random() * Math.PI / 3, Math.random() * Math.PI / 3, Math.random() * Math.PI / 3 );
|
||||
|
||||
}
|
||||
|
||||
stop() {
|
||||
|
||||
this.completed = true;
|
||||
this.confetti.completed ++;
|
||||
|
||||
}
|
||||
|
||||
update( delta ) {
|
||||
|
||||
this.mesh.position.y += this.speed * delta;
|
||||
this.mesh.rotation[ this.revolutionAxis ] += this.revolutionSpeed;
|
||||
|
||||
if ( this.mesh.position.y < - this.confetti.height - this.size )
|
||||
( this.confetti.playing ) ? this.reset( false ) : this.stop();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Confetti };
|
||||
@@ -0,0 +1,580 @@
|
||||
import { Tween, Easing } from './Tween.js';
|
||||
import { Draggable } from './Draggable.js';
|
||||
|
||||
const STILL = 0;
|
||||
const PREPARING = 1;
|
||||
const ROTATING = 2;
|
||||
const ANIMATING = 3;
|
||||
|
||||
class Controls {
|
||||
|
||||
constructor( game ) {
|
||||
|
||||
this.game = game;
|
||||
|
||||
this.flipConfig = 0;
|
||||
|
||||
this.flipEasings = [ Easing.Power.Out( 3 ), Easing.Sine.Out(), Easing.Back.Out( 1.5 ) ];
|
||||
this.flipSpeeds = [ 125, 200, 300 ];
|
||||
|
||||
this.raycaster = new THREE.Raycaster();
|
||||
|
||||
const helperMaterial = new THREE.MeshBasicMaterial( { depthWrite: false, transparent: true, opacity: 0, color: 0x0033ff } );
|
||||
|
||||
this.group = new THREE.Object3D();
|
||||
this.group.name = 'controls';
|
||||
this.game.cube.object.add( this.group );
|
||||
|
||||
this.helper = new THREE.Mesh(
|
||||
new THREE.PlaneBufferGeometry( 200, 200 ),
|
||||
helperMaterial.clone()
|
||||
);
|
||||
|
||||
this.helper.rotation.set( 0, Math.PI / 4, 0 );
|
||||
this.game.world.scene.add( this.helper );
|
||||
|
||||
this.edges = new THREE.Mesh(
|
||||
new THREE.BoxBufferGeometry( 1, 1, 1 ),
|
||||
helperMaterial.clone(),
|
||||
);
|
||||
|
||||
this.game.world.scene.add( this.edges );
|
||||
|
||||
this.onSolved = () => {};
|
||||
this.onMove = () => {};
|
||||
|
||||
this.momentum = [];
|
||||
|
||||
this.scramble = null;
|
||||
this.state = STILL;
|
||||
this.enabled = false;
|
||||
|
||||
this.initDraggable();
|
||||
|
||||
}
|
||||
|
||||
enable() {
|
||||
|
||||
this.draggable.enable();
|
||||
this.enabled = true;
|
||||
|
||||
}
|
||||
|
||||
disable() {
|
||||
|
||||
this.draggable.disable();
|
||||
this.enabled = false;
|
||||
|
||||
}
|
||||
|
||||
initDraggable() {
|
||||
|
||||
this.draggable = new Draggable( this.game.dom.game );
|
||||
|
||||
this.draggable.onDragStart = position => {
|
||||
|
||||
if ( this.scramble !== null ) return;
|
||||
if ( this.state === PREPARING || this.state === ROTATING ) return;
|
||||
|
||||
this.gettingDrag = this.state === ANIMATING;
|
||||
|
||||
const edgeIntersect = this.getIntersect( position.current, this.edges, false );
|
||||
|
||||
if ( edgeIntersect !== false ) {
|
||||
|
||||
this.dragIntersect = this.getIntersect( position.current, this.game.cube.cubes, true );
|
||||
|
||||
}
|
||||
|
||||
if ( edgeIntersect !== false && this.dragIntersect !== false ) {
|
||||
|
||||
this.dragNormal = edgeIntersect.face.normal.round();
|
||||
this.flipType = 'layer';
|
||||
|
||||
this.attach( this.helper, this.edges )
|
||||
|
||||
this.helper.rotation.set( 0, 0, 0 );
|
||||
this.helper.position.set( 0, 0, 0 );
|
||||
this.helper.lookAt( this.dragNormal );
|
||||
this.helper.translateZ( 0.5 );
|
||||
this.helper.updateMatrixWorld();
|
||||
|
||||
this.detach( this.helper, this.edges );
|
||||
|
||||
} else {
|
||||
|
||||
this.dragNormal = new THREE.Vector3( 0, 0, 1 );
|
||||
this.flipType = 'cube';
|
||||
|
||||
this.helper.position.set( 0, 0, 0 );
|
||||
this.helper.rotation.set( 0, Math.PI / 4, 0 );
|
||||
this.helper.updateMatrixWorld();
|
||||
|
||||
}
|
||||
|
||||
let planeIntersect = this.getIntersect( position.current, this.helper, false );
|
||||
if ( planeIntersect === false ) return;
|
||||
|
||||
this.dragCurrent = this.helper.worldToLocal( planeIntersect.point );
|
||||
this.dragTotal = new THREE.Vector3();
|
||||
this.state = ( this.state === STILL ) ? PREPARING : this.state;
|
||||
|
||||
};
|
||||
|
||||
this.draggable.onDragMove = position => {
|
||||
|
||||
if ( this.scramble !== null ) return;
|
||||
if ( this.state === STILL || ( this.state === ANIMATING && this.gettingDrag === false ) ) return;
|
||||
|
||||
const planeIntersect = this.getIntersect( position.current, this.helper, false );
|
||||
if ( planeIntersect === false ) return;
|
||||
|
||||
const point = this.helper.worldToLocal( planeIntersect.point.clone() );
|
||||
|
||||
this.dragDelta = point.clone().sub( this.dragCurrent ).setZ( 0 );
|
||||
this.dragTotal.add( this.dragDelta );
|
||||
this.dragCurrent = point;
|
||||
this.addMomentumPoint( this.dragDelta );
|
||||
|
||||
if ( this.state === PREPARING && this.dragTotal.length() > 0.05 ) {
|
||||
|
||||
this.dragDirection = this.getMainAxis( this.dragTotal );
|
||||
|
||||
if ( this.flipType === 'layer' ) {
|
||||
|
||||
const direction = new THREE.Vector3();
|
||||
direction[ this.dragDirection ] = 1;
|
||||
|
||||
const worldDirection = this.helper.localToWorld( direction ).sub( this.helper.position );
|
||||
const objectDirection = this.edges.worldToLocal( worldDirection ).round();
|
||||
|
||||
this.flipAxis = objectDirection.cross( this.dragNormal ).negate();
|
||||
|
||||
this.selectLayer( this.getLayer( false ) );
|
||||
|
||||
} else {
|
||||
|
||||
const axis = ( this.dragDirection != 'x' )
|
||||
? ( ( this.dragDirection == 'y' && position.current.x > this.game.world.width / 2 ) ? 'z' : 'x' )
|
||||
: 'y';
|
||||
|
||||
this.flipAxis = new THREE.Vector3();
|
||||
this.flipAxis[ axis ] = 1 * ( ( axis == 'x' ) ? - 1 : 1 );
|
||||
|
||||
}
|
||||
|
||||
this.flipAngle = 0;
|
||||
this.state = ROTATING;
|
||||
|
||||
} else if ( this.state === ROTATING ) {
|
||||
|
||||
const rotation = this.dragDelta[ this.dragDirection ];
|
||||
|
||||
if ( this.flipType === 'layer' ) {
|
||||
|
||||
this.group.rotateOnAxis( this.flipAxis, rotation );
|
||||
this.flipAngle += rotation;
|
||||
|
||||
} else {
|
||||
|
||||
this.edges.rotateOnWorldAxis( this.flipAxis, rotation );
|
||||
this.game.cube.object.rotation.copy( this.edges.rotation );
|
||||
this.flipAngle += rotation;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.draggable.onDragEnd = position => {
|
||||
|
||||
if ( this.scramble !== null ) return;
|
||||
if ( this.state !== ROTATING ) {
|
||||
|
||||
this.gettingDrag = false;
|
||||
this.state = STILL;
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
this.state = ANIMATING;
|
||||
|
||||
const momentum = this.getMomentum()[ this.dragDirection ];
|
||||
const flip = ( Math.abs( momentum ) > 0.05 && Math.abs( this.flipAngle ) < Math.PI / 2 );
|
||||
|
||||
const angle = flip
|
||||
? this.roundAngle( this.flipAngle + Math.sign( this.flipAngle ) * ( Math.PI / 4 ) )
|
||||
: this.roundAngle( this.flipAngle );
|
||||
|
||||
const delta = angle - this.flipAngle;
|
||||
|
||||
if ( this.flipType === 'layer' ) {
|
||||
|
||||
this.rotateLayer( delta, false, layer => {
|
||||
|
||||
this.game.storage.saveGame();
|
||||
|
||||
this.state = this.gettingDrag ? PREPARING : STILL;
|
||||
this.gettingDrag = false;
|
||||
|
||||
this.checkIsSolved();
|
||||
|
||||
} );
|
||||
|
||||
} else {
|
||||
|
||||
this.rotateCube( delta, () => {
|
||||
|
||||
this.state = this.gettingDrag ? PREPARING : STILL;
|
||||
this.gettingDrag = false;
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
rotateLayer( rotation, scramble, callback ) {
|
||||
|
||||
const config = scramble ? 0 : this.flipConfig;
|
||||
|
||||
const easing = this.flipEasings[ config ];
|
||||
const duration = this.flipSpeeds[ config ]
|
||||
const bounce = ( config == 2 ) ? this.bounceCube() : ( () => {} );
|
||||
|
||||
this.rotationTween = new Tween( {
|
||||
easing: easing,
|
||||
duration: duration,
|
||||
onUpdate: tween => {
|
||||
|
||||
let deltaAngle = tween.delta * rotation;
|
||||
this.group.rotateOnAxis( this.flipAxis, deltaAngle );
|
||||
bounce( tween.value, deltaAngle, rotation );
|
||||
|
||||
},
|
||||
onComplete: () => {
|
||||
|
||||
if ( ! scramble ) this.onMove();
|
||||
|
||||
const layer = this.flipLayer.slice( 0 );
|
||||
|
||||
this.game.cube.object.rotation.setFromVector3( this.snapRotation( this.game.cube.object.rotation.toVector3() ) );
|
||||
this.group.rotation.setFromVector3( this.snapRotation( this.group.rotation.toVector3() ) );
|
||||
this.deselectLayer( this.flipLayer );
|
||||
|
||||
callback( layer );
|
||||
|
||||
},
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
bounceCube() {
|
||||
|
||||
let fixDelta = true;
|
||||
|
||||
return ( progress, delta, rotation ) => {
|
||||
|
||||
if ( progress >= 1 ) {
|
||||
|
||||
if ( fixDelta ) {
|
||||
|
||||
delta = ( progress - 1 ) * rotation;
|
||||
fixDelta = false;
|
||||
|
||||
}
|
||||
|
||||
this.game.cube.object.rotateOnAxis( this.flipAxis, delta );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
rotateCube( rotation, callback ) {
|
||||
|
||||
const config = this.flipConfig;
|
||||
const easing = [ Easing.Power.Out( 4 ), Easing.Sine.Out(), Easing.Back.Out( 2 ) ][ config ];
|
||||
const duration = [ 100, 150, 350 ][ config ]
|
||||
|
||||
this.rotationTween = new Tween( {
|
||||
easing: easing,
|
||||
duration: duration,
|
||||
onUpdate: tween => {
|
||||
|
||||
this.edges.rotateOnWorldAxis( this.flipAxis, tween.delta * rotation );
|
||||
this.game.cube.object.rotation.copy( this.edges.rotation );
|
||||
|
||||
},
|
||||
onComplete: () => {
|
||||
|
||||
this.edges.rotation.setFromVector3( this.snapRotation( this.edges.rotation.toVector3() ) );
|
||||
this.game.cube.object.rotation.copy( this.edges.rotation );
|
||||
callback();
|
||||
|
||||
},
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
selectLayer( layer ) {
|
||||
|
||||
this.group.rotation.set( 0, 0, 0 );
|
||||
this.movePieces( layer, this.game.cube.object, this.group );
|
||||
this.flipLayer = layer;
|
||||
|
||||
}
|
||||
|
||||
deselectLayer( layer ) {
|
||||
|
||||
this.movePieces( layer, this.group, this.game.cube.object );
|
||||
this.flipLayer = null;
|
||||
|
||||
}
|
||||
|
||||
movePieces( layer, from, to ) {
|
||||
|
||||
from.updateMatrixWorld();
|
||||
to.updateMatrixWorld();
|
||||
|
||||
layer.forEach( index => {
|
||||
|
||||
const piece = this.game.cube.pieces[ index ];
|
||||
|
||||
piece.applyMatrix( from.matrixWorld );
|
||||
from.remove( piece );
|
||||
piece.applyMatrix( new THREE.Matrix4().getInverse( to.matrixWorld ) );
|
||||
to.add( piece );
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
getLayer( position ) {
|
||||
|
||||
const scalar = { 2: 6, 3: 3, 4: 4, 5: 3 }[ this.game.cube.size ];
|
||||
const layer = [];
|
||||
|
||||
let axis;
|
||||
|
||||
if ( position === false ) {
|
||||
|
||||
const piece = this.dragIntersect.object.parent;
|
||||
|
||||
axis = this.getMainAxis( this.flipAxis );
|
||||
position = piece.position.clone() .multiplyScalar( scalar ) .round();
|
||||
|
||||
} else {
|
||||
|
||||
axis = this.getMainAxis( position );
|
||||
|
||||
}
|
||||
|
||||
this.game.cube.pieces.forEach( piece => {
|
||||
|
||||
const piecePosition = piece.position.clone().multiplyScalar( scalar ).round();
|
||||
|
||||
if ( piecePosition[ axis ] == position[ axis ] ) layer.push( piece.name );
|
||||
|
||||
} );
|
||||
|
||||
return layer;
|
||||
|
||||
}
|
||||
|
||||
keyboardMove( type, move, callback ) {
|
||||
|
||||
if ( this.state !== STILL ) return;
|
||||
if ( this.enabled !== true ) return;
|
||||
|
||||
if ( type === 'LAYER' ) {
|
||||
|
||||
const layer = this.getLayer( move.position );
|
||||
|
||||
this.flipAxis = new THREE.Vector3();
|
||||
this.flipAxis[ move.axis ] = 1;
|
||||
this.state = ROTATING;
|
||||
|
||||
this.selectLayer( layer );
|
||||
this.rotateLayer( move.angle, false, layer => {
|
||||
|
||||
this.game.storage.saveGame();
|
||||
this.state = STILL;
|
||||
this.checkIsSolved();
|
||||
|
||||
} );
|
||||
|
||||
} else if ( type === 'CUBE' ) {
|
||||
|
||||
this.flipAxis = new THREE.Vector3();
|
||||
this.flipAxis[ move.axis ] = 1;
|
||||
this.state = ROTATING;
|
||||
|
||||
this.rotateCube( move.angle, () => {
|
||||
|
||||
this.state = STILL;
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
scrambleCube() {
|
||||
|
||||
if ( this.scramble == null ) {
|
||||
|
||||
this.scramble = this.game.scrambler;
|
||||
this.scramble.callback = ( typeof callback !== 'function' ) ? () => {} : callback;
|
||||
|
||||
}
|
||||
|
||||
const converted = this.scramble.converted;
|
||||
const move = converted[ 0 ];
|
||||
const layer = this.getLayer( move.position );
|
||||
|
||||
this.flipAxis = new THREE.Vector3();
|
||||
this.flipAxis[ move.axis ] = 1;
|
||||
|
||||
this.selectLayer( layer );
|
||||
this.rotateLayer( move.angle, true, () => {
|
||||
|
||||
converted.shift();
|
||||
|
||||
if ( converted.length > 0 ) {
|
||||
|
||||
this.scrambleCube();
|
||||
|
||||
} else {
|
||||
|
||||
this.scramble = null;
|
||||
this.game.storage.saveGame();
|
||||
|
||||
}
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
getIntersect( position, object, multiple ) {
|
||||
|
||||
this.raycaster.setFromCamera(
|
||||
this.draggable.convertPosition( position.clone() ),
|
||||
this.game.world.camera
|
||||
);
|
||||
|
||||
const intersect = ( multiple )
|
||||
? this.raycaster.intersectObjects( object )
|
||||
: this.raycaster.intersectObject( object );
|
||||
|
||||
return ( intersect.length > 0 ) ? intersect[ 0 ] : false;
|
||||
|
||||
}
|
||||
|
||||
getMainAxis( vector ) {
|
||||
|
||||
return Object.keys( vector ).reduce(
|
||||
( a, b ) => Math.abs( vector[ a ] ) > Math.abs( vector[ b ] ) ? a : b
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
detach( child, parent ) {
|
||||
|
||||
child.applyMatrix( parent.matrixWorld );
|
||||
parent.remove( child );
|
||||
this.game.world.scene.add( child );
|
||||
|
||||
}
|
||||
|
||||
attach( child, parent ) {
|
||||
|
||||
child.applyMatrix( new THREE.Matrix4().getInverse( parent.matrixWorld ) );
|
||||
this.game.world.scene.remove( child );
|
||||
parent.add( child );
|
||||
|
||||
}
|
||||
|
||||
addMomentumPoint( delta ) {
|
||||
|
||||
const time = Date.now();
|
||||
|
||||
this.momentum = this.momentum.filter( moment => time - moment.time < 500 );
|
||||
|
||||
if ( delta !== false ) this.momentum.push( { delta, time } );
|
||||
|
||||
}
|
||||
|
||||
getMomentum() {
|
||||
|
||||
const points = this.momentum.length;
|
||||
const momentum = new THREE.Vector2();
|
||||
|
||||
this.addMomentumPoint( false );
|
||||
|
||||
this.momentum.forEach( ( point, index ) => {
|
||||
|
||||
momentum.add( point.delta.multiplyScalar( index / points ) )
|
||||
|
||||
} );
|
||||
|
||||
return momentum;
|
||||
|
||||
}
|
||||
|
||||
roundAngle( angle ) {
|
||||
|
||||
const round = Math.PI / 2;
|
||||
return Math.sign( angle ) * Math.round( Math.abs( angle) / round ) * round;
|
||||
|
||||
}
|
||||
|
||||
snapRotation( angle ) {
|
||||
|
||||
return angle.set(
|
||||
this.roundAngle( angle.x ),
|
||||
this.roundAngle( angle.y ),
|
||||
this.roundAngle( angle.z )
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
checkIsSolved() {
|
||||
|
||||
const start = performance.now();
|
||||
|
||||
let solved = true;
|
||||
const sides = { 'x-': [], 'x+': [], 'y-': [], 'y+': [], 'z-': [], 'z+': [] };
|
||||
|
||||
const positions = [];
|
||||
|
||||
this.game.cube.edges.forEach( edge => {
|
||||
|
||||
const position = edge.parent
|
||||
.localToWorld( edge.position.clone() )
|
||||
.sub( this.game.cube.object.position );
|
||||
|
||||
const mainAxis = this.getMainAxis( position );
|
||||
const mainSign = position.multiplyScalar( 2 ).round()[ mainAxis ] < 1 ? '-' : '+';
|
||||
|
||||
sides[ mainAxis + mainSign ].push( edge.name );
|
||||
|
||||
} );
|
||||
|
||||
Object.keys( sides ).forEach( side => {
|
||||
|
||||
if ( ! sides[ side ].every( value => value === sides[ side ][ 0 ] ) ) solved = false;
|
||||
|
||||
} );
|
||||
|
||||
if ( solved ) this.onSolved();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Controls };
|
||||
@@ -0,0 +1,236 @@
|
||||
import { RoundedBoxGeometry } from './plugins/RoundedBoxGeometry.js';
|
||||
import { RoundedPlaneGeometry } from './plugins/RoundedPlaneGeometry.js';
|
||||
|
||||
class Cube {
|
||||
|
||||
constructor( game ) {
|
||||
|
||||
this.game = game;
|
||||
this.size = 3;
|
||||
|
||||
this.geometry = {
|
||||
pieceCornerRadius: 0.12,
|
||||
edgeCornerRoundness: 0.15,
|
||||
edgeScale: 0.82,
|
||||
edgeDepth: 0.01,
|
||||
};
|
||||
|
||||
this.holder = new THREE.Object3D();
|
||||
this.object = new THREE.Object3D();
|
||||
this.animator = new THREE.Object3D();
|
||||
|
||||
this.holder.add( this.animator );
|
||||
this.animator.add( this.object );
|
||||
|
||||
this.game.world.scene.add( this.holder );
|
||||
|
||||
}
|
||||
|
||||
init() {
|
||||
|
||||
this.cubes = [];
|
||||
this.object.children = [];
|
||||
this.object.add( this.game.controls.group );
|
||||
|
||||
if ( this.size === 2 ) this.scale = 1.25;
|
||||
else if ( this.size === 3 ) this.scale = 1;
|
||||
else if ( this.size > 3 ) this.scale = 3 / this.size;
|
||||
|
||||
this.object.scale.set( this.scale, this.scale, this.scale );
|
||||
|
||||
const controlsScale = this.size === 2 ? 0.825 : 1;
|
||||
this.game.controls.edges.scale.set( controlsScale, controlsScale, controlsScale );
|
||||
|
||||
this.generatePositions();
|
||||
this.generateModel();
|
||||
|
||||
this.pieces.forEach( piece => {
|
||||
|
||||
this.cubes.push( piece.userData.cube );
|
||||
this.object.add( piece );
|
||||
|
||||
} );
|
||||
|
||||
this.holder.traverse( node => {
|
||||
|
||||
if ( node.frustumCulled ) node.frustumCulled = false;
|
||||
|
||||
} );
|
||||
|
||||
this.updateColors( this.game.themes.getColors() );
|
||||
|
||||
this.sizeGenerated = this.size;
|
||||
|
||||
}
|
||||
|
||||
resize( force = false ) {
|
||||
|
||||
if ( this.size !== this.sizeGenerated || force ) {
|
||||
|
||||
this.size = this.game.preferences.ranges.size.value;
|
||||
|
||||
this.reset();
|
||||
this.init();
|
||||
|
||||
this.game.saved = false;
|
||||
this.game.timer.reset();
|
||||
this.game.storage.clearGame();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
reset() {
|
||||
|
||||
this.game.controls.edges.rotation.set( 0, 0, 0 );
|
||||
|
||||
this.holder.rotation.set( 0, 0, 0 );
|
||||
this.object.rotation.set( 0, 0, 0 );
|
||||
this.animator.rotation.set( 0, 0, 0 );
|
||||
|
||||
}
|
||||
|
||||
generatePositions() {
|
||||
|
||||
const m = this.size - 1;
|
||||
const first = this.size % 2 !== 0
|
||||
? 0 - Math.floor(this.size / 2)
|
||||
: 0.5 - this.size / 2;
|
||||
|
||||
let x, y, z;
|
||||
|
||||
this.positions = [];
|
||||
|
||||
for ( x = 0; x < this.size; x ++ ) {
|
||||
for ( y = 0; y < this.size; y ++ ) {
|
||||
for ( z = 0; z < this.size; z ++ ) {
|
||||
|
||||
let position = new THREE.Vector3(first + x, first + y, first + z);
|
||||
let edges = [];
|
||||
|
||||
if ( x == 0 ) edges.push(0);
|
||||
if ( x == m ) edges.push(1);
|
||||
if ( y == 0 ) edges.push(2);
|
||||
if ( y == m ) edges.push(3);
|
||||
if ( z == 0 ) edges.push(4);
|
||||
if ( z == m ) edges.push(5);
|
||||
|
||||
position.edges = edges;
|
||||
this.positions.push( position );
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
generateModel() {
|
||||
|
||||
this.pieces = [];
|
||||
this.edges = [];
|
||||
|
||||
const pieceSize = 1 / 3;
|
||||
|
||||
const mainMaterial = new THREE.MeshLambertMaterial();
|
||||
|
||||
const pieceMesh = new THREE.Mesh(
|
||||
new RoundedBoxGeometry( pieceSize, this.geometry.pieceCornerRadius, 3 ),
|
||||
mainMaterial.clone()
|
||||
);
|
||||
|
||||
const edgeGeometry = RoundedPlaneGeometry(
|
||||
pieceSize,
|
||||
this.geometry.edgeCornerRoundness,
|
||||
this.geometry.edgeDepth
|
||||
);
|
||||
|
||||
this.positions.forEach( ( position, index ) => {
|
||||
|
||||
const piece = new THREE.Object3D();
|
||||
const pieceCube = pieceMesh.clone();
|
||||
const pieceEdges = [];
|
||||
|
||||
piece.position.copy( position.clone().divideScalar( 3 ) );
|
||||
piece.add( pieceCube );
|
||||
piece.name = index;
|
||||
piece.edgesName = '';
|
||||
|
||||
position.edges.forEach( position => {
|
||||
|
||||
const edge = new THREE.Mesh( edgeGeometry, mainMaterial.clone() );
|
||||
const name = [ 'L', 'R', 'D', 'U', 'B', 'F' ][ position ];
|
||||
const distance = pieceSize / 2;
|
||||
|
||||
edge.position.set(
|
||||
distance * [ - 1, 1, 0, 0, 0, 0 ][ position ],
|
||||
distance * [ 0, 0, - 1, 1, 0, 0 ][ position ],
|
||||
distance * [ 0, 0, 0, 0, - 1, 1 ][ position ]
|
||||
);
|
||||
|
||||
edge.rotation.set(
|
||||
Math.PI / 2 * [ 0, 0, 1, - 1, 0, 0 ][ position ],
|
||||
Math.PI / 2 * [ - 1, 1, 0, 0, 2, 0 ][ position ],
|
||||
0
|
||||
);
|
||||
|
||||
edge.scale.set(
|
||||
this.geometry.edgeScale,
|
||||
this.geometry.edgeScale,
|
||||
this.geometry.edgeScale
|
||||
);
|
||||
|
||||
edge.name = name;
|
||||
|
||||
piece.add( edge );
|
||||
pieceEdges.push( name );
|
||||
this.edges.push( edge );
|
||||
|
||||
} );
|
||||
|
||||
piece.userData.edges = pieceEdges;
|
||||
piece.userData.cube = pieceCube;
|
||||
|
||||
piece.userData.start = {
|
||||
position: piece.position.clone(),
|
||||
rotation: piece.rotation.clone(),
|
||||
};
|
||||
|
||||
this.pieces.push( piece );
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
updateColors( colors ) {
|
||||
|
||||
if ( typeof this.pieces !== 'object' && typeof this.edges !== 'object' ) return;
|
||||
|
||||
this.pieces.forEach( piece => piece.userData.cube.material.color.setHex( colors.P ) );
|
||||
this.edges.forEach( edge => edge.material.color.setHex( colors[ edge.name ] ) );
|
||||
|
||||
}
|
||||
|
||||
loadFromData( data ) {
|
||||
|
||||
this.size = data.size;
|
||||
|
||||
this.reset();
|
||||
this.init();
|
||||
|
||||
this.pieces.forEach( piece => {
|
||||
|
||||
const index = data.names.indexOf( piece.name );
|
||||
|
||||
const position = data.positions[index];
|
||||
const rotation = data.rotations[index];
|
||||
|
||||
piece.position.set( position.x, position.y, position.z );
|
||||
piece.rotation.set( rotation.x, rotation.y, rotation.z );
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Cube };
|
||||
@@ -0,0 +1,132 @@
|
||||
window.addEventListener( 'touchmove', () => {} );
|
||||
document.addEventListener( 'touchmove', event => { event.preventDefault(); }, { passive: false } );
|
||||
|
||||
class Draggable {
|
||||
|
||||
constructor( element, options ) {
|
||||
|
||||
this.position = {
|
||||
current: new THREE.Vector2(),
|
||||
start: new THREE.Vector2(),
|
||||
delta: new THREE.Vector2(),
|
||||
old: new THREE.Vector2(),
|
||||
drag: new THREE.Vector2(),
|
||||
};
|
||||
|
||||
this.options = Object.assign( {
|
||||
calcDelta: false,
|
||||
}, options || {} );
|
||||
|
||||
this.element = element;
|
||||
this.touch = null;
|
||||
|
||||
this.drag = {
|
||||
|
||||
start: ( event ) => {
|
||||
|
||||
if ( event.type == 'mousedown' && event.which != 1 ) return;
|
||||
if ( event.type == 'touchstart' && event.touches.length > 1 ) return;
|
||||
|
||||
this.getPositionCurrent( event );
|
||||
|
||||
if ( this.options.calcDelta ) {
|
||||
|
||||
this.position.start = this.position.current.clone();
|
||||
this.position.delta.set( 0, 0 );
|
||||
this.position.drag.set( 0, 0 );
|
||||
|
||||
}
|
||||
|
||||
this.touch = ( event.type == 'touchstart' );
|
||||
|
||||
this.onDragStart( this.position );
|
||||
|
||||
window.addEventListener( ( this.touch ) ? 'touchmove' : 'mousemove', this.drag.move, false );
|
||||
window.addEventListener( ( this.touch ) ? 'touchend' : 'mouseup', this.drag.end, false );
|
||||
|
||||
},
|
||||
|
||||
move: ( event ) => {
|
||||
|
||||
if ( this.options.calcDelta ) {
|
||||
|
||||
this.position.old = this.position.current.clone();
|
||||
|
||||
}
|
||||
|
||||
this.getPositionCurrent( event );
|
||||
|
||||
if ( this.options.calcDelta ) {
|
||||
|
||||
this.position.delta = this.position.current.clone().sub( this.position.old );
|
||||
this.position.drag = this.position.current.clone().sub( this.position.start );
|
||||
|
||||
}
|
||||
|
||||
this.onDragMove( this.position );
|
||||
|
||||
},
|
||||
|
||||
end: ( event ) => {
|
||||
|
||||
this.getPositionCurrent( event );
|
||||
|
||||
this.onDragEnd( this.position );
|
||||
|
||||
window.removeEventListener( ( this.touch ) ? 'touchmove' : 'mousemove', this.drag.move, false );
|
||||
window.removeEventListener( ( this.touch ) ? 'touchend' : 'mouseup', this.drag.end, false );
|
||||
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
this.onDragStart = () => {};
|
||||
this.onDragMove = () => {};
|
||||
this.onDragEnd = () => {};
|
||||
|
||||
this.enable();
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
enable() {
|
||||
|
||||
this.element.addEventListener( 'touchstart', this.drag.start, false );
|
||||
this.element.addEventListener( 'mousedown', this.drag.start, false );
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
disable() {
|
||||
|
||||
this.element.removeEventListener( 'touchstart', this.drag.start, false );
|
||||
this.element.removeEventListener( 'mousedown', this.drag.start, false );
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
getPositionCurrent( event ) {
|
||||
|
||||
const dragEvent = event.touches
|
||||
? ( event.touches[ 0 ] || event.changedTouches[ 0 ] )
|
||||
: event;
|
||||
|
||||
this.position.current.set( dragEvent.pageX, dragEvent.pageY );
|
||||
|
||||
}
|
||||
|
||||
convertPosition( position ) {
|
||||
|
||||
position.x = ( position.x / this.element.offsetWidth ) * 2 - 1;
|
||||
position.y = - ( ( position.y / this.element.offsetHeight ) * 2 - 1 );
|
||||
|
||||
return position;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Draggable };
|
||||
@@ -0,0 +1,83 @@
|
||||
const Easing = {
|
||||
|
||||
Power: {
|
||||
|
||||
In: power => {
|
||||
|
||||
power = Math.round( power || 1 );
|
||||
|
||||
return t => Math.pow( t, power );
|
||||
|
||||
},
|
||||
|
||||
Out: power => {
|
||||
|
||||
power = Math.round( power || 1 );
|
||||
|
||||
return t => 1 - Math.abs( Math.pow( t - 1, power ) );
|
||||
|
||||
},
|
||||
|
||||
InOut: power => {
|
||||
|
||||
power = Math.round( power || 1 );
|
||||
|
||||
return t => ( t < 0.5 )
|
||||
? Math.pow( t * 2, power ) / 2
|
||||
: ( 1 - Math.abs( Math.pow( ( t * 2 - 1 ) - 1, power ) ) ) / 2 + 0.5;
|
||||
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
Sine: {
|
||||
|
||||
In: () => t => 1 + Math.sin( Math.PI / 2 * t - Math.PI / 2 ),
|
||||
|
||||
Out: () => t => Math.sin( Math.PI / 2 * t ),
|
||||
|
||||
InOut: () => t => ( 1 + Math.sin( Math.PI * t - Math.PI / 2 ) ) / 2,
|
||||
|
||||
},
|
||||
|
||||
Back: {
|
||||
|
||||
Out: s => {
|
||||
|
||||
s = s || 1.70158;
|
||||
|
||||
return t => { return ( t -= 1 ) * t * ( ( s + 1 ) * t + s ) + 1; };
|
||||
|
||||
},
|
||||
|
||||
In: s => {
|
||||
|
||||
s = s || 1.70158;
|
||||
|
||||
return t => { return t * t * ( ( s + 1 ) * t - s ); };
|
||||
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
Elastic: {
|
||||
|
||||
Out: ( amplitude, period ) => {
|
||||
|
||||
let PI2 = Math.PI * 2;
|
||||
|
||||
let p1 = ( amplitude >= 1 ) ? amplitude : 1;
|
||||
let p2 = ( period || 0.3 ) / ( amplitude < 1 ? amplitude : 1 );
|
||||
let p3 = p2 / PI2 * ( Math.asin( 1 / p1 ) || 0 );
|
||||
|
||||
p2 = PI2 / p2;
|
||||
|
||||
return t => { return p1 * Math.pow( 2, -10 * t ) * Math.sin( ( t - p3 ) * p2 ) + 1 }
|
||||
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
export { Easing };
|
||||
@@ -0,0 +1,417 @@
|
||||
import { World } from './World.js';
|
||||
import { Cube } from './Cube.js';
|
||||
import { Controls } from './Controls.js';
|
||||
import { Scrambler } from './Scrambler.js';
|
||||
import { Transition } from './Transition.js';
|
||||
import { Timer } from './Timer.js';
|
||||
import { Preferences } from './Preferences.js';
|
||||
import { Confetti } from './Confetti.js';
|
||||
import { Scores } from './Scores.js';
|
||||
import { Storage } from './Storage.js';
|
||||
import { Themes } from './Themes.js';
|
||||
import { ThemeEditor } from './ThemeEditor.js';
|
||||
import { States } from './States.js';
|
||||
// import { Keyboard } from './Keyboard.js';
|
||||
|
||||
import { Icons } from './Icons.js';
|
||||
|
||||
const STATE = {
|
||||
Menu: 0,
|
||||
Playing: 1,
|
||||
Complete: 2,
|
||||
Stats: 3,
|
||||
Prefs: 4,
|
||||
Theme: 5,
|
||||
};
|
||||
|
||||
const BUTTONS = {
|
||||
Menu: [ 'stats', 'prefs' ],
|
||||
Playing: [ 'back' ],
|
||||
Complete: [],
|
||||
Stats: [],
|
||||
Prefs: [ 'back', 'theme' ],
|
||||
Theme: [ 'back', 'reset' ],
|
||||
None: [],
|
||||
};
|
||||
|
||||
const SHOW = true;
|
||||
const HIDE = false;
|
||||
|
||||
const FAST = true;
|
||||
const SLOW = false;
|
||||
|
||||
class Game {
|
||||
|
||||
constructor() {
|
||||
|
||||
this.dom = {
|
||||
ui: document.querySelector( '.ui' ),
|
||||
game: document.querySelector( '.ui__game' ),
|
||||
back: document.querySelector( '.ui__background' ),
|
||||
prefs: document.querySelector( '.ui__prefs' ),
|
||||
theme: document.querySelector( '.ui__theme' ),
|
||||
stats: document.querySelector( '.ui__stats' ),
|
||||
texts: {
|
||||
title: document.querySelector( '.text--title' ),
|
||||
note: document.querySelector( '.text--note' ),
|
||||
timer: document.querySelector( '.text--timer' ),
|
||||
complete: document.querySelector( '.text--complete' ),
|
||||
best: document.querySelector( '.text--best-time' ),
|
||||
theme: document.querySelector( '.text--theme' ),
|
||||
},
|
||||
buttons: {
|
||||
prefs: document.querySelector( '.btn--prefs' ),
|
||||
back: document.querySelector( '.btn--back' ),
|
||||
stats: document.querySelector( '.btn--stats' ),
|
||||
reset: document.querySelector( '.btn--reset' ),
|
||||
theme: document.querySelector( '.btn--theme' ),
|
||||
},
|
||||
};
|
||||
|
||||
this.world = new World( this );
|
||||
this.cube = new Cube( this );
|
||||
this.controls = new Controls( this );
|
||||
this.scrambler = new Scrambler( this );
|
||||
this.transition = new Transition( this );
|
||||
this.timer = new Timer( this );
|
||||
this.preferences = new Preferences( this );
|
||||
this.scores = new Scores( this );
|
||||
this.storage = new Storage( this );
|
||||
this.confetti = new Confetti( this );
|
||||
this.themes = new Themes( this );
|
||||
this.themeEditor = new ThemeEditor( this );
|
||||
|
||||
this.initActions();
|
||||
|
||||
this.state = STATE.Menu;
|
||||
this.newGame = false;
|
||||
this.saved = false;
|
||||
|
||||
this.storage.init();
|
||||
this.preferences.init();
|
||||
this.cube.init();
|
||||
this.transition.init();
|
||||
|
||||
this.storage.loadGame();
|
||||
this.scores.calcStats();
|
||||
|
||||
setTimeout( () => {
|
||||
|
||||
this.transition.float();
|
||||
this.transition.cube( SHOW );
|
||||
|
||||
setTimeout( () => this.transition.title( SHOW ), 700 );
|
||||
setTimeout( () => this.transition.buttons( BUTTONS.Menu, BUTTONS.None ), 1000 );
|
||||
|
||||
}, 500 );
|
||||
|
||||
}
|
||||
|
||||
initActions() {
|
||||
|
||||
let tappedTwice = false;
|
||||
|
||||
this.dom.game.addEventListener( 'click', event => {
|
||||
|
||||
if ( this.transition.activeTransitions > 0 ) return;
|
||||
if ( this.state === STATE.Playing ) return;
|
||||
|
||||
if ( this.state === STATE.Menu ) {
|
||||
|
||||
if ( ! tappedTwice ) {
|
||||
|
||||
tappedTwice = true;
|
||||
setTimeout( () => tappedTwice = false, 300 );
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
this.game( SHOW );
|
||||
|
||||
} else if ( this.state === STATE.Complete ) {
|
||||
|
||||
this.complete( HIDE );
|
||||
|
||||
} else if ( this.state === STATE.Stats ) {
|
||||
|
||||
this.stats( HIDE );
|
||||
|
||||
}
|
||||
|
||||
}, false );
|
||||
|
||||
this.controls.onMove = () => {
|
||||
|
||||
if ( this.newGame ) {
|
||||
|
||||
this.timer.start( true );
|
||||
this.newGame = false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.dom.buttons.back.onclick = event => {
|
||||
|
||||
if ( this.transition.activeTransitions > 0 ) return;
|
||||
|
||||
if ( this.state === STATE.Playing ) {
|
||||
|
||||
this.game( HIDE );
|
||||
|
||||
} else if ( this.state === STATE.Prefs ) {
|
||||
|
||||
this.prefs( HIDE );
|
||||
|
||||
} else if ( this.state === STATE.Theme ) {
|
||||
|
||||
this.theme( HIDE );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.dom.buttons.reset.onclick = event => {
|
||||
|
||||
if ( this.state === STATE.Theme ) {
|
||||
|
||||
this.themeEditor.resetTheme();
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
this.dom.buttons.prefs.onclick = event => this.prefs( SHOW );
|
||||
|
||||
this.dom.buttons.theme.onclick = event => this.theme( SHOW );
|
||||
|
||||
this.dom.buttons.stats.onclick = event => this.stats( SHOW );
|
||||
|
||||
this.controls.onSolved = () => this.complete( SHOW );
|
||||
|
||||
}
|
||||
|
||||
game( show ) {
|
||||
|
||||
if ( show ) {
|
||||
|
||||
if ( ! this.saved ) {
|
||||
|
||||
this.scrambler.scramble();
|
||||
this.controls.scrambleCube();
|
||||
this.newGame = true;
|
||||
|
||||
}
|
||||
|
||||
const duration = this.saved ? 0 :
|
||||
this.scrambler.converted.length * ( this.controls.flipSpeeds[0] + 10 );
|
||||
|
||||
this.state = STATE.Playing;
|
||||
this.saved = true;
|
||||
|
||||
this.transition.buttons( BUTTONS.None, BUTTONS.Menu );
|
||||
|
||||
this.transition.zoom( STATE.Playing, duration );
|
||||
this.transition.title( HIDE );
|
||||
|
||||
setTimeout( () => {
|
||||
|
||||
this.transition.timer( SHOW );
|
||||
this.transition.buttons( BUTTONS.Playing, BUTTONS.None );
|
||||
|
||||
}, this.transition.durations.zoom - 1000 );
|
||||
|
||||
setTimeout( () => {
|
||||
|
||||
this.controls.enable();
|
||||
if ( ! this.newGame ) this.timer.start( true )
|
||||
|
||||
}, this.transition.durations.zoom );
|
||||
|
||||
} else {
|
||||
|
||||
this.state = STATE.Menu;
|
||||
|
||||
this.transition.buttons( BUTTONS.Menu, BUTTONS.Playing );
|
||||
|
||||
this.transition.zoom( STATE.Menu, 0 );
|
||||
|
||||
this.controls.disable();
|
||||
if ( ! this.newGame ) this.timer.stop();
|
||||
this.transition.timer( HIDE );
|
||||
|
||||
setTimeout( () => this.transition.title( SHOW ), this.transition.durations.zoom - 1000 );
|
||||
|
||||
this.playing = false;
|
||||
this.controls.disable();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
prefs( show ) {
|
||||
|
||||
if ( show ) {
|
||||
|
||||
if ( this.transition.activeTransitions > 0 ) return;
|
||||
|
||||
this.state = STATE.Prefs;
|
||||
|
||||
this.transition.buttons( BUTTONS.Prefs, BUTTONS.Menu );
|
||||
|
||||
this.transition.title( HIDE );
|
||||
this.transition.cube( HIDE );
|
||||
|
||||
setTimeout( () => this.transition.preferences( SHOW ), 1000 );
|
||||
|
||||
} else {
|
||||
|
||||
this.cube.resize();
|
||||
|
||||
this.state = STATE.Menu;
|
||||
|
||||
this.transition.buttons( BUTTONS.Menu, BUTTONS.Prefs );
|
||||
|
||||
this.transition.preferences( HIDE );
|
||||
|
||||
setTimeout( () => this.transition.cube( SHOW ), 500 );
|
||||
setTimeout( () => this.transition.title( SHOW ), 1200 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
theme( show ) {
|
||||
|
||||
this.themeEditor.colorPicker( show );
|
||||
|
||||
if ( show ) {
|
||||
|
||||
if ( this.transition.activeTransitions > 0 ) return;
|
||||
|
||||
this.cube.loadFromData( States[ '3' ][ 'checkerboard' ] );
|
||||
|
||||
this.themeEditor.setHSL( null, false );
|
||||
|
||||
this.state = STATE.Theme;
|
||||
|
||||
this.transition.buttons( BUTTONS.Theme, BUTTONS.Prefs );
|
||||
|
||||
this.transition.preferences( HIDE );
|
||||
|
||||
setTimeout( () => this.transition.cube( SHOW, true ), 500 );
|
||||
setTimeout( () => this.transition.theming( SHOW ), 1000 );
|
||||
|
||||
} else {
|
||||
|
||||
this.state = STATE.Prefs;
|
||||
|
||||
this.transition.buttons( BUTTONS.Prefs, BUTTONS.Theme );
|
||||
|
||||
this.transition.cube( HIDE, true );
|
||||
this.transition.theming( HIDE );
|
||||
|
||||
setTimeout( () => this.transition.preferences( SHOW ), 1000 );
|
||||
setTimeout( () => {
|
||||
|
||||
const gameCubeData = JSON.parse( localStorage.getItem( 'theCube_savedState' ) );
|
||||
|
||||
if ( !gameCubeData ) {
|
||||
|
||||
this.cube.resize( true );
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
this.cube.loadFromData( gameCubeData );
|
||||
|
||||
}, 1500 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
stats( show ) {
|
||||
|
||||
if ( show ) {
|
||||
|
||||
if ( this.transition.activeTransitions > 0 ) return;
|
||||
|
||||
this.state = STATE.Stats;
|
||||
|
||||
this.transition.buttons( BUTTONS.Stats, BUTTONS.Menu );
|
||||
|
||||
this.transition.title( HIDE );
|
||||
this.transition.cube( HIDE );
|
||||
|
||||
setTimeout( () => this.transition.stats( SHOW ), 1000 );
|
||||
|
||||
} else {
|
||||
|
||||
this.state = STATE.Menu;
|
||||
|
||||
this.transition.buttons( BUTTONS.Menu, BUTTONS.None );
|
||||
|
||||
this.transition.stats( HIDE );
|
||||
|
||||
setTimeout( () => this.transition.cube( SHOW ), 500 );
|
||||
setTimeout( () => this.transition.title( SHOW ), 1200 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
complete( show ) {
|
||||
|
||||
if ( show ) {
|
||||
|
||||
this.transition.buttons( BUTTONS.Complete, BUTTONS.Playing );
|
||||
|
||||
this.state = STATE.Complete;
|
||||
this.saved = false;
|
||||
|
||||
this.controls.disable();
|
||||
this.timer.stop();
|
||||
this.storage.clearGame();
|
||||
|
||||
this.bestTime = this.scores.addScore( this.timer.deltaTime );
|
||||
|
||||
this.transition.zoom( STATE.Menu, 0 );
|
||||
this.transition.elevate( SHOW );
|
||||
|
||||
setTimeout( () => {
|
||||
|
||||
this.transition.complete( SHOW, this.bestTime )
|
||||
this.confetti.start();
|
||||
|
||||
}, 1000 );
|
||||
|
||||
} else {
|
||||
|
||||
this.state = STATE.Stats;
|
||||
this.saved = false;
|
||||
|
||||
this.transition.timer( HIDE );
|
||||
this.transition.complete( HIDE, this.bestTime );
|
||||
this.transition.cube( HIDE );
|
||||
this.timer.reset();
|
||||
|
||||
setTimeout( () => {
|
||||
|
||||
this.cube.reset();
|
||||
this.confetti.stop();
|
||||
|
||||
this.transition.stats( SHOW )
|
||||
this.transition.elevate( 0 );
|
||||
|
||||
}, 1000 );
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
window.game = new Game();
|
||||
@@ -0,0 +1,107 @@
|
||||
class IconsConverter {
|
||||
|
||||
constructor( options ) {
|
||||
|
||||
options = Object.assign( {
|
||||
tagName: 'icon',
|
||||
className: 'icon',
|
||||
styles: false,
|
||||
icons: {},
|
||||
observe: false,
|
||||
convert: false,
|
||||
}, options || {} );
|
||||
|
||||
this.tagName = options.tagName;
|
||||
this.className = options.className;
|
||||
this.icons = options.icons;
|
||||
|
||||
this.svgTag = document.createElementNS( 'http://www.w3.org/2000/svg', 'svg' );
|
||||
this.svgTag.setAttribute( 'class', this.className );
|
||||
|
||||
if ( options.styles ) this.addStyles();
|
||||
if ( options.convert ) this.convertAllIcons();
|
||||
|
||||
if ( options.observe ) {
|
||||
|
||||
const MutationObserver = window.MutationObserver || window.WebKitMutationObserver;
|
||||
this.observer = new MutationObserver( mutations => { this.convertAllIcons(); } );
|
||||
this.observer.observe( document.documentElement, { childList: true, subtree: true } );
|
||||
|
||||
}
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
convertAllIcons() {
|
||||
|
||||
document.querySelectorAll( this.tagName ).forEach( icon => { this.convertIcon( icon ); } );
|
||||
|
||||
}
|
||||
|
||||
convertIcon( icon ) {
|
||||
|
||||
const svgData = this.icons[ icon.attributes[0].localName ];
|
||||
|
||||
if ( typeof svgData === 'undefined' ) return;
|
||||
|
||||
const svg = this.svgTag.cloneNode( true );
|
||||
const viewBox = svgData.viewbox.split( ' ' );
|
||||
|
||||
svg.setAttributeNS( null, 'viewBox', svgData.viewbox );
|
||||
svg.style.width = viewBox[2] / viewBox[3] + 'em';
|
||||
svg.style.height = '1em';
|
||||
svg.innerHTML = svgData.content;
|
||||
|
||||
icon.parentNode.replaceChild( svg, icon );
|
||||
|
||||
}
|
||||
|
||||
addStyles() {
|
||||
|
||||
const style = document.createElement( 'style' );
|
||||
style.innerHTML = `.${this.className} { display: inline-block; font-size: inherit; overflow: visible; vertical-align: -0.125em; preserveAspectRatio: none; }`;
|
||||
document.head.appendChild( style );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
const Icons = new IconsConverter( {
|
||||
|
||||
icons: {
|
||||
settings: {
|
||||
viewbox: '0 0 512 512',
|
||||
content: '<path fill="currentColor" d="M444.788 291.1l42.616 24.599c4.867 2.809 7.126 8.618 5.459 13.985-11.07 35.642-29.97 67.842-54.689 94.586a12.016 12.016 0 0 1-14.832 2.254l-42.584-24.595a191.577 191.577 0 0 1-60.759 35.13v49.182a12.01 12.01 0 0 1-9.377 11.718c-34.956 7.85-72.499 8.256-109.219.007-5.49-1.233-9.403-6.096-9.403-11.723v-49.184a191.555 191.555 0 0 1-60.759-35.13l-42.584 24.595a12.016 12.016 0 0 1-14.832-2.254c-24.718-26.744-43.619-58.944-54.689-94.586-1.667-5.366.592-11.175 5.459-13.985L67.212 291.1a193.48 193.48 0 0 1 0-70.199l-42.616-24.599c-4.867-2.809-7.126-8.618-5.459-13.985 11.07-35.642 29.97-67.842 54.689-94.586a12.016 12.016 0 0 1 14.832-2.254l42.584 24.595a191.577 191.577 0 0 1 60.759-35.13V25.759a12.01 12.01 0 0 1 9.377-11.718c34.956-7.85 72.499-8.256 109.219-.007 5.49 1.233 9.403 6.096 9.403 11.723v49.184a191.555 191.555 0 0 1 60.759 35.13l42.584-24.595a12.016 12.016 0 0 1 14.832 2.254c24.718 26.744 43.619 58.944 54.689 94.586 1.667 5.366-.592 11.175-5.459 13.985L444.788 220.9a193.485 193.485 0 0 1 0 70.2zM336 256c0-44.112-35.888-80-80-80s-80 35.888-80 80 35.888 80 80 80 80-35.888 80-80z" />',
|
||||
},
|
||||
back: {
|
||||
viewbox: '0 0 512 512',
|
||||
content: '<path transform="translate(512, 0) scale(-1,1)" fill="currentColor" d="M503.691 189.836L327.687 37.851C312.281 24.546 288 35.347 288 56.015v80.053C127.371 137.907 0 170.1 0 322.326c0 61.441 39.581 122.309 83.333 154.132 13.653 9.931 33.111-2.533 28.077-18.631C66.066 312.814 132.917 274.316 288 272.085V360c0 20.7 24.3 31.453 39.687 18.164l176.004-152c11.071-9.562 11.086-26.753 0-36.328z" />',
|
||||
},
|
||||
trophy: {
|
||||
viewbox: '0 0 576 512',
|
||||
content: '<path fill="currentColor" d="M552 64H448V24c0-13.3-10.7-24-24-24H152c-13.3 0-24 10.7-24 24v40H24C10.7 64 0 74.7 0 88v56c0 66.5 77.9 131.7 171.9 142.4C203.3 338.5 240 360 240 360v72h-48c-35.3 0-64 20.7-64 56v12c0 6.6 5.4 12 12 12h296c6.6 0 12-5.4 12-12v-12c0-35.3-28.7-56-64-56h-48v-72s36.7-21.5 68.1-73.6C498.4 275.6 576 210.3 576 144V88c0-13.3-10.7-24-24-24zM64 144v-16h64.2c1 32.6 5.8 61.2 12.8 86.2-47.5-16.4-77-49.9-77-70.2zm448 0c0 20.2-29.4 53.8-77 70.2 7-25 11.8-53.6 12.8-86.2H512v16zm-127.3 4.7l-39.6 38.6 9.4 54.6c1.7 9.8-8.7 17.2-17.4 12.6l-49-25.8-49 25.8c-8.8 4.6-19.1-2.9-17.4-12.6l9.4-54.6-39.6-38.6c-7.1-6.9-3.2-19 6.7-20.5l54.8-8 24.5-49.6c4.4-8.9 17.1-8.9 21.5 0l24.5 49.6 54.8 8c9.6 1.5 13.5 13.6 6.4 20.5z" />',
|
||||
},
|
||||
cancel: {
|
||||
viewbox: '0 0 352 512',
|
||||
content: '<path fill="currentColor" d="M242.72 256l100.07-100.07c12.28-12.28 12.28-32.19 0-44.48l-22.24-22.24c-12.28-12.28-32.19-12.28-44.48 0L176 189.28 75.93 89.21c-12.28-12.28-32.19-12.28-44.48 0L9.21 111.45c-12.28 12.28-12.28 32.19 0 44.48L109.28 256 9.21 356.07c-12.28 12.28-12.28 32.19 0 44.48l22.24 22.24c12.28 12.28 32.2 12.28 44.48 0L176 322.72l100.07 100.07c12.28 12.28 32.2 12.28 44.48 0l22.24-22.24c12.28-12.28 12.28-32.19 0-44.48L242.72 256z" />',
|
||||
},
|
||||
theme: {
|
||||
viewbox: '0 0 512 512',
|
||||
content: '<path fill="currentColor" d="M204.3 5C104.9 24.4 24.8 104.3 5.2 203.4c-37 187 131.7 326.4 258.8 306.7 41.2-6.4 61.4-54.6 42.5-91.7-23.1-45.4 9.9-98.4 60.9-98.4h79.7c35.8 0 64.8-29.6 64.9-65.3C511.5 97.1 368.1-26.9 204.3 5zM96 320c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm32-128c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm128-64c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32zm128 64c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32z"/>',
|
||||
},
|
||||
reset: {
|
||||
viewbox: '0 0 512 512',
|
||||
content: '<path fill="currentColor" d="M370.72 133.28C339.458 104.008 298.888 87.962 255.848 88c-77.458.068-144.328 53.178-162.791 126.85-1.344 5.363-6.122 9.15-11.651 9.15H24.103c-7.498 0-13.194-6.807-11.807-14.176C33.933 94.924 134.813 8 256 8c66.448 0 126.791 26.136 171.315 68.685L463.03 40.97C478.149 25.851 504 36.559 504 57.941V192c0 13.255-10.745 24-24 24H345.941c-21.382 0-32.09-25.851-16.971-40.971l41.75-41.749zM32 296h134.059c21.382 0 32.09 25.851 16.971 40.971l-41.75 41.75c31.262 29.273 71.835 45.319 114.876 45.28 77.418-.07 144.315-53.144 162.787-126.849 1.344-5.363 6.122-9.15 11.651-9.15h57.304c7.498 0 13.194 6.807 11.807 14.176C478.067 417.076 377.187 504 256 504c-66.448 0-126.791-26.136-171.315-68.685L48.97 471.03C33.851 486.149 8 475.441 8 454.059V320c0-13.255 10.745-24 24-24z" />',
|
||||
},
|
||||
trash: {
|
||||
viewbox: '0 0 448 512',
|
||||
content: '<path fill="currentColor" d="M432 32H312l-9.4-18.7A24 24 0 0 0 281.1 0H166.8a23.72 23.72 0 0 0-21.4 13.3L136 32H16A16 16 0 0 0 0 48v32a16 16 0 0 0 16 16h416a16 16 0 0 0 16-16V48a16 16 0 0 0-16-16zM53.2 467a48 48 0 0 0 47.9 45h245.8a48 48 0 0 0 47.9-45L416 128H32z" />',
|
||||
},
|
||||
},
|
||||
|
||||
convert: true,
|
||||
|
||||
} );
|
||||
|
||||
export { Icons };
|
||||
@@ -0,0 +1,65 @@
|
||||
const SHIFT = 16;
|
||||
|
||||
const CTRL = [
|
||||
81, // q
|
||||
87, // w
|
||||
69, // e
|
||||
65, // a
|
||||
83, // s
|
||||
68, // d
|
||||
];
|
||||
|
||||
const ROTATION = [
|
||||
90, // z
|
||||
88, // x
|
||||
67, // c
|
||||
];
|
||||
|
||||
class Keyboard {
|
||||
|
||||
constructor( game ) {
|
||||
|
||||
this.game = game;
|
||||
this.shift = false;
|
||||
|
||||
this.keydown = this.keydown.bind( this );
|
||||
this.keyup = this.keyup.bind( this );
|
||||
|
||||
window.addEventListener( 'keydown', this.keydown, false );
|
||||
window.addEventListener( 'keyup', this.keyup, false );
|
||||
|
||||
}
|
||||
|
||||
keydown( e ) {
|
||||
|
||||
if ( e.keyCode === SHIFT ) this.shift = true;
|
||||
|
||||
if ( CTRL.includes( e.keyCode ) ) {
|
||||
|
||||
const modifier = ( this.shift ) ? `'` : ``;
|
||||
const face = { 65: 'L', 68: 'R', 87: 'U', 83: 'D', 81: 'F', 69: 'B' }[ e.keyCode ];
|
||||
|
||||
const convertedMove = this.game.scrambler.convertMove( face + modifier );
|
||||
|
||||
this.game.controls.keyboardMove( 'LAYER', convertedMove, () => {} );
|
||||
|
||||
} else if ( ROTATION.includes( e.keyCode ) ) {
|
||||
|
||||
const axis = { 90: 'x', 88: 'y', 67: 'z' }[ e.keyCode ];
|
||||
const angle = ( this.shift ? 1 : -1 ) * Math.PI / 2;
|
||||
|
||||
this.game.controls.keyboardMove( 'CUBE', { axis, angle }, () => {} );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
keyup( e ) {
|
||||
|
||||
if ( e.keyCode === SHIFT ) this.shift = false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Keyboard };
|
||||
@@ -0,0 +1,135 @@
|
||||
const ARROW = [
|
||||
37, // left
|
||||
39, // right
|
||||
38, // up
|
||||
40, // down
|
||||
];
|
||||
|
||||
const CTRL = [
|
||||
81, // q
|
||||
87, // w
|
||||
69, // e
|
||||
65, // a
|
||||
83, // s
|
||||
68, // d
|
||||
90, // z
|
||||
88, // x
|
||||
67, // c
|
||||
];
|
||||
|
||||
class Keyboard {
|
||||
|
||||
constructor( game ) {
|
||||
|
||||
this.game = game;
|
||||
this.ctrl = false;
|
||||
|
||||
this.keydown = this.keydown.bind( this );
|
||||
this.keyup = this.keyup.bind( this );
|
||||
|
||||
window.addEventListener( 'keydown', this.keydown, false );
|
||||
window.addEventListener( 'keyup', this.keyup, false );
|
||||
|
||||
const dbg = document.querySelector('#dbg');
|
||||
|
||||
const animate = () => {
|
||||
|
||||
dbg.innerHTML = ( this.ctrl ) ? this.ctrl : '';
|
||||
|
||||
requestAnimationFrame( animate );
|
||||
|
||||
}
|
||||
|
||||
animate();
|
||||
|
||||
}
|
||||
|
||||
keydown( e ) {
|
||||
|
||||
const { keyCode } = e;
|
||||
const { ctrl, game } = this;
|
||||
// const { }
|
||||
|
||||
if ( CTRL.includes( keyCode ) ) {
|
||||
|
||||
this.ctrl = keyCode;
|
||||
|
||||
}
|
||||
|
||||
if ( ARROW.includes( keyCode) && ctrl !== false ) {
|
||||
|
||||
let face = false, modifier = false;
|
||||
|
||||
if ( ctrl === 65 ) { // A
|
||||
|
||||
face = 'L';
|
||||
if (keyCode === 38) modifier = "'"; // UP
|
||||
if (keyCode === 40) modifier = ''; // DOWN
|
||||
|
||||
}
|
||||
|
||||
if ( ctrl === 68 ) { // D
|
||||
|
||||
face = 'R';
|
||||
if (keyCode === 38) modifier = ''; // UP
|
||||
if (keyCode === 40) modifier = "'"; // DOWN
|
||||
|
||||
}
|
||||
|
||||
if ( ctrl === 87 ) { // W
|
||||
|
||||
face = 'U';
|
||||
if (keyCode === 37) modifier = ''; // LEFT
|
||||
if (keyCode === 39) modifier = "'"; // RIGHT
|
||||
|
||||
}
|
||||
|
||||
if ( ctrl === 83 ) { // S
|
||||
|
||||
face = 'D';
|
||||
if (keyCode === 37) modifier = "'"; // LEFT
|
||||
if (keyCode === 39) modifier = ''; // RIGHT
|
||||
|
||||
}
|
||||
|
||||
if ( ctrl === 81 ) { // Q
|
||||
|
||||
face = 'F';
|
||||
if (keyCode === 37) modifier = "'"; // LEFT
|
||||
if (keyCode === 39) modifier = ''; // RIGHT
|
||||
|
||||
}
|
||||
|
||||
if ( ctrl === 69 ) { // E
|
||||
|
||||
face = 'B';
|
||||
if (keyCode === 37) modifier = ''; // LEFT
|
||||
if (keyCode === 39) modifier = "'"; // RIGHT
|
||||
|
||||
}
|
||||
|
||||
if ( face === false || modifier === false ) return;
|
||||
|
||||
const convertedMove = game.scrambler.convertMove( face + modifier );
|
||||
game.controls.execute( convertedMove, () => {} );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
keyup( e ) {
|
||||
|
||||
const { keyCode } = e;
|
||||
const { ctrl } = this;
|
||||
|
||||
if ( ctrl == keyCode ) {
|
||||
|
||||
this.ctrl = false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Keyboard };
|
||||
@@ -0,0 +1,121 @@
|
||||
import { Range } from './Range.js';
|
||||
|
||||
class Preferences {
|
||||
|
||||
constructor( game ) {
|
||||
|
||||
this.game = game;
|
||||
|
||||
}
|
||||
|
||||
init() {
|
||||
|
||||
const getProgressInRange = ( value, start, end ) => {
|
||||
|
||||
return Math.min( Math.max( (value - start) / (end - start), 0 ), 1 );
|
||||
|
||||
}
|
||||
|
||||
this.ranges = {
|
||||
|
||||
size: new Range( 'size', {
|
||||
value: this.game.cube.size,
|
||||
range: [ 2, 5 ],
|
||||
step: 1,
|
||||
onUpdate: value => {
|
||||
|
||||
this.game.cube.size = value;
|
||||
|
||||
this.game.preferences.ranges.scramble.list.forEach( ( item, i ) => {
|
||||
|
||||
item.innerHTML = this.game.scrambler.scrambleLength[ this.game.cube.size ][ i ];
|
||||
|
||||
} );
|
||||
|
||||
},
|
||||
onComplete: () => this.game.storage.savePreferences(),
|
||||
} ),
|
||||
|
||||
flip: new Range( 'flip', {
|
||||
value: this.game.controls.flipConfig,
|
||||
range: [ 0, 2 ],
|
||||
step: 1,
|
||||
onUpdate: value => {
|
||||
|
||||
this.game.controls.flipConfig = value;
|
||||
|
||||
},
|
||||
onComplete: () => this.game.storage.savePreferences(),
|
||||
} ),
|
||||
|
||||
scramble: new Range( 'scramble', {
|
||||
value: this.game.scrambler.dificulty,
|
||||
range: [ 0, 2 ],
|
||||
step: 1,
|
||||
onUpdate: value => {
|
||||
|
||||
this.game.scrambler.dificulty = value;
|
||||
|
||||
},
|
||||
onComplete: () => this.game.storage.savePreferences()
|
||||
} ),
|
||||
|
||||
fov: new Range( 'fov', {
|
||||
value: this.game.world.fov,
|
||||
range: [ 2, 45 ],
|
||||
onUpdate: value => {
|
||||
|
||||
this.game.world.fov = value;
|
||||
this.game.world.resize();
|
||||
|
||||
},
|
||||
onComplete: () => this.game.storage.savePreferences()
|
||||
} ),
|
||||
|
||||
theme: new Range( 'theme', {
|
||||
value: { cube: 0, erno: 1, dust: 2, camo: 3, rain: 4 }[ this.game.themes.theme ],
|
||||
range: [ 0, 4 ],
|
||||
step: 1,
|
||||
onUpdate: value => {
|
||||
|
||||
const theme = [ 'cube', 'erno', 'dust', 'camo', 'rain' ][ value ]
|
||||
this.game.themes.setTheme( theme );
|
||||
|
||||
},
|
||||
onComplete: () => this.game.storage.savePreferences()
|
||||
} ),
|
||||
|
||||
hue: new Range( 'hue', {
|
||||
value: 0,
|
||||
range: [ 0, 360 ],
|
||||
onUpdate: value => this.game.themeEditor.updateHSL(),
|
||||
onComplete: () => this.game.storage.savePreferences(),
|
||||
} ),
|
||||
|
||||
saturation: new Range( 'saturation', {
|
||||
value: 100,
|
||||
range: [ 0, 100 ],
|
||||
onUpdate: value => this.game.themeEditor.updateHSL(),
|
||||
onComplete: () => this.game.storage.savePreferences(),
|
||||
} ),
|
||||
|
||||
lightness: new Range( 'lightness', {
|
||||
value: 50,
|
||||
range: [ 0, 100 ],
|
||||
onUpdate: value => this.game.themeEditor.updateHSL(),
|
||||
onComplete: () => this.game.storage.savePreferences(),
|
||||
} ),
|
||||
|
||||
};
|
||||
|
||||
this.ranges.scramble.list.forEach( ( item, i ) => {
|
||||
|
||||
item.innerHTML = this.game.scrambler.scrambleLength[ this.game.cube.size ][ i ];
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Preferences };
|
||||
@@ -0,0 +1,171 @@
|
||||
import { Draggable } from './Draggable.js';
|
||||
|
||||
const RangeHTML = [
|
||||
|
||||
'<div class="range">',
|
||||
'<div class="range__label"></div>',
|
||||
'<div class="range__track">',
|
||||
'<div class="range__track-line"></div>',
|
||||
'<div class="range__handle"><div></div></div>',
|
||||
'</div>',
|
||||
'<div class="range__list"></div>',
|
||||
'</div>',
|
||||
|
||||
].join( '\n' );
|
||||
|
||||
document.querySelectorAll( 'range' ).forEach( el => {
|
||||
|
||||
const temp = document.createElement( 'div' );
|
||||
temp.innerHTML = RangeHTML;
|
||||
|
||||
const range = temp.querySelector( '.range' );
|
||||
const rangeLabel = range.querySelector( '.range__label' );
|
||||
const rangeList = range.querySelector( '.range__list' );
|
||||
|
||||
range.setAttribute( 'name', el.getAttribute( 'name' ) );
|
||||
rangeLabel.innerHTML = el.getAttribute( 'title' );
|
||||
|
||||
if ( el.hasAttribute( 'color' ) ) {
|
||||
|
||||
range.classList.add( 'range--type-color' );
|
||||
range.classList.add( 'range--color-' + el.getAttribute( 'name' ) );
|
||||
|
||||
}
|
||||
|
||||
if ( el.hasAttribute( 'list' ) ) {
|
||||
|
||||
el.getAttribute( 'list' ).split( ',' ).forEach( listItemText => {
|
||||
|
||||
const listItem = document.createElement( 'div' );
|
||||
listItem.innerHTML = listItemText;
|
||||
rangeList.appendChild( listItem );
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
el.parentNode.replaceChild( range, el );
|
||||
|
||||
} );
|
||||
|
||||
class Range {
|
||||
|
||||
constructor( name, options ) {
|
||||
|
||||
options = Object.assign( {
|
||||
range: [ 0, 1 ],
|
||||
value: 0,
|
||||
step: 0,
|
||||
onUpdate: () => {},
|
||||
onComplete: () => {},
|
||||
}, options || {} );
|
||||
|
||||
this.element = document.querySelector( '.range[name="' + name + '"]' );
|
||||
this.track = this.element.querySelector( '.range__track' );
|
||||
this.handle = this.element.querySelector( '.range__handle' );
|
||||
this.list = [].slice.call( this.element.querySelectorAll( '.range__list div' ) );
|
||||
|
||||
this.value = options.value;
|
||||
this.min = options.range[0];
|
||||
this.max = options.range[1];
|
||||
this.step = options.step;
|
||||
|
||||
this.onUpdate = options.onUpdate;
|
||||
this.onComplete = options.onComplete;
|
||||
|
||||
this.setValue( this.value );
|
||||
|
||||
this.initDraggable();
|
||||
|
||||
}
|
||||
|
||||
setValue( value ) {
|
||||
|
||||
this.value = this.round( this.limitValue( value ) );
|
||||
this.setHandlePosition();
|
||||
|
||||
}
|
||||
|
||||
initDraggable() {
|
||||
|
||||
const oldPosition = null;
|
||||
|
||||
let current;
|
||||
|
||||
this.draggable = new Draggable( this.handle, { calcDelta: true } );
|
||||
|
||||
this.draggable.onDragStart = position => {
|
||||
|
||||
current = this.positionFromValue( this.value );
|
||||
this.handle.style.left = current + 'px';
|
||||
|
||||
}
|
||||
|
||||
this.draggable.onDragMove = position => {
|
||||
|
||||
current = this.limitPosition( current + position.delta.x );
|
||||
this.value = this.round( this.valueFromPosition( current ) );
|
||||
this.setHandlePosition();
|
||||
|
||||
this.onUpdate( this.value );
|
||||
|
||||
}
|
||||
|
||||
this.draggable.onDragEnd = position => {
|
||||
|
||||
this.onComplete( this.value );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
round( value ) {
|
||||
|
||||
if ( this.step < 1 ) return value;
|
||||
|
||||
return Math.round( ( value - this.min ) / this.step ) * this.step + this.min;
|
||||
|
||||
}
|
||||
|
||||
limitValue( value ) {
|
||||
|
||||
const max = Math.max( this.max, this.min );
|
||||
const min = Math.min( this.max, this.min );
|
||||
|
||||
return Math.min( Math.max( value, min ), max );
|
||||
|
||||
}
|
||||
|
||||
limitPosition( position ) {
|
||||
|
||||
return Math.min( Math.max( position, 0 ), this.track.offsetWidth );
|
||||
|
||||
}
|
||||
|
||||
percentsFromValue( value ) {
|
||||
|
||||
return ( value - this.min ) / ( this.max - this.min );
|
||||
|
||||
}
|
||||
|
||||
valueFromPosition( position ) {
|
||||
|
||||
return this.min + ( this.max - this.min ) * ( position / this.track.offsetWidth );
|
||||
|
||||
}
|
||||
|
||||
positionFromValue( value ) {
|
||||
|
||||
return this.percentsFromValue( value ) * this.track.offsetWidth;
|
||||
|
||||
}
|
||||
|
||||
setHandlePosition() {
|
||||
|
||||
this.handle.style.left = this.percentsFromValue( this.value ) * 100 + '%';
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Range };
|
||||
@@ -0,0 +1,108 @@
|
||||
class Scores {
|
||||
|
||||
constructor( game ) {
|
||||
|
||||
this.game = game;
|
||||
|
||||
this.data = {
|
||||
2: {
|
||||
scores: [],
|
||||
solves: 0,
|
||||
best: 0,
|
||||
worst: 0,
|
||||
},
|
||||
3: {
|
||||
scores: [],
|
||||
solves: 0,
|
||||
best: 0,
|
||||
worst: 0,
|
||||
},
|
||||
4: {
|
||||
scores: [],
|
||||
solves: 0,
|
||||
best: 0,
|
||||
worst: 0,
|
||||
},
|
||||
5: {
|
||||
scores: [],
|
||||
solves: 0,
|
||||
best: 0,
|
||||
worst: 0,
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
addScore( time ) {
|
||||
|
||||
const data = this.data[ this.game.cube.sizeGenerated ];
|
||||
|
||||
data.scores.push( time );
|
||||
data.solves++;
|
||||
|
||||
if ( data.scores.lenght > 100 ) data.scores.shift();
|
||||
|
||||
let bestTime = false
|
||||
|
||||
if ( time < data.best || data.best === 0 ) {
|
||||
|
||||
data.best = time;
|
||||
bestTime = true;
|
||||
|
||||
}
|
||||
|
||||
if ( time > data.worst ) data.worst = time;
|
||||
|
||||
this.game.storage.saveScores();
|
||||
|
||||
return bestTime;
|
||||
|
||||
}
|
||||
|
||||
calcStats() {
|
||||
|
||||
const s = this.game.cube.sizeGenerated;
|
||||
const data = this.data[ s ];
|
||||
|
||||
this.setStat( 'cube-size', `${s}<i>x</i>${s}<i>x</i>${s}` );
|
||||
this.setStat( 'total-solves', data.solves );
|
||||
this.setStat( 'best-time', this.convertTime( data.best ) );
|
||||
this.setStat( 'worst-time', this.convertTime( data.worst ) );
|
||||
this.setStat( 'average-5', this.getAverage( 5 ) );
|
||||
this.setStat( 'average-12', this.getAverage( 12 ) );
|
||||
this.setStat( 'average-25', this.getAverage( 25 ) );
|
||||
|
||||
}
|
||||
|
||||
setStat( name, value ) {
|
||||
|
||||
if ( value === 0 ) value = '-';
|
||||
|
||||
this.game.dom.stats.querySelector( `.stats[name="${name}"] b` ).innerHTML = value;
|
||||
|
||||
}
|
||||
|
||||
getAverage( count ) {
|
||||
|
||||
const data = this.data[ this.game.cube.sizeGenerated ];
|
||||
|
||||
if ( data.scores.length < count ) return 0;
|
||||
|
||||
return this.convertTime( data.scores.slice( -count ).reduce( ( a, b ) => a + b, 0 ) / count );
|
||||
|
||||
}
|
||||
|
||||
convertTime( time ) {
|
||||
|
||||
if ( time <= 0 ) return 0;
|
||||
|
||||
const seconds = parseInt( ( time / 1000 ) % 60 );
|
||||
const minutes = parseInt( ( time / ( 1000 * 60 ) ) );
|
||||
|
||||
return minutes + ':' + ( seconds < 10 ? '0' : '' ) + seconds;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Scores };
|
||||
@@ -0,0 +1,96 @@
|
||||
class Scrambler {
|
||||
|
||||
constructor( game ) {
|
||||
|
||||
this.game = game;
|
||||
|
||||
this.dificulty = 0;
|
||||
|
||||
this.scrambleLength = {
|
||||
2: [ 7, 9, 11 ],
|
||||
3: [ 20, 25, 30 ],
|
||||
4: [ 30, 40, 50 ],
|
||||
5: [ 40, 60, 80 ],
|
||||
};
|
||||
|
||||
this.moves = [];
|
||||
this.conveted = [];
|
||||
this.pring = '';
|
||||
|
||||
}
|
||||
|
||||
scramble( scramble ) {
|
||||
|
||||
let count = 0;
|
||||
this.moves = ( typeof scramble !== 'undefined' ) ? scramble.split( ' ' ) : [];
|
||||
|
||||
if ( this.moves.length < 1 ) {
|
||||
|
||||
const scrambleLength = this.scrambleLength[ this.game.cube.size ][ this.dificulty ];
|
||||
|
||||
const faces = this.game.cube.size < 4 ? 'UDLRFB' : 'UuDdLlRrFfBb';
|
||||
const modifiers = [ "", "'", "2" ];
|
||||
const total = ( typeof scramble === 'undefined' ) ? scrambleLength : scramble;
|
||||
|
||||
while ( count < total ) {
|
||||
|
||||
const move =
|
||||
faces[ Math.floor( Math.random() * faces.length ) ] +
|
||||
modifiers[ Math.floor( Math.random() * 3 ) ];
|
||||
|
||||
if ( count > 0 && move.charAt( 0 ) == this.moves[ count - 1 ].charAt( 0 ) ) continue;
|
||||
if ( count > 1 && move.charAt( 0 ) == this.moves[ count - 2 ].charAt( 0 ) ) continue;
|
||||
|
||||
this.moves.push( move );
|
||||
count ++;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.callback = () => {};
|
||||
this.convert();
|
||||
this.print = this.moves.join( ' ' );
|
||||
|
||||
return this;
|
||||
|
||||
}
|
||||
|
||||
convert( moves ) {
|
||||
|
||||
this.converted = [];
|
||||
|
||||
this.moves.forEach( move => {
|
||||
|
||||
const convertedMove = this.convertMove( move );
|
||||
const modifier = move.charAt( 1 );
|
||||
|
||||
this.converted.push( convertedMove );
|
||||
if ( modifier == "2" ) this.converted.push( convertedMove );
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
convertMove( move ) {
|
||||
|
||||
const face = move.charAt( 0 );
|
||||
const modifier = move.charAt( 1 );
|
||||
|
||||
const axis = { D: 'y', U: 'y', L: 'x', R: 'x', F: 'z', B: 'z' }[ face.toUpperCase() ];
|
||||
let row = { D: -1, U: 1, L: -1, R: 1, F: 1, B: -1 }[ face.toUpperCase() ];
|
||||
|
||||
if ( this.game.cube.size > 3 && face !== face.toLowerCase() ) row = row * 2;
|
||||
|
||||
const position = new THREE.Vector3();
|
||||
position[ { D: 'y', U: 'y', L: 'x', R: 'x', F: 'z', B: 'z' }[ face.toUpperCase() ] ] = row;
|
||||
|
||||
const angle = ( Math.PI / 2 ) * - row * ( ( modifier == "'" ) ? - 1 : 1 );
|
||||
|
||||
return { position, axis, angle, name: move };
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Scrambler };
|
||||
@@ -0,0 +1,68 @@
|
||||
const States = {
|
||||
3: {
|
||||
checkerboard: {
|
||||
names: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 ],
|
||||
positions: [
|
||||
{ "x": 1/3, "y": -1/3, "z": 1/3 },
|
||||
{ "x": -1/3, "y": 1/3, "z": 0 },
|
||||
{ "x": 1/3, "y": -1/3, "z": -1/3 },
|
||||
{ "x": -1/3, "y": 0, "z": -1/3 },
|
||||
{ "x": 1/3, "y": 0, "z": 0 },
|
||||
{ "x": -1/3, "y": 0, "z": 1/3 },
|
||||
{ "x": 1/3, "y": 1/3, "z": 1/3 },
|
||||
{ "x": -1/3, "y": -1/3, "z": 0 },
|
||||
{ "x": 1/3, "y": 1/3, "z": -1/3 },
|
||||
{ "x": 0, "y": 1/3, "z": -1/3 },
|
||||
{ "x": 0, "y": -1/3, "z": 0 },
|
||||
{ "x": 0, "y": 1/3, "z": 1/3 },
|
||||
{ "x": 0, "y": 0, "z": 1/3 },
|
||||
{ "x": 0, "y": 0, "z": 0 },
|
||||
{ "x": 0, "y": 0, "z": -1/3 },
|
||||
{ "x": 0, "y": -1/3, "z": -1/3 },
|
||||
{ "x": 0, "y": 1/3, "z": 0 },
|
||||
{ "x": 0, "y": -1/3, "z": 1/3 },
|
||||
{ "x": -1/3, "y": -1/3, "z": 1/3 },
|
||||
{ "x": 1/3, "y": 1/3, "z": 0 },
|
||||
{ "x": -1/3, "y": -1/3, "z": -1/3 },
|
||||
{ "x": 1/3, "y": 0, "z": -1/3 },
|
||||
{ "x": -1/3, "y": 0, "z": 0 },
|
||||
{ "x": 1/3, "y": 0, "z": 1/3 },
|
||||
{ "x": -1/3, "y": 1/3, "z": 1/3 },
|
||||
{ "x": 1/3, "y": -1/3, "z": 0 },
|
||||
{ "x": -1/3, "y": 1/3, "z": -1/3 }
|
||||
],
|
||||
rotations: [
|
||||
{ "x": -Math.PI, "y": 0, "z": Math.PI, },
|
||||
{ "x": Math.PI, "y": 0, "z": 0 },
|
||||
{ "x": -Math.PI, "y": 0, "z": Math.PI },
|
||||
{ "x": 0, "y": 0, "z": 0 },
|
||||
{ "x": 0, "y": 0, "z": Math.PI },
|
||||
{ "x": 0, "y": 0, "z": 0 },
|
||||
{ "x": -Math.PI, "y": 0, "z": Math.PI },
|
||||
{ "x": Math.PI, "y": 0, "z": 0 },
|
||||
{ "x": -Math.PI, "y": 0, "z": Math.PI },
|
||||
{ "x": 0, "y": 0, "z": Math.PI },
|
||||
{ "x": 0, "y": 0, "z": 0 },
|
||||
{ "x": 0, "y": 0, "z": Math.PI },
|
||||
{ "x": -Math.PI, "y": 0, "z": 0 },
|
||||
{ "x": Math.PI, "y": 0, "z": Math.PI },
|
||||
{ "x": Math.PI, "y": 0, "z": 0 },
|
||||
{ "x": 0, "y": 0, "z": Math.PI },
|
||||
{ "x": 0, "y": 0, "z": 0 },
|
||||
{ "x": 0, "y": 0, "z": Math.PI },
|
||||
{ "x": Math.PI, "y": 0, "z": Math.PI },
|
||||
{ "x": -Math.PI, "y": 0, "z": 0 },
|
||||
{ "x": Math.PI, "y": 0, "z": Math.PI },
|
||||
{ "x": 0, "y": 0, "z": 0 },
|
||||
{ "x": 0, "y": 0, "z": Math.PI },
|
||||
{ "x": 0, "y": 0, "z": 0 },
|
||||
{ "x": Math.PI, "y": 0, "z": Math.PI },
|
||||
{ "x": -Math.PI, "y": 0, "z": 0 },
|
||||
{ "x": Math.PI, "y": 0, "z": Math.PI }
|
||||
],
|
||||
size: 3,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
export { States };
|
||||
@@ -0,0 +1,200 @@
|
||||
class Storage {
|
||||
|
||||
constructor( game ) {
|
||||
|
||||
this.game = game;
|
||||
|
||||
const userVersion = localStorage.getItem( 'theCube_version' );
|
||||
|
||||
if ( ! userVersion || userVersion !== window.gameVersion ) {
|
||||
|
||||
this.clearGame();
|
||||
this.clearPreferences();
|
||||
this.migrateScores();
|
||||
localStorage.setItem( 'theCube_version', window.gameVersion );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
init() {
|
||||
|
||||
this.loadPreferences();
|
||||
this.loadScores();
|
||||
|
||||
}
|
||||
|
||||
loadGame() {
|
||||
|
||||
try {
|
||||
|
||||
const gameInProgress = localStorage.getItem( 'theCube_playing' ) === 'true';
|
||||
|
||||
if ( ! gameInProgress ) throw new Error();
|
||||
|
||||
const gameCubeData = JSON.parse( localStorage.getItem( 'theCube_savedState' ) );
|
||||
const gameTime = parseInt( localStorage.getItem( 'theCube_time' ) );
|
||||
|
||||
if ( ! gameCubeData || gameTime === null ) throw new Error();
|
||||
if ( gameCubeData.size !== this.game.cube.sizeGenerated ) throw new Error();
|
||||
|
||||
this.game.cube.loadFromData( gameCubeData );
|
||||
|
||||
this.game.timer.deltaTime = gameTime;
|
||||
|
||||
this.game.saved = true;
|
||||
|
||||
} catch( e ) {
|
||||
|
||||
this.game.saved = false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
saveGame() {
|
||||
|
||||
const gameInProgress = true;
|
||||
const gameCubeData = { names: [], positions: [], rotations: [] };
|
||||
const gameTime = this.game.timer.deltaTime;
|
||||
|
||||
gameCubeData.size = this.game.cube.sizeGenerated;
|
||||
|
||||
this.game.cube.pieces.forEach( piece => {
|
||||
|
||||
gameCubeData.names.push( piece.name );
|
||||
gameCubeData.positions.push( piece.position );
|
||||
gameCubeData.rotations.push( piece.rotation.toVector3() );
|
||||
|
||||
} );
|
||||
|
||||
localStorage.setItem( 'theCube_playing', gameInProgress );
|
||||
localStorage.setItem( 'theCube_savedState', JSON.stringify( gameCubeData ) );
|
||||
localStorage.setItem( 'theCube_time', gameTime );
|
||||
|
||||
}
|
||||
|
||||
clearGame() {
|
||||
|
||||
localStorage.removeItem( 'theCube_playing' );
|
||||
localStorage.removeItem( 'theCube_savedState' );
|
||||
localStorage.removeItem( 'theCube_time' );
|
||||
|
||||
}
|
||||
|
||||
loadScores() {
|
||||
|
||||
try {
|
||||
|
||||
const scoresData = JSON.parse( localStorage.getItem( 'theCube_scores' ) );
|
||||
|
||||
if ( ! scoresData ) throw new Error();
|
||||
|
||||
this.game.scores.data = scoresData;
|
||||
|
||||
} catch( e ) {}
|
||||
|
||||
}
|
||||
|
||||
saveScores() {
|
||||
|
||||
const scoresData = this.game.scores.data;
|
||||
|
||||
localStorage.setItem( 'theCube_scores', JSON.stringify( scoresData ) );
|
||||
|
||||
}
|
||||
|
||||
clearScores() {
|
||||
|
||||
localStorage.removeItem( 'theCube_scores' );
|
||||
|
||||
}
|
||||
|
||||
migrateScores() {
|
||||
|
||||
try {
|
||||
|
||||
const scoresData = JSON.parse( localStorage.getItem( 'theCube_scoresData' ) );
|
||||
const scoresBest = parseInt( localStorage.getItem( 'theCube_scoresBest' ) );
|
||||
const scoresWorst = parseInt( localStorage.getItem( 'theCube_scoresWorst' ) );
|
||||
const scoresSolves = parseInt( localStorage.getItem( 'theCube_scoresSolves' ) );
|
||||
|
||||
if ( ! scoresData || ! scoresBest || ! scoresSolves || ! scoresWorst ) return false;
|
||||
|
||||
this.game.scores.data[ 3 ].scores = scoresData;
|
||||
this.game.scores.data[ 3 ].best = scoresBest;
|
||||
this.game.scores.data[ 3 ].solves = scoresSolves;
|
||||
this.game.scores.data[ 3 ].worst = scoresWorst;
|
||||
|
||||
localStorage.removeItem( 'theCube_scoresData' );
|
||||
localStorage.removeItem( 'theCube_scoresBest' );
|
||||
localStorage.removeItem( 'theCube_scoresWorst' );
|
||||
localStorage.removeItem( 'theCube_scoresSolves' );
|
||||
|
||||
} catch( e ) {}
|
||||
|
||||
}
|
||||
|
||||
loadPreferences() {
|
||||
|
||||
try {
|
||||
|
||||
const preferences = JSON.parse( localStorage.getItem( 'theCube_preferences' ) );
|
||||
|
||||
if ( ! preferences ) throw new Error();
|
||||
|
||||
this.game.cube.size = parseInt( preferences.cubeSize );
|
||||
this.game.controls.flipConfig = parseInt( preferences.flipConfig );
|
||||
this.game.scrambler.dificulty = parseInt( preferences.dificulty );
|
||||
|
||||
this.game.world.fov = parseFloat( preferences.fov );
|
||||
this.game.world.resize();
|
||||
|
||||
this.game.themes.colors = preferences.colors;
|
||||
this.game.themes.setTheme( preferences.theme );
|
||||
|
||||
return true;
|
||||
|
||||
} catch (e) {
|
||||
|
||||
this.game.cube.size = 3;
|
||||
this.game.controls.flipConfig = 0;
|
||||
this.game.scrambler.dificulty = 1;
|
||||
|
||||
this.game.world.fov = 10;
|
||||
this.game.world.resize();
|
||||
|
||||
this.game.themes.setTheme( 'cube' );
|
||||
|
||||
this.savePreferences();
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
savePreferences() {
|
||||
|
||||
const preferences = {
|
||||
cubeSize: this.game.cube.size,
|
||||
flipConfig: this.game.controls.flipConfig,
|
||||
dificulty: this.game.scrambler.dificulty,
|
||||
fov: this.game.world.fov,
|
||||
theme: this.game.themes.theme,
|
||||
colors: this.game.themes.colors,
|
||||
};
|
||||
|
||||
localStorage.setItem( 'theCube_preferences', JSON.stringify( preferences ) );
|
||||
|
||||
}
|
||||
|
||||
clearPreferences() {
|
||||
|
||||
localStorage.removeItem( 'theCube_preferences' );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Storage };
|
||||
@@ -0,0 +1,179 @@
|
||||
import { Tween, Easing } from './Tween.js';
|
||||
|
||||
class ThemeEditor {
|
||||
|
||||
constructor( game ) {
|
||||
|
||||
this.game = game;
|
||||
|
||||
this.editColor = 'R';
|
||||
|
||||
this.getPieceColor = this.getPieceColor.bind( this );
|
||||
|
||||
}
|
||||
|
||||
colorFromHSL( h, s, l ) {
|
||||
|
||||
h = Math.round( h );
|
||||
s = Math.round( s );
|
||||
l = Math.round( l );
|
||||
|
||||
return new THREE.Color( `hsl(${h}, ${s}%, ${l}%)` );
|
||||
|
||||
}
|
||||
|
||||
setHSL( color = null, animate = false ) {
|
||||
|
||||
this.editColor = ( color === null) ? 'R' : color;
|
||||
|
||||
const hsl = new THREE.Color( this.game.themes.getColors()[ this.editColor ] );
|
||||
|
||||
const { h, s, l } = hsl.getHSL( hsl );
|
||||
const { hue, saturation, lightness } = this.game.preferences.ranges;
|
||||
|
||||
if ( animate ) {
|
||||
|
||||
const ho = hue.value / 360;
|
||||
const so = saturation.value / 100;
|
||||
const lo = lightness.value / 100;
|
||||
|
||||
const colorOld = this.colorFromHSL( hue.value, saturation.value, lightness.value );
|
||||
|
||||
if ( this.tweenHSL ) this.tweenHSL.stop()
|
||||
|
||||
this.tweenHSL = new Tween( {
|
||||
duration: 200,
|
||||
easing: Easing.Sine.Out(),
|
||||
onUpdate: tween => {
|
||||
|
||||
hue.setValue( ( ho + ( h - ho ) * tween.value ) * 360 );
|
||||
saturation.setValue( ( so + ( s - so ) * tween.value ) * 100 );
|
||||
lightness.setValue( ( lo + ( l - lo ) * tween.value ) * 100 );
|
||||
|
||||
const colorTween = colorOld.clone().lerp( hsl, tween.value );
|
||||
|
||||
const colorTweenStyle = colorTween.getStyle();
|
||||
const colorTweenHex = colorTween.getHSL( colorTween );
|
||||
|
||||
hue.handle.style.color = colorTweenStyle;
|
||||
saturation.handle.style.color = colorTweenStyle;
|
||||
lightness.handle.style.color = colorTweenStyle;
|
||||
|
||||
saturation.track.style.color =
|
||||
this.colorFromHSL( colorTweenHex.h * 360, 100, 50 ).getStyle();
|
||||
lightness.track.style.color =
|
||||
this.colorFromHSL( colorTweenHex.h * 360, colorTweenHex.s * 100, 50 ).getStyle();
|
||||
|
||||
this.game.dom.theme.style.display = 'none';
|
||||
this.game.dom.theme.offsetHeight;
|
||||
this.game.dom.theme.style.display = '';
|
||||
|
||||
},
|
||||
onComplete: () => {
|
||||
|
||||
this.updateHSL();
|
||||
this.game.storage.savePreferences();
|
||||
|
||||
},
|
||||
} );
|
||||
|
||||
} else {
|
||||
|
||||
hue.setValue( h * 360 );
|
||||
saturation.setValue( s * 100 );
|
||||
lightness.setValue( l * 100 );
|
||||
|
||||
this.updateHSL();
|
||||
this.game.storage.savePreferences();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
updateHSL() {
|
||||
|
||||
const { hue, saturation, lightness } = this.game.preferences.ranges;
|
||||
|
||||
const h = hue.value;
|
||||
const s = saturation.value;
|
||||
const l = lightness.value;
|
||||
|
||||
const color = this.colorFromHSL( h, s, l ).getStyle();
|
||||
|
||||
hue.handle.style.color = color;
|
||||
saturation.handle.style.color = color;
|
||||
lightness.handle.style.color = color;
|
||||
|
||||
saturation.track.style.color = this.colorFromHSL( h, 100, 50 ).getStyle();
|
||||
lightness.track.style.color = this.colorFromHSL( h, s, 50 ).getStyle();
|
||||
|
||||
this.game.dom.theme.style.display = 'none';
|
||||
this.game.dom.theme.offsetHeight;
|
||||
this.game.dom.theme.style.display = '';
|
||||
|
||||
const theme = this.game.themes.theme;
|
||||
|
||||
this.game.themes.colors[ theme ][ this.editColor ] = this.colorFromHSL( h, s, l ).getHex();
|
||||
this.game.themes.setTheme();
|
||||
|
||||
}
|
||||
|
||||
colorPicker( enable ) {
|
||||
|
||||
if ( enable ) {
|
||||
|
||||
this.game.dom.game.addEventListener( 'click', this.getPieceColor, false );
|
||||
|
||||
} else {
|
||||
|
||||
this.game.dom.game.removeEventListener( 'click', this.getPieceColor, false );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
getPieceColor( event ) {
|
||||
|
||||
const clickEvent = event.touches
|
||||
? ( event.touches[ 0 ] || event.changedTouches[ 0 ] )
|
||||
: event;
|
||||
|
||||
const clickPosition = new THREE.Vector2( clickEvent.pageX, clickEvent.pageY );
|
||||
|
||||
let edgeIntersect = this.game.controls.getIntersect( clickPosition, this.game.cube.edges, true );
|
||||
let pieceIntersect = this.game.controls.getIntersect( clickPosition, this.game.cube.cubes, true );
|
||||
|
||||
if ( edgeIntersect !== false ) {
|
||||
|
||||
const edge = edgeIntersect.object;
|
||||
|
||||
const position = edge.parent
|
||||
.localToWorld( edge.position.clone() )
|
||||
.sub( this.game.cube.object.position )
|
||||
.sub( this.game.cube.animator.position );
|
||||
|
||||
const mainAxis = this.game.controls.getMainAxis( position );
|
||||
if ( position.multiplyScalar( 2 ).round()[ mainAxis ] < 1 ) edgeIntersect = false;
|
||||
|
||||
}
|
||||
|
||||
const name = edgeIntersect ? edgeIntersect.object.name : pieceIntersect ? 'P' : 'G';
|
||||
|
||||
this.setHSL( name, true );
|
||||
|
||||
}
|
||||
|
||||
resetTheme() {
|
||||
|
||||
this.game.themes.colors[ this.game.themes.theme ] =
|
||||
JSON.parse( JSON.stringify( this.game.themes.defaults[ this.game.themes.theme ] ) );
|
||||
|
||||
this.game.themes.setTheme();
|
||||
|
||||
this.setHSL( this.editColor, true );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { ThemeEditor };
|
||||
@@ -0,0 +1,94 @@
|
||||
class Themes {
|
||||
|
||||
constructor( game ) {
|
||||
|
||||
this.game = game;
|
||||
this.theme = null;
|
||||
|
||||
this.defaults = {
|
||||
cube: {
|
||||
U: 0xfff7ff, // white
|
||||
D: 0xffef48, // yellow
|
||||
F: 0xef3923, // red
|
||||
R: 0x41aac8, // blue
|
||||
B: 0xff8c0a, // orange
|
||||
L: 0x82ca38, // green
|
||||
P: 0x08101a, // piece
|
||||
G: 0xd1d5db, // background
|
||||
},
|
||||
erno: {
|
||||
U: 0xffffff,
|
||||
D: 0xffd500,
|
||||
F: 0xc41e3a,
|
||||
R: 0x0051ba,
|
||||
B: 0xff5800,
|
||||
L: 0x009e60,
|
||||
P: 0x08101a,
|
||||
G: 0x8abdff,
|
||||
},
|
||||
dust: {
|
||||
U: 0xfff6eb,
|
||||
D: 0xe7c48d,
|
||||
F: 0x8f253e,
|
||||
R: 0x607e69,
|
||||
B: 0xbe6f62,
|
||||
L: 0x849f5d,
|
||||
P: 0x08101a,
|
||||
G: 0xE7C48D,
|
||||
},
|
||||
camo: {
|
||||
U: 0xfff6eb,
|
||||
D: 0xbfb672,
|
||||
F: 0x37241c,
|
||||
R: 0x718456,
|
||||
B: 0x805831,
|
||||
L: 0x37431d,
|
||||
P: 0x08101a,
|
||||
G: 0xBFB672,
|
||||
},
|
||||
rain: {
|
||||
U: 0xfafaff,
|
||||
D: 0xedb92d,
|
||||
F: 0xce2135,
|
||||
R: 0x449a89,
|
||||
B: 0xec582f,
|
||||
L: 0xa3a947,
|
||||
P: 0x08101a,
|
||||
G: 0x87b9ac,
|
||||
},
|
||||
};
|
||||
|
||||
this.colors = JSON.parse( JSON.stringify( this.defaults ) );
|
||||
|
||||
}
|
||||
|
||||
getColors() {
|
||||
|
||||
return this.colors[ this.theme ];
|
||||
|
||||
}
|
||||
|
||||
setTheme( theme = false, force = false ) {
|
||||
|
||||
if ( theme === this.theme && force === false ) return;
|
||||
if ( theme !== false ) this.theme = theme;
|
||||
|
||||
const colors = this.getColors();
|
||||
|
||||
this.game.dom.prefs.querySelectorAll( '.range__handle div' ).forEach( range => {
|
||||
|
||||
range.style.background = '#' + colors.R.toString(16).padStart(6, '0');
|
||||
|
||||
} );
|
||||
|
||||
this.game.cube.updateColors( colors );
|
||||
|
||||
this.game.confetti.updateColors( colors );
|
||||
|
||||
this.game.dom.back.style.background = '#' + colors.G.toString(16).padStart(6, '0');
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Themes };
|
||||
@@ -0,0 +1,79 @@
|
||||
import { Animation } from './Animation.js';
|
||||
|
||||
class Timer extends Animation {
|
||||
|
||||
constructor( game ) {
|
||||
|
||||
super( false );
|
||||
|
||||
this.game = game;
|
||||
this.reset();
|
||||
|
||||
}
|
||||
|
||||
start( continueGame ) {
|
||||
|
||||
this.startTime = continueGame ? ( Date.now() - this.deltaTime ) : Date.now();
|
||||
this.deltaTime = 0;
|
||||
this.converted = this.convert();
|
||||
|
||||
super.start();
|
||||
|
||||
}
|
||||
|
||||
reset() {
|
||||
|
||||
this.startTime = 0;
|
||||
this.currentTime = 0;
|
||||
this.deltaTime = 0;
|
||||
this.converted = '0:00';
|
||||
|
||||
}
|
||||
|
||||
stop() {
|
||||
|
||||
this.currentTime = Date.now();
|
||||
this.deltaTime = this.currentTime - this.startTime;
|
||||
this.convert();
|
||||
|
||||
super.stop();
|
||||
|
||||
return { time: this.converted, millis: this.deltaTime };
|
||||
|
||||
}
|
||||
|
||||
update() {
|
||||
|
||||
const old = this.converted;
|
||||
|
||||
this.currentTime = Date.now();
|
||||
this.deltaTime = this.currentTime - this.startTime;
|
||||
this.convert();
|
||||
|
||||
if ( this.converted != old ) {
|
||||
|
||||
localStorage.setItem( 'theCube_time', this.deltaTime );
|
||||
this.setText();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
convert() {
|
||||
|
||||
const seconds = parseInt( ( this.deltaTime / 1000 ) % 60 );
|
||||
const minutes = parseInt( ( this.deltaTime / ( 1000 * 60 ) ) );
|
||||
|
||||
this.converted = minutes + ':' + ( seconds < 10 ? '0' : '' ) + seconds;
|
||||
|
||||
}
|
||||
|
||||
setText() {
|
||||
|
||||
this.game.dom.texts.timer.innerHTML = this.converted;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Timer };
|
||||
@@ -0,0 +1,478 @@
|
||||
import { Tween, Easing } from './Tween.js';
|
||||
|
||||
class Transition {
|
||||
|
||||
constructor( game ) {
|
||||
|
||||
this.game = game;
|
||||
|
||||
this.tweens = {};
|
||||
this.durations = {};
|
||||
this.data = {
|
||||
cubeY: -0.2,
|
||||
cameraZoom: 0.85,
|
||||
};
|
||||
|
||||
this.activeTransitions = 0;
|
||||
|
||||
}
|
||||
|
||||
init() {
|
||||
|
||||
this.game.controls.disable();
|
||||
|
||||
this.game.cube.object.position.y = this.data.cubeY;
|
||||
this.game.cube.animator.position.y = 4;
|
||||
this.game.cube.animator.rotation.x = - Math.PI / 3;
|
||||
this.game.world.camera.zoom = this.data.cameraZoom;
|
||||
this.game.world.camera.updateProjectionMatrix();
|
||||
|
||||
this.tweens.buttons = {};
|
||||
this.tweens.timer = [];
|
||||
this.tweens.title = [];
|
||||
this.tweens.best = [];
|
||||
this.tweens.complete = [];
|
||||
this.tweens.prefs = [];
|
||||
this.tweens.theme = [];
|
||||
this.tweens.stats = [];
|
||||
|
||||
}
|
||||
|
||||
buttons( show, hide ) {
|
||||
|
||||
const buttonTween = ( button, show ) => {
|
||||
|
||||
return new Tween( {
|
||||
target: button.style,
|
||||
duration: 300,
|
||||
easing: show ? Easing.Power.Out( 2 ) : Easing.Power.In( 3 ),
|
||||
from: { opacity: show ? 0 : 1 },
|
||||
to: { opacity: show ? 1 : 0 },
|
||||
onUpdate: tween => {
|
||||
|
||||
const translate = show ? 1 - tween.value : tween.value;
|
||||
button.style.transform = `translate3d(0, ${translate * 1.5}em, 0)`;
|
||||
|
||||
},
|
||||
onComplete: () => button.style.pointerEvents = show ? 'all' : 'none'
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
hide.forEach( button =>
|
||||
this.tweens.buttons[ button ] = buttonTween( this.game.dom.buttons[ button ], false )
|
||||
);
|
||||
|
||||
setTimeout( () => show.forEach( button => {
|
||||
|
||||
this.tweens.buttons[ button ] = buttonTween( this.game.dom.buttons[ button ], true )
|
||||
|
||||
} ), hide ? 500 : 0 );
|
||||
|
||||
}
|
||||
|
||||
cube( show, theming = false ) {
|
||||
|
||||
this.activeTransitions++;
|
||||
|
||||
try { this.tweens.cube.stop() } catch(e) {};
|
||||
|
||||
const currentY = this.game.cube.animator.position.y;
|
||||
const currentRotation = this.game.cube.animator.rotation.x;
|
||||
|
||||
this.tweens.cube = new Tween( {
|
||||
duration: show ? 3000 : 1250,
|
||||
easing: show ? Easing.Elastic.Out( 0.8, 0.6 ) : Easing.Back.In( 1 ),
|
||||
onUpdate: tween => {
|
||||
|
||||
this.game.cube.animator.position.y = show
|
||||
? ( theming ? 0.9 + ( 1 - tween.value ) * 3.5 : ( 1 - tween.value ) * 4 )
|
||||
: currentY + tween.value * 4;
|
||||
|
||||
this.game.cube.animator.rotation.x = show
|
||||
? ( 1 - tween.value ) * Math.PI / 3
|
||||
: currentRotation + tween.value * - Math.PI / 3;
|
||||
|
||||
},
|
||||
} );
|
||||
|
||||
if ( theming ) {
|
||||
|
||||
if ( show ) {
|
||||
|
||||
this.game.world.camera.zoom = 0.75;
|
||||
this.game.world.camera.updateProjectionMatrix();
|
||||
|
||||
} else {
|
||||
|
||||
setTimeout( () => {
|
||||
|
||||
this.game.world.camera.zoom = this.data.cameraZoom;
|
||||
this.game.world.camera.updateProjectionMatrix();
|
||||
|
||||
}, 1500 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.durations.cube = show ? 1500 : 1500;
|
||||
|
||||
setTimeout( () => this.activeTransitions--, this.durations.cube );
|
||||
|
||||
}
|
||||
|
||||
float() {
|
||||
|
||||
try { this.tweens.float.stop() } catch(e) {};
|
||||
|
||||
this.tweens.float = new Tween( {
|
||||
duration: 1500,
|
||||
easing: Easing.Sine.InOut(),
|
||||
yoyo: true,
|
||||
onUpdate: tween => {
|
||||
|
||||
this.game.cube.holder.position.y = (- 0.02 + tween.value * 0.04);
|
||||
this.game.cube.holder.rotation.x = 0.005 - tween.value * 0.01;
|
||||
this.game.cube.holder.rotation.z = - this.game.cube.holder.rotation.x;
|
||||
this.game.cube.holder.rotation.y = this.game.cube.holder.rotation.x;
|
||||
|
||||
this.game.controls.edges.position.y =
|
||||
this.game.cube.holder.position.y + this.game.cube.object.position.y;
|
||||
|
||||
},
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
zoom( play, time ) {
|
||||
|
||||
this.activeTransitions++;
|
||||
|
||||
const zoom = ( play ) ? 1 : this.data.cameraZoom;
|
||||
const duration = ( time > 0 ) ? Math.max( time, 1500 ) : 1500;
|
||||
const rotations = ( time > 0 ) ? Math.round( duration / 1500 ) : 1;
|
||||
const easing = Easing.Power.InOut( ( time > 0 ) ? 2 : 3 );
|
||||
|
||||
this.tweens.zoom = new Tween( {
|
||||
target: this.game.world.camera,
|
||||
duration: duration,
|
||||
easing: easing,
|
||||
to: { zoom: zoom },
|
||||
onUpdate: () => { this.game.world.camera.updateProjectionMatrix(); },
|
||||
} );
|
||||
|
||||
this.tweens.rotate = new Tween( {
|
||||
target: this.game.cube.animator.rotation,
|
||||
duration: duration,
|
||||
easing: easing,
|
||||
to: { y: - Math.PI * 2 * rotations },
|
||||
onComplete: () => { this.game.cube.animator.rotation.y = 0; },
|
||||
} );
|
||||
|
||||
this.durations.zoom = duration;
|
||||
|
||||
setTimeout( () => this.activeTransitions--, this.durations.zoom );
|
||||
|
||||
}
|
||||
|
||||
elevate( complete ) {
|
||||
|
||||
this.activeTransitions++;
|
||||
|
||||
const cubeY =
|
||||
|
||||
this.tweens.elevate = new Tween( {
|
||||
target: this.game.cube.object.position,
|
||||
duration: complete ? 1500 : 0,
|
||||
easing: Easing.Power.InOut( 3 ),
|
||||
to: { y: complete ? -0.05 : this.data.cubeY }
|
||||
} );
|
||||
|
||||
this.durations.elevate = 1500;
|
||||
|
||||
setTimeout( () => this.activeTransitions--, this.durations.elevate );
|
||||
|
||||
}
|
||||
|
||||
complete( show, best ) {
|
||||
|
||||
this.activeTransitions++;
|
||||
|
||||
const text = best ? this.game.dom.texts.best : this.game.dom.texts.complete;
|
||||
|
||||
if ( text.querySelector( 'span i' ) === null )
|
||||
text.querySelectorAll( 'span' ).forEach( span => this.splitLetters( span ) );
|
||||
|
||||
const letters = text.querySelectorAll( '.icon, i' );
|
||||
|
||||
this.flipLetters( best ? 'best' : 'complete', letters, show );
|
||||
|
||||
text.style.opacity = 1;
|
||||
|
||||
const duration = this.durations[ best ? 'best' : 'complete' ];
|
||||
|
||||
if ( ! show ) setTimeout( () => this.game.dom.texts.timer.style.transform = '', duration );
|
||||
|
||||
setTimeout( () => this.activeTransitions--, duration );
|
||||
|
||||
}
|
||||
|
||||
stats( show ) {
|
||||
|
||||
if ( show ) this.game.scores.calcStats();
|
||||
|
||||
this.activeTransitions++;
|
||||
|
||||
this.tweens.stats.forEach( tween => { tween.stop(); tween = null; } );
|
||||
|
||||
let tweenId = -1;
|
||||
|
||||
const stats = this.game.dom.stats.querySelectorAll( '.stats' );
|
||||
const easing = show ? Easing.Power.Out( 2 ) : Easing.Power.In( 3 );
|
||||
|
||||
stats.forEach( ( stat, index ) => {
|
||||
|
||||
const delay = index * ( show ? 80 : 60 );
|
||||
|
||||
this.tweens.stats[ tweenId++ ] = new Tween( {
|
||||
delay: delay,
|
||||
duration: 400,
|
||||
easing: easing,
|
||||
onUpdate: tween => {
|
||||
|
||||
const translate = show ? ( 1 - tween.value ) * 2 : tween.value;
|
||||
const opacity = show ? tween.value : ( 1 - tween.value );
|
||||
|
||||
stat.style.transform = `translate3d(0, ${translate}em, 0)`;
|
||||
stat.style.opacity = opacity;
|
||||
|
||||
}
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
this.durations.stats = 0;
|
||||
|
||||
setTimeout( () => this.activeTransitions--, this.durations.stats );
|
||||
|
||||
}
|
||||
|
||||
preferences( show ) {
|
||||
|
||||
this.ranges( this.game.dom.prefs.querySelectorAll( '.range' ), 'prefs', show );
|
||||
|
||||
}
|
||||
|
||||
theming( show ) {
|
||||
|
||||
this.ranges( this.game.dom.theme.querySelectorAll( '.range' ), 'prefs', show );
|
||||
|
||||
}
|
||||
|
||||
ranges( ranges, type, show ) {
|
||||
|
||||
this.activeTransitions++;
|
||||
|
||||
this.tweens[ type ].forEach( tween => { tween.stop(); tween = null; } );
|
||||
|
||||
const easing = show ? Easing.Power.Out(2) : Easing.Power.In(3);
|
||||
|
||||
let tweenId = -1;
|
||||
let listMax = 0;
|
||||
|
||||
ranges.forEach( ( range, rangeIndex ) => {
|
||||
|
||||
const label = range.querySelector( '.range__label' );
|
||||
const track = range.querySelector( '.range__track-line' );
|
||||
const handle = range.querySelector( '.range__handle' );
|
||||
const list = range.querySelectorAll( '.range__list div' );
|
||||
|
||||
const delay = rangeIndex * ( show ? 120 : 100 );
|
||||
|
||||
label.style.opacity = show ? 0 : 1;
|
||||
track.style.opacity = show ? 0 : 1;
|
||||
handle.style.opacity = show ? 0 : 1;
|
||||
handle.style.pointerEvents = show ? 'all' : 'none';
|
||||
|
||||
this.tweens[ type ][ tweenId++ ] = new Tween( {
|
||||
delay: show ? delay : delay,
|
||||
duration: 400,
|
||||
easing: easing,
|
||||
onUpdate: tween => {
|
||||
|
||||
const translate = show ? ( 1 - tween.value ) : tween.value;
|
||||
const opacity = show ? tween.value : ( 1 - tween.value );
|
||||
|
||||
label.style.transform = `translate3d(0, ${translate}em, 0)`;
|
||||
label.style.opacity = opacity;
|
||||
|
||||
}
|
||||
} );
|
||||
|
||||
this.tweens[ type ][ tweenId++ ] = new Tween( {
|
||||
delay: show ? delay + 100 : delay,
|
||||
duration: 400,
|
||||
easing: easing,
|
||||
onUpdate: tween => {
|
||||
|
||||
const translate = show ? ( 1 - tween.value ) : tween.value;
|
||||
const scale = show ? tween.value : ( 1 - tween.value );
|
||||
const opacity = scale;
|
||||
|
||||
track.style.transform = `translate3d(0, ${translate}em, 0) scale3d(${scale}, 1, 1)`;
|
||||
track.style.opacity = opacity;
|
||||
|
||||
}
|
||||
} );
|
||||
|
||||
this.tweens[ type ][ tweenId++ ] = new Tween( {
|
||||
delay: show ? delay + 100 : delay,
|
||||
duration: 400,
|
||||
easing: easing,
|
||||
onUpdate: tween => {
|
||||
|
||||
const translate = show ? ( 1 - tween.value ) : tween.value;
|
||||
const opacity = 1 - translate;
|
||||
const scale = 0.5 + opacity * 0.5;
|
||||
|
||||
handle.style.transform = `translate3d(0, ${translate}em, 0) scale3d(${scale}, ${scale}, ${scale})`;
|
||||
handle.style.opacity = opacity;
|
||||
|
||||
}
|
||||
} );
|
||||
|
||||
list.forEach( ( listItem, labelIndex ) => {
|
||||
|
||||
listItem.style.opacity = show ? 0 : 1;
|
||||
|
||||
this.tweens[ type ][ tweenId++ ] = new Tween( {
|
||||
delay: show ? delay + 200 + labelIndex * 50 : delay,
|
||||
duration: 400,
|
||||
easing: easing,
|
||||
onUpdate: tween => {
|
||||
|
||||
const translate = show ? ( 1 - tween.value ) : tween.value;
|
||||
const opacity = show ? tween.value : ( 1 - tween.value );
|
||||
|
||||
listItem.style.transform = `translate3d(0, ${translate}em, 0)`;
|
||||
listItem.style.opacity = opacity;
|
||||
|
||||
}
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
listMax = list.length > listMax ? list.length - 1 : listMax;
|
||||
|
||||
range.style.opacity = 1;
|
||||
|
||||
} );
|
||||
|
||||
this.durations[ type ] = show
|
||||
? ( ( ranges.length - 1 ) * 100 ) + 200 + listMax * 50 + 400
|
||||
: ( ( ranges.length - 1 ) * 100 ) + 400;
|
||||
|
||||
setTimeout( () => this.activeTransitions--, this.durations[ type ] );
|
||||
|
||||
}
|
||||
|
||||
title( show ) {
|
||||
|
||||
this.activeTransitions++;
|
||||
|
||||
const title = this.game.dom.texts.title;
|
||||
|
||||
if ( title.querySelector( 'span i' ) === null )
|
||||
title.querySelectorAll( 'span' ).forEach( span => this.splitLetters( span ) );
|
||||
|
||||
const letters = title.querySelectorAll( 'i' );
|
||||
|
||||
this.flipLetters( 'title', letters, show );
|
||||
|
||||
title.style.opacity = 1;
|
||||
|
||||
const note = this.game.dom.texts.note;
|
||||
|
||||
this.tweens.title[ letters.length ] = new Tween( {
|
||||
target: note.style,
|
||||
easing: Easing.Sine.InOut(),
|
||||
duration: show ? 800 : 400,
|
||||
yoyo: show ? true : null,
|
||||
from: { opacity: show ? 0 : ( parseFloat( getComputedStyle( note ).opacity ) ) },
|
||||
to: { opacity: show ? 1 : 0 },
|
||||
} );
|
||||
|
||||
setTimeout( () => this.activeTransitions--, this.durations.title );
|
||||
|
||||
}
|
||||
|
||||
timer( show ) {
|
||||
|
||||
this.activeTransitions++;
|
||||
|
||||
const timer = this.game.dom.texts.timer;
|
||||
|
||||
timer.style.opacity = 0;
|
||||
this.game.timer.convert();
|
||||
this.game.timer.setText();
|
||||
|
||||
this.splitLetters( timer );
|
||||
const letters = timer.querySelectorAll( 'i' );
|
||||
this.flipLetters( 'timer', letters, show );
|
||||
|
||||
timer.style.opacity = 1;
|
||||
|
||||
setTimeout( () => this.activeTransitions--, this.durations.timer );
|
||||
|
||||
}
|
||||
|
||||
splitLetters( element ) {
|
||||
|
||||
const text = element.innerHTML;
|
||||
|
||||
element.innerHTML = '';
|
||||
|
||||
text.split( '' ).forEach( letter => {
|
||||
|
||||
const i = document.createElement( 'i' );
|
||||
|
||||
i.innerHTML = letter;
|
||||
|
||||
element.appendChild( i );
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
flipLetters( type, letters, show ) {
|
||||
|
||||
try { this.tweens[ type ].forEach( tween => tween.stop() ) } catch(e) {};
|
||||
|
||||
letters.forEach( ( letter, index ) => {
|
||||
|
||||
letter.style.opacity = show ? 0 : 1;
|
||||
|
||||
this.tweens[ type ][ index ] = new Tween( {
|
||||
easing: Easing.Sine.Out(),
|
||||
duration: show ? 800 : 400,
|
||||
delay: index * 50,
|
||||
onUpdate: tween => {
|
||||
|
||||
const rotation = show ? ( 1 - tween.value ) * -80 : tween.value * 80;
|
||||
|
||||
letter.style.transform = `rotate3d(0, 1, 0, ${rotation}deg)`;
|
||||
letter.style.opacity = show ? tween.value : ( 1 - tween.value );
|
||||
|
||||
},
|
||||
} );
|
||||
|
||||
} );
|
||||
|
||||
this.durations[ type ] = ( letters.length - 1 ) * 50 + ( show ? 800 : 400 );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Transition };
|
||||
@@ -0,0 +1,103 @@
|
||||
import { Animation } from './Animation.js';
|
||||
import { Easing } from './Easing.js';
|
||||
|
||||
class Tween extends Animation {
|
||||
|
||||
constructor( options ) {
|
||||
|
||||
super( false );
|
||||
|
||||
this.duration = options.duration || 500;
|
||||
this.easing = options.easing || ( t => t );
|
||||
this.onUpdate = options.onUpdate || ( () => {} );
|
||||
this.onComplete = options.onComplete || ( () => {} );
|
||||
|
||||
this.delay = options.delay || false;
|
||||
this.yoyo = options.yoyo ? false : null;
|
||||
|
||||
this.progress = 0;
|
||||
this.value = 0;
|
||||
this.delta = 0;
|
||||
|
||||
this.getFromTo( options );
|
||||
|
||||
if ( this.delay ) setTimeout( () => super.start(), this.delay );
|
||||
else super.start();
|
||||
|
||||
this.onUpdate( this );
|
||||
|
||||
}
|
||||
|
||||
update( delta ) {
|
||||
|
||||
const old = this.value * 1;
|
||||
const direction = ( this.yoyo === true ) ? - 1 : 1;
|
||||
|
||||
this.progress += ( delta / this.duration ) * direction;
|
||||
|
||||
this.value = this.easing( this.progress );
|
||||
this.delta = this.value - old;
|
||||
|
||||
if ( this.values !== null ) this.updateFromTo();
|
||||
|
||||
if ( this.yoyo !== null ) this.updateYoyo();
|
||||
else if ( this.progress <= 1 ) this.onUpdate( this );
|
||||
else {
|
||||
|
||||
this.progress = 1;
|
||||
this.value = 1;
|
||||
this.onUpdate( this );
|
||||
this.onComplete( this );
|
||||
super.stop();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
updateYoyo() {
|
||||
|
||||
if ( this.progress > 1 || this.progress < 0 ) {
|
||||
|
||||
this.value = this.progress = ( this.progress > 1 ) ? 1 : 0;
|
||||
this.yoyo = ! this.yoyo;
|
||||
|
||||
}
|
||||
|
||||
this.onUpdate( this )
|
||||
|
||||
}
|
||||
|
||||
updateFromTo() {
|
||||
|
||||
this.values.forEach( key => {
|
||||
|
||||
this.target[ key ] = this.from[ key ] + ( this.to[ key ] - this.from[ key ] ) * this.value;
|
||||
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
getFromTo( options ) {
|
||||
|
||||
if ( ! options.target || ! options.to ) {
|
||||
|
||||
this.values = null;
|
||||
return;
|
||||
|
||||
}
|
||||
|
||||
this.target = options.target || null;
|
||||
this.from = options.from || {};
|
||||
this.to = options.to || null;
|
||||
this.values = [];
|
||||
|
||||
if ( Object.keys( this.from ).length < 1 )
|
||||
Object.keys( this.to ).forEach( key => { this.from[ key ] = this.target[ key ]; } );
|
||||
|
||||
Object.keys( this.to ).forEach( key => { this.values.push( key ) } );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { Tween, Easing };
|
||||
@@ -0,0 +1,93 @@
|
||||
import { Animation } from './Animation.js';
|
||||
|
||||
class World extends Animation {
|
||||
|
||||
constructor( game ) {
|
||||
|
||||
super( true );
|
||||
|
||||
this.game = game;
|
||||
|
||||
this.container = this.game.dom.game;
|
||||
this.scene = new THREE.Scene();
|
||||
|
||||
this.renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } );
|
||||
this.renderer.setPixelRatio( window.devicePixelRatio );
|
||||
this.container.appendChild( this.renderer.domElement );
|
||||
|
||||
this.camera = new THREE.PerspectiveCamera( 2, 1, 0.1, 10000 );
|
||||
|
||||
this.stage = { width: 2, height: 3 };
|
||||
this.fov = 10;
|
||||
|
||||
this.createLights();
|
||||
|
||||
this.onResize = [];
|
||||
|
||||
this.resize();
|
||||
window.addEventListener( 'resize', () => this.resize(), false );
|
||||
|
||||
}
|
||||
|
||||
update() {
|
||||
|
||||
this.renderer.render( this.scene, this.camera );
|
||||
|
||||
}
|
||||
|
||||
resize() {
|
||||
|
||||
this.width = this.container.offsetWidth;
|
||||
this.height = this.container.offsetHeight;
|
||||
|
||||
this.renderer.setSize( this.width, this.height );
|
||||
|
||||
this.camera.fov = this.fov;
|
||||
this.camera.aspect = this.width / this.height;
|
||||
|
||||
const aspect = this.stage.width / this.stage.height;
|
||||
const fovRad = this.fov * THREE.Math.DEG2RAD;
|
||||
|
||||
let distance = ( aspect < this.camera.aspect )
|
||||
? ( this.stage.height / 2 ) / Math.tan( fovRad / 2 )
|
||||
: ( this.stage.width / this.camera.aspect ) / ( 2 * Math.tan( fovRad / 2 ) );
|
||||
|
||||
distance *= 0.5;
|
||||
|
||||
this.camera.position.set( distance, distance, distance);
|
||||
this.camera.lookAt( this.scene.position );
|
||||
this.camera.updateProjectionMatrix();
|
||||
|
||||
const docFontSize = ( aspect < this.camera.aspect )
|
||||
? ( this.height / 100 ) * aspect
|
||||
: this.width / 100;
|
||||
|
||||
document.documentElement.style.fontSize = docFontSize + 'px';
|
||||
|
||||
if ( this.onResize ) this.onResize.forEach( cb => cb() );
|
||||
|
||||
}
|
||||
|
||||
createLights() {
|
||||
|
||||
this.lights = {
|
||||
holder: new THREE.Object3D,
|
||||
ambient: new THREE.AmbientLight( 0xffffff, 0.69 ),
|
||||
front: new THREE.DirectionalLight( 0xffffff, 0.36 ),
|
||||
back: new THREE.DirectionalLight( 0xffffff, 0.19 ),
|
||||
};
|
||||
|
||||
this.lights.front.position.set( 1.5, 5, 3 );
|
||||
this.lights.back.position.set( -1.5, -5, -3 );
|
||||
|
||||
this.lights.holder.add( this.lights.ambient );
|
||||
this.lights.holder.add( this.lights.front );
|
||||
this.lights.holder.add( this.lights.back );
|
||||
|
||||
this.scene.add( this.lights.holder );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export { World };
|
||||
@@ -0,0 +1,467 @@
|
||||
function RoundedBoxGeometry( size, radius, radiusSegments ) {
|
||||
|
||||
THREE.BufferGeometry.call( this );
|
||||
|
||||
this.type = 'RoundedBoxGeometry';
|
||||
|
||||
radiusSegments = ! isNaN( radiusSegments ) ? Math.max( 1, Math.floor( radiusSegments ) ) : 1;
|
||||
|
||||
var width, height, depth;
|
||||
|
||||
width = height = depth = size;
|
||||
radius = size * radius;
|
||||
|
||||
radius = Math.min( radius, Math.min( width, Math.min( height, Math.min( depth ) ) ) / 2 );
|
||||
|
||||
var edgeHalfWidth = width / 2 - radius;
|
||||
var edgeHalfHeight = height / 2 - radius;
|
||||
var edgeHalfDepth = depth / 2 - radius;
|
||||
|
||||
this.parameters = {
|
||||
width: width,
|
||||
height: height,
|
||||
depth: depth,
|
||||
radius: radius,
|
||||
radiusSegments: radiusSegments
|
||||
};
|
||||
|
||||
var rs1 = radiusSegments + 1;
|
||||
var totalVertexCount = ( rs1 * radiusSegments + 1 ) << 3;
|
||||
|
||||
var positions = new THREE.BufferAttribute( new Float32Array( totalVertexCount * 3 ), 3 );
|
||||
var normals = new THREE.BufferAttribute( new Float32Array( totalVertexCount * 3 ), 3 );
|
||||
|
||||
var
|
||||
cornerVerts = [],
|
||||
cornerNormals = [],
|
||||
normal = new THREE.Vector3(),
|
||||
vertex = new THREE.Vector3(),
|
||||
vertexPool = [],
|
||||
normalPool = [],
|
||||
indices = []
|
||||
;
|
||||
|
||||
var
|
||||
lastVertex = rs1 * radiusSegments,
|
||||
cornerVertNumber = rs1 * radiusSegments + 1
|
||||
;
|
||||
|
||||
doVertices();
|
||||
doFaces();
|
||||
doCorners();
|
||||
doHeightEdges();
|
||||
doWidthEdges();
|
||||
doDepthEdges();
|
||||
|
||||
function doVertices() {
|
||||
|
||||
var cornerLayout = [
|
||||
new THREE.Vector3( 1, 1, 1 ),
|
||||
new THREE.Vector3( 1, 1, - 1 ),
|
||||
new THREE.Vector3( - 1, 1, - 1 ),
|
||||
new THREE.Vector3( - 1, 1, 1 ),
|
||||
new THREE.Vector3( 1, - 1, 1 ),
|
||||
new THREE.Vector3( 1, - 1, - 1 ),
|
||||
new THREE.Vector3( - 1, - 1, - 1 ),
|
||||
new THREE.Vector3( - 1, - 1, 1 )
|
||||
];
|
||||
|
||||
for ( var j = 0; j < 8; j ++ ) {
|
||||
|
||||
cornerVerts.push( [] );
|
||||
cornerNormals.push( [] );
|
||||
|
||||
}
|
||||
|
||||
var PIhalf = Math.PI / 2;
|
||||
var cornerOffset = new THREE.Vector3( edgeHalfWidth, edgeHalfHeight, edgeHalfDepth );
|
||||
|
||||
for ( var y = 0; y <= radiusSegments; y ++ ) {
|
||||
|
||||
var v = y / radiusSegments;
|
||||
var va = v * PIhalf;
|
||||
var cosVa = Math.cos( va );
|
||||
var sinVa = Math.sin( va );
|
||||
|
||||
if ( y == radiusSegments ) {
|
||||
|
||||
vertex.set( 0, 1, 0 );
|
||||
var vert = vertex.clone().multiplyScalar( radius ).add( cornerOffset );
|
||||
cornerVerts[ 0 ].push( vert );
|
||||
vertexPool.push( vert );
|
||||
var norm = vertex.clone();
|
||||
cornerNormals[ 0 ].push( norm );
|
||||
normalPool.push( norm );
|
||||
continue;
|
||||
|
||||
}
|
||||
|
||||
for ( var x = 0; x <= radiusSegments; x ++ ) {
|
||||
|
||||
var u = x / radiusSegments;
|
||||
var ha = u * PIhalf;
|
||||
vertex.x = cosVa * Math.cos( ha );
|
||||
vertex.y = sinVa;
|
||||
vertex.z = cosVa * Math.sin( ha );
|
||||
|
||||
var vert = vertex.clone().multiplyScalar( radius ).add( cornerOffset );
|
||||
cornerVerts[ 0 ].push( vert );
|
||||
vertexPool.push( vert );
|
||||
|
||||
var norm = vertex.clone().normalize();
|
||||
cornerNormals[ 0 ].push( norm );
|
||||
normalPool.push( norm );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for ( var i = 1; i < 8; i ++ ) {
|
||||
|
||||
for ( var j = 0; j < cornerVerts[ 0 ].length; j ++ ) {
|
||||
|
||||
var vert = cornerVerts[ 0 ][ j ].clone().multiply( cornerLayout[ i ] );
|
||||
cornerVerts[ i ].push( vert );
|
||||
vertexPool.push( vert );
|
||||
|
||||
var norm = cornerNormals[ 0 ][ j ].clone().multiply( cornerLayout[ i ] );
|
||||
cornerNormals[ i ].push( norm );
|
||||
normalPool.push( norm );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function doCorners() {
|
||||
|
||||
var indexInd = 0;
|
||||
|
||||
var flips = [
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true
|
||||
];
|
||||
|
||||
var lastRowOffset = rs1 * ( radiusSegments - 1 );
|
||||
|
||||
for ( var i = 0; i < 8; i ++ ) {
|
||||
|
||||
var cornerOffset = cornerVertNumber * i;
|
||||
|
||||
for ( var v = 0; v < radiusSegments - 1; v ++ ) {
|
||||
|
||||
var r1 = v * rs1;
|
||||
var r2 = ( v + 1 ) * rs1;
|
||||
|
||||
for ( var u = 0; u < radiusSegments; u ++ ) {
|
||||
|
||||
var u1 = u + 1;
|
||||
var a = cornerOffset + r1 + u;
|
||||
var b = cornerOffset + r1 + u1;
|
||||
var c = cornerOffset + r2 + u;
|
||||
var d = cornerOffset + r2 + u1;
|
||||
|
||||
if ( ! flips[ i ] ) {
|
||||
|
||||
indices.push( a );
|
||||
indices.push( b );
|
||||
indices.push( c );
|
||||
|
||||
indices.push( b );
|
||||
indices.push( d );
|
||||
indices.push( c );
|
||||
|
||||
} else {
|
||||
|
||||
indices.push( a );
|
||||
indices.push( c );
|
||||
indices.push( b );
|
||||
|
||||
indices.push( b );
|
||||
indices.push( c );
|
||||
indices.push( d );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for ( var u = 0; u < radiusSegments; u ++ ) {
|
||||
|
||||
var a = cornerOffset + lastRowOffset + u;
|
||||
var b = cornerOffset + lastRowOffset + u + 1;
|
||||
var c = cornerOffset + lastVertex;
|
||||
|
||||
if ( ! flips[ i ] ) {
|
||||
|
||||
indices.push( a );
|
||||
indices.push( b );
|
||||
indices.push( c );
|
||||
|
||||
} else {
|
||||
|
||||
indices.push( a );
|
||||
indices.push( c );
|
||||
indices.push( b );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function doFaces() {
|
||||
|
||||
var a = lastVertex;
|
||||
var b = lastVertex + cornerVertNumber;
|
||||
var c = lastVertex + cornerVertNumber * 2;
|
||||
var d = lastVertex + cornerVertNumber * 3;
|
||||
|
||||
indices.push( a );
|
||||
indices.push( b );
|
||||
indices.push( c );
|
||||
indices.push( a );
|
||||
indices.push( c );
|
||||
indices.push( d );
|
||||
|
||||
a = lastVertex + cornerVertNumber * 4;
|
||||
b = lastVertex + cornerVertNumber * 5;
|
||||
c = lastVertex + cornerVertNumber * 6;
|
||||
d = lastVertex + cornerVertNumber * 7;
|
||||
|
||||
indices.push( a );
|
||||
indices.push( c );
|
||||
indices.push( b );
|
||||
indices.push( a );
|
||||
indices.push( d );
|
||||
indices.push( c );
|
||||
|
||||
a = 0;
|
||||
b = cornerVertNumber;
|
||||
c = cornerVertNumber * 4;
|
||||
d = cornerVertNumber * 5;
|
||||
|
||||
indices.push( a );
|
||||
indices.push( c );
|
||||
indices.push( b );
|
||||
indices.push( b );
|
||||
indices.push( c );
|
||||
indices.push( d );
|
||||
|
||||
a = cornerVertNumber * 2;
|
||||
b = cornerVertNumber * 3;
|
||||
c = cornerVertNumber * 6;
|
||||
d = cornerVertNumber * 7;
|
||||
|
||||
indices.push( a );
|
||||
indices.push( c );
|
||||
indices.push( b );
|
||||
indices.push( b );
|
||||
indices.push( c );
|
||||
indices.push( d );
|
||||
|
||||
a = radiusSegments;
|
||||
b = radiusSegments + cornerVertNumber * 3;
|
||||
c = radiusSegments + cornerVertNumber * 4;
|
||||
d = radiusSegments + cornerVertNumber * 7;
|
||||
|
||||
indices.push( a );
|
||||
indices.push( b );
|
||||
indices.push( c );
|
||||
indices.push( b );
|
||||
indices.push( d );
|
||||
indices.push( c );
|
||||
|
||||
a = radiusSegments + cornerVertNumber;
|
||||
b = radiusSegments + cornerVertNumber * 2;
|
||||
c = radiusSegments + cornerVertNumber * 5;
|
||||
d = radiusSegments + cornerVertNumber * 6;
|
||||
|
||||
indices.push( a );
|
||||
indices.push( c );
|
||||
indices.push( b );
|
||||
indices.push( b );
|
||||
indices.push( c );
|
||||
indices.push( d );
|
||||
|
||||
}
|
||||
|
||||
function doHeightEdges() {
|
||||
|
||||
for ( var i = 0; i < 4; i ++ ) {
|
||||
|
||||
var cOffset = i * cornerVertNumber;
|
||||
var cRowOffset = 4 * cornerVertNumber + cOffset;
|
||||
var needsFlip = i & 1 === 1;
|
||||
|
||||
for ( var u = 0; u < radiusSegments; u ++ ) {
|
||||
|
||||
var u1 = u + 1;
|
||||
var a = cOffset + u;
|
||||
var b = cOffset + u1;
|
||||
var c = cRowOffset + u;
|
||||
var d = cRowOffset + u1;
|
||||
|
||||
if ( ! needsFlip ) {
|
||||
|
||||
indices.push( a );
|
||||
indices.push( b );
|
||||
indices.push( c );
|
||||
indices.push( b );
|
||||
indices.push( d );
|
||||
indices.push( c );
|
||||
|
||||
} else {
|
||||
|
||||
indices.push( a );
|
||||
indices.push( c );
|
||||
indices.push( b );
|
||||
indices.push( b );
|
||||
indices.push( c );
|
||||
indices.push( d );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function doDepthEdges() {
|
||||
|
||||
var cStarts = [ 0, 2, 4, 6 ];
|
||||
var cEnds = [ 1, 3, 5, 7 ];
|
||||
|
||||
for ( var i = 0; i < 4; i ++ ) {
|
||||
|
||||
var cStart = cornerVertNumber * cStarts[ i ];
|
||||
var cEnd = cornerVertNumber * cEnds[ i ];
|
||||
|
||||
var needsFlip = 1 >= i;
|
||||
|
||||
for ( var u = 0; u < radiusSegments; u ++ ) {
|
||||
|
||||
var urs1 = u * rs1;
|
||||
var u1rs1 = ( u + 1 ) * rs1;
|
||||
|
||||
var a = cStart + urs1;
|
||||
var b = cStart + u1rs1;
|
||||
var c = cEnd + urs1;
|
||||
var d = cEnd + u1rs1;
|
||||
|
||||
if ( needsFlip ) {
|
||||
|
||||
indices.push( a );
|
||||
indices.push( c );
|
||||
indices.push( b );
|
||||
indices.push( b );
|
||||
indices.push( c );
|
||||
indices.push( d );
|
||||
|
||||
} else {
|
||||
|
||||
indices.push( a );
|
||||
indices.push( b );
|
||||
indices.push( c );
|
||||
indices.push( b );
|
||||
indices.push( d );
|
||||
indices.push( c );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function doWidthEdges() {
|
||||
|
||||
var end = radiusSegments - 1;
|
||||
|
||||
var cStarts = [ 0, 1, 4, 5 ];
|
||||
var cEnds = [ 3, 2, 7, 6 ];
|
||||
var needsFlip = [ 0, 1, 1, 0 ];
|
||||
|
||||
for ( var i = 0; i < 4; i ++ ) {
|
||||
|
||||
var cStart = cStarts[ i ] * cornerVertNumber;
|
||||
var cEnd = cEnds[ i ] * cornerVertNumber;
|
||||
|
||||
for ( var u = 0; u <= end; u ++ ) {
|
||||
|
||||
var a = cStart + radiusSegments + u * rs1;
|
||||
var b = cStart + ( u != end ? radiusSegments + ( u + 1 ) * rs1 : cornerVertNumber - 1 );
|
||||
|
||||
var c = cEnd + radiusSegments + u * rs1;
|
||||
var d = cEnd + ( u != end ? radiusSegments + ( u + 1 ) * rs1 : cornerVertNumber - 1 );
|
||||
|
||||
if ( ! needsFlip[ i ] ) {
|
||||
|
||||
indices.push( a );
|
||||
indices.push( b );
|
||||
indices.push( c );
|
||||
indices.push( b );
|
||||
indices.push( d );
|
||||
indices.push( c );
|
||||
|
||||
} else {
|
||||
|
||||
indices.push( a );
|
||||
indices.push( c );
|
||||
indices.push( b );
|
||||
indices.push( b );
|
||||
indices.push( c );
|
||||
indices.push( d );
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
var index = 0;
|
||||
|
||||
for ( var i = 0; i < vertexPool.length; i ++ ) {
|
||||
|
||||
positions.setXYZ(
|
||||
index,
|
||||
vertexPool[ i ].x,
|
||||
vertexPool[ i ].y,
|
||||
vertexPool[ i ].z
|
||||
);
|
||||
|
||||
normals.setXYZ(
|
||||
index,
|
||||
normalPool[ i ].x,
|
||||
normalPool[ i ].y,
|
||||
normalPool[ i ].z
|
||||
);
|
||||
|
||||
index ++;
|
||||
|
||||
}
|
||||
|
||||
this.setIndex( new THREE.BufferAttribute( new Uint16Array( indices ), 1 ) );
|
||||
this.addAttribute( 'position', positions );
|
||||
this.addAttribute( 'normal', normals );
|
||||
|
||||
}
|
||||
|
||||
RoundedBoxGeometry.prototype = Object.create( THREE.BufferGeometry.prototype );
|
||||
RoundedBoxGeometry.constructor = RoundedBoxGeometry;
|
||||
|
||||
export { RoundedBoxGeometry };
|
||||
@@ -0,0 +1,30 @@
|
||||
function RoundedPlaneGeometry( size, radius, depth ) {
|
||||
|
||||
var x, y, width, height;
|
||||
|
||||
x = y = - size / 2;
|
||||
width = height = size;
|
||||
radius = size * radius;
|
||||
|
||||
const shape = new THREE.Shape();
|
||||
|
||||
shape.moveTo( x, y + radius );
|
||||
shape.lineTo( x, y + height - radius );
|
||||
shape.quadraticCurveTo( x, y + height, x + radius, y + height );
|
||||
shape.lineTo( x + width - radius, y + height );
|
||||
shape.quadraticCurveTo( x + width, y + height, x + width, y + height - radius );
|
||||
shape.lineTo( x + width, y + radius );
|
||||
shape.quadraticCurveTo( x + width, y, x + width - radius, y );
|
||||
shape.lineTo( x + radius, y );
|
||||
shape.quadraticCurveTo( x, y, x, y + radius );
|
||||
|
||||
const geometry = new THREE.ExtrudeBufferGeometry(
|
||||
shape,
|
||||
{ depth: depth, bevelEnabled: false, curveSegments: 3 }
|
||||
);
|
||||
|
||||
return geometry;
|
||||
|
||||
}
|
||||
|
||||
export { RoundedPlaneGeometry };
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"output": {
|
||||
"dir": "../../assets/css/",
|
||||
"structure": "nested"
|
||||
},
|
||||
"compile": {
|
||||
"style": "compressed",
|
||||
"line-numbers": false,
|
||||
"line-comments": false,
|
||||
"load-path": false,
|
||||
"plugin-path": false,
|
||||
"sourcemap": false,
|
||||
"omit-map-comment": false,
|
||||
"precision": false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
.btn
|
||||
-webkit-appearance: none
|
||||
-moz-appearance: none
|
||||
appearance: none
|
||||
background-color: transparent
|
||||
border-radius: 0
|
||||
border-width: 0
|
||||
position: absolute
|
||||
pointer-events: none
|
||||
font-size: 1.2em
|
||||
color: rgba(#000, 0.25)
|
||||
opacity: 0
|
||||
|
||||
&:after
|
||||
position: absolute
|
||||
content: ''
|
||||
width: 3em
|
||||
height: 3em
|
||||
left: 50%
|
||||
top: 50%
|
||||
margin-left: -1.5em
|
||||
margin-top: -1.5em
|
||||
border-radius: 100%
|
||||
|
||||
&--tr
|
||||
top: 0.8em
|
||||
right: 0.8em
|
||||
|
||||
&--tl
|
||||
top: 0.8em
|
||||
left: 0.8em
|
||||
|
||||
&--bl
|
||||
bottom: 0.8em
|
||||
left: 0.8em
|
||||
|
||||
&--br
|
||||
bottom: 0.8em
|
||||
right: 0.8em
|
||||
|
||||
&--bc
|
||||
bottom: 0.8em
|
||||
left: calc(50% - 0.5em)
|
||||
|
||||
svg
|
||||
display: block
|
||||
@@ -0,0 +1,40 @@
|
||||
*
|
||||
&,
|
||||
&:before,
|
||||
&:after
|
||||
-webkit-user-select: none
|
||||
-moz-user-select: none
|
||||
user-select: none
|
||||
box-sizing: border-box
|
||||
cursor: inherit
|
||||
margin: 0
|
||||
padding: 0
|
||||
outline: none
|
||||
font-size: inherit
|
||||
font-family: inherit
|
||||
font-weight: inherit
|
||||
font-style: inherit
|
||||
text-transform: uppercase
|
||||
|
||||
&:focus
|
||||
outline: none
|
||||
|
||||
html
|
||||
-webkit-tap-highlight-color: transparent
|
||||
-webkit-text-size-adjust: 100%
|
||||
-webkit-font-smoothing: antialiased
|
||||
-moz-osx-font-smoothing: grayscale
|
||||
-ms-text-size-adjust: 100%
|
||||
-webkit-text-size-adjust: 100%
|
||||
overflow: hidden
|
||||
height: 100%
|
||||
|
||||
body
|
||||
font-family: 'BungeeFont', sans-serif
|
||||
font-weight: normal
|
||||
font-style: normal
|
||||
line-height: 1
|
||||
cursor: default
|
||||
overflow: hidden
|
||||
height: 100%
|
||||
font-size: 5rem
|
||||
@@ -0,0 +1,6 @@
|
||||
.icon
|
||||
display: inline-block
|
||||
font-size: inherit
|
||||
overflow: visible
|
||||
vertical-align: -0.125em
|
||||
preserveAspectRatio: none
|
||||
@@ -0,0 +1,137 @@
|
||||
.range
|
||||
position: relative
|
||||
width: 14em
|
||||
z-index: 1
|
||||
opacity: 0
|
||||
|
||||
&:not(:last-child)
|
||||
margin-bottom: 2em
|
||||
|
||||
&__label
|
||||
position: relative
|
||||
font-size: 0.9em
|
||||
line-height: 0.75em
|
||||
padding-bottom: 0.5em
|
||||
z-index: 2
|
||||
|
||||
&__track
|
||||
position: relative
|
||||
height: 1em
|
||||
margin-left: 0.5em
|
||||
margin-right: 0.5em
|
||||
z-index: 3
|
||||
|
||||
&-line
|
||||
position: absolute
|
||||
background: rgba(#000, 0.2)
|
||||
height: 2px
|
||||
top: 50%
|
||||
margin-top: -1px
|
||||
left: -0.5em
|
||||
right: -0.5em
|
||||
transform-origin: left center
|
||||
|
||||
&__handle
|
||||
position: absolute
|
||||
width: 0
|
||||
height: 0
|
||||
top: 50%
|
||||
left: 0
|
||||
cursor: pointer
|
||||
z-index: 1
|
||||
|
||||
div
|
||||
transition: background 500ms ease
|
||||
position: absolute
|
||||
left: 0
|
||||
top: 0
|
||||
width: 0.9em
|
||||
height: 0.9em
|
||||
border-radius: 0.2em
|
||||
margin-left: -0.45em
|
||||
margin-top: -0.45em
|
||||
background: #41aac8
|
||||
border-bottom: 2px solid rgba(#000, 0.2)
|
||||
|
||||
.range.is-active &
|
||||
transform: scale(1.25)
|
||||
|
||||
&:after
|
||||
content: ''
|
||||
position: absolute
|
||||
left: 0
|
||||
top: 0
|
||||
width: 3em
|
||||
height: 3em
|
||||
margin-left: -1.5em
|
||||
margin-top: -1.5em
|
||||
|
||||
&__list
|
||||
display: flex
|
||||
flex-flow: row nowrap
|
||||
justify-content: space-between
|
||||
position: relative
|
||||
padding-top: 0.5em
|
||||
font-size: 0.55em
|
||||
color: rgba(#000, 0.5)
|
||||
z-index: 1
|
||||
|
||||
&--type
|
||||
&-color
|
||||
&:not(:last-child)
|
||||
margin-bottom: 1em
|
||||
|
||||
.range
|
||||
&__list
|
||||
display: none
|
||||
|
||||
&__handle
|
||||
> div
|
||||
background: currentColor !important
|
||||
|
||||
&__track
|
||||
&-line
|
||||
background: transparent
|
||||
|
||||
&:after
|
||||
+inset
|
||||
content: ''
|
||||
opacity: 0.5
|
||||
|
||||
&--color
|
||||
&-hue
|
||||
.range
|
||||
&__handle
|
||||
color: red
|
||||
|
||||
&__track
|
||||
color: red
|
||||
|
||||
&-line
|
||||
&:after
|
||||
background: linear-gradient(to right, red, yellow, lime, cyan, blue, magenta, red)
|
||||
|
||||
|
||||
&-saturation
|
||||
.range
|
||||
&__handle
|
||||
color: red
|
||||
|
||||
&__track
|
||||
color: red
|
||||
|
||||
&-line
|
||||
&:after
|
||||
background: linear-gradient(to right, gray, currentColor)
|
||||
|
||||
&-lightness
|
||||
.range
|
||||
&__handle
|
||||
color: red
|
||||
|
||||
&__track
|
||||
color: red
|
||||
|
||||
&-line
|
||||
&:after
|
||||
background: linear-gradient(to right, black, currentColor, white)
|
||||
@@ -0,0 +1,26 @@
|
||||
.stats
|
||||
position: relative
|
||||
width: 14em
|
||||
z-index: 1
|
||||
display: flex
|
||||
justify-content: space-between
|
||||
opacity: 0
|
||||
|
||||
&:not(:last-child)
|
||||
margin-bottom: 1.5em
|
||||
|
||||
> i
|
||||
display: block
|
||||
color: rgba(#000, 0.5)
|
||||
font-size: 0.9em
|
||||
|
||||
> b
|
||||
display: block
|
||||
font-size: 0.9em
|
||||
|
||||
> i
|
||||
font-size: 0.75em
|
||||
|
||||
&[name="worst-time"]
|
||||
display: none
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
.text
|
||||
position: absolute
|
||||
left: 0
|
||||
right: 0
|
||||
text-align: center
|
||||
line-height: 0.75
|
||||
perspective: 100rem
|
||||
opacity: 0
|
||||
|
||||
i
|
||||
display: inline-block
|
||||
opacity: 0
|
||||
white-space: pre-wrap
|
||||
|
||||
&--title
|
||||
bottom: 75%
|
||||
font-size: 4.4em
|
||||
height: 1.2em
|
||||
|
||||
span
|
||||
display: block
|
||||
|
||||
&:first-child
|
||||
font-size: 0.5em
|
||||
margin-bottom: 0.2em
|
||||
|
||||
&--note
|
||||
top: 87%
|
||||
font-size: 1em
|
||||
|
||||
&--timer
|
||||
bottom: 78%
|
||||
font-size: 3.5em
|
||||
line-height: 1
|
||||
|
||||
&--complete,
|
||||
&--best-time
|
||||
font-size: 1.5em
|
||||
top: 83%
|
||||
line-height: 1em
|
||||
@@ -0,0 +1,52 @@
|
||||
.ui
|
||||
pointer-events: none
|
||||
color: #070d15
|
||||
|
||||
&,
|
||||
&__background,
|
||||
&__game,
|
||||
&__texts,
|
||||
&__prefs,
|
||||
&__theme,
|
||||
&__stats,
|
||||
&__buttons
|
||||
+inset
|
||||
overflow: hidden
|
||||
|
||||
&__background
|
||||
z-index: 1
|
||||
transition: background 500ms ease
|
||||
background: #d1d5db
|
||||
|
||||
&:after
|
||||
+inset
|
||||
content: ''
|
||||
background-image: linear-gradient(to bottom, rgba(#fff, 1) 50%, rgba(#fff, 0) 100%)
|
||||
|
||||
&__game
|
||||
pointer-events: all
|
||||
z-index: 2
|
||||
|
||||
canvas
|
||||
display: block
|
||||
width: 100%
|
||||
height: 100%
|
||||
|
||||
&__texts
|
||||
z-index: 3
|
||||
|
||||
&__prefs,
|
||||
&__stats,
|
||||
&__theme
|
||||
display: flex
|
||||
flex-flow: column nowrap
|
||||
justify-content: center
|
||||
align-items: center
|
||||
overflow: hidden
|
||||
z-index: 4
|
||||
|
||||
&__theme
|
||||
padding-top: 15em
|
||||
|
||||
&__buttons
|
||||
z-index: 5
|
||||
@@ -0,0 +1,6 @@
|
||||
=inset($position: absolute)
|
||||
position: $position
|
||||
top: 0
|
||||
left: 0
|
||||
right: 0
|
||||
bottom: 0
|
||||
@@ -0,0 +1,13 @@
|
||||
@import "fonts/bungee"
|
||||
|
||||
@import "variables/easings"
|
||||
|
||||
@import "mixins/inset"
|
||||
|
||||
@import "components/document"
|
||||
@import "components/icon"
|
||||
@import "components/range"
|
||||
@import "components/stats"
|
||||
@import "components/text"
|
||||
@import "components/btn"
|
||||
@import "components/ui"
|
||||
@@ -0,0 +1,31 @@
|
||||
$easeInSine: cubic-bezier(0.47, 0, 0.745, 0.715)
|
||||
$easeOutSine: cubic-bezier(0.39, 0.575, 0.565, 1)
|
||||
$easeInOutSine: cubic-bezier(0.39, 0.575, 0.565, 1)
|
||||
|
||||
$easeInQuad: cubic-bezier(0.55, 0.085, 0.68, 0.53)
|
||||
$easeOutQuad: cubic-bezier(0.25, 0.46, 0.45, 0.94)
|
||||
$easeInOutQuad: cubic-bezier(0.25, 0.46, 0.45, 0.94)
|
||||
|
||||
$easeInCubic: cubic-bezier(0.55, 0.055, 0.675, 0.19)
|
||||
$easeOutCubic: cubic-bezier(0.215, 0.61, 0.355, 1)
|
||||
$easeInOutCubic: cubic-bezier(0.215, 0.61, 0.355, 1)
|
||||
|
||||
$easeInQuart: cubic-bezier(0.895, 0.03, 0.685, 0.22)
|
||||
$easeOutQuart: cubic-bezier(0.165, 0.84, 0.44, 1)
|
||||
$easeInOutQuart: cubic-bezier(0.165, 0.84, 0.44, 1)
|
||||
|
||||
$easeInQuint: cubic-bezier(0.755, 0.05, 0.855, 0.06)
|
||||
$easeOutQuint: cubic-bezier(0.23, 1, 0.32, 1)
|
||||
$easeInOutQuint: cubic-bezier(0.23, 1, 0.32, 1)
|
||||
|
||||
$easeInExpo: cubic-bezier(0.95, 0.05, 0.795, 0.035)
|
||||
$easeOutExpo: cubic-bezier(0.19, 1, 0.22, 1)
|
||||
$easeInOutExpo: cubic-bezier(0.19, 1, 0.22, 1)
|
||||
|
||||
$easeInCirc: cubic-bezier(0.6, 0.04, 0.98, 0.335)
|
||||
$easeOutCirc: cubic-bezier(0.075, 0.82, 0.165, 1)
|
||||
$easeInOutCirc: cubic-bezier(0.075, 0.82, 0.165, 1)
|
||||
|
||||
$easeInBack: cubic-bezier(0.6, -0.28, 0.735, 0.045)
|
||||
$easeOutBack: cubic-bezier(0.175, 0.885, 0.32, 1.275)
|
||||
$easeInOutBack: cubic-bezier(0.68, -0.55, 0.265, 1.55)
|
||||
@@ -0,0 +1,7 @@
|
||||
//! UpUp
|
||||
//! version : 1.0.0
|
||||
//! author : Tal Ater @TalAter
|
||||
//! license : MIT
|
||||
//! https://github.com/TalAter/UpUp
|
||||
(function(e){"use strict";var t=navigator.serviceWorker;if(!t)return this.UpUp=null,e;var n={"service-worker-url":"upup.sw.min.js"},r=!1,s="font-weight: bold; color: #00f;";this.UpUp={start:function(e){this.addSettings(e),t.register(n["service-worker-url"],{scope:"./"}).then(function(e){r&&console.log("Service worker registration successful with scope: %c"+e.scope,s);(e.installing||t.controller||e.active).postMessage({action:"set-settings",settings:n})}).catch(function(e){r&&console.log("Service worker registration failed: %c"+e,s)})},addSettings:function(t){"string"==typeof(t=t||{})&&(t={content:t}),["content","content-url","assets","service-worker-url","cache-version"].forEach(function(r){t[r]!==e&&(n[r]=t[r])})},debug:function(e){r=!(arguments.length>0)||!!e}}}).call(this);
|
||||
//# sourceMappingURL=upup.min.js.map
|
||||
@@ -0,0 +1,7 @@
|
||||
//! UpUp Service Worker
|
||||
//! version : 1.0.0
|
||||
//! author : Tal Ater @TalAter
|
||||
//! license : MIT
|
||||
//! https://github.com/TalAter/UpUp
|
||||
var _CACHE_NAME_PREFIX="upup-cache",_calculateHash=function(e){var t,n=0,s=(e=e.toString()).length;if(0===s)return n;for(t=0;t<s;t++)n=(n<<5)-n+e.charCodeAt(t),n|=0;return n};self.addEventListener("message",function(e){"set-settings"===e.data.action&&_parseSettingsAndCache(e.data.settings)}),self.addEventListener("fetch",function(e){e.respondWith(fetch(e.request).catch(function(){return caches.match(e.request).then(function(t){return t||("navigate"===e.request.mode||"GET"===e.request.method&&e.request.headers.get("accept").includes("text/html")?caches.match("sw-offline-content"):void 0)})}))});var _parseSettingsAndCache=function(e){var t=_CACHE_NAME_PREFIX+"-"+(e["cache-version"]?e["cache-version"]+"-":"")+_calculateHash(e.content+e["content-url"]+e.assets);return caches.open(t).then(function(t){return e.assets&&t.addAll(e.assets.map(function(e){return new Request(e,{mode:"no-cors"})})),e["content-url"]?fetch(e["content-url"],{mode:"no-cors"}).then(function(e){return t.put("sw-offline-content",e)}):e.content?t.put("sw-offline-content",_buildResponse(e.content)):t.put("sw-offline-content",_buildResponse("You are offline"))}).then(function(){return caches.keys().then(function(e){return Promise.all(e.map(function(e){if(e.startsWith(_CACHE_NAME_PREFIX)&&t!==e)return caches.delete(e)}))})})},_buildResponse=function(e){return new Response(e,{headers:{"Content-Type":"text/html"}})};
|
||||
//# sourceMappingURL=upup.sw.min.js.map
|
||||