├── .gitignore ├── LICENSE ├── README.md ├── bower.json ├── dist ├── illusion-number.css ├── illusion-number.js ├── illusion-number.min.css └── illusion-number.min.js ├── example ├── datetime.html └── index.html ├── gulpfile.js ├── package.json └── src ├── illusion-number.js └── illusion-number.scss /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | temp/ 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Malash 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Illusion Number 2 | 3 | ![Illusion Number Gif](https://gist.githubusercontent.com/malash/9ff5b2897f05d0fae6dd/raw/1b069c33becefa4ca5c19efc955bbc1bc67f441a/illusion-number.gif) 4 | 5 | ## Overview 6 | 7 | Implementing an illusion number transform as showing in GIF written in Javascript and CSS. 8 | I find the GIF image from SNS website and don't know its source. Please leave me a issuse if you know it and I will be appreciate for you :) 9 | 10 | ## Demo 11 | 12 | Timer: [https://rawgit.com/malash/illusion-number/master/example/index.html](https://rawgit.com/malash/illusion-number/master/example/index.html) 13 | 14 | Datetime: [https://rawgit.com/malash/illusion-number/master/example/datetime.html](https://rawgit.com/malash/illusion-number/master/example/datetime.html) 15 | 16 | You can find more demos in the `example` folder. 17 | 18 | ## Usage 19 | 20 | Use `bower` to install `illusion-number` to your project: 21 | 22 | ```bash 23 | $ bower install illusion-number 24 | ``` 25 | 26 | Then include `.js` and `.css` files: 27 | 28 | ```html 29 | 30 | 31 | ``` 32 | 33 | Put an empty `div` emelent to your HTML file: 34 | 35 | ```html 36 |
37 | ``` 38 | 39 | Call global variable `IllusionNumber`'s method `play` with the element and options: 40 | 41 | ```javascript 42 | IllusionNumber.play(document.getElementById('test'), { 43 | from: '0', 44 | to: '1' 45 | }); 46 | ``` 47 | 48 | ## Documentation 49 | 50 | ### IllusionNumber.play(ele, options) 51 | 52 | #### ele 53 | 54 | Type: `element` 55 | 56 | Required: `true` 57 | 58 | The DOM element to bind 59 | 60 | #### options.from 61 | 62 | Type: `number` or `char` 63 | 64 | Required: `true` 65 | 66 | #### options.to 67 | 68 | Type: `number` or `char` 69 | 70 | Required: `true` 71 | 72 | The animation transforms from `options.from` bitmap to `options.to` bitmap. 73 | 74 | #### options.size 75 | 76 | Type: `number` or `string` 77 | 78 | Default: `250px` 79 | 80 | The animation canvas size. Some valid value: `250`, `'14px'`, `'3em'`, `'2rem'` 81 | 82 | #### options.animationDuration 83 | 84 | Type: `number` 85 | 86 | Default: `1` 87 | 88 | The animation duration defaults to 1 second. The type of value could be `number` or `float`. 89 | 90 | #### options.reverse 91 | 92 | Type: `boolean` 93 | 94 | Default: `false` 95 | 96 | If `options.reverse` is `true`, the anamation direction will be reversed. 97 | 98 | ### IllusionNumber.setBitmap(char, bitmap) 99 | 100 | Set or override a bitmap. 101 | 102 | For example: 103 | 104 | ```javascript 105 | IllusionNumber.setBitmap('A', [ 106 | [0, 1, 0], 107 | [1, 0, 0], 108 | [1, 1, 1], 109 | [1, 0, 1], 110 | [1, 0, 1] 111 | ]); 112 | IllusionNumber.play(document.getElementById('test'), { 113 | from: '9', 114 | to: 'A' 115 | }); 116 | 117 | ``` 118 | 119 | #### char 120 | 121 | Type: `char` 122 | 123 | Required: `true` 124 | 125 | Character to set. 126 | 127 | #### bitmap 128 | 129 | Type: `number[][]` 130 | 131 | Required: `true` 132 | 133 | Character's bitmap. The tile displays for value `1` and hides for value `0` at the same position in 2-dimensional array. 134 | 135 | ## Development 136 | 137 | We use `gulp` as build system. 138 | 139 | For development: 140 | 141 | ```bash 142 | npm install 143 | gulp dev 144 | ``` 145 | 146 | For build: 147 | 148 | ```bash 149 | npm install 150 | gulp build 151 | ``` 152 | 153 | ## License 154 | 155 | The MIT License (MIT) 156 | 157 | Copyright (c) 2015 [Malash](https://malash.me/) <> 158 | 159 | Permission is hereby granted, free of charge, to any person obtaining a copy 160 | of this software and associated documentation files (the "Software"), to deal 161 | in the Software without restriction, including without limitation the rights 162 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 163 | copies of the Software, and to permit persons to whom the Software is 164 | furnished to do so, subject to the following conditions: 165 | 166 | The above copyright notice and this permission notice shall be included in 167 | all copies or substantial portions of the Software. 168 | 169 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 170 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 171 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 172 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 173 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 174 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 175 | THE SOFTWARE. 176 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "illusion-number", 3 | "version": "0.2.1", 4 | "homepage": "https://github.com/malash/illusion-number", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/malash/illusion-number.git" 8 | }, 9 | "description": "Illusion Number", 10 | "main": [ 11 | "dist/illusion-number.min.css", 12 | "dist/illusion-number.min.js" 13 | ], 14 | "moduleType": [ 15 | "globals" 16 | ], 17 | "keywords": [ 18 | "illusion", 19 | "number" 20 | ], 21 | "authors": [ 22 | { 23 | "name": "Malash", 24 | "email": "i@malash.me" 25 | } 26 | ], 27 | "license": "MIT", 28 | "ignore": [ 29 | "**/.*", 30 | "node_modules", 31 | "bower_components", 32 | "test", 33 | "tests" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /dist/illusion-number.css: -------------------------------------------------------------------------------- 1 | .illusion-number { 2 | position: relative; 3 | /* 2^(-1/2) = 0.70710678118655 */ 4 | padding: 0 0.70711em; 5 | width: 3em; 6 | height: 5em; 7 | overflow: hidden; 8 | background-color: #000000; 9 | /* DEBUG */ 10 | /* perspective: 500px; */ } 11 | .illusion-number .illusion-number-tiles { 12 | position: absolute; 13 | width: 3em; 14 | height: 5em; 15 | -webkit-transform-style: preserve-3d; 16 | -moz-transform-style: preserve-3d; 17 | transform-style: preserve-3d; } 18 | .illusion-number .illusion-number-tiles div { 19 | position: absolute; 20 | -webkit-backface-visibility: hidden; 21 | -moz-backface-visibility: hidden; 22 | backface-visibility: hidden; 23 | /* fix gap between tiles*/ 24 | width: 1.05em; 25 | height: 1.05em; 26 | margin: 0; 27 | padding: 0; 28 | -webkit-animation-duration: 1s; 29 | -moz-animation-duration: 1s; 30 | animation-duration: 1s; 31 | -webkit-animation-iteration-count: 1; 32 | -moz-animation-iteration-count: 1; 33 | animation-iteration-count: 1; 34 | -webkit-animation-fill-mode: forwards; 35 | -moz-animation-fill-mode: forwards; 36 | animation-fill-mode: forwards; 37 | -webkit-animation-timing-function: ease; 38 | -moz-animation-timing-function: ease; 39 | animation-timing-function: ease; } 40 | .illusion-number .illusion-number-tiles .illusion-number-tile-x-0 { 41 | top: 0em; } 42 | .illusion-number .illusion-number-tiles .illusion-number-tile-x-1 { 43 | top: 1em; } 44 | .illusion-number .illusion-number-tiles .illusion-number-tile-x-2 { 45 | top: 2em; } 46 | .illusion-number .illusion-number-tiles .illusion-number-tile-x-3 { 47 | top: 3em; } 48 | .illusion-number .illusion-number-tiles .illusion-number-tile-x-4 { 49 | top: 4em; } 50 | .illusion-number .illusion-number-tiles .illusion-number-tile-y-0 { 51 | left: 0em; 52 | -webkit-transform-origin: 1.5em 0 0; 53 | -moz-transform-origin: 1.5em 0 0; 54 | transform-origin: 1.5em 0 0; } 55 | .illusion-number .illusion-number-tiles .illusion-number-tile-y-1 { 56 | left: 1em; 57 | -webkit-transform-origin: 0.5em 0 0; 58 | -moz-transform-origin: 0.5em 0 0; 59 | transform-origin: 0.5em 0 0; } 60 | .illusion-number .illusion-number-tiles .illusion-number-tile-y-2 { 61 | left: 2em; 62 | -webkit-transform-origin: -0.5em 0 0; 63 | -moz-transform-origin: -0.5em 0 0; 64 | transform-origin: -0.5em 0 0; } 65 | .illusion-number .illusion-number-tiles .illusion-number-animation-out-1 { 66 | -webkit-animation-name: illusion-number-animation-out-1; 67 | -moz-animation-name: illusion-number-animation-out-1; 68 | animation-name: illusion-number-animation-out-1; } 69 | 70 | @-webkit-keyframes illusion-number-animation-out-1 { 71 | 0% { 72 | -webkit-transform: rotateY(0) translateZ(1.5em); 73 | transform: rotateY(0) translateZ(1.5em); 74 | background-color: #dcdcdc; } 75 | 100% { 76 | -webkit-transform: rotateY(-90deg) translateZ(1.5em); 77 | transform: rotateY(-90deg) translateZ(1.5em); 78 | background-color: #707070; } } 79 | 80 | @-moz-keyframes illusion-number-animation-out-1 { 81 | 0% { 82 | -moz-transform: rotateY(0) translateZ(1.5em); 83 | transform: rotateY(0) translateZ(1.5em); 84 | background-color: #dcdcdc; } 85 | 100% { 86 | -moz-transform: rotateY(-90deg) translateZ(1.5em); 87 | transform: rotateY(-90deg) translateZ(1.5em); 88 | background-color: #707070; } } 89 | 90 | @keyframes illusion-number-animation-out-1 { 91 | 0% { 92 | -webkit-transform: rotateY(0) translateZ(1.5em); 93 | -moz-transform: rotateY(0) translateZ(1.5em); 94 | transform: rotateY(0) translateZ(1.5em); 95 | background-color: #dcdcdc; } 96 | 100% { 97 | -webkit-transform: rotateY(-90deg) translateZ(1.5em); 98 | -moz-transform: rotateY(-90deg) translateZ(1.5em); 99 | transform: rotateY(-90deg) translateZ(1.5em); 100 | background-color: #707070; } } 101 | .illusion-number .illusion-number-tiles .illusion-number-animation-in-1 { 102 | -webkit-animation-name: illusion-number-animation-in-1; 103 | -moz-animation-name: illusion-number-animation-in-1; 104 | animation-name: illusion-number-animation-in-1; } 105 | 106 | @-webkit-keyframes illusion-number-animation-in-1 { 107 | 0% { 108 | -webkit-transform: rotateY(90deg) translateZ(1.5em); 109 | transform: rotateY(90deg) translateZ(1.5em); 110 | background-color: #dcdcdc; } 111 | 100% { 112 | -webkit-transform: rotateY(0) translateZ(1.5em); 113 | transform: rotateY(0) translateZ(1.5em); 114 | background-color: #dcdcdc; } } 115 | 116 | @-moz-keyframes illusion-number-animation-in-1 { 117 | 0% { 118 | -moz-transform: rotateY(90deg) translateZ(1.5em); 119 | transform: rotateY(90deg) translateZ(1.5em); 120 | background-color: #dcdcdc; } 121 | 100% { 122 | -moz-transform: rotateY(0) translateZ(1.5em); 123 | transform: rotateY(0) translateZ(1.5em); 124 | background-color: #dcdcdc; } } 125 | 126 | @keyframes illusion-number-animation-in-1 { 127 | 0% { 128 | -webkit-transform: rotateY(90deg) translateZ(1.5em); 129 | -moz-transform: rotateY(90deg) translateZ(1.5em); 130 | transform: rotateY(90deg) translateZ(1.5em); 131 | background-color: #dcdcdc; } 132 | 100% { 133 | -webkit-transform: rotateY(0) translateZ(1.5em); 134 | -moz-transform: rotateY(0) translateZ(1.5em); 135 | transform: rotateY(0) translateZ(1.5em); 136 | background-color: #dcdcdc; } } 137 | .illusion-number .illusion-number-tiles .illusion-number-animation-out-2 { 138 | -webkit-animation-name: illusion-number-animation-out-2; 139 | -moz-animation-name: illusion-number-animation-out-2; 140 | animation-name: illusion-number-animation-out-2; } 141 | 142 | @-webkit-keyframes illusion-number-animation-out-2 { 143 | 0% { 144 | -webkit-transform: rotateY(0) translateZ(0.5em); 145 | transform: rotateY(0) translateZ(0.5em); 146 | background-color: #dcdcdc; } 147 | 100% { 148 | -webkit-transform: rotateY(-90deg) translateZ(0.5em); 149 | transform: rotateY(-90deg) translateZ(0.5em); 150 | background-color: #707070; } } 151 | 152 | @-moz-keyframes illusion-number-animation-out-2 { 153 | 0% { 154 | -moz-transform: rotateY(0) translateZ(0.5em); 155 | transform: rotateY(0) translateZ(0.5em); 156 | background-color: #dcdcdc; } 157 | 100% { 158 | -moz-transform: rotateY(-90deg) translateZ(0.5em); 159 | transform: rotateY(-90deg) translateZ(0.5em); 160 | background-color: #707070; } } 161 | 162 | @keyframes illusion-number-animation-out-2 { 163 | 0% { 164 | -webkit-transform: rotateY(0) translateZ(0.5em); 165 | -moz-transform: rotateY(0) translateZ(0.5em); 166 | transform: rotateY(0) translateZ(0.5em); 167 | background-color: #dcdcdc; } 168 | 100% { 169 | -webkit-transform: rotateY(-90deg) translateZ(0.5em); 170 | -moz-transform: rotateY(-90deg) translateZ(0.5em); 171 | transform: rotateY(-90deg) translateZ(0.5em); 172 | background-color: #707070; } } 173 | .illusion-number .illusion-number-tiles .illusion-number-animation-in-2 { 174 | -webkit-animation-name: illusion-number-animation-in-2; 175 | -moz-animation-name: illusion-number-animation-in-2; 176 | animation-name: illusion-number-animation-in-2; } 177 | 178 | @-webkit-keyframes illusion-number-animation-in-2 { 179 | 0% { 180 | -webkit-transform: rotateY(90deg) translateZ(0.5em); 181 | transform: rotateY(90deg) translateZ(0.5em); 182 | background-color: #dcdcdc; } 183 | 100% { 184 | -webkit-transform: rotateY(0) translateZ(0.5em); 185 | transform: rotateY(0) translateZ(0.5em); 186 | background-color: #dcdcdc; } } 187 | 188 | @-moz-keyframes illusion-number-animation-in-2 { 189 | 0% { 190 | -moz-transform: rotateY(90deg) translateZ(0.5em); 191 | transform: rotateY(90deg) translateZ(0.5em); 192 | background-color: #dcdcdc; } 193 | 100% { 194 | -moz-transform: rotateY(0) translateZ(0.5em); 195 | transform: rotateY(0) translateZ(0.5em); 196 | background-color: #dcdcdc; } } 197 | 198 | @keyframes illusion-number-animation-in-2 { 199 | 0% { 200 | -webkit-transform: rotateY(90deg) translateZ(0.5em); 201 | -moz-transform: rotateY(90deg) translateZ(0.5em); 202 | transform: rotateY(90deg) translateZ(0.5em); 203 | background-color: #dcdcdc; } 204 | 100% { 205 | -webkit-transform: rotateY(0) translateZ(0.5em); 206 | -moz-transform: rotateY(0) translateZ(0.5em); 207 | transform: rotateY(0) translateZ(0.5em); 208 | background-color: #dcdcdc; } } 209 | .illusion-number .illusion-number-tiles .illusion-number-animation-out-3 { 210 | -webkit-animation-name: illusion-number-animation-out-3; 211 | -moz-animation-name: illusion-number-animation-out-3; 212 | animation-name: illusion-number-animation-out-3; } 213 | 214 | @-webkit-keyframes illusion-number-animation-out-3 { 215 | 0% { 216 | -webkit-transform: rotateY(0) translateZ(-0.5em); 217 | transform: rotateY(0) translateZ(-0.5em); 218 | background-color: #dcdcdc; } 219 | 100% { 220 | -webkit-transform: rotateY(-90deg) translateZ(-0.5em); 221 | transform: rotateY(-90deg) translateZ(-0.5em); 222 | background-color: #707070; } } 223 | 224 | @-moz-keyframes illusion-number-animation-out-3 { 225 | 0% { 226 | -moz-transform: rotateY(0) translateZ(-0.5em); 227 | transform: rotateY(0) translateZ(-0.5em); 228 | background-color: #dcdcdc; } 229 | 100% { 230 | -moz-transform: rotateY(-90deg) translateZ(-0.5em); 231 | transform: rotateY(-90deg) translateZ(-0.5em); 232 | background-color: #707070; } } 233 | 234 | @keyframes illusion-number-animation-out-3 { 235 | 0% { 236 | -webkit-transform: rotateY(0) translateZ(-0.5em); 237 | -moz-transform: rotateY(0) translateZ(-0.5em); 238 | transform: rotateY(0) translateZ(-0.5em); 239 | background-color: #dcdcdc; } 240 | 100% { 241 | -webkit-transform: rotateY(-90deg) translateZ(-0.5em); 242 | -moz-transform: rotateY(-90deg) translateZ(-0.5em); 243 | transform: rotateY(-90deg) translateZ(-0.5em); 244 | background-color: #707070; } } 245 | .illusion-number .illusion-number-tiles .illusion-number-animation-in-3 { 246 | -webkit-animation-name: illusion-number-animation-in-3; 247 | -moz-animation-name: illusion-number-animation-in-3; 248 | animation-name: illusion-number-animation-in-3; } 249 | 250 | @-webkit-keyframes illusion-number-animation-in-3 { 251 | 0% { 252 | -webkit-transform: rotateY(90deg) translateZ(-0.5em); 253 | transform: rotateY(90deg) translateZ(-0.5em); 254 | background-color: #dcdcdc; } 255 | 100% { 256 | -webkit-transform: rotateY(0) translateZ(-0.5em); 257 | transform: rotateY(0) translateZ(-0.5em); 258 | background-color: #dcdcdc; } } 259 | 260 | @-moz-keyframes illusion-number-animation-in-3 { 261 | 0% { 262 | -moz-transform: rotateY(90deg) translateZ(-0.5em); 263 | transform: rotateY(90deg) translateZ(-0.5em); 264 | background-color: #dcdcdc; } 265 | 100% { 266 | -moz-transform: rotateY(0) translateZ(-0.5em); 267 | transform: rotateY(0) translateZ(-0.5em); 268 | background-color: #dcdcdc; } } 269 | 270 | @keyframes illusion-number-animation-in-3 { 271 | 0% { 272 | -webkit-transform: rotateY(90deg) translateZ(-0.5em); 273 | -moz-transform: rotateY(90deg) translateZ(-0.5em); 274 | transform: rotateY(90deg) translateZ(-0.5em); 275 | background-color: #dcdcdc; } 276 | 100% { 277 | -webkit-transform: rotateY(0) translateZ(-0.5em); 278 | -moz-transform: rotateY(0) translateZ(-0.5em); 279 | transform: rotateY(0) translateZ(-0.5em); 280 | background-color: #dcdcdc; } } 281 | .illusion-number .illusion-number-tiles-reverse div { 282 | -webkit-animation-direction: reverse; 283 | -moz-animation-direction: reverse; 284 | animation-direction: reverse; } 285 | -------------------------------------------------------------------------------- /dist/illusion-number.js: -------------------------------------------------------------------------------- 1 | !(function(document, window) { 2 | 3 | var bitmaps = { 4 | '0': [ 5 | [1, 1, 1], 6 | [1, 0, 1], 7 | [1, 0, 1], 8 | [1, 0, 1], 9 | [1, 1, 1] 10 | ], 11 | '1': [ 12 | [1, 1, 0], 13 | [0, 1, 0], 14 | [0, 1, 0], 15 | [0, 1, 0], 16 | [1, 1, 1] 17 | ], 18 | '2': [ 19 | [1, 1, 1], 20 | [0, 0, 1], 21 | [1, 1, 1], 22 | [1, 0, 0], 23 | [1, 1, 1] 24 | ], 25 | '3': [ 26 | [1, 1, 1], 27 | [0, 0, 1], 28 | [0, 1, 1], 29 | [0, 0, 1], 30 | [1, 1, 1] 31 | ], 32 | '4': [ 33 | [1, 0, 1], 34 | [1, 0, 1], 35 | [1, 1, 1], 36 | [0, 0, 1], 37 | [0, 0, 1] 38 | ], 39 | '5': [ 40 | [1, 1, 1], 41 | [1, 0, 0], 42 | [1, 1, 1], 43 | [0, 0, 1], 44 | [1, 1, 1] 45 | ], 46 | '6': [ 47 | [1, 1, 1], 48 | [1, 0, 0], 49 | [1, 1, 1], 50 | [1, 0, 1], 51 | [1, 1, 1] 52 | ], 53 | '7': [ 54 | [1, 1, 1], 55 | [0, 0, 1], 56 | [0, 0, 1], 57 | [0, 0, 1], 58 | [0, 0, 1] 59 | ], 60 | '8': [ 61 | [1, 1, 1], 62 | [1, 0, 1], 63 | [1, 1, 1], 64 | [1, 0, 1], 65 | [1, 1, 1] 66 | ], 67 | '9': [ 68 | [1, 1, 1], 69 | [1, 0, 1], 70 | [1, 1, 1], 71 | [0, 0, 1], 72 | [1, 1, 1] 73 | ], 74 | }; 75 | 76 | function getBitmap(number, x, y) { 77 | if (bitmaps[number] === undefined) { 78 | return 0; 79 | } 80 | if (bitmaps[number][x] === undefined) { 81 | return 0; 82 | } 83 | if (bitmaps[number][x][y] === undefined) { 84 | return 0; 85 | } 86 | return bitmaps[number][x][y]; 87 | } 88 | 89 | function getTilePositionsMap(from ,to) { 90 | var tilePositionsMap = { 91 | 'in': [], 92 | 'out': [] 93 | }; 94 | var x, y, yComparison; 95 | for (x = 0; x < 5; x++) { 96 | tilePositionsMap['in'][x] = []; 97 | tilePositionsMap['out'][x] = []; 98 | for (y = 0; y < 3; y++) { 99 | tilePositionsMap['out'][x][y] = []; 100 | if (getBitmap(from, x, y)) { 101 | for (yComparison = 0; yComparison < 3; yComparison++) { 102 | if (!getBitmap(to, x, yComparison - 1) && getBitmap(to, x, yComparison)) { 103 | tilePositionsMap['out'][x][y].push(yComparison + 1); 104 | } 105 | } 106 | } 107 | 108 | tilePositionsMap['in'][x][y] = []; 109 | if (getBitmap(to, x, y)) { 110 | for (yComparison = 2; yComparison >=0; yComparison--) { 111 | if (!getBitmap(from, x, yComparison + 1) && getBitmap(from, x, yComparison)) { 112 | tilePositionsMap['in'][x][y].push(3 - yComparison); 113 | } 114 | } 115 | } 116 | } 117 | } 118 | return tilePositionsMap; 119 | } 120 | 121 | function error(err) { 122 | if (console && console.error) { 123 | console.error(err); 124 | } 125 | return err; 126 | } 127 | 128 | window.IllusionNumber = { 129 | /** 130 | * Play illusion animation on element provided 131 | * @method play 132 | * @for IllusionNumber 133 | * @param {element} ele - the DOM element to bind 134 | * @param {object} options - play options 135 | * @param {number | char} options.from - animation will transform from bitmap of options.from 136 | * @param {number | char} options.to - animation will transform to bitmap of options.to 137 | * @param {number | string} [options.size = 250px] - the canvas size (14px, 3em, 2rem, etc.) 138 | * @param {number} [options.animationDuration = 1] - the animation duration (sec, could be float), default to 1 139 | */ 140 | play: function(ele, options) { 141 | options = options || {}; 142 | if (!ele) { 143 | return error(new Error('IllusionNumber.play: ' + ele + ' is not an invalid element')); 144 | } 145 | var from = options.from; 146 | if (typeof from === 'undefined' || typeof bitmaps[from] === 'undefined') { 147 | return error(new Error('IllusionNumber.play: ' + from + ' is not an invalid \'from\' char')); 148 | } 149 | var to = options.to; 150 | if (typeof to === 'undefined') { 151 | to = (parseInt(from) + 1) % 10; 152 | } 153 | if (typeof bitmaps[to] === 'undefined') { 154 | return error(new Error('IllusionNumber.play: ' + to + ' is not an invalid \'to\' char')); 155 | } 156 | var size = options.size || '250px'; 157 | if (parseFloat(size) == size) { 158 | size = size + 'px'; 159 | } 160 | var sizeResult; 161 | if (/^((\d+)(\.\d+)?)([^\d]+)$/.test(size)) { 162 | sizeResult = size.match(/^((\d+)(\.\d+)?)([^\d]+)$/); 163 | size = (parseFloat(sizeResult[1]) / 6) + sizeResult[4]; 164 | } 165 | tilePositionsMap = getTilePositionsMap(from, to); 166 | var eleRoot = document.createElement('div'); 167 | eleRoot.classList.add('illusion-number'); 168 | eleRoot.style.fontSize = size; 169 | var eleTiles = document.createElement('div'); 170 | eleTiles.classList.add('illusion-number-tiles'); 171 | if (options.reverse) { 172 | eleTiles.classList.add('illusion-number-tiles-reverse'); 173 | } 174 | for (var indexSurface in ['in', 'out']) { 175 | var surface = ['in', 'out'][indexSurface]; 176 | for (var x = 0; x < 5; x++) { 177 | for (var y = 0; y < 3; y++) { 178 | var tilePositions = tilePositionsMap[surface][x][y]; 179 | for (var indexTilePosition in tilePositions) { 180 | var tilePosition = tilePositions[indexTilePosition]; 181 | var eleTile = document.createElement('div'); 182 | eleTile.classList.add('illusion-number-tile-x-' + x); 183 | eleTile.classList.add('illusion-number-tile-y-' + y); 184 | eleTile.classList.add('illusion-number-animation-' + surface + '-' + tilePosition); 185 | if (options.animationDuration && !isNaN(parseFloat(options.animationDuration))) { 186 | eleTile.style.animationDuration = parseFloat(options.animationDuration) + 's'; 187 | } 188 | eleTiles.appendChild(eleTile); 189 | } 190 | } 191 | } 192 | } 193 | eleRoot.appendChild(eleTiles); 194 | ele.innerHTML = ''; 195 | ele.appendChild(eleRoot); 196 | }, 197 | /** 198 | * Set or override a custom bitmap 199 | * @method setBitmap 200 | * @for IllusionNumber 201 | * @param {char} char - character to set 202 | * @param {number[][]} bitmap - character bitmap, for example: 203 | [ 204 | [1, 1, 1], 205 | [1, 0, 1], 206 | [1, 0, 1], 207 | [1, 0, 1], 208 | [1, 1, 1] 209 | ] 210 | */ 211 | setBitmap: function(char, bitmap) { 212 | bitmaps[char] = bitmap; 213 | } 214 | }; 215 | }(document, window)); -------------------------------------------------------------------------------- /dist/illusion-number.min.css: -------------------------------------------------------------------------------- 1 | .illusion-number{position:relative;padding:0 .70711em;width:3em;height:5em;overflow:hidden;background-color:#000}.illusion-number .illusion-number-tiles{position:absolute;width:3em;height:5em;-webkit-transform-style:preserve-3d;-moz-transform-style:preserve-3d;transform-style:preserve-3d}.illusion-number .illusion-number-tiles div{position:absolute;-webkit-backface-visibility:hidden;-moz-backface-visibility:hidden;backface-visibility:hidden;width:1.05em;height:1.05em;margin:0;padding:0;-webkit-animation-duration:1s;-moz-animation-duration:1s;animation-duration:1s;-webkit-animation-iteration-count:1;-moz-animation-iteration-count:1;animation-iteration-count:1;-webkit-animation-fill-mode:forwards;-moz-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-timing-function:ease;-moz-animation-timing-function:ease;animation-timing-function:ease}.illusion-number .illusion-number-tiles .illusion-number-tile-x-0{top:0}.illusion-number .illusion-number-tiles .illusion-number-tile-x-1{top:1em}.illusion-number .illusion-number-tiles .illusion-number-tile-x-2{top:2em}.illusion-number .illusion-number-tiles .illusion-number-tile-x-3{top:3em}.illusion-number .illusion-number-tiles .illusion-number-tile-x-4{top:4em}.illusion-number .illusion-number-tiles .illusion-number-tile-y-0{left:0;-webkit-transform-origin:1.5em 0 0;-moz-transform-origin:1.5em 0 0;transform-origin:1.5em 0 0}.illusion-number .illusion-number-tiles .illusion-number-tile-y-1{left:1em;-webkit-transform-origin:.5em 0 0;-moz-transform-origin:.5em 0 0;transform-origin:.5em 0 0}.illusion-number .illusion-number-tiles .illusion-number-tile-y-2{left:2em;-webkit-transform-origin:-.5em 0 0;-moz-transform-origin:-.5em 0 0;transform-origin:-.5em 0 0}.illusion-number .illusion-number-tiles .illusion-number-animation-out-1{-webkit-animation-name:illusion-number-animation-out-1;-moz-animation-name:illusion-number-animation-out-1;animation-name:illusion-number-animation-out-1}@-webkit-keyframes illusion-number-animation-out-1{0%{-webkit-transform:rotateY(0) translateZ(1.5em);transform:rotateY(0) translateZ(1.5em);background-color:#dcdcdc}100%{-webkit-transform:rotateY(-90deg) translateZ(1.5em);transform:rotateY(-90deg) translateZ(1.5em);background-color:#707070}}@-moz-keyframes illusion-number-animation-out-1{0%{-moz-transform:rotateY(0) translateZ(1.5em);transform:rotateY(0) translateZ(1.5em);background-color:#dcdcdc}100%{-moz-transform:rotateY(-90deg) translateZ(1.5em);transform:rotateY(-90deg) translateZ(1.5em);background-color:#707070}}@keyframes illusion-number-animation-out-1{0%{-webkit-transform:rotateY(0) translateZ(1.5em);-moz-transform:rotateY(0) translateZ(1.5em);transform:rotateY(0) translateZ(1.5em);background-color:#dcdcdc}100%{-webkit-transform:rotateY(-90deg) translateZ(1.5em);-moz-transform:rotateY(-90deg) translateZ(1.5em);transform:rotateY(-90deg) translateZ(1.5em);background-color:#707070}}.illusion-number .illusion-number-tiles .illusion-number-animation-in-1{-webkit-animation-name:illusion-number-animation-in-1;-moz-animation-name:illusion-number-animation-in-1;animation-name:illusion-number-animation-in-1}@-webkit-keyframes illusion-number-animation-in-1{0%{-webkit-transform:rotateY(90deg) translateZ(1.5em);transform:rotateY(90deg) translateZ(1.5em);background-color:#dcdcdc}100%{-webkit-transform:rotateY(0) translateZ(1.5em);transform:rotateY(0) translateZ(1.5em);background-color:#dcdcdc}}@-moz-keyframes illusion-number-animation-in-1{0%{-moz-transform:rotateY(90deg) translateZ(1.5em);transform:rotateY(90deg) translateZ(1.5em);background-color:#dcdcdc}100%{-moz-transform:rotateY(0) translateZ(1.5em);transform:rotateY(0) translateZ(1.5em);background-color:#dcdcdc}}@keyframes illusion-number-animation-in-1{0%{-webkit-transform:rotateY(90deg) translateZ(1.5em);-moz-transform:rotateY(90deg) translateZ(1.5em);transform:rotateY(90deg) translateZ(1.5em);background-color:#dcdcdc}100%{-webkit-transform:rotateY(0) translateZ(1.5em);-moz-transform:rotateY(0) translateZ(1.5em);transform:rotateY(0) translateZ(1.5em);background-color:#dcdcdc}}.illusion-number .illusion-number-tiles .illusion-number-animation-out-2{-webkit-animation-name:illusion-number-animation-out-2;-moz-animation-name:illusion-number-animation-out-2;animation-name:illusion-number-animation-out-2}@-webkit-keyframes illusion-number-animation-out-2{0%{-webkit-transform:rotateY(0) translateZ(.5em);transform:rotateY(0) translateZ(.5em);background-color:#dcdcdc}100%{-webkit-transform:rotateY(-90deg) translateZ(.5em);transform:rotateY(-90deg) translateZ(.5em);background-color:#707070}}@-moz-keyframes illusion-number-animation-out-2{0%{-moz-transform:rotateY(0) translateZ(.5em);transform:rotateY(0) translateZ(.5em);background-color:#dcdcdc}100%{-moz-transform:rotateY(-90deg) translateZ(.5em);transform:rotateY(-90deg) translateZ(.5em);background-color:#707070}}@keyframes illusion-number-animation-out-2{0%{-webkit-transform:rotateY(0) translateZ(.5em);-moz-transform:rotateY(0) translateZ(.5em);transform:rotateY(0) translateZ(.5em);background-color:#dcdcdc}100%{-webkit-transform:rotateY(-90deg) translateZ(.5em);-moz-transform:rotateY(-90deg) translateZ(.5em);transform:rotateY(-90deg) translateZ(.5em);background-color:#707070}}.illusion-number .illusion-number-tiles .illusion-number-animation-in-2{-webkit-animation-name:illusion-number-animation-in-2;-moz-animation-name:illusion-number-animation-in-2;animation-name:illusion-number-animation-in-2}@-webkit-keyframes illusion-number-animation-in-2{0%{-webkit-transform:rotateY(90deg) translateZ(.5em);transform:rotateY(90deg) translateZ(.5em);background-color:#dcdcdc}100%{-webkit-transform:rotateY(0) translateZ(.5em);transform:rotateY(0) translateZ(.5em);background-color:#dcdcdc}}@-moz-keyframes illusion-number-animation-in-2{0%{-moz-transform:rotateY(90deg) translateZ(.5em);transform:rotateY(90deg) translateZ(.5em);background-color:#dcdcdc}100%{-moz-transform:rotateY(0) translateZ(.5em);transform:rotateY(0) translateZ(.5em);background-color:#dcdcdc}}@keyframes illusion-number-animation-in-2{0%{-webkit-transform:rotateY(90deg) translateZ(.5em);-moz-transform:rotateY(90deg) translateZ(.5em);transform:rotateY(90deg) translateZ(.5em);background-color:#dcdcdc}100%{-webkit-transform:rotateY(0) translateZ(.5em);-moz-transform:rotateY(0) translateZ(.5em);transform:rotateY(0) translateZ(.5em);background-color:#dcdcdc}}.illusion-number .illusion-number-tiles .illusion-number-animation-out-3{-webkit-animation-name:illusion-number-animation-out-3;-moz-animation-name:illusion-number-animation-out-3;animation-name:illusion-number-animation-out-3}@-webkit-keyframes illusion-number-animation-out-3{0%{-webkit-transform:rotateY(0) translateZ(-.5em);transform:rotateY(0) translateZ(-.5em);background-color:#dcdcdc}100%{-webkit-transform:rotateY(-90deg) translateZ(-.5em);transform:rotateY(-90deg) translateZ(-.5em);background-color:#707070}}@-moz-keyframes illusion-number-animation-out-3{0%{-moz-transform:rotateY(0) translateZ(-.5em);transform:rotateY(0) translateZ(-.5em);background-color:#dcdcdc}100%{-moz-transform:rotateY(-90deg) translateZ(-.5em);transform:rotateY(-90deg) translateZ(-.5em);background-color:#707070}}@keyframes illusion-number-animation-out-3{0%{-webkit-transform:rotateY(0) translateZ(-.5em);-moz-transform:rotateY(0) translateZ(-.5em);transform:rotateY(0) translateZ(-.5em);background-color:#dcdcdc}100%{-webkit-transform:rotateY(-90deg) translateZ(-.5em);-moz-transform:rotateY(-90deg) translateZ(-.5em);transform:rotateY(-90deg) translateZ(-.5em);background-color:#707070}}.illusion-number .illusion-number-tiles .illusion-number-animation-in-3{-webkit-animation-name:illusion-number-animation-in-3;-moz-animation-name:illusion-number-animation-in-3;animation-name:illusion-number-animation-in-3}@-webkit-keyframes illusion-number-animation-in-3{0%{-webkit-transform:rotateY(90deg) translateZ(-.5em);transform:rotateY(90deg) translateZ(-.5em);background-color:#dcdcdc}100%{-webkit-transform:rotateY(0) translateZ(-.5em);transform:rotateY(0) translateZ(-.5em);background-color:#dcdcdc}}@-moz-keyframes illusion-number-animation-in-3{0%{-moz-transform:rotateY(90deg) translateZ(-.5em);transform:rotateY(90deg) translateZ(-.5em);background-color:#dcdcdc}100%{-moz-transform:rotateY(0) translateZ(-.5em);transform:rotateY(0) translateZ(-.5em);background-color:#dcdcdc}}@keyframes illusion-number-animation-in-3{0%{-webkit-transform:rotateY(90deg) translateZ(-.5em);-moz-transform:rotateY(90deg) translateZ(-.5em);transform:rotateY(90deg) translateZ(-.5em);background-color:#dcdcdc}100%{-webkit-transform:rotateY(0) translateZ(-.5em);-moz-transform:rotateY(0) translateZ(-.5em);transform:rotateY(0) translateZ(-.5em);background-color:#dcdcdc}}.illusion-number .illusion-number-tiles-reverse div{-webkit-animation-direction:reverse;-moz-animation-direction:reverse;animation-direction:reverse} -------------------------------------------------------------------------------- /dist/illusion-number.min.js: -------------------------------------------------------------------------------- 1 | !function(n,i){function r(n,i,r){return void 0===a[n]?0:void 0===a[n][i]?0:void 0===a[n][i][r]?0:a[n][i][r]}function e(n,i){var e,o,a,t={"in":[],out:[]};for(e=0;5>e;e++)for(t["in"][e]=[],t.out[e]=[],o=0;3>o;o++){if(t.out[e][o]=[],r(n,e,o))for(a=0;3>a;a++)!r(i,e,a-1)&&r(i,e,a)&&t.out[e][o].push(a+1);if(t["in"][e][o]=[],r(i,e,o))for(a=2;a>=0;a--)!r(n,e,a+1)&&r(n,e,a)&&t["in"][e][o].push(3-a)}return t}function o(n){return console&&console.error&&console.error(n),n}var a={0:[[1,1,1],[1,0,1],[1,0,1],[1,0,1],[1,1,1]],1:[[1,1,0],[0,1,0],[0,1,0],[0,1,0],[1,1,1]],2:[[1,1,1],[0,0,1],[1,1,1],[1,0,0],[1,1,1]],3:[[1,1,1],[0,0,1],[0,1,1],[0,0,1],[1,1,1]],4:[[1,0,1],[1,0,1],[1,1,1],[0,0,1],[0,0,1]],5:[[1,1,1],[1,0,0],[1,1,1],[0,0,1],[1,1,1]],6:[[1,1,1],[1,0,0],[1,1,1],[1,0,1],[1,1,1]],7:[[1,1,1],[0,0,1],[0,0,1],[0,0,1],[0,0,1]],8:[[1,1,1],[1,0,1],[1,1,1],[1,0,1],[1,1,1]],9:[[1,1,1],[1,0,1],[1,1,1],[0,0,1],[1,1,1]]};i.IllusionNumber={play:function(i,r){if(r=r||{},!i)return o(new Error("IllusionNumber.play: "+i+" is not an invalid element"));var t=r.from;if("undefined"==typeof t||"undefined"==typeof a[t])return o(new Error("IllusionNumber.play: "+t+" is not an invalid 'from' char"));var s=r.to;if("undefined"==typeof s&&(s=(parseInt(t)+1)%10),"undefined"==typeof a[s])return o(new Error("IllusionNumber.play: "+s+" is not an invalid 'to' char"));var l=r.size||"250px";parseFloat(l)==l&&(l+="px");var u;/^((\d+)(\.\d+)?)([^\d]+)$/.test(l)&&(u=l.match(/^((\d+)(\.\d+)?)([^\d]+)$/),l=parseFloat(u[1])/6+u[4]),tilePositionsMap=e(t,s);var d=n.createElement("div");d.classList.add("illusion-number"),d.style.fontSize=l;var f=n.createElement("div");f.classList.add("illusion-number-tiles"),r.reverse&&f.classList.add("illusion-number-tiles-reverse");for(var p in["in","out"])for(var v=["in","out"][p],m=0;5>m;m++)for(var c=0;3>c;c++){var y=tilePositionsMap[v][m][c];for(var b in y){var h=y[b],L=n.createElement("div");L.classList.add("illusion-number-tile-x-"+m),L.classList.add("illusion-number-tile-y-"+c),L.classList.add("illusion-number-animation-"+v+"-"+h),r.animationDuration&&!isNaN(parseFloat(r.animationDuration))&&(L.style.animationDuration=parseFloat(r.animationDuration)+"s"),f.appendChild(L)}}d.appendChild(f),i.innerHTML="",i.appendChild(d)},setBitmap:function(n,i){a[n]=i}}}(document,window); -------------------------------------------------------------------------------- /example/datetime.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Illusion Number Datetime Example 6 | 7 | 8 | 9 | 48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | 71 | 186 | 187 | 188 | 189 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Illusion Number Example 6 | 7 | 8 | 9 |
10 | 11 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use scrice'; 2 | 3 | var gulp = require('gulp'), 4 | sass = require('gulp-sass'), 5 | plugins = require('gulp-load-plugins')(), 6 | livereload = plugins.livereload, 7 | logger = require('winston'), 8 | fs = require('fs'); 9 | 10 | gulp.task('css', function() { 11 | return gulp.src('src/*.scss') 12 | .pipe(sass({ indentWidth: 4 }).on('error', sass.logError)) 13 | .pipe(plugins.autoprefixer({ 14 | browsers: ['last 2 version', '> 5%', 'ie 10', 'ff 10', 'opera 15', 'chrome 12'] 15 | })) 16 | .pipe(gulp.dest('dist/')) 17 | .pipe(plugins.minifyCss()) 18 | .pipe(plugins.rename(function (path) { 19 | path.extname = '.min.css'; 20 | })) 21 | .pipe(gulp.dest('dist/')); 22 | }); 23 | 24 | gulp.task('js', function() { 25 | return gulp.src('src/*.js') 26 | .pipe(gulp.dest('dist/')) 27 | .pipe(plugins.uglify()) 28 | .pipe(plugins.rename(function (path) { 29 | path.extname = '.min.js'; 30 | })) 31 | .pipe(gulp.dest('dist/')); 32 | }); 33 | 34 | gulp.task('dev', function() { 35 | livereload.listen({ 36 | start: true 37 | }); 38 | gulp.watch('src/*.*', ['css', 'js']).on('change', function(file) { 39 | livereload.changed('example/index.html'); 40 | }); 41 | gulp.watch('example/*').on('change', function(file) { 42 | livereload.changed(file); 43 | }) 44 | }); 45 | 46 | gulp.task('build', ['css', 'js']); 47 | gulp.task('default', ['build']); 48 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "illusion-number", 3 | "version": "0.2.1", 4 | "description": "Illusion Number", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/malash/illusion-number.git" 8 | }, 9 | "main": "index.js", 10 | "scripts": { 11 | "test": "exit 0" 12 | }, 13 | "author": { 14 | "name": "Malash", 15 | "email": "i@malash.me", 16 | "url": "https://malash.me/" 17 | }, 18 | "license": "MIT", 19 | "dependencies": {}, 20 | "devDependencies": { 21 | "gulp": "^3.9.0", 22 | "gulp-autoprefixer": "^3.0.1", 23 | "gulp-livereload": "^3.8.0", 24 | "gulp-load-plugins": "^0.10.0", 25 | "gulp-minify-css": "^1.2.1", 26 | "gulp-rename": "^1.2.2", 27 | "gulp-sass": "^2.0.4", 28 | "gulp-uglify": "^1.4.1", 29 | "winston": "^1.0.1" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/illusion-number.js: -------------------------------------------------------------------------------- 1 | !(function(document, window) { 2 | 3 | var bitmaps = { 4 | '0': [ 5 | [1, 1, 1], 6 | [1, 0, 1], 7 | [1, 0, 1], 8 | [1, 0, 1], 9 | [1, 1, 1] 10 | ], 11 | '1': [ 12 | [1, 1, 0], 13 | [0, 1, 0], 14 | [0, 1, 0], 15 | [0, 1, 0], 16 | [1, 1, 1] 17 | ], 18 | '2': [ 19 | [1, 1, 1], 20 | [0, 0, 1], 21 | [1, 1, 1], 22 | [1, 0, 0], 23 | [1, 1, 1] 24 | ], 25 | '3': [ 26 | [1, 1, 1], 27 | [0, 0, 1], 28 | [0, 1, 1], 29 | [0, 0, 1], 30 | [1, 1, 1] 31 | ], 32 | '4': [ 33 | [1, 0, 1], 34 | [1, 0, 1], 35 | [1, 1, 1], 36 | [0, 0, 1], 37 | [0, 0, 1] 38 | ], 39 | '5': [ 40 | [1, 1, 1], 41 | [1, 0, 0], 42 | [1, 1, 1], 43 | [0, 0, 1], 44 | [1, 1, 1] 45 | ], 46 | '6': [ 47 | [1, 1, 1], 48 | [1, 0, 0], 49 | [1, 1, 1], 50 | [1, 0, 1], 51 | [1, 1, 1] 52 | ], 53 | '7': [ 54 | [1, 1, 1], 55 | [0, 0, 1], 56 | [0, 0, 1], 57 | [0, 0, 1], 58 | [0, 0, 1] 59 | ], 60 | '8': [ 61 | [1, 1, 1], 62 | [1, 0, 1], 63 | [1, 1, 1], 64 | [1, 0, 1], 65 | [1, 1, 1] 66 | ], 67 | '9': [ 68 | [1, 1, 1], 69 | [1, 0, 1], 70 | [1, 1, 1], 71 | [0, 0, 1], 72 | [1, 1, 1] 73 | ], 74 | }; 75 | 76 | function getBitmap(number, x, y) { 77 | if (bitmaps[number] === undefined) { 78 | return 0; 79 | } 80 | if (bitmaps[number][x] === undefined) { 81 | return 0; 82 | } 83 | if (bitmaps[number][x][y] === undefined) { 84 | return 0; 85 | } 86 | return bitmaps[number][x][y]; 87 | } 88 | 89 | function getTilePositionsMap(from ,to) { 90 | var tilePositionsMap = { 91 | 'in': [], 92 | 'out': [] 93 | }; 94 | var x, y, yComparison; 95 | for (x = 0; x < 5; x++) { 96 | tilePositionsMap['in'][x] = []; 97 | tilePositionsMap['out'][x] = []; 98 | for (y = 0; y < 3; y++) { 99 | tilePositionsMap['out'][x][y] = []; 100 | if (getBitmap(from, x, y)) { 101 | for (yComparison = 0; yComparison < 3; yComparison++) { 102 | if (!getBitmap(to, x, yComparison - 1) && getBitmap(to, x, yComparison)) { 103 | tilePositionsMap['out'][x][y].push(yComparison + 1); 104 | } 105 | } 106 | } 107 | 108 | tilePositionsMap['in'][x][y] = []; 109 | if (getBitmap(to, x, y)) { 110 | for (yComparison = 2; yComparison >=0; yComparison--) { 111 | if (!getBitmap(from, x, yComparison + 1) && getBitmap(from, x, yComparison)) { 112 | tilePositionsMap['in'][x][y].push(3 - yComparison); 113 | } 114 | } 115 | } 116 | } 117 | } 118 | return tilePositionsMap; 119 | } 120 | 121 | function error(err) { 122 | if (console && console.error) { 123 | console.error(err); 124 | } 125 | return err; 126 | } 127 | 128 | window.IllusionNumber = { 129 | /** 130 | * Play illusion animation on element provided 131 | * @method play 132 | * @for IllusionNumber 133 | * @param {element} ele - the DOM element to bind 134 | * @param {object} options - play options 135 | * @param {number | char} options.from - animation will transform from bitmap of options.from 136 | * @param {number | char} options.to - animation will transform to bitmap of options.to 137 | * @param {number | string} [options.size = 250px] - the canvas size (14px, 3em, 2rem, etc.) 138 | * @param {number} [options.animationDuration = 1] - the animation duration (sec, could be float), default to 1 139 | */ 140 | play: function(ele, options) { 141 | options = options || {}; 142 | if (!ele) { 143 | return error(new Error('IllusionNumber.play: ' + ele + ' is not an invalid element')); 144 | } 145 | var from = options.from; 146 | if (typeof from === 'undefined' || typeof bitmaps[from] === 'undefined') { 147 | return error(new Error('IllusionNumber.play: ' + from + ' is not an invalid \'from\' char')); 148 | } 149 | var to = options.to; 150 | if (typeof to === 'undefined') { 151 | to = (parseInt(from) + 1) % 10; 152 | } 153 | if (typeof bitmaps[to] === 'undefined') { 154 | return error(new Error('IllusionNumber.play: ' + to + ' is not an invalid \'to\' char')); 155 | } 156 | var size = options.size || '250px'; 157 | if (parseFloat(size) == size) { 158 | size = size + 'px'; 159 | } 160 | var sizeResult; 161 | if (/^((\d+)(\.\d+)?)([^\d]+)$/.test(size)) { 162 | sizeResult = size.match(/^((\d+)(\.\d+)?)([^\d]+)$/); 163 | size = (parseFloat(sizeResult[1]) / 6) + sizeResult[4]; 164 | } 165 | tilePositionsMap = getTilePositionsMap(from, to); 166 | var eleRoot = document.createElement('div'); 167 | eleRoot.classList.add('illusion-number'); 168 | eleRoot.style.fontSize = size; 169 | var eleTiles = document.createElement('div'); 170 | eleTiles.classList.add('illusion-number-tiles'); 171 | if (options.reverse) { 172 | eleTiles.classList.add('illusion-number-tiles-reverse'); 173 | } 174 | for (var indexSurface in ['in', 'out']) { 175 | var surface = ['in', 'out'][indexSurface]; 176 | for (var x = 0; x < 5; x++) { 177 | for (var y = 0; y < 3; y++) { 178 | var tilePositions = tilePositionsMap[surface][x][y]; 179 | for (var indexTilePosition in tilePositions) { 180 | var tilePosition = tilePositions[indexTilePosition]; 181 | var eleTile = document.createElement('div'); 182 | eleTile.classList.add('illusion-number-tile-x-' + x); 183 | eleTile.classList.add('illusion-number-tile-y-' + y); 184 | eleTile.classList.add('illusion-number-animation-' + surface + '-' + tilePosition); 185 | if (options.animationDuration && !isNaN(parseFloat(options.animationDuration))) { 186 | eleTile.style.animationDuration = parseFloat(options.animationDuration) + 's'; 187 | } 188 | eleTiles.appendChild(eleTile); 189 | } 190 | } 191 | } 192 | } 193 | eleRoot.appendChild(eleTiles); 194 | ele.innerHTML = ''; 195 | ele.appendChild(eleRoot); 196 | }, 197 | /** 198 | * Set or override a custom bitmap 199 | * @method setBitmap 200 | * @for IllusionNumber 201 | * @param {char} char - character to set 202 | * @param {number[][]} bitmap - character bitmap, for example: 203 | [ 204 | [1, 1, 1], 205 | [1, 0, 1], 206 | [1, 0, 1], 207 | [1, 0, 1], 208 | [1, 1, 1] 209 | ] 210 | */ 211 | setBitmap: function(char, bitmap) { 212 | bitmaps[char] = bitmap; 213 | } 214 | }; 215 | }(document, window)); -------------------------------------------------------------------------------- /src/illusion-number.scss: -------------------------------------------------------------------------------- 1 | $animationDuration: 1s; 2 | $tileWidth: 1em; 3 | $tileHeight: 1em; 4 | $lightColor: #dcdcdc; 5 | $darkColor: #707070; 6 | $backgroundColor: #000000; 7 | 8 | .illusion-number { 9 | position: relative; 10 | /* 2^(-1/2) = 0.70710678118655 */ 11 | padding: 0 0.70710678118655 * $tileWidth; 12 | width: 3 * $tileWidth; 13 | height: 5 * $tileHeight; 14 | overflow: hidden; 15 | background-color: $backgroundColor; 16 | /* DEBUG */ 17 | /* perspective: 500px; */ 18 | .illusion-number-tiles { 19 | position: absolute; 20 | width: 3 * $tileWidth; 21 | height: 5 * $tileHeight; 22 | transform-style: preserve-3d; 23 | div { 24 | position: absolute; 25 | backface-visibility: hidden; 26 | /* fix gap between tiles*/ 27 | width: 1.05 * $tileWidth; 28 | height: 1.05 * $tileHeight; 29 | margin: 0; 30 | padding: 0; 31 | animation-duration: $animationDuration; 32 | animation-iteration-count: 1; 33 | animation-fill-mode: forwards; 34 | animation-timing-function: ease; 35 | } 36 | @for $x from 0 through 4 { 37 | .illusion-number-tile-x-#{$x} { 38 | top: $x * $tileHeight; 39 | } 40 | } 41 | @for $y from 0 through 2 { 42 | .illusion-number-tile-y-#{$y} { 43 | left: $y * $tileWidth; 44 | transform-origin: (1.5 - $y) * $tileWidth 0 0; 45 | } 46 | } 47 | @mixin illusion-number-animation($deg, $tilePosition) { 48 | transform: rotateY($deg) translateZ(map-get(("1": 1.5em, "2": 0.5em, "3": -0.5em), #{$tilePosition})); 49 | background-color: map-get(("0": #{$lightColor}, "-90deg": #{$darkColor}, "90deg": #{$lightColor}), #{$deg}) 50 | } 51 | 52 | @for $tilePosition from 1 through 3 { 53 | .illusion-number-animation-out-#{$tilePosition} { 54 | animation-name: illusion-number-animation-out-#{$tilePosition}; 55 | } 56 | @keyframes illusion-number-animation-out-#{$tilePosition} { 57 | 0% { 58 | @include illusion-number-animation(0, $tilePosition); 59 | } 60 | 100% { 61 | @include illusion-number-animation(-90deg, $tilePosition); 62 | } 63 | } 64 | .illusion-number-animation-in-#{$tilePosition} { 65 | animation-name: illusion-number-animation-in-#{$tilePosition}; 66 | } 67 | @keyframes illusion-number-animation-in-#{$tilePosition} { 68 | 0% { 69 | @include illusion-number-animation(90deg, $tilePosition); 70 | } 71 | 100% { 72 | @include illusion-number-animation(0, $tilePosition); 73 | } 74 | } 75 | } 76 | } 77 | .illusion-number-tiles-reverse div { 78 | animation-direction: reverse; 79 | } 80 | } --------------------------------------------------------------------------------