├── .gitignore ├── LICENSE ├── README.md ├── demo ├── canvas.html ├── css │ ├── demo.css │ ├── demoStyles.css │ └── demoStyles.scss ├── images │ ├── demo.png │ ├── ebi1.png │ ├── ikura1.png │ ├── negitoro.png │ ├── nori.png │ ├── rice1.png │ ├── rice2.png │ ├── salmon1.png │ ├── salmon2.png │ ├── wasabi1.png │ ├── wasabi2.png │ ├── wasabi3.png │ └── wasabi4.png ├── index.html └── js │ ├── demo.js │ ├── pt-core.min.js │ └── sushi.js ├── dist ├── module │ └── roll.js ├── roll.js ├── roll.js.map └── roll.min.js ├── gulpfile.js ├── package.json └── src ├── roll.js └── roll_standalone.js /.gitignore: -------------------------------------------------------------------------------- 1 | .sass-cache 2 | node_modules 3 | .idea -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # roll.js 2 | 3 | ![roll.js demo](http://williamngan.github.io/roll/demo/images/demo.png) 4 | 5 | A little js library (~8kb min, 3kb gzip, no dependencies) to help you keep track of position, scrolling, and pagination. 6 | Nothing too fancy, but since I couldn't find a suitable library for these purposes, I made one for a friend and myself and you too! 7 | Ping me [@williamngan](http://twitter.com/williamngan) if you have questions or comments. 8 | 9 | ## Demo 10 | Here's a **[DOM scrolling demo](http://williamngan.github.io/roll/demo/index.html)** (with some weird iPhone paintings :satisfied:) 11 | 12 | Here's a **[Canvas demo](http://williamngan.github.io/roll/demo/canvas.html)** 13 | 14 | ## Basic Usage 15 | 16 | Simply create a new instance, specifying the viewport size (500px in this example). 17 | 18 | `var roll = new Roll( 500 );` 19 | 20 | Next, add a couple of *steps* to the roll instance. You may use the static helper `Roll.chunk( stepSize, padding )` to create a step object. 21 | 22 | ```javascript 23 | roll.addStep( Roll.chunk(500, 20) ); // Add a step of 500px with 20px padding 24 | roll.addStep( Roll.chunk(700, 20) ); // Add a step of 700px with 20px padding 25 | ``` 26 | 27 | When the pane is moved, usually via the function `roll.move( position )`, 28 | the roll instance will emit a `roll` event and possibly a `step` event. 29 | You can listen to these events in the usual manner. (see [EventEmitter docs](https://nodejs.org/api/events.html) ). For example, 30 | ```javascript 31 | roll.on( "roll", function(step, currProgress, currPosition, totalProgress) { 32 | // implement your logic here 33 | }) 34 | 35 | roll.on( "step", function(curr, last) { 36 | // implement your logic here 37 | }) 38 | ``` 39 | 40 | ## DOM Usage 41 | 42 | A common usage is to keep track of scrolling in a DOM element, and then do pagination or animation based on scroll position. 43 | 44 | There are a couple of static helpers to simplify this task. First, create a `roll` instance using `Roll.DOM( viewportID, scrollpaneID, stepsID, stepClass, padding)`. For example, 45 | 46 | ```javascript 47 | var roll = Roll.DOM("#viewport", "#pane", "#steps", ".step", 100 ); 48 | ``` 49 | 50 | The html structure for a *scrolling slideshow* may look like this. Also see a [sample css](https://github.com/williamngan/roll/blob/master/demo/css/demo.css) that corresponds to that html. 51 | 52 | ```html 53 |
54 |
55 |
Hello
56 |
World
57 |
How's it going
58 |
59 |
60 |
61 |
62 |
63 | ``` 64 | 65 | Then this will keep track of vertical scrolling in the #viewport DOM element. You can then listen for the `roll` and `step` events as shown in Basic Usage, and implement your own logic. 66 | 67 | One more thing: `Roll.stepHandler(...)` is a helper to go through a slideshow with `step` event. It will add css classes to the `.step` elements based on which step is in view. 68 | 69 | ```javascript 70 | roll.on( "step", Roll.stepHandler( roll, views, "prev", "next", "curr", true ) ); 71 | ``` 72 | 73 | In the above snippet, `roll` is the roll instance, `views` is an array of the .step DOM elements, and `"prev", "next" "curr"` are css class names to assign to previous, next, and current step elements. 74 | 75 | A good way to get started is to take a look at the demos above, and then check out the source code in [demo folder](https://github.com/williamngan/roll/tree/master/demo). 76 | 77 | ## Compiling 78 | 79 | This library is written in javascript ES6 and compiled with Babel. If you want to change the source code and rebuild, simply `npm install` to get the dev dependencies, 80 | and then run `gulp` to watch and build. 81 | 82 | ## NPM 83 | [https://www.npmjs.com/package/rolljs](https://www.npmjs.com/package/rolljs) 84 | -------------------------------------------------------------------------------- /demo/canvas.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 39 | 40 | 41 | 42 | 43 | 44 | 45 |
46 |

Using roll.js in html canvas.

47 |

← Move the mouse around on the canvas to start.

48 |

49 |

50 |

51 |

52 |

53 |
54 | 55 |
56 |
57 |

Demo code · Made with pt.js

58 |
59 | 60 | 138 | 139 | 140 | -------------------------------------------------------------------------------- /demo/css/demo.css: -------------------------------------------------------------------------------- 1 | /** 2 | This is a sample css file scrolling DOM elements using roll.js. 3 | The DOM structure usually looks like this 4 | 5 |
6 |
7 |
One
8 |
Two
9 |
Three
10 |
etc...
11 |
12 |
13 |
14 |
15 |
16 | */ 17 | 18 | 19 | html, body { 20 | /* optional */ 21 | overflow: hidden; 22 | } 23 | 24 | #roll { 25 | 26 | position: relative; 27 | width: 500px; 28 | height: 500px; 29 | 30 | /* 31 | Roll's size can also be defined by absolute positions. 32 | position: absolute; 33 | top: 0; bottom: 0; left: 0; right: 0; 34 | */ 35 | } 36 | 37 | 38 | /* this is the viewport container */ 39 | #wrapper { 40 | overflow: auto; 41 | width: 100%; 42 | height: 100%; 43 | position: relative; 44 | 45 | } 46 | 47 | 48 | /* this is the full size scrolling pane */ 49 | #pane {} 50 | 51 | 52 | /* this is a container for the steps */ 53 | #steps { 54 | position: absolute; 55 | overflow: hidden; 56 | top: 0; 57 | left: 0; 58 | width: 100%; 59 | height: 100%; 60 | } 61 | 62 | 63 | /* the step class */ 64 | .step { 65 | width: 100%; 66 | height: 100%; 67 | 68 | position: absolute; 69 | top: 100%; 70 | left: 0; 71 | 72 | /* optional transition */ 73 | transition: top .5s; 74 | } 75 | 76 | 77 | /* what's the styles for previous steps */ 78 | .step.prev { 79 | top: -100%; 80 | } 81 | 82 | 83 | /* what's the styles for next steps */ 84 | .step.next { 85 | top: 100%; 86 | } 87 | 88 | 89 | /* what's the styles for current steps */ 90 | .step.curr { 91 | top: 0; 92 | } 93 | -------------------------------------------------------------------------------- /demo/css/demoStyles.css: -------------------------------------------------------------------------------- 1 | html, body { 2 | overflow: hidden; 3 | margin: 0; 4 | font: 10px/1.5 "Montserrat", sans-serif; } 5 | 6 | #menu { 7 | position: absolute; 8 | padding: 30px; 9 | top: 0; 10 | left: 0; 11 | width: 300px; 12 | bottom: 0; 13 | box-sizing: border-box; 14 | background: rgba(255, 255, 255, 0.1); } 15 | 16 | #progress { 17 | position: absolute; 18 | top: 0; 19 | left: 300px; 20 | width: 1px; 21 | height: 0; 22 | background: #ff0; } 23 | 24 | h1 { 25 | font-size: 5em; 26 | margin: 0 0 20px; 27 | color: #fff; } 28 | 29 | p { 30 | font-size: 1.2em; 31 | color: #fff; } 32 | p.field { 33 | line-height: 20px; 34 | clear: right; } 35 | p.field span { 36 | display: inline-block; 37 | margin-right: 10px; } 38 | p.field .label { 39 | opacity: .65; } 40 | p.field .value { 41 | font-weight: bold; 42 | font-size: 18px; 43 | float: right; } 44 | 45 | hr { 46 | border: none; 47 | border-bottom: 1px dotted rgba(255, 255, 255, 0.1); 48 | margin: 30px 0; } 49 | 50 | a { 51 | display: inline-block; 52 | color: #ff0; 53 | border-bottom: 1px solid rgba(255, 255, 0, 0); 54 | text-decoration: none; } 55 | a:hover { 56 | border-bottom-color: yellow; } 57 | 58 | #roll { 59 | background: #5CD; 60 | position: absolute; 61 | top: 0; 62 | left: 0px; 63 | bottom: 0; 64 | right: 0; 65 | width: auto; 66 | height: auto; } 67 | 68 | .step { 69 | color: #fff; 70 | opacity: 1; 71 | transition: top .5s, opacity .5s; } 72 | .step h1 { 73 | top: 30px; 74 | right: 30px; 75 | font-size: 40vh; 76 | width: 100%; 77 | text-align: right; 78 | position: absolute; 79 | color: rgba(0, 0, 0, 0.1); 80 | line-height: 1; 81 | letter-spacing: -0.03em; } 82 | 83 | .step.prev, .step.next { 84 | opacity: 0; } 85 | -------------------------------------------------------------------------------- /demo/css/demoStyles.scss: -------------------------------------------------------------------------------- 1 | html, body { 2 | overflow: hidden; 3 | margin: 0; 4 | 5 | font: 10px/1.5 "Montserrat", sans-serif; 6 | } 7 | 8 | #menu { 9 | position: absolute; 10 | padding: 30px; 11 | top: 0; left: 0; width: 300px; bottom: 0; 12 | box-sizing: border-box; 13 | background: rgba(255,255,255,.1); 14 | } 15 | 16 | #progress { 17 | position: absolute; 18 | top: 0; left: 300px; 19 | width: 1px; height: 0; 20 | background: #ff0; 21 | } 22 | 23 | h1 { 24 | font-size: 5em; 25 | margin: 0 0 20px; 26 | color: #fff; 27 | } 28 | p { 29 | font-size: 1.2em; 30 | color: #fff; 31 | 32 | &.field { 33 | line-height: 20px; 34 | span { 35 | display: inline-block; 36 | margin-right: 10px; 37 | } 38 | .label { 39 | opacity: .65; 40 | } 41 | .value { 42 | font-weight: bold; 43 | font-size: 18px; 44 | float: right; 45 | } 46 | 47 | clear: right; 48 | } 49 | } 50 | 51 | hr { 52 | border: none; 53 | border-bottom: 1px dotted rgba(255,255,255,.1); 54 | margin: 30px 0; 55 | } 56 | a { 57 | display: inline-block; 58 | color: #ff0; 59 | border-bottom: 1px solid rgba(255,255,0,0); 60 | text-decoration: none; 61 | 62 | &:hover { 63 | border-bottom-color: rgba(255,255,0,1); 64 | } 65 | } 66 | 67 | 68 | #roll { 69 | background: #5CD; 70 | 71 | position: absolute; 72 | top: 0; left: 0px; bottom: 0; right: 0; 73 | width: auto; height: auto; 74 | } 75 | 76 | #wrapper { 77 | 78 | } 79 | 80 | 81 | #pane { 82 | 83 | } 84 | 85 | #steps { 86 | 87 | } 88 | 89 | .step { 90 | color: #fff; 91 | opacity: 1; 92 | transition: top .5s, opacity .5s; 93 | 94 | h1 { 95 | top: 30px; 96 | right: 30px; 97 | font-size: 40vh; 98 | width: 100%; 99 | text-align: right; 100 | position: absolute; 101 | color: rgba(0,0,0,.1); 102 | line-height: 1; 103 | letter-spacing: -0.03em; 104 | } 105 | } 106 | 107 | .step.prev, .step.next { 108 | opacity: 0; 109 | } -------------------------------------------------------------------------------- /demo/images/demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/williamngan/roll/f06ecb159a4a1c1dc631c6a13e067e89ae0902df/demo/images/demo.png -------------------------------------------------------------------------------- /demo/images/ebi1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/williamngan/roll/f06ecb159a4a1c1dc631c6a13e067e89ae0902df/demo/images/ebi1.png -------------------------------------------------------------------------------- /demo/images/ikura1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/williamngan/roll/f06ecb159a4a1c1dc631c6a13e067e89ae0902df/demo/images/ikura1.png -------------------------------------------------------------------------------- /demo/images/negitoro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/williamngan/roll/f06ecb159a4a1c1dc631c6a13e067e89ae0902df/demo/images/negitoro.png -------------------------------------------------------------------------------- /demo/images/nori.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/williamngan/roll/f06ecb159a4a1c1dc631c6a13e067e89ae0902df/demo/images/nori.png -------------------------------------------------------------------------------- /demo/images/rice1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/williamngan/roll/f06ecb159a4a1c1dc631c6a13e067e89ae0902df/demo/images/rice1.png -------------------------------------------------------------------------------- /demo/images/rice2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/williamngan/roll/f06ecb159a4a1c1dc631c6a13e067e89ae0902df/demo/images/rice2.png -------------------------------------------------------------------------------- /demo/images/salmon1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/williamngan/roll/f06ecb159a4a1c1dc631c6a13e067e89ae0902df/demo/images/salmon1.png -------------------------------------------------------------------------------- /demo/images/salmon2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/williamngan/roll/f06ecb159a4a1c1dc631c6a13e067e89ae0902df/demo/images/salmon2.png -------------------------------------------------------------------------------- /demo/images/wasabi1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/williamngan/roll/f06ecb159a4a1c1dc631c6a13e067e89ae0902df/demo/images/wasabi1.png -------------------------------------------------------------------------------- /demo/images/wasabi2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/williamngan/roll/f06ecb159a4a1c1dc631c6a13e067e89ae0902df/demo/images/wasabi2.png -------------------------------------------------------------------------------- /demo/images/wasabi3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/williamngan/roll/f06ecb159a4a1c1dc631c6a13e067e89ae0902df/demo/images/wasabi3.png -------------------------------------------------------------------------------- /demo/images/wasabi4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/williamngan/roll/f06ecb159a4a1c1dc631c6a13e067e89ae0902df/demo/images/wasabi4.png -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Roll.js Demos 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 |
21 | 22 |
23 |

1

24 |

2

25 |

3

26 |

4

27 |

5

28 |
29 |
30 |
31 |
32 |
33 | 34 |
35 | 36 | 37 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /demo/js/demo.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | // initiate roll 4 | var roll = Roll.DOM( "#wrapper", "#pane", "#steps", ".step", 100 ); 5 | 6 | var views = document.querySelectorAll( ".step" ); 7 | views[0].className = "step curr"; // set first step's class name as "curr" 8 | 9 | 10 | // define how you want to track the elements as you scroll 11 | function track() { 12 | 13 | // vendor prefix for old browsers 14 | function _vendor( elem, prop, val ) { 15 | var vs = ["webkit", "Webkit", "Moz", "ms"]; 16 | for (var i=0; i= 0) ? "Step "+(step+1) : "(padding)"; 27 | 28 | var vals = { 29 | numSteps: roll.steps.length, 30 | viewportHeight: roll.getViewportHeight(), 31 | paneHeight: roll.getHeight(), 32 | currStep: curr, 33 | currPos: position + "px", 34 | currStepProgress: Math.floor( stepProgress * 100 ) + "%", 35 | totalProgress: Math.floor( totalProgress * 100) + "%" 36 | }; 37 | 38 | for (var k in vals) { 39 | var el = document.querySelector("#"+k); 40 | if (el) { 41 | el.textContent = vals[k]; 42 | } 43 | } 44 | 45 | if (step >= 0) { 46 | var currStep = document.querySelector( "#s" + step ); 47 | var ings = currStep.querySelectorAll( ".ingredient" ); 48 | for (var i = 0; i < ings.length; i++) { 49 | var ang1 = parseInt( ings[i].getAttribute( "data-angle" ) ); 50 | var ang2 = parseInt( ings[i].getAttribute( "data-rotate" ) ); 51 | //var tm = "rotate(" + (ang1 + (stepProgress*0.2) * ang2 ) + "deg) scale(" + (0.25 + totalProgress * 0.5) + ")"; 52 | var tm = "scale(" + (0.25 + totalProgress * 0.5) + ") translate(0, "+Math.floor(-ang1*stepProgress*3)+"px)"; 53 | ings[i].style.transform = tm; 54 | _vendor( ings[i], "Transform", tm ); 55 | } 56 | } 57 | 58 | var progress = document.querySelector("#progress"); 59 | progress.style.height = Math.floor(roll.getViewportHeight() * totalProgress) + "px"; 60 | 61 | }); 62 | } 63 | 64 | // start tracking 65 | track(); 66 | 67 | // a global function to scroll to a specific step in the roll instance. 68 | window.goto = function(index) { 69 | var viewport = document.querySelector( "#wrapper" ); 70 | roll.scroll(index, viewport); 71 | }; 72 | 73 | 74 | // re-initiate roll when resized 75 | window.addEventListener("resize", function(evt) { 76 | roll = Roll.DOM( "#wrapper", "#pane", "#steps", ".step", 100 ); 77 | track(); 78 | goto(0); 79 | }); 80 | 81 | goto(0); 82 | 83 | })(); -------------------------------------------------------------------------------- /demo/js/pt-core.min.js: -------------------------------------------------------------------------------- 1 | var CanvasSpace,Circle,Color,Const,Curve,DOMSpace,Form,Grid,Line,Matrix,Pair,Particle,ParticleSystem,Point,PointSet,Rectangle,Space,Timer,Triangle,Util,Vector,extend=function(t,i){function e(){this.constructor=t}for(var n in i)hasProp.call(i,n)&&(t[n]=i[n]);return e.prototype=i.prototype,t.prototype=new e,t.__super__=i.prototype,t},hasProp={}.hasOwnProperty,slice=[].slice;Const=function(){function t(){}return t.xy="xy",t.yz="yz",t.xz="xz",t.xyz="xyz",t.identical=-1,t.right=3,t.bottom_right=4,t.bottom=5,t.bottom_left=6,t.left=7,t.top_left=0,t.top=1,t.top_right=2,t.sideLabels=["identical","right","bottom right","bottom","bottom left","left","top left","top","top right"],t.epsilon=1e-4,t.pi=Math.PI,t.two_pi=6.283185307179586,t.half_pi=1.5707963267948966,t.quarter_pi=.7853981633974483,t.one_degree=.017453292519943295,t.rad_to_deg=57.29577951308232,t.deg_to_rad=.017453292519943295,t.gravity=9.81,t.newton=.10197,t.gaussian=.3989422804014327,t}(),this.Const=Const,Matrix=function(){function t(){}return t.rotateAnchor2D=function(t,i,e){var n,s,r;return null==e&&(e=Const.xy),n=i.get2D(e),s=Math.cos(t),r=Math.sin(t),[s,r,0,-r,s,0,n.x*(1-s)+n.y*r,n.y*(1-s)-n.x*r,1]},t.reflectAnchor2D=function(t,i){var e,n,s,r;return null==i&&(i=Const.xy),s=t.intercept(i),e=2*Math.atan(s.slope),n=Math.cos(e),r=Math.sin(e),[n,r,0,r,-n,0,-s.yi*r,s.yi+s.yi*n,1]},t.shearAnchor2D=function(t,i,e,n){var s,r,o;return null==n&&(n=Const.xy),s=e.get2D(n),r=Math.tan(t),o=Math.tan(i),[1,r,0,o,1,0,-s.y*o,-s.x*r,1]},t.scaleAnchor2D=function(t,i,e,n){var s;return null==n&&(n=Const.xy),s=e.get2D(n),[t,0,0,0,i,0,-s.x*t+s.x,-s.y*i+s.y,1]},t.scale2D=function(t,i){return[t,0,0,0,i,0,0,0,1]},t.shear2D=function(t,i){return[1,Math.tan(t),0,Math.tan(i),1,0,0,0,1]},t.rotate2D=function(t,i){return[t,i,0,-i,t,0,0,0,1]},t.translate2D=function(t,i){return[1,0,0,0,1,0,t,i,1]},t.transform2D=function(t,i,e,n){var s,r,o;return null==e&&(e=Const.xy),null==n&&(n=!1),s=t.get2D(e),r=s.x*i[0]+s.y*i[3]+i[6],o=s.x*i[1]+s.y*i[4]+i[7],s.x=r,s.y=o,s=s.get2D(e,!0),n?s:(t.set(s),t)},t}(),this.Matrix=Matrix,Util=function(){function t(){}return t.toRadian=function(t){return t*Const.deg_to_rad},t.toDegree=function(t){return t*Const.rad_to_deg},t.toHexColor=function(t){var i;return i=Math.floor(t).toString(16),1===i.length?"0"+i:i},t.toRGBColor=function(t,i,e){var n,s,r;return null==i&&(i=!1),null==e&&(e=1),"#"===t[0]&&(t=t.substr(1)),3===t.length?(r=parseInt(t[0]+t[0],16),s=parseInt(t[1]+t[1],16),n=parseInt(t[2]+t[2],16)):t.length>=6?(r=parseInt(t[0]+t[1],16),s=parseInt(t[2]+t[3],16),n=parseInt(t[4]+t[5],16)):(r=0,s=0,n=0),i?"rgba("+r+","+s+","+n+","+e+")":[r,s,n,e]},t.bound=function(t,i,e){var n,s;return null==e&&(e=!1),n=t%i,s=i/2,n>s?n-=i:-s>n&&(n+=i),e&&0>n?n+i:n},t.boundAngle=function(i,e){return t.bound(i,360,e)},t.boundRadian=function(i,e){return t.bound(i,Const.two_pi,e)},t.boundingBox=function(t,i){var e,n,s,r,o;for(null==i&&(i=!1),r=new Point(Number.POSITIVE_INFINITY,Number.POSITIVE_INFINITY),s=new Point(Number.NEGATIVE_INFINITY,Number.NEGATIVE_INFINITY),e=0,n=t.length;n>e;e++)o=t[e],o.xs.x&&(s.x=o.x),o.y>s.y&&(s.y=o.y),i&&(o.zs.z&&(s.z=o.z));return new Rectangle(r).to(s)},t.lerp=function(t,i,e){return(1-e)*t+e*i},t.centroid=function(t){var i,e,n,s;for(i=new Vector,e=0,n=t.length;n>e;e++)s=t[e],i.add(s);return i.divide(t.length)},t.same=function(t,i,e){return null==e&&(e=Const.epsilon),Math.abs(t-i)=Math.min(i,e)&&t<=Math.max(i,e)},t.randomRange=function(t,i){var e;return null==i&&(i=0),e=t>i?t-i:i-t,t+Math.random()*e},t.mixin=function(t,i){var e,n;for(e in i)n=i[e],i.hasOwnProperty(e)&&(t.prototype[e]=i[e]);return t},t.extend=function(t,i){return t.prototype=Object.create(i.prototype),t.prototype.constructor=t,t},t.clonePoints=function(t){var i,e,n,s;for(s=[],i=0,e=t.length;e>i;i++)n=t[i],s.push(n.clone());return s},t.contextRotateOrigin=function(t,i,e,n,s){var r,o;return null==n&&(n=!1),o=i.size(),n||(n=o.$multiply(.5),n.add(i)),s&&(r=s.size(),Form.rect(t,s),t.clip()),t.translate(n.x,n.y),t.rotate(e),t.translate(-n.x,-n.y)},t.sinCosTable=function(){var t,i,e,n;for(t=[],n=[],i=e=0;360>=e;i=e+=1)t[i]=Math.cos(i*Math.PI/180),n[i]=Math.sin(i*Math.PI/180);return{sin:n,cos:t}},t.chance=function(t){return Math.random()=this.duration?this._time=Date.now():void 0},t.prototype.setEasing=function(t){return this._ease=t},t.prototype.check=function(){var t;return t=Math.min(Date.now()-this._time,this.duration),this._ease(t,0,1,this.duration)},t.prototype.track=function(t){var i;return clearInterval(this._intervalID),this.start(!0),i=this,this._intervalID=setInterval(function(){var e;return e=i.check(),t(e),e>=1?clearInterval(i._intervalID):void 0},25),this._intervalID},t}(),this.Timer=Timer,Space=function(){function t(t){null==t&&(t="space"),this.id=t,this.size=new Vector,this.center=new Vector,this._timePrev=0,this._timeDiff=0,this._timeEnd=-1,this.items={},this._animID=-1,this._animCount=0,this._animPause=!1,this._refresh=!0}return t.prototype.refresh=function(t){return this._refresh=t,this},t.prototype.render=function(t){return this},t.prototype.resize=function(t,i){},t.prototype.clear=function(){},t.prototype.add=function(t){var i;if(null==t.animate||"function"!=typeof t.animate)throw"a player object for Space.add must define animate()";return i=this._animCount++,this.items[i]=t,t.animateID=i,null!=t.onSpaceResize&&t.onSpaceResize(this.size.x,this.size.y),this},t.prototype.remove=function(t){return delete this.items[t.animateID],this},t.prototype.removeAll=function(){return this.items={},this},t.prototype.play=function(t){var i;if(null==t&&(t=0),this._animID=requestAnimationFrame(function(t){return function(i){return t.play(i)}}(this)),!this._animPause){this._timeDiff=t-this._timePrev;try{this._playItems(t)}catch(e){throw i=e,cancelAnimationFrame(this._animID),console.error(i.stack),i}return this._timePrev=t,this}},t.prototype._playItems=function(t){var i,e,n;this._refresh&&this.clear(),e=this.items;for(i in e)n=e[i],n.animate(t,this._timeDiff,this.ctx);return this._timeEnd>=0&&t>this._timeEnd&&cancelAnimationFrame(this._animID),this},t.prototype.pause=function(t){return null==t&&(t=!1),this._animPause=t?!this._animPause:!0,this},t.prototype.resume=function(){return this._animPause=!1,this},t.prototype.stop=function(t){return null==t&&(t=0),this._timeEnd=t,this},t.prototype.playTime=function(t){return null==t&&(t=5e3),this.play(),this.stop(t)},t}(),this.Space=Space,CanvasSpace=function(t){function i(t,e,n){null==t&&(t="pt_space"),null==e&&(e=!1),null==n&&(n="2d"),i.__super__.constructor.apply(this,arguments),this.space=document.querySelector("#"+this.id),this.appended=!0,this.space||(this.space=document.createElement("canvas"),this.space.setAttribute("id",this.id),this.appended=!1),this._mdown=!1,this._mdrag=!1,this.bgcolor=e,this.ctx=this.space.getContext(n)}return extend(i,t),i.prototype.display=function(t,i){var e,n;if(null==t&&(t="#pt"),!this.appended){if(e=document.querySelector(t),n=e.getBoundingClientRect(),!e)throw"Cannot add canvas to element "+t;this.resize(n.width,n.height),window.addEventListener("resize",function(t){return n=e.getBoundingClientRect(),this.resize(n.width,n.height,t)}.bind(this)),this.space.parentNode!==e&&e.appendChild(this.space),this.appended=!0,setTimeout(function(){return this.space.dispatchEvent(new Event("ready")),i?i(n.width,n.height,this.space):void 0}.bind(this))}return this},i.prototype.resize=function(t,i,e){var n,s,r;this.size.set(t,i),this.center=new Vector(t/2,i/2),this.space.setAttribute("width",Math.floor(t)),this.space.setAttribute("height",Math.floor(i)),r=this.items;for(n in r)s=r[n],null!=s.onSpaceResize&&s.onSpaceResize(t,i,e);return this.render(this.ctx),this},i.prototype.clear=function(t){var i;return t&&(this.bgcolor=t),i=this.ctx.fillStyle,this.bgcolor?(this.ctx.fillStyle=this.bgcolor,this.ctx.fillRect(0,0,this.size.x,this.size.y)):this.ctx.clearRect(0,0,this.size.x,this.size.y),this.ctx.fillStyle=i,this},i.prototype.animate=function(t){var i,e,n;this.ctx.save(),this._refresh&&this.clear(),e=this.items;for(i in e)n=e[i],n.animate(t,this._timeDiff,this.ctx);return this._timeEnd>=0&&t>this._timeEnd&&cancelAnimationFrame(this._animID),this.ctx.restore(),this},i.prototype.bindCanvas=function(t,i){return this.space.addEventListener(t,i)},i.prototype.bindMouse=function(t){return null==t&&(t=!0),t?(this.space.addEventListener("mousedown",this._mouseDown.bind(this)),this.space.addEventListener("mouseup",this._mouseUp.bind(this)),this.space.addEventListener("mouseover",this._mouseOver.bind(this)),this.space.addEventListener("mouseout",this._mouseOut.bind(this)),this.space.addEventListener("mousemove",this._mouseMove.bind(this))):(this.space.removeEventListener("mousedown",this._mouseDown.bind(this)),this.space.removeEventListener("mouseup",this._mouseUp.bind(this)),this.space.removeEventListener("mouseover",this._mouseOver.bind(this)),this.space.removeEventListener("mouseout",this._mouseOut.bind(this)),this.space.removeEventListener("mousemove",this._mouseMove.bind(this)))},i.prototype._mouseAction=function(t,i){var e,n,s,r,o,h;r=this.items,o=[];for(e in r)h=r[e],n=i.offsetX||i.layerX,s=i.offsetY||i.layerY,o.push(null!=h.onMouseAction?h.onMouseAction(t,n,s,i):void 0);return o},i.prototype._mouseDown=function(t){return this._mouseAction("down",t),this._mdown=!0},i.prototype._mouseUp=function(t){return this._mouseAction("up",t),this._mdrag&&this._mouseAction("drop",t),this._mdown=!1,this._mdrag=!1},i.prototype._mouseMove=function(t){return this._mouseAction("move",t),this._mdown?(this._mdrag=!0,this._mouseAction("drag",t)):void 0},i.prototype._mouseOver=function(t){return this._mouseAction("over",t)},i.prototype._mouseOut=function(t){return this._mouseAction("out",t),this._mdrag&&this._mouseAction("drop",t),this._mdrag=!1},i}(Space),this.CanvasSpace=CanvasSpace,DOMSpace=function(t){function i(t,e,n){null==t&&(t="pt_space"),null==e&&(e=!1),null==n&&(n="html"),i.__super__.constructor.apply(this,arguments),this.space=document.querySelector("#"+this.id),this.css={width:"100%",height:"100%"},this.appended=!0,this.space||this._createSpaceElement(),this._mdown=!1,this._mdrag=!1,this.bgcolor=e,this.ctx={}}return extend(i,t),i.prototype._createSpaceElement=function(){return this.space=document.createElement("div"),this.space.setAttribute("id",this.id),this.appended=!1},i.prototype.setCSS=function(t,i,e){return null==e&&(e=!1),this.css[t]=e?i+"px":i,this},i.prototype.updateCSS=function(){var t,i,e,n;i=this.css,e=[];for(t in i)n=i[t],e.push(this.space.style[t]=n);return e},i.prototype.display=function(t,i){var e,n;if(null==t&&(t="#pt"),!this.appended){if(e=document.querySelector(t),n=e.getBoundingClientRect(),!e)throw"Cannot add canvas to element "+t;this.resize(n.width,n.height),window.addEventListener("resize",function(t){return n=e.getBoundingClientRect(),this.resize(n.width,n.height,t)}.bind(this)),this.space.parentNode!==e&&e.appendChild(this.space),this.appended=!0,setTimeout(function(){return this.space.dispatchEvent(new Event("ready")),i?i(n.width,n.height,this.space):void 0}.bind(this))}return this},i.prototype.resize=function(t,i,e){var n,s,r;this.size.set(t,i),this.center=new Vector(t/2,i/2),r=this.items;for(n in r)s=r[n],null!=s.onSpaceResize&&s.onSpaceResize(t,i,e);return this},i.prototype.clear=function(){return this.space.innerHML=""},i.prototype.animate=function(t){var i,e,n;e=this.items;for(i in e)n=e[i],n.animate(t,this._timeDiff,this.ctx);return this._timeEnd>=0&&t>this._timeEnd&&cancelAnimationFrame(this._animID),this},i.prototype.bindCanvas=function(t,i){return this.space.addEventListener(t,i)},i.prototype.bindMouse=function(t){return null==t&&(t=!0),t?(this.space.addEventListener("mousedown",this._mouseDown.bind(this)),this.space.addEventListener("mouseup",this._mouseUp.bind(this)),this.space.addEventListener("mouseover",this._mouseOver.bind(this)),this.space.addEventListener("mouseout",this._mouseOut.bind(this)),this.space.addEventListener("mousemove",this._mouseMove.bind(this))):(this.space.removeEventListener("mousedown",this._mouseDown.bind(this)),this.space.removeEventListener("mouseup",this._mouseUp.bind(this)),this.space.removeEventListener("mouseover",this._mouseOver.bind(this)),this.space.removeEventListener("mouseout",this._mouseOut.bind(this)),this.space.removeEventListener("mousemove",this._mouseMove.bind(this)))},i.prototype._mouseAction=function(t,i){var e,n,s,r,o,h;r=this.items,o=[];for(e in r)h=r[e],n=i.offsetX||i.layerX,s=i.offsetY||i.layerY,o.push(null!=h.onMouseAction?h.onMouseAction(t,n,s,i):void 0);return o},i.prototype._mouseDown=function(t){return this._mouseAction("down",t),this._mdown=!0},i.prototype._mouseUp=function(t){return this._mouseAction("up",t),this._mdrag&&this._mouseAction("drop",t),this._mdown=!1,this._mdrag=!1},i.prototype._mouseMove=function(t){return this._mouseAction("move",t),this._mdown?(this._mdrag=!0,this._mouseAction("drag",t)):void 0},i.prototype._mouseOver=function(t){return this._mouseAction("over",t)},i.prototype._mouseOut=function(t){return this._mouseAction("out",t),this._mdrag&&this._mouseAction("drop",t),this._mdrag=!1},i.attr=function(t,i){var e,n,s;n=[];for(e in i)s=i[e],n.push(t.setAttribute(e,s));return n},i.css=function(t){var i,e,n;e="";for(i in t)n=t[i],n&&(e+=i+": "+n+"; ");return e},i}(Space),this.DOMSpace=DOMSpace,Form=function(){function t(t){this.cc=t.ctx,this.cc.fillStyle="#999",this.cc.strokeStyle="#666",this.cc.lineWidth=1,this.cc.font="11px sans-serif",this.filled=!0,this.stroked=!0,this.fontSize=11,this.fontFace="sans-serif"}return t.context=function(t){var i,e;if(e=document.getElementById(t),i=e&&e.getContext?e.getContext("2d"):!1,!i)throw"Cannot initiate canvas 2d context";return i},t.line=function(t,i){if(!i.p1)throw i.toString()+" is not a Pair";return t.beginPath(),t.moveTo(i.x,i.y),t.lineTo(i.p1.x,i.p1.y),t.stroke()},t.lines=function(i,e){var n,s,r,o;for(o=[],n=0,s=e.length;s>n;n++)r=e[n],o.push(t.line(i,r));return o},t.rect=function(t,i,e,n){if(null==e&&(e=!0),null==n&&(n=!1),!i.p1)throw""+(i.toString()===!a(Pair));return t.beginPath(),t.moveTo(i.x,i.y),t.lineTo(i.x,i.p1.y),t.lineTo(i.p1.x,i.p1.y),t.lineTo(i.p1.x,i.y),t.closePath(),n&&t.stroke(),e?t.fill():void 0},t.circle=function(t,i,e,n){null==e&&(e=!0),null==n&&(n=!1),t.beginPath(),t.arc(i.x,i.y,i.radius,0,Const.two_pi,!1),e&&t.fill(),n&&t.stroke()},t.triangle=function(t,i,e,n){null==e&&(e=!0),null==n&&(n=!1),t.beginPath(),t.moveTo(i.x,i.y),t.lineTo(i.p1.x,i.p1.y),t.lineTo(i.p2.x,i.p2.y),t.closePath(),e&&t.fill(),n&&t.stroke()},t.point=function(t,i,e,n,s,r){var o,h,u,a;return null==e&&(e=2),null==n&&(n=!0),null==s&&(s=!1),null==r&&(r=!1),r?(t.beginPath(),t.arc(i.x,i.y,e,0,Const.two_pi,!1)):(o=i.x-e,u=i.y-e,h=i.x+e,a=i.y+e,t.beginPath(),t.moveTo(o,u),t.lineTo(o,a),t.lineTo(h,a),t.lineTo(h,u),t.closePath()),n&&t.fill(),s&&t.stroke(),i},t.points=function(i,e,n,s,r,o){var h,u,a,p;for(null==n&&(n=2),null==s&&(s=!0),null==r&&(r=!1),null==o&&(o=!1),p=[],h=0,u=e.length;u>h;h++)a=e[h],p.push(t.point(i,a,n,s,r,o));return p},t.polygon=function(t,i,e,n,s){var r,o,h;if(null==e&&(e=!0),null==n&&(n=!0),null==s&&(s=!0),!(i.length<=1)){for(t.beginPath(),t.moveTo(i[0].x,i[0].y),r=o=1,h=i.length;h>o;r=o+=1)t.lineTo(i[r].x,i[r].y);e&&t.closePath(),n&&t.fill(),s&&t.stroke()}},t.curve=function(i,e){return t.polygon(i,e,!1,!1,!0)},t.text=function(t,i,e,n){return t.fillText(e,i.x,i.y,n)},t.prototype.fill=function(t){return this.cc.fillStyle=t?t:"transparent",this.filled=!!t,this},t.prototype.stroke=function(t,i,e){return this.cc.strokeStyle=t?t:"transparent",this.stroked=!!t,i&&(this.cc.lineWidth=i),e&&(this.cc.lineJoin=e),this},t.prototype.font=function(t,i){return null==i&&(i=this.fontFace),this.fontSize=t,this.cc.font=t+"px "+i,this},t.prototype.draw=function(t){return this.sketch(t)},t.prototype.sketch=function(i){return i.floor(),i instanceof Circle?t.circle(this.cc,i,this.filled,this.stroked):i instanceof Rectangle?t.rect(this.cc,i,this.filled,this.stroked):i instanceof Triangle?t.triangle(this.cc,i,this.filled,this.stroked):i instanceof Line||i instanceof Pair?t.line(this.cc,i):i instanceof PointSet?t.polygon(this.cc,i.points):(i instanceof Vector||i instanceof Point)&&t.point(this.cc,i),this},t.prototype.point=function(i,e,n){return null==e&&(e=2),null==n&&(n=!1),t.point(this.cc,i,e,this.filled,this.stroked,n),this},t.prototype.points=function(i,e,n){return null==e&&(e=2),null==n&&(n=!1),t.points(this.cc,i,e,this.filled,this.stroked,n),this},t.prototype.line=function(i){return t.line(this.cc,i),this},t.prototype.lines=function(i){return t.lines(this.cc,i),this},t.prototype.rect=function(i){return t.rect(this.cc,i,this.filled,this.stroked),this},t.prototype.circle=function(i){return t.circle(this.cc,i,this.filled,this.stroked),this},t.prototype.triangle=function(i){return t.triangle(this.cc,i,this.filled,this.stroked),this},t.prototype.polygon=function(i,e){return t.polygon(this.cc,i,e,this.filled,this.stroked),this},t.prototype.curve=function(i){return t.curve(this.cc,i),this},t.prototype.text=function(t,i,e,n,s){var r;return null==e&&(e=1e3),r=new Vector(t),n&&r.add(n,0),s&&r.add(0,s),this.cc.fillText(i,r.x,r.y,e),this},t}(),this.Form=Form,Point=function(){function t(i){this.copy(t.get(arguments))}return t.get=function(t){return t.length>0?"object"==typeof t[0]?t[0]instanceof Array||t[0].length>0?{x:t[0][0]||0,y:t[0][1]||0,z:t[0][2]||0}:{x:t[0].x||0,y:t[0].y||0,z:t[0].z||0}:{x:t[0]||0,y:t[1]||0,z:t[2]||0}:{x:0,y:0,z:0}},t.prototype.quadrant=function(t,i){return null==i&&(i=Const.epsilon),t.near(this)?Const.identical:Math.abs(t.x-this.x)this.x?Const.top_right:t.ythis.y&&t.x1?t:t[0]},i.prototype.add=function(t){var i;return"number"==typeof arguments[0]&&1===arguments.length?(this.x+=arguments[0],this.y+=arguments[0],this.z+=arguments[0]):(i=Point.get(arguments),this.x+=i.x,this.y+=i.y,this.z+=i.z),this},i.prototype.$add=function(t){var e;return e=this._getArgs(arguments),new i(this).add(e)},i.prototype.subtract=function(t){var i;return"number"==typeof arguments[0]&&1===arguments.length?(this.x-=arguments[0],this.y-=arguments[0],this.z-=arguments[0]):(i=Point.get(arguments),this.x-=i.x,this.y-=i.y,this.z-=i.z),this},i.prototype.$subtract=function(t){var e;return e=this._getArgs(arguments),new i(this).subtract(e)},i.prototype.multiply=function(t){var i;return 1===arguments.length&&("number"==typeof arguments[0]||"object"==typeof arguments[0]&&1===arguments[0].length)?(this.x*=arguments[0],this.y*=arguments[0],this.z*=arguments[0]):(i=Point.get(arguments),this.x*=i.x,this.y*=i.y,this.z*=i.z),this},i.prototype.$multiply=function(t){var e;return e=this._getArgs(arguments),new i(this).multiply(e)},i.prototype.divide=function(t){var i;return 1===arguments.length&&("number"==typeof arguments[0]||"object"==typeof arguments[0]&&1===arguments[0].length)?(this.x/=arguments[0],this.y/=arguments[0],this.z/=arguments[0]):(i=Point.get(arguments),this.x/=i.x,this.y/=i.y,this.z/=i.z),this},i.prototype.$divide=function(t){var e;return e=this._getArgs(arguments),new i(this).divide(e)},i.prototype.op=function(){var t,i,e,n,s,r;for(n=arguments[0],t=2<=arguments.length?slice.call(arguments,1):[],r=this.toArray(),i=0,e=r.length;e>i;i++)s=r[i],s[n].apply(s,t);return this},i.prototype.$op=function(){var t,i,e,n,s,r,o;for(s=arguments[0],t=2<=arguments.length?slice.call(arguments,1):[],i=this.clone(),o=i.toArray(),e=0,n=o.length;n>e;e++)r=o[e],r[s].apply(r,t);return i},i.prototype.angle=function(t){var i,e;return 0===arguments.length?Math.atan2(this.y,this.x):("string"==typeof arguments[0]?(i=arguments[0],e=arguments.length>1?this.$subtract(arguments[1]).multiply(-1):void 0):(e=this.$subtract(arguments[0]).multiply(-1),i=!1),e&&!i?Math.atan2(e.y,e.x):i===Const.xy?e?Math.atan2(e.y,e.x):Math.atan2(this.y,this.x):i===Const.yz?e?Math.atan2(e.z,e.y):Math.atan2(this.z,this.y):i===Const.xz?e?Math.atan2(e.z,e.x):Math.atan2(this.z,this.x):void 0)},i.prototype.angleBetween=function(t,i){return null==i&&(i=Const.xy),Util.boundRadian(this.angle(i),!0)-Util.boundRadian(t.angle(i),!0)},i.prototype.magnitude=function(t){var i,e,n,s,r,o;return n={x:this.x*this.x,y:this.y*this.y,z:this.z*this.z},o=arguments.length>=1&&!arguments[arguments.length-1],i=o?function(t){return t}:Math.sqrt,0===arguments.length?i(n.x+n.y+n.z):("string"==typeof arguments[0]?(e=arguments[0],r=arguments.length>1&&arguments[1]?this.$subtract(arguments[1]):void 0):(r=this.$subtract(arguments[0]),e=!1),s=r?{x:r.x*r.x,y:r.y*r.y,z:r.z*r.z}:n,r&&!e?i(s.x+s.y+s.z):e===Const.xy?i(s.x+s.y):e===Const.yz?i(s.y+s.z):e===Const.xz?i(s.x+s.z):void 0)},i.prototype.distance=function(t,i){return null==i&&(i=Const.xy),this.magnitude(i,t)},i.prototype.normalize=function(){return this.set(this.$normalize()),this},i.prototype.$normalize=function(){var t;return t=this.magnitude(),0===t?new i:new i(this.x/t,this.y/t,this.z/t)},i.prototype.abs=function(){return this.x=Math.abs(this.x),this.y=Math.abs(this.y),this.z=Math.abs(this.z),this},i.prototype.dot=function(t,i){return null==i&&(i=Const.xyz),i===Const.xyz?this.x*t.x+this.y*t.y+this.z*t.z:i===Const.xy?this.x*t.x+this.y*t.y:i===Const.yz?this.y*t.y+this.z*t.z:i===Const.xz?this.x*t.x+this.z*t.z:this.x*t.x+this.y*t.y+this.z*t.z},i.prototype.projection=function(t,e){var n,s,r,o;return null==e&&(e=Const.xyz),o=t.magnitude(),n=this.$normalize(),s=new i(t.x/o,t.y/o,t.z/o),r=n.dot(s,e),n.$multiply(o*r)},i.prototype.cross=function(t){return new i(this.y*t.z-this.z*t.y,this.z*t.x-this.x*t.z,this.x*t.y-this.y*t.x)},i.prototype.bisect=function(t,i){return null==i&&(i=!1),i?this.$add(t).divide(2):this.$normalize().add(t.$normalize()).divide(2)},i.prototype.perpendicular=function(t){switch(null==t&&(t=Const.xy),t){case Const.xy:return[new i(-this.y,this.x,this.z),new i(this.y,-this.x,this.z)];case Const.yz:return[new i(this.x,-this.z,this.y),new i(this.x,this.z,-this.y)];case Const.xz:return[new i(-this.z,this.y,this.x),new i(this.z,-this.y,this.x)];default:return[new i(-this.y,this.x,this.z),new i(this.y,-this.x,this.z)]}},i.prototype.isPerpendicular=function(t,i){return null==i&&(i=Const.xyz),0===this.dot(t,i)},i.prototype.surfaceNormal=function(t){return this.cross(t).normalize(!0)},i.prototype.moveTo=function(t){var i,e,n,s,r,o;for(o=Point.get(arguments),i=this.$subtract(o),r=this.toArray(),e=0,n=r.length;n>e;e++)s=r[e],s.subtract(i);return this},i.prototype.moveBy=function(t){var i,e,n,s,r;for(i=Point.get(arguments),r=this.toArray(),e=0,n=r.length;n>e;e++)s=r[e],s.add(i);return this},i.prototype.rotate2D=function(t,i,e){var n,s,r,o,h;for(null==e&&(e=Const.xy),i||(i=new Point(0,0,0)),r=Matrix.rotateAnchor2D(t,i,e),h=this.toArray(),n=0,s=h.length;s>n;n++)o=h[n],Matrix.transform2D(o,r,e);return this},i.prototype.reflect2D=function(t,i){var e,n,s,r,o;for(null==i&&(i=Const.xy),s=Matrix.reflectAnchor2D(t,i),o=this.toArray(),e=0,n=o.length;n>e;e++)r=o[e],Matrix.transform2D(r,s,i);return this},i.prototype.scale2D=function(t,i,e,n){var s,r,o,h,u;for(null==n&&(n=Const.xy),e||(e=new Point(0,0,0)),o=Matrix.scaleAnchor2D(t,i,e,n),u=this.toArray(),s=0,r=u.length;r>s;s++)h=u[s],Matrix.transform2D(h,o,n);return this},i.prototype.shear2D=function(t,i,e,n){var s,r,o,h,u;for(null==n&&(n=Const.xy),e||(e=new Point(0,0,0)),o=Matrix.shearAnchor2D(t,i,e,n),u=this.toArray(),s=0,r=u.length;r>s;s++)h=u[s],Matrix.transform2D(h,o,n);return this},i.prototype.clone=function(){return new i(this)},i.prototype.toString=function(){return"Vector "+this.x+", "+this.y+", "+this.z},i}(Point),this.Vector=Vector,Color=function(t){function i(t){i.__super__.constructor.apply(this,arguments),this.alpha=arguments.length>=4?Math.min(1,Math.max(arguments[3],0)):1,this.mode=arguments.length>=5?arguments[4]:"rgb"}return extend(i,t),i.XYZ={D65:{x:95.047,y:100,z:108.883}},i.parseHex=function(t,e){var n,s;return null==e&&(e=!1),0===t.indexOf("#")&&(t=t.substr(1)),3===t.length&&(t=""+t[0]+t[0]+t[1]+t[1]+t[2]+t[2]),8===t.length&&(this.alpha=1&t.substr(6),t=t.substring(0,6)),n=parseInt(t,16),s=[n>>16,n>>8&255,255&n],e?new i(s[0],s[1],s[2]):s},i.prototype.setMode=function(t){if(t=t.toLowerCase(),t!==this.mode){switch(this.mode){case"hsl":this.copy(Point.get(i.HSLtoRGB(this.x,this.y,this.z)));break;case"hsb":this.copy(Point.get(i.HSBtoRGB(this.x,this.y,this.z)));break;case"lab":this.copy(Point.get(i.LABtoRGB(this.x,this.y,this.z)));break;case"lch":this.copy(Point.get(i.LCHtoRGB(this.x,this.y,this.z)));break;case"xyz":this.copy(Point.get(i.XYZtoRGB(this.x,this.y,this.z)))}switch(t){case"hsl":this.copy(Point.get(i.RGBtoHSL(this.x,this.y,this.z)));break;case"hsb":this.copy(Point.get(i.RGBtoHSB(this.x,this.y,this.z)));break;case"lab":this.copy(Point.get(i.RGBtoLAB(this.x,this.y,this.z)));break;case"lch":this.copy(Point.get(i.RGBtoLCH(this.x,this.y,this.z)));break;case"xyz":this.copy(Point.get(i.RGBtoXYZ(this.x,this.y,this.z)))}}return this.mode=t,this},i.prototype.hex=function(){var t,i,e,n;return"rgb"===this.mode&&this.floor(),i=this.values("rgb"!==this.mode),t=function(t){return t=t.toString(16),t.length<2?"0"+t:t},e=function(){var e,s,r;for(r=[],e=0,s=i.length;s>e;e++)n=i[e],r.push(t(n));return r}(),"#"+e[0]+e[1]+e[2]},i.prototype.rgb=function(){var t;return"rgb"===this.mode&&this.floor(),t=this.values("rgb"!==this.mode),"rgb("+t[0]+", "+t[1]+", "+t[2]+")"},i.prototype.rgba=function(){var t;return"rgb"===this.mode&&this.floor(),t=this.values("rgb"!==this.mode),"rgba("+t[0]+", "+t[1]+", "+t[2]+", "+this.alpha+")"},i.prototype.values=function(t){var e,n;if(null==t&&(t=!1),e=[this.x,this.y,this.z],t&&"rgb"!==this.mode)switch(this.mode){case"hsl":e=i.HSLtoRGB(this.x,this.y,this.z);break;case"hsb":e=i.HSBtoRGB(this.x,this.y,this.z);break;case"lab":e=i.LABtoRGB(this.x,this.y,this.z);break;case"lch":e=i.LCHtoRGB(this.x,this.y,this.z);break;case"xyz":e=i.XYZtoRGB(this.x,this.y,this.z)}return function(){var t,i,s;for(s=[],t=0,i=e.length;i>t;t++)n=e[t],s.push(Math.floor(n));return s}()},i.RGBtoHSL=function(t,i,e,n,s){var r,o,h,u,a,p;if(n||(t/=255,i/=255,e/=255),u=Math.max(t,i,e),a=Math.min(t,i,e),o=(u+a)/2,p=o,h=o,u===a)o=0,p=0;else switch(r=u-a,p=h>.5?r/(2-u-a):r/(u+a),u){case t:o=(i-e)/r+(e>i?6:0);break;case i:o=(e-t)/r+2;break;case e:o=(t-i)/r+4;break;default:o=0}return s?[o/60,p,h]:[60*o,p,h]},i.HSLtoRGB=function(t,i,e,n,s){var r,o,h,u,a,p;return 0===i?s?[1,1,1]:[255,255,255]:(n||(t/=360),a=.5>=e?e*(1+i):e+i-e*i,u=2*e-a,h=function(t,i,e){return 0>e?e+=1:e>1&&(e-=1),1>6*e?t+(i-t)*e*6:1>2*e?i:2>3*e?t+(i-t)*(2/3-e)*6:t},p=h(u,a,t+1/3),o=h(u,a,t),r=h(u,a,t-1/3),s?[p,o,r]:[255*p,255*o,255*r])},i.RGBtoHSB=function(t,i,e,n,s){var r,o,h,u,a,p;if(n||(t/=255,i/=255,e/=255),h=Math.max(t,i,e),u=Math.min(t,i,e),r=h-u,a=0===h?0:r/h,p=h,h===u)o=0;else switch(h){case t:o=(i-e)/r+(e>i?6:0);break;case i:o=(e-t)/r+2;break;case e:o=(t-i)/r+4;break;default:o=0}return s?[o/60,a,p]:[60*o,a,p]},i.HSBtoRGB=function(t,i,e,n,s){var r,o,h,u,a,p;switch(n||(t/=360),o=Math.floor(6*t),r=6*t-o,h=e*(1-i),u=e*(1-r*i),p=e*(1-(1-r)*i),o%6){case 0:a=[e,p,h];break;case 1:a=[u,e,h];break;case 2:a=[h,e,p];break;case 3:a=[h,u,e];break;case 4:a=[p,h,e];break;case 5:a=[e,h,u];break;default:a=[0,0,0]}return s?a:[255*a[0],255*a[1],255*a[2]]},i.RGBtoLAB=function(t,e,n,s,r){var o;return s&&(t*=255,e*=255,n*=255),o=i.RGBtoXYZ(t,e,n),i.XYZtoLAB(o[0],o[1],o[2])},i.LABtoRGB=function(t,e,n,s,r){var o,h;return s&&(t*=100,e=127*(e-.5),n=127*(n-.5)),h=i.LABtoXYZ(t,e,n),o=i.XYZtoRGB(h[0],h[1],h[2]),r?[o[0]/255,o[1]/255,o[2]/255]:o},i.RGBtoLCH=function(t,e,n,s,r){var o,h;return s&&(t*=255,e*=255,n*=255),o=i.RGBtoLAB(t,e,n),h=i.LABtoLCH(o[0],o[1],o[2]),r?[h[0]/100,h[1]/100,h[2]/360]:h},i.LCHtoRGB=function(t,e,n,s,r){var o,h,u;return s&&(t*=100,e*=100,n*=360),o=i.LCHtoLAB(t,e,n),u=i.LABtoXYZ(o[0],o[1],o[2]),h=i.XYZtoRGB(u[0],u[1],u[2]),r?[h[0]/255,h[1]/255,h[2]/255]:h},i.XYZtoRGB=function(t,i,e,n,s){var r,o,h,u,a;for(n||(t/=100,i/=100,e/=100),a=[3.2404542*t+-1.5371385*i+e*-.4985314,t*-.969266+1.8760108*i+.041556*e,.0556434*t+i*-.2040259+1.0572252*e],o=h=0,u=a.length;u>h;o=++h)r=a[o],a[o]=0>r?0:Math.min(1,r>.0031308?1.055*Math.pow(r,1/2.4)-.055:12.92*r);return s?a:[Math.round(255*a[0]),Math.round(255*a[1]),Math.round(255*a[2])]},i.RGBtoXYZ=function(t,i,e,n,s){return n||(t/=255,i/=255,e/=255),t=t>.04045?Math.pow((t+.055)/1.055,2.4):t/12.92,i=i>.04045?Math.pow((i+.055)/1.055,2.4):i/12.92,e=e>.04045?Math.pow((e+.055)/1.055,2.4):e/12.92,s||(t=100*t,i=100*i,e=100*e),[.4124564*t+.3575761*i+.1804375*e,.2126729*t+.7151522*i+.072175*e,.0193339*t+.119192*i+.9503041*e]},i.XYZtoLAB=function(t,e,n){var s,r;return t/=i.XYZ.D65.x,e/=i.XYZ.D65.y,n/=i.XYZ.D65.z,s=function(t){return t>.008856?Math.pow(t,1/3):7.787*t+16/116},r=s(e),[116*r-16,500*(s(t)-r),200*(r-s(n))]},i.LABtoXYZ=function(t,e,n){var s,r,o,h,u;return h=(t+16)/116,r=e/500+h,u=h-n/200,s=function(t){var i;return i=Math.pow(t,3),i>.008856?i:(t-16/116)/7.787},o=[Math.min(i.XYZ.D65.x,i.XYZ.D65.x*s(r)),Math.min(i.XYZ.D65.y,i.XYZ.D65.y*s(h)),Math.min(i.XYZ.D65.y,i.XYZ.D65.z*s(u))]},i.XYZtoLUV=function(t,e,n){var s,r,o,h,u;return h=4*t/(t+15*e+3*n),u=9*e/(t+15*e+3*n),e/=100,e=e>.008856?Math.pow(e,1/3):7.787*e+16/116,r=4*i.XYZ.D65.x/(i.XYZ.D65.x+15*i.XYZ.D65.y+3*i.XYZ.D65.z),o=9*i.XYZ.D65.y/(i.XYZ.D65.x+15*i.XYZ.D65.y+3*i.XYZ.D65.z),s=116*e-16,[s,13*s*(h-r),13*s*(u-o)]},i.LUVtoXYZ=function(t,e,n){var s,r,o,h,u;return u=(t+16)/116,s=u*u*u,u=s>.008856?s:(u-16/116)/7.787,r=4*i.XYZ.D65.x/(i.XYZ.D65.x+15*i.XYZ.D65.y+3*i.XYZ.D65.z), 2 | o=9*i.XYZ.D65.y/(i.XYZ.D65.x+15*i.XYZ.D65.y+3*i.XYZ.D65.z),e=e/(13*t)+r,n=n/(13*t)+o,u=100*u,h=-9*u*e/((e-4)*n-e*n),[h,u,(9*u-15*n*u-n*h)/(3*n)]},i.LABtoLCH=function(t,i,e){var n;return n=Math.atan2(e,i),n=n>0?180*n/Math.PI:360-180*Math.abs(n)/Math.PI,[t,Math.sqrt(i*i+e*e),n]},i.LCHtoLAB=function(t,i,e){var n;return n=Math.PI*e/180,[t,Math.cos(n)*i,Math.sin(n)*i]},i.LUVtoLCH=function(t,i,e){return LABtoLCH(t,i,e)},i.LCHtoLUV=function(t,i,e){return LCHtoLAB(t,i,e)},i}(Vector),this.Color=Color,Circle=function(t){function i(){i.__super__.constructor.apply(this,arguments),this.radius=null!=arguments[3]?arguments[3]:0}return extend(i,t),i.prototype.setRadius=function(t){return this.radius=t,this},i.prototype.intersectPoint=function(t){var i,e;return e=new Vector(Point.get(arguments)),i=e.$subtract(this),i.x*i.x+i.y*i.yo?i?[]:!1:i?(h=Math.sqrt(o),y=-a+h,f=-a-h,p=new Point(t.x-r.x*y,t.y-r.y*y),c=new Point(t.x-r.x*f,t.y-r.y*f),0===o?[p]:[p,c]):!0)},i.prototype.intersectLine=function(t,i){var e,n,s,r,o,h;if(null==i&&(i=!0),h=this.intersectPath(t),h&&h.length>0){for(o=[],e=t.bounds(),n=0,s=h.length;s>n;n++)if(r=h[n],Rectangle.contain(r,e,e.p1)){if(!i)return!0;o.push(r)}return i?o:o.length>0}return i?[]:!1},i.prototype.intersectLines=function(t,i){return null==i&&(i=!0),Line.intersectLines(this,t,i)},i.prototype.intersectCircle=function(t,i){var e,n,s,r,o,h,u,a;return null==i&&(i=!0),h=t.$subtract(this),o=h.magnitude(!1),r=Math.sqrt(o),n=this.radius*this.radius,s=t.radius*t.radius,r>this.radius+t.radius?i?[]:!1:r0):t instanceof Point?(n=t.$subtract(this),n.x*n.x+n.y*n.y0;)e=Math.min(i,this.frame_ms),this.integrate(s/1e3,e/1e3),i-=e,s+=e,n.push(this.life.age++);return n},i.prototype.integrate=function(t,i){return this.integrateRK4(t,i)},i.prototype.forces=function(t,i){return{force:new Vector}},i.prototype.impulse=function(t){return this.momentum.add(t),this.velocity=this.momentum.$divide(this.mass)},i.prototype._evaluate=function(t,i,e){var n,s;return null==i&&(i=0),null==e&&(e=!1),s=0!==i&&e?{position:this.$add(e.velocity.$multiply(i)),momentum:this.momentum.$add(e.force.$multiply(i))}:{position:new Vector(this),momentum:new Vector(this.momentum)},s.velocity=s.momentum.$divide(this.mass),n=this.forces(s,t+i),{velocity:s.velocity,force:n.force}},i.prototype.integrateRK4=function(t,i){var e,n,s,r,o;return e=function(t,i,e,n){var s;return s=new Vector((t.x+2*(i.x+e.x)+n.x)/6,(t.y+2*(i.y+e.y)+n.y)/6,(t.z+2*(i.z+e.z)+n.z)/6)},n=this._evaluate(t,0),s=this._evaluate(t,.5*i,n),r=this._evaluate(t,.5*i,s),o=this._evaluate(t,i,r),this.add(e(n.velocity,s.velocity,r.velocity,o.velocity)),this.momentum.add(e(n.force,s.force,r.force,o.force))},i.prototype.integrateEuler=function(t,i){var e;return e=this.forces({position:new Vector(this),momentum:new Vector(this.momentum)},t+i),this.add(this.velocity),this.momentum.add(e.force),this.velocity=this.momentum.$divide(this.mass)},i.prototype.collideLine2d=function(t,i){var e,n,s,r,o,h,u,a,p,c,l,y,f,m,d,g,x,v,z,_;if(null==i&&(i=!0),o=new Vector(this),r=Math.abs(t.getDistanceFromPoint(o)),n=Math.abs(r)t.p1.x?(this.x-this.radiust.p1.x&&(this.x=t.p1.x-this.radius),this.velocity.x*=-1,this.momentum=this.velocity.$multiply(this.mass),!0):this.y-this.radiust.p1.y?(this.y-this.radiust.p1.y&&(this.y=t.p1.y-this.radius),this.velocity.y*=-1,this.momentum=this.velocity.$multiply(this.mass),!0):!1},i.prototype.collideParticle2d=function(t){return this.hasIntersect(t)?i.collideParticle2d(this,t,!0):!1},i.collideParticle2d=function(t,i,e,n){var s,r,o,h,u,a,p,c,l,y,f,m,d,g,x,v,z;return null==e&&(e=!0),null==n&&(n=!0),y=t.$subtract(i).normalize(),d=new Vector(-y.y,y.x),h=y.dot(t.velocity),u=d.dot(t.velocity),a=y.dot(i.velocity),p=d.dot(i.velocity),s=(h*(t.mass-i.mass)+2*i.mass*a)/(t.mass+i.mass),r=(a*(i.mass-t.mass)+2*t.mass*h)/(t.mass+i.mass),g=y.$multiply(s),x=d.$multiply(u),v=y.$multiply(r),z=d.$multiply(p),f=g.$add(x),m=v.$add(z),n&&(c=t.magnitude(i),co;s=++o)p=c[s],p.life.complete?n.push(s):p.life.active&&p.animate(t,i,e);if(n.length>0){for(l=[],a=0,u=n.length;u>a;a++)r=n[a],l.push(this.particles.splice(r,1));return l}},t}(),this.ParticleSystem=ParticleSystem,Pair=function(t){function i(){i.__super__.constructor.apply(this,arguments),this.p1=new Vector(this.x,this.y,this.z),4===arguments.length?(this.z=0,this.p1.set(arguments[2],arguments[3])):6===arguments.length&&this.p1.set(arguments[3],arguments[4],arguments[5])}return extend(i,t),i.prototype.to=function(){return this.p1=new Vector(Point.get(arguments)),this},i.prototype.getAt=function(t){return 1===t||"p1"===t?this.p1:this},i.prototype.$getAt=function(t){return new Vector(this.getAt(t))},i.prototype.relative=function(){return this.p1.add(this),this},i.prototype.$relative=function(){return this.$add(this.p1)},i.prototype.bounds=function(){return new i(this.$min(this.p1)).to(this.$max(this.p1))},i.prototype.withinBounds=function(t,i){var e,n;return i?(e=this.get2D(i),n=this.p1.get2D(i),e.x===n.x?t.y>=Math.min(e.y,n.y)&&t.y<=Math.max(e.y,n.y):e.y===n.y?t.x>=Math.min(e.x,n.x)&&t.x<=Math.max(e.x,n.x):t.x>=Math.min(e.x,n.x)&&t.y>=Math.min(e.y,n.y)&&t.x<=Math.max(e.x,n.x)&&t.y<=Math.max(e.y,n.y)):t.x>=Math.min(this.x,this.p1.x)&&t.y>=Math.min(this.y,this.p1.y)&&t.z>=Math.min(this.z,this.p1.z)&&t.x<=Math.max(this.x,this.p1.x)&&t.y<=Math.max(this.y,this.p1.y)&&t.z<=Math.max(this.z,this.p1.z)},i.prototype.interpolate=function(t,i){var e;return null==i&&(i=!1),e=i?this.$relative():this.p1,new Vector((1-t)*this.x+t*e.x,(1-t)*this.y+t*e.y,(1-t)*this.z+t*e.z)},i.prototype.midpoint=function(){return this.interpolate(.5)},i.prototype.direction=function(t){return t?this.$subtract(this.p1):this.p1.$subtract(this)},i.prototype.size=function(){return arguments.length>0?(this.p1=this.$add(Point.get(arguments)),this):this.p1.$subtract(this).abs()},i.prototype.length=function(t){var i,e,n,s;return null==t&&(t=!0),s=this.z-this.p1.z,n=this.y-this.p1.y,e=this.x-this.p1.x,i=e*e+n*n+s*s,t?Math.sqrt(i):i},i.prototype.collinear=function(t){return(this.p1.x-this.x)*(t.y-this.y)-(t.x-this.x)*(this.p1.y-this.y)},i.prototype.resetBounds=function(){var t;return t=this.$min(this.p1),this.p1.set(this.$max(this.p1)),this.set(t),this},i.prototype.equal=function(t){return null==t&&(t=!1),arguments[0]instanceof i?i.__super__.equal.call(this,arguments[0])&&this.p1.equal(arguments[0].p1):i.__super__.equal.apply(this,arguments)},i.prototype.clone=function(){var t;return t=new i(this),t.to(this.p1.clone()),t},i.prototype.floor=function(){return i.__super__.floor.apply(this,arguments),this.p1.floor()},i.prototype.toString=function(){return"Pair of vectors from ("+this.x+", "+this.y+", "+this.z+") to ("+this.p1.x+", "+this.p1.y+", "+this.p1.z+")"},i.prototype.toArray=function(){return[this,this.p1]},i}(Vector),this.Pair=Pair,Line=function(t){function i(){i.__super__.constructor.apply(this,arguments)}return extend(i,t),i.slope=function(t,i,e){var n,s;return null==e&&(e=Const.xy),n=t.get2D(e),s=i.get2D(e),s.x-n.x===0?!1:(s.y-n.y)/(s.x-n.x)},i.intercept=function(t,i,e){var n,s,r,o;return null==e&&(e=Const.xy),r=t.get2D(e),o=i.get2D(e),o.x-r.x===0?!1:(s=(o.y-r.y)/(o.x-r.x),n=r.y-s*r.x,{slope:s,yi:n,xi:0===s?!1:-n/s})},i.isPerpendicularLine=function(t,e,n){var s,r;return null==n&&(n=Const.xy),s=i.slope(t,t.p1,n),r=i.slope(e,e.p1,n),s===!1?0===r:r===!1?0===s:s*r===-1},i.prototype.slope=function(t){return null==t&&(t=Const.xy),i.slope(this,this.p1,t)},i.prototype.intercept=function(t){return null==t&&(t=Const.xy),i.intercept(this,this.p1,t)},i.prototype.getPerpendicular=function(t,e,n,s){var r,o,h;return null==e&&(e=10),null==n&&(n=!1),null==s&&(s=Const.xy),o=this.direction().normalize().perpendicular(s),h=n?o[1]:o[0],r=new i(this.interpolate(t)),r.to(h.multiply(e).add(r)),r},i.prototype.getDistanceFromPoint=function(t){var i,e;return e=this.$subtract(this.p1),i=new Vector(-e.y,e.x).normalize(),this.$subtract(t).dot(i)},i.prototype.getPerpendicularFromPoint=function(t,i){var e;return null==i&&(i=!0),e=this.p1.$subtract(this).projection(t.$subtract(this)),i?e.add(this):e},i.prototype.intersectPath=function(t,i){var e,n,s,r,o,h,u;return null==i&&(i=Const.xy),e=this.intercept(i),n=t.intercept(i),r=this.get2D(i),s=t.get2D(i),e===!1?n===!1?!1:(u=-n.slope*(s.x-r.x)+s.y,i===Const.xy?new Vector(r.x,u):new Vector(r.x,u).get2D(i,!0)):n===!1?(u=-e.slope*(r.x-s.x)+r.y,new Vector(s.x,u)):n.slope!==e.slope?(o=(e.slope*r.x-n.slope*s.x+s.y-r.y)/(e.slope-n.slope),h=e.slope*(o-r.x)+r.y,i===Const.xy?new Vector(o,h):new Vector(o,h).get2D(i,!0)):e.yi===n.yi?null:!1},i.prototype.intersectLine=function(t,i){var e;return null==i&&(i=Const.xy),e=this.intersectPath(t,i),e&&this.withinBounds(e,i)&&t.withinBounds(e,i)?e:null===e?null:!1},i.intersectLines=function(t,i,e){var n,s,r,o,h,u,a;if(null==e&&(e=!0),!t.intersectLine)throw"No intersectLine function found in "+t.toString();a=[];for(n in i)if(h=i[n],s=t.intersectLine(h,e)){if(!e)return!0;if(s.length>0)for(r=0,o=s.length;o>r;r++)u=s[r],a.push(u)}return e?a:!1},i.prototype.intersectGridLine=function(t,i,e){var n,s,r,o;if(null==i&&(i=!1),null==e&&(e=Const.xy),n=this.get2D(e),s=this.p1.get2D(e),r=t.get2D(e),o=t.p1.get2D(e),s.x-n.x===0){if(o.y-r.y===0&&Util.within(n.x,r.x,o.x)&&(i||Util.within(r.y,n.y,s.y)))return new Vector(n.x,r.y)}else{if(s.y-n.y!==0)return!1;if(o.x-r.x===0&&Util.within(n.y,r.y,o.y)&&(i||Util.within(r.x,n.x,s.x)))return new Vector(r.x,n.y)}},i.prototype.subpoints=function(t){var i,e,n,s;for(n=[],s=i=0,e=t;e>=0?e>=i:i>=e;s=e>=0?++i:--i)n.push(this.interpolate(s/t));return n},i.prototype.clone=function(t){return new i(this).to(this.p1)},i}(Pair),this.Line=Line,Rectangle=function(t){function i(){i.__super__.constructor.apply(this,arguments),this.center=new Vector}return extend(i,t),i.contain=function(t,i,e){return t.x>=i.x&&t.x<=e.x&&t.y>=i.y&&t.y<=e.y&&t.z>=i.z&&t.z<=e.z},i.prototype.toString=function(){var t;return t=this.size(),"Rectangle x1 "+this.x+", y1 "+this.y+", z1 "+this.z+", x2 "+this.p1.x+", y2 "+this.p1.y+", z2 "+this.p1.z+", width "+t.x+", height "+t.y},i.prototype.toPointSet=function(){var t;return t=this.corners(),new PointSet(this).to([t.topRight,t.bottomRight,t.bottomLeft,t.topLeft])},i.prototype.to=function(t){return this.p1=new Vector(Point.get(arguments)),this.resetBounds(),this.center=this.midpoint(),this},i.prototype.setCenter=function(t){var i;return 0===arguments.length?void(this.center=this.midpoint()):(i=this.size().$divide(2),this.center.set(Point.get(arguments)),this.set(this.center.$subtract(i)),this.p1.set(this.center.$add(i)),this)},i.prototype.resizeTo=function(){return this.p1=new Vector(Point.get(arguments)),this.relative(),this.center=this.midpoint(),this},i.prototype.resizeCenterTo=function(){var t;return t=new Vector(Point.get(arguments)).divide(2),this.set(this.center.$subtract(t)),this.p1.set(this.center.$add(t)),this},i.prototype.enclose=function(t){return this.set(this.$min(t)),this.p1.set(this.p1.$max(t.p1)),this.center=this.midpoint(),this},i.prototype.$enclose=function(t){return this.clone().enclose(t)},i.prototype.isEnclosed=function(t){var i,e;return i=this.$subtract(t).multiply(this.p1.$subtract(t.p1)),e=this.size().subtract(t.size()),i.x<=0&&i.y<=0&&i.z<=0&&e.x*e.y>=0},i.prototype.isLarger=function(t){var i,e;return i=this.size(),e=t.size(),i.x*i.y>e.x*e.y},i.prototype.intersectPoint=function(){var t;return t=Point.get(arguments),t.x>=this.x&&t.x<=this.p1.x&&t.y>=this.y&&t.y<=this.p1.y&&t.z>=this.z&&t.z<=this.p1.z},i.prototype.intersectPath=function(t,i){var e,n,s,r,o,h;for(null==i&&(i=!0),h=this.sides(),r=[],e=0,n=h.length;n>e;e++)if(o=h[e],s=o.intersectPath(t),s&&this.intersectPoint(s)){if(!i)return!0;r.push(s)}return i?r:!1},i.prototype.intersectLine=function(t,i){var e,n,s,r,o,h,u,a,p;if(null==i&&(i=!0),e=this.intersectPoint(t),n=this.intersectPoint(t.p1),e&&n&&i)return[];if(!e&&!n&&(r=t.bounds(),!this.intersectRectangle(r,!1)))return i?[]:!1;for(p=this.sides(),u=[],s=0,o=p.length;o>s;s++)if(a=p[s],h=t.intersectLine(a)){if(!i)return!0;u.push(h)}return i?u:!1},i.prototype.intersectLines=function(t,i){return null==i&&(i=!0),Line.intersectLines(this,t,i)},i.prototype.intersectRectangle=function(t,i){var e,n,s,r,o,h,u,a,p,c,l,y,f,m;if(null==i&&(i=!0),y=this.p1.x>=t.x&&this.x<=t.p1.x,f=this.p1.y>=t.y&&this.y<=t.p1.y,m=this.p1.z>=t.z&&this.z<=t.p1.z,e=y&&f&&m,!i)return e;if(this.isEnclosed(t))return i?[]:!0;if(!e)return[];for(c=this.sides(),l=t.sides(),u=[],n=0,s=c.length;s>n;n++)for(a=c[n],o=0,r=l.length;r>o;o++)p=l[o],h=a.intersectGridLine(p),h&&u.push(h);return u},i.prototype.hasIntersect=function(t,e){return null==e&&(e=!1),t instanceof Circle?t.intersectLines(this.sides(),e):t instanceof i?this.intersectRectangle(t,e):t instanceof PointSet||t instanceof Triangle?this.intersectLines(t.sides(),e):t instanceof Pair?this.intersectLine(t,e):t instanceof Point?i.contain(t,this,this.p1):e?[]:!1},i.prototype.corners=function(){return{topLeft:new Vector(Math.min(this.x,this.p1.x),Math.min(this.y,this.p1.y),Math.max(this.z,this.p1.z)),topRight:new Vector(Math.max(this.x,this.p1.x),Math.min(this.y,this.p1.y),Math.min(this.z,this.p1.z)),bottomLeft:new Vector(Math.min(this.x,this.p1.x),Math.max(this.y,this.p1.y),Math.max(this.z,this.p1.z)),bottomRight:new Vector(Math.max(this.x,this.p1.x),Math.max(this.y,this.p1.y),Math.min(this.z,this.p1.z))}},i.prototype.sides=function(){var t;return t=this.corners(),[new Line(t.topLeft).to(t.topRight),new Line(t.topRight).to(t.bottomRight),new Line(t.bottomRight).to(t.bottomLeft),new Line(t.bottomLeft).to(t.topLeft)]},i.prototype.quadrants=function(){var t;return t=this.corners(),{topLeft:new this.__proto__.constructor(t.topLeft).to(this.center),topRight:new this.__proto__.constructor(t.topRight).to(this.center),bottomLeft:new this.__proto__.constructor(t.bottomLeft).to(this.center),bottomRight:new this.__proto__.constructor(t.bottomRight).to(this.center)}},i.prototype.clone=function(){var t;return t=new i(this).to(this.p1),t.to(this.p1.clone()),t},i}(Pair),this.Rectangle=Rectangle,Grid=function(t){function i(){i.__super__.constructor.apply(this,arguments),this.cell={type:"fix-fix",size:new Vector},this.rows=0,this.columns=0,this.layout=[],this.cellCallback=null}return extend(i,t),i.prototype.toString=function(){var t;return t=this.size(),"Grid width "+t.x+", height "+t.y+", columns "+this.columns+", rows "+this.rows+", "+("cell ("+this.cell.size.x+", "+this.cell.size.y+"), type "+this.cell.type)},i.prototype.init=function(t,i,e,n){var s;return null==e&&(e="fix"),null==n&&(n="fix"),s=this.size(),this.cell.type=e+"-"+n,this.rows=i,this.columns=t,"stretch"===e?(this.cell.size.x=s.x/t,this.columns=t):"flex"===e?(this.columns=Math.round(s.x/t),this.cell.size.x=s.x/this.columns):(this.cell.size.x=t,this.columns=Math.floor(s.x/this.cell.size.x)),"stretch"===n?(this.cell.size.y=s.y/i,this.rows=i):"flex"===n?(this.rows=Math.round(s.y/i),this.cell.size.y=s.y/this.rows):(this.cell.size.y=i,this.rows=Math.floor(s.y/this.cell.size.y)),this},i.prototype.generate=function(t){return"function"==typeof t&&(this.cellCallback=t),this},i.prototype.create=function(){var t,i,e,n,s,r,o,h,u;if(!this.cellCallback)return this;for(t=n=0,h=this.columns;h>=0?h>n:n>h;t=h>=0?++n:--n)for(o=s=0,u=this.rows;u>=0?u>s:s>u;o=u>=0?++s:--s)i=this.cell.size.clone(),r=this.$add(i.$multiply(t,o)),e=this.layout.length>0&&this.layout[0].length>0?1===this.layout[o][t]:!1,this.cellCallback(i,r,o,t,this.cell.type,e);return this},i.prototype.getCellSize=function(){return this.cell.size.clone()},i.prototype.cellToRectangle=function(t,i,e){var n;return null==e&&(e=!1),e||t>=0&&t=0&&i=0?r>e:e>r;s=r>=0?++e:--e)for(this.layout[s]=[],i=n=0,o=this.columns;o>=0?o>n:n>o;i=o>=0?++n:--n)this.layout[s][i]=0,t&&t(this,s,i);return this},i.prototype.occupy=function(t,i,e,n,s){var r,o,h,u,a,p;if(null==s&&(s=!0),this.rows<=0||this.columns<=0)return this;for(this.layout.length<1&&this.resetLayout(),r=o=0,a=e;a>=0?a>o:o>a;r=a>=0?++o:--o)for(u=h=0,p=n;p>=0?p>h:h>p;u=p>=0?++h:--h)this.layout[Math.min(this.layout.length-1,i+u)][t+r]=s?1:0;return this},i.prototype.canFit=function(t,i,e,n){var s,r,o,h,u,a,p,c,l;for(o=h=a=i,p=Math.min(this.rows,i+n);p>=a?p>h:h>p;o=p>=a?++h:--h)for(r=u=c=t,l=Math.min(this.columns,t+e);l>=c?l>u:u>l;r=l>=c?++u:--u)if(s=this.layout[o][r],null!=s&&s>0)return!1;return!0},i.prototype.fit=function(t,i){var e,n,s,r,o,h,u,a,p,c,l;for(r=Math.min(t,this.columns),h=a=0,c=this.rows;c>=0?c>a:a>c;h=c>=0?++a:--a)for(s=r,u=0,o=p=0,l=this.columns;l>=0?l>p:p>l;o=l>=0?++p:--p)if(n=this.layout[h][o],null!=n&&n>0)u++,s=r;else if(s--,0>=s)return this.occupy(u,h,r,i),e=new Rectangle(this.$add(this.cell.size.$multiply(u,h))),e.resizeTo(this.cell.size.$multiply(r,i)),{row:h,column:u,columnSize:r,rowSize:i,bound:e};return!1},i.prototype.neighbors=function(t,i){var e,n,s,r,o;for(o=[[t-1,i-1],[t,i-1],[t+1,i-1],[t+1,i],[t+1,i+1],[t,i+1],[t-1,i+1],[t-1,i]],r=[],e=0,n=o.length;n>e;e++)s=o[e],r.push(s[0]>=0&&s[0]=0&&s[1]t;t++)e=n[t],s+=e.x+","+e.y+","+e.z+", ";return s+" ]"},i.prototype.toArray=function(){return this.points.slice()},i.prototype.to=function(t){var i,e,n,s;if(arguments.length>0)if(Array.isArray(arguments[0])&&arguments[0].length>0&&"object"==typeof arguments[0][0])for(s=arguments[0],i=0,e=s.length;e>i;i++)n=s[i],this.points.push(new Vector(n));else this.points.push(new Vector(Point.get(arguments)));return this},i.prototype.getAt=function(t){return this.points[Math.min(this.points.length-1,Math.max(0,t))]},i.prototype.$getAt=function(t){return new Vector(this.getAt(t))},i.prototype.setAt=function(t,i){return this.points[t]=i,this},i.prototype.count=function(){return this.points.length},i.prototype.connectFromAnchor=function(t){var i,e,n,s;if(arguments.length>0)if(Array.isArray(arguments[0])&&arguments[0].length>0)for(s=arguments[0],i=0,e=s.length;e>i;i++)n=s[i],this.points.push(this.$add(n));else this.points.push(this.$add(Point.get(arguments)));return this},i.prototype.disconnect=function(t){return null==t&&(t=-1),this.points=0>t?this.points.slice(0,this.points.length+t):this.points.slice(t+1),this},i.prototype.sides=function(t){var i,e,n,s,r,o;for(null==t&&(t=!0),e=null,o=[],r=this.points,i=0,n=r.length;n>i;i++)s=r[i],e&&o.push(new Line(e).to(s)),e=s;return this.points.length>1&&t&&o.push(new Line(e).to(this.points[0])),o},i.prototype.angles=function(t){var i,e,n,s,r,o;for(null==t&&(t=Const.xy),i=[],e=n=1,s=this.points.length-1;s>n;e=n+=1)r=this.points[e-1].$subtract(this.points[e]),o=this.points[e+1].$subtract(this.points[e]),i.push({p0:this.points[e-1],p1:this.points[e],p2:this.points[e+1],angle:r.angleBetween(o)});return i},i.prototype.bounds=function(){return Util.boundingBox(this.points)},i.prototype.centroid=function(){return Util.centroid(this.points)},i.prototype.convexHull=function(t){var i,e,n,s,r;if(null==t&&(t=!0),this.points.length<3)return[];for(t?(r=this.points.slice(),r.sort(function(t,i){return t.x-i.x})):r=this.points,n=function(t,i,e){return(i.x-t.x)*(e.y-t.y)-(e.x-t.x)*(i.y-t.y)>0},i=[],n(r[0],r[1],r[2])?(i.push(r[0]),i.push(r[1])):(i.push(r[1]),i.push(r[0])),i.unshift(r[2]),i.push(r[2]),e=3;e=i;n=i+=1)s=n/t,r.push([s,s*s,s*s*s]);return r},i.prototype.controlPoints=function(t,i){var e,n,s,r,o;return null==t&&(t=0),null==i&&(i=!1),e=function(t){return function(i){var e;return e=i=n;e=n+=1)o.push(this.catmullRomPoint(a[e],i));for(s=0;s=r;e=r+=1)o.push(this.catmullRomPoint(a[e],i));s++}return o},i.prototype.catmullRomPoint=function(t,i){var e,n,s,r,o,h,u,a,p,c;return o=t[0],h=t[1],u=t[2],e=-.5*u+h-.5*o,n=1.5*u-2.5*h+1,s=-1.5*u+2*h+.5*o,r=.5*u-.5*h,a=e*i.p0.x+n*i.p1.x+s*i.p2.x+r*i.p3.x,p=e*i.p0.y+n*i.p1.y+s*i.p2.y+r*i.p3.y,c=this.is3D?e*i.p0.z+n*i.p1.z+s*i.p2.z+r*i.p3.z:0,new Point(a,p,c)},i.prototype.cardinal=function(t,i){var e,n,s,r,o,h,u,a,p;if(null==t&&(t=10),null==i&&(i=.5),this.points.length<2)return[];for(h=[],p=this._getSteps(t),e=this.controlPoints(0,!0),n=s=0,u=t;u>=s;n=s+=1)h.push(this.cardinalPoint(p[n],e,i));for(r=0;r=o;n=o+=1)h.push(this.cardinalPoint(p[n],e,i));r++}return h},i.prototype.cardinalPoint=function(t,i,e){var n,s,r,o,h,u,a,p,c,l,y,f;return null==e&&(e=.5),a=t[0],p=t[1],c=t[2],n=e*(-1*c+2*p-a),s=e*(-1*c+p),r=2*c-3*p+1,o=e*(c-2*p+a),h=-2*c+3*p,u=e*(c-p),l=i.p0.x*n+i.p1.x*s+r*i.p1.x+i.p2.x*o+h*i.p2.x+i.p3.x*u,y=i.p0.y*n+i.p1.y*s+r*i.p1.y+i.p2.y*o+h*i.p2.y+i.p3.y*u,f=this.is3D?i.p0.z*n+i.p1.z*s+r*i.p1.z+i.p2.z*o+h*i.p2.z+i.p3.z*u:0,new Point(l,y,f)},i.prototype.bezier=function(t){var i,e,n,s,r,o,h;if(null==t&&(t=10),this.points.length<4)return[];for(r=[],h=this._getSteps(t),s=0;s<=this.points.length-3;)if(i=this.controlPoints(s)){for(e=n=0,o=t;o>=n;e=n+=1)r.push(this.bezierPoint(h[e],i));s+=3}return r},i.prototype.bezierPoint=function(t,i){var e,n,s,r,o,h,u,a,p,c;return o=t[0],h=t[1],u=t[2],e=-1*u+3*h-3*o+1,n=3*u-6*h+3*o,s=-3*u+3*h,r=u,a=e*i.p0.x+n*i.p1.x+s*i.p2.x+r*i.p3.x,p=e*i.p0.y+n*i.p1.y+s*i.p2.y+r*i.p3.y,c=this.is3D?e*i.p0.z+n*i.p1.z+s*i.p2.z+r*i.p3.z:0,new Point(a,p,c)},i.prototype.bspline=function(t,i){var e,n,s,r,o,h,u,a,p;if(null==t&&(t=10),null==i&&(i=!1),this.points.length<2)return[];for(h=[],p=this._getSteps(t),r=0;r=o;n=o+=1)h.push(this.bsplineTensionPoint(p[n],e,i));else for(n=s=0,u=t;u>=s;n=s+=1)h.push(this.bsplinePoint(p[n],e));r++}return h},i.prototype.bsplinePoint=function(t,i){var e,n,s,r,o,h,u,a,p,c;return o=t[0],h=t[1],u=t[2],e=-.16666666666*u+.5*h-.5*o+.16666666666,n=.5*u-h+.66666666666,s=-.5*u+.5*h+.5*o+.16666666666,r=.16666666666*u,a=e*i.p0.x+n*i.p1.x+s*i.p2.x+r*i.p3.x,p=e*i.p0.y+n*i.p1.y+s*i.p2.y+r*i.p3.y,c=this.is3D?e*i.p0.z+n*i.p1.z+s*i.p2.z+r*i.p3.z:0,new Point(a,p,c)},i.prototype.bsplineTensionPoint=function(t,i,e){var n,s,r,o,h,u,a,p,c,l,y,f;return null==e&&(e=1),a=t[0],p=t[1],c=t[2],n=e*(-.16666666666*c+.5*p-.5*a+.16666666666),s=e*(-1.5*c+2*p-.33333333333),r=2*c-3*p+1,o=e*(1.5*c-2.5*p+.5*a+.16666666666),h=-2*c+3*p,u=.16666666666*e*c,l=n*i.p0.x+s*i.p1.x+r*i.p1.x+o*i.p2.x+h*i.p2.x+u*i.p3.x,y=n*i.p0.y+s*i.p1.y+r*i.p1.y+o*i.p2.y+h*i.p2.y+u*i.p3.y,f=this.is3D?n*i.p0.z+s*i.p1.z+r*i.p1.y+o*i.p2.z+h*i.p2.z+u*i.p3.z:0,new Point(l,y,f)},i}(PointSet),this.Curve=Curve,Triangle=function(t){function i(){i.__super__.constructor.apply(this,arguments),this.p1=new Vector(this.x-1,this.y-1,this.z),this.p2=new Vector(this.x+1,this.y+1,this.z)}return extend(i,t),i.prototype.to=function(t){return arguments.length>0&&("object"==typeof arguments[0]&&2===arguments.length?(this.p1.set(arguments[0]),this.p2.set(arguments[1])):arguments.length<6?(this.p1.set([arguments[0],arguments[1]]),this.p2.set([arguments[2],arguments[3]])):(this.p1.set([arguments[0],arguments[1],arguments[2]]),this.p2.set([arguments[3],arguments[4],arguments[5]]))),this},i.prototype.toArray=function(){return[this,this.p1,this.p2]},i.prototype.toString=function(){return"Triangle ("+this.x+", "+this.y+", "+this.z+"), ("+this.p1.x+", "+this.p1.y+", "+this.p1.z+"), ("+this.p2.x+", "+this.p2.y+", "+this.p2.z+")"},i.prototype.getAt=function(t){return 1===t||"p1"===t?this.p1:2===t||"p2"===t?this.p2:this},i.prototype.$getAt=function(t){return new Vector(this.getAt(t))},i.prototype.toPointSet=function(){var t;return t=new Vector(this),new PointSet(t).to([t,this.p1,this.p2])},i.prototype.sides=function(){return[new Line(this).to(this.p1),new Line(this.p1).to(this.p2),new Line(this.p2).to(this)]},i.prototype.angles=function(t){var i;return null==t&&(t=Const.xy),i=[this.p2.$subtract(this).angleBetween(this.p1.$subtract(this),t),this.$subtract(this.p1).angleBetween(this.p2.$subtract(this.p1),t)],i.push(Math.PI-i[0]-i[1]),i},i.prototype.medial=function(){var t,e,n;return n=this.sides(),t=function(){var t,i,s;for(s=[],t=0,i=n.length;i>t;t++)e=n[t],s.push(e.midpoint());return s}(),new i(t[0]).to(t[1],t[2])},i.prototype.perimeter=function(){var t,i;return i=this.sides(),t=[i[0].length(),i[1].length(),i[2].length()],{sides:i,value:t[0]+t[1]+t[2],lengths:t}},i.prototype.area=function(){var t,i;return i=this.perimeter(),t=i.value/2,{value:Math.sqrt(t*(t-i.lengths[0])*(t-i.lengths[1])*(t-i.lengths[2])),perimeter:i}},i.prototype.oppositeSide=function(t){return"p1"===t?new Line(this).to(this.p2):"p2"===t?new Line(this).to(this.p1):new Line(this.p1).to(this.p2)},i.prototype.adjacentSides=function(t){return"p1"===t?[new Line(this.p1).to(this),new Line(this.p1).to(this.p2)]:"p2"===t?[new Line(this.p2).to(this),new Line(this.p2).to(this.p1)]:[new Line(this).to(this.p1),new Line(this).to(this.p2)]},i.prototype.bisector=function(t,i,e){var n,s,r;return null==i&&(i=!1),null==e&&(e=100),n=this.adjacentSides(t),r=new Vector(n[0]),n[0].moveTo(0,0),n[1].moveTo(0,0),s=n[0].p1.bisect(n[1].p1),i?new Line(r).to(s.multiply(e).add(r)):s},i.prototype.altitude=function(t){return"p1"===t||"p2"===t?new Line(this[t]).to(this.oppositeSide(t).getPerpendicularFromPoint(this[t])):new Line(this).to(this.oppositeSide().getPerpendicularFromPoint(this))},i.prototype.centroid=function(){var t,i,e;return t=this.$divide(3),i=this.p1.$divide(3),e=this.p2.$divide(3),new Vector(t.x+i.x+e.x,t.y+i.y+e.y,t.z+i.z+e.z)},i.prototype.orthocenter=function(){var t,i;return t=this.altitude(),i=this.altitude("p1"),t.intersectPath(i,Const.xyz)},i.prototype.incenter=function(){var t,i;return t=this.bisector("p0",!0),i=this.bisector("p1",!0),t.intersectPath(i,Const.xyz)},i.prototype.incircle=function(){var t,i,e;return i=this.incenter(),t=this.area(),e=2*t.value/t.perimeter.value,new Circle(i).setRadius(e)},i.prototype.circumcenter=function(){var t,i;return t=this.medial(),i=[new Line(t).to(this.$subtract(t).perpendicular()[0].$add(t)),new Line(t.p1).to(this.p1.$subtract(t.p1).perpendicular()[0].$add(t.p1)),new Line(t.p2).to(this.p2.$subtract(t.p2).perpendicular()[0].$add(t.p2))],{center:i[0].intersectPath(i[1],Const.xyz),bisectors:i}},i.prototype.circumcircle=function(){var t,i;return t=this.circumcenter(),i=this.magnitude(t.center),new Circle(t.center).setRadius(i)},i.prototype.intersectPoint=function(t){var i,e,n;return n=this.sides(),i=function(){var i,s,r;for(r=[],i=0,s=n.length;s>i;i++)e=n[i],r.push(e.collinear(t)>0);return r}(),i[0]===i[1]&&i[1]===i[2]},i.prototype.intersectPath=function(t,i,e){var n,s,r,o,h,u;for(null==i&&(i=!0),null==e&&(e=Const.xy),u=this.sides(),o=[],n=0,s=u.length;s>n;n++)if(h=u[n],r=h.intersectPath(t),r&&h.withinBounds(r,e)){if(!i)return!0;o.push(r)}return i?o:!1},i.prototype.intersectLine=function(t,i,e){var n,s,r,o,h;for(null==i&&(i=!0),null==e&&(e=Const.xy),n=this.intersectPath(t,!0,e),h=[],s=0,r=n.length;r>s;s++)if(o=n[s],t.withinBounds(o)){if(!i)return!0;h.push(o)}return i?h:!1},i.prototype.intersectLines=function(t,i){return null==i&&(i=!0),Line.intersectLines(this,t,i)},i.prototype.intersectPath3D=function(t,i){var e,n,s,r,o,h,u,a,p,c,l;return s=this.p1.$subtract(this),r=this.p2.$subtract(this),n=t.direction().normalize(),h=n.cross(r),e=s.dot(h),e>-Const.epsilon&&ec||c>1?!1:(u=p.cross(s),l=n.dot(u)*o,0>l||l>1?!1:(a=r.dot(u)*o,a>Const.epsilon?i?[c,l,a]:!0:!1)))},i.prototype.intersectRectangle=function(t,i){ 3 | return null==i&&(i=!0),t.intersectLines(this.sides(),i)},i.prototype.intersectCircle=function(t,i){return null==i&&(i=!0),t.intersectLines(this.sides(),i)},i.prototype.intersectTriangle=function(t,i){return null==i&&(i=!0),t.intersectLines(this.sides(),i)},i.prototype.clone=function(){return new i(this).to(this.p1,this.p2)},i}(Vector),this.Triangle=Triangle; -------------------------------------------------------------------------------- /demo/js/sushi.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | 3 | var stuff =[ 4 | {name: "salmon1", w: 360, h: 245}, 5 | {name: "salmon2", w: 362, h: 432}, 6 | {name: "rice1", w: 317, h: 358}, 7 | {name: "rice2", w: 318, h: 200}, 8 | {name: "wasabi1", w: 88, h: 101}, 9 | //{name: "wasabi2", w: 108, h: 55}, 10 | //{name: "wasabi3", w: 108, h: 90}, 11 | //{name: "negitoro", w: 418, h: 438}, 12 | {name: "ebi1", w: 322, h: 729}, 13 | {name: "ikura1", w: 382, h: 400} 14 | ]; 15 | 16 | 17 | function getIngredients() { 18 | var ings = []; 19 | for (var i=0; i<6; i++) { 20 | var dice = Math.floor(Math.random()*stuff.length); 21 | ings.push( stuff[dice] ); 22 | } 23 | return ings; 24 | } 25 | 26 | 27 | function _vendor( elem, prop, val ) { 28 | var vs = ["webkit", "Webkit", "Moz", "ms"]; 29 | for (var i=0; i 0) { 66 | var last = this.steps[this.steps.length - 1]; 67 | d = last.p2 + last.pad; 68 | } 69 | 70 | // append new steps 71 | for (var i = 0; i < s.length; i++) { 72 | s[i].p1 = d; 73 | s[i].p2 = s[i].p1 + s[i].size; 74 | d = s[i].p2 + s[i].pad; 75 | this.steps.push(s[i]); 76 | } 77 | 78 | // recalculate pane size 79 | this.getHeight(true); 80 | 81 | return this; 82 | } 83 | 84 | /** 85 | * Get step by index 86 | * @param index 87 | */ 88 | }, { 89 | key: "getStepAt", 90 | value: function getStepAt(index) { 91 | return this.steps[Math.max(0, Math.min(this.steps.length - 1, index))]; 92 | } 93 | 94 | /** 95 | * Calculate and return current step. When padding > 0, step will be -1 when current progress is on the padding area. This allows you to check progress against padding. 96 | * @returns {number} 97 | */ 98 | }, { 99 | key: "getStep", 100 | value: function getStep() { 101 | for (var i = 0; i < this.steps.length; i++) { 102 | var st = this.steps[i]; 103 | if (st.p1 >= -this.viewportSize && st.p2 <= st.size) { 104 | this.current = i; 105 | return i; 106 | } 107 | } 108 | return -1; 109 | } 110 | 111 | /** 112 | * Get current progress within the current step 113 | * @returns 0-1 if step.pad is 0. Otherwise it will range from negative to positive. 114 | */ 115 | }, { 116 | key: "getStepProgress", 117 | value: function getStepProgress() { 118 | var curr = this.steps[this.current]; 119 | return 1 - curr.p2 / curr.size; 120 | } 121 | 122 | /** 123 | * Get current position 124 | * @returns {number|*} 125 | */ 126 | }, { 127 | key: "getPosition", 128 | value: function getPosition() { 129 | return this.pos; 130 | } 131 | 132 | /** 133 | * Get total height of the pane (including padding) 134 | * @returns {*} 135 | */ 136 | }, { 137 | key: "getHeight", 138 | value: function getHeight() { 139 | var recalc = arguments.length <= 0 || arguments[0] === undefined ? false : arguments[0]; 140 | 141 | if (recalc) this.paneSize = this.steps.reduce(function (a, b) { 142 | return a + b.size + b.pad; 143 | }, 0); 144 | return this.paneSize; 145 | } 146 | 147 | /** 148 | * Get viewport's height (same as this.viewportSize) 149 | * @returns {*} 150 | */ 151 | }, { 152 | key: "getViewportHeight", 153 | value: function getViewportHeight() { 154 | return this.viewportSize; 155 | } 156 | 157 | /** 158 | * Move the roll. This will emit two events `roll(step, currProgress, currPosition, totalProgress)` and `step(curr, last)` 159 | * @param pos new position 160 | * @returns {Roll} 161 | */ 162 | }, { 163 | key: "move", 164 | value: function move(pos) { 165 | var last = this.pos; 166 | this.pos = -pos; 167 | var diff = this.pos - last; 168 | 169 | for (var i = 0; i < this.steps.length; i++) { 170 | var s = this.steps[i]; 171 | s.p1 += diff; 172 | s.p2 = s.p1 + s.size; 173 | } 174 | 175 | var curr = this.getStep(); 176 | var progress = this.getStepProgress(); 177 | this.emit("roll", curr, progress, pos, pos / (this.paneSize - this.viewportSize)); 178 | 179 | if (curr != this.last && curr >= 0) { 180 | this.emit("step", curr, this.last, this.viewportSize); 181 | this.last = curr; 182 | } 183 | 184 | return this; 185 | } 186 | 187 | /** 188 | * Animated scrolling a DOM element 189 | * @param index step index 190 | * @param scrollPane a DOM element with scrolling (overflow-y). 191 | * @param speed optional speed of animated scroll. Defaults to 0.1. Larger is faster 192 | * @param isVertical optional boolean to indicate horizontal or vertical scroll 193 | */ 194 | }, { 195 | key: "scroll", 196 | value: function scroll(index, scrollPane) { 197 | var _this = this; 198 | 199 | var speed = arguments.length <= 2 || arguments[2] === undefined ? 0.1 : arguments[2]; 200 | var isVertical = arguments.length <= 3 || arguments[3] === undefined ? true : arguments[3]; 201 | 202 | if (!scrollPane || scrollPane.scrollTop == null) throw "scrollPane parameter requires a DOM element with scrollTop property"; 203 | clearInterval(this.movingInterval); 204 | var _temp = Number.NEGATIVE_INFINITY; 205 | var dir = isVertical ? "scrollTop" : "scrollLeft"; 206 | 207 | this.movingInterval = setInterval(function () { 208 | var target = _this.getStepAt(index); 209 | var d = (target.p1 + target.size / 4) * speed; 210 | scrollPane[dir] += d; 211 | if (Math.abs(d) < 1 || _temp === scrollPane[dir]) clearInterval(_this.movingInterval); 212 | _temp = scrollPane[dir]; 213 | }, 17); 214 | } 215 | 216 | /** 217 | * A convenient static function to create a step object 218 | * @param size chunk size 219 | * @param pad optional padding (default to 0) 220 | * @returns {{p1: number, p2: *, size: *, pad: number}} 221 | */ 222 | }], [{ 223 | key: "chunk", 224 | value: function chunk(size) { 225 | var pad = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1]; 226 | 227 | return { 228 | p1: 0, 229 | p2: size, 230 | size: size, 231 | pad: pad 232 | }; 233 | } 234 | 235 | /** 236 | * A convenient static function to compare a step with current step, and transform it to a name 237 | * @param step the step to check 238 | * @param currStep current step 239 | * @param prev optional class name for step is < currStep. Defaults to "prev" 240 | * @param next optional class name for step is > currStep. Defaults to "next" 241 | * @param match optional class name for step = currStep. Defaults to "curr" 242 | * @returns {string} 243 | */ 244 | }, { 245 | key: "stepName", 246 | value: function stepName(step, currStep) { 247 | var prev = arguments.length <= 2 || arguments[2] === undefined ? "prev" : arguments[2]; 248 | var next = arguments.length <= 3 || arguments[3] === undefined ? "next" : arguments[3]; 249 | var match = arguments.length <= 4 || arguments[4] === undefined ? "curr" : arguments[4]; 250 | 251 | return step === currStep ? match : step < currStep ? prev : next; 252 | } 253 | 254 | /** 255 | * Static helper to get a handle function for Roll's "step" event. The handler function will add class names to each step element based on current step value. 256 | * @param roll a Roll instance 257 | * @param views a list of DOM elements which are the steps 258 | * @param prev optional class name for step is < currStep. Defaults to "prev" 259 | * @param next optional class name for step is > currStep. Defaults to "next" 260 | * @param match optional class name for step = currStep. Defaults to "curr" 261 | * @returns {Function} 262 | */ 263 | }, { 264 | key: "stepHandler", 265 | value: function stepHandler(roll, views) { 266 | var prev = arguments.length <= 2 || arguments[2] === undefined ? "prev" : arguments[2]; 267 | var next = arguments.length <= 3 || arguments[3] === undefined ? "next" : arguments[3]; 268 | var match = arguments.length <= 4 || arguments[4] === undefined ? "curr" : arguments[4]; 269 | var trackTopPos = arguments.length <= 5 || arguments[5] === undefined ? false : arguments[5]; 270 | 271 | return function (curr, last, viewportHeight) { 272 | for (var i = 0; i < roll.steps.length; i++) { 273 | var cls = Roll.stepName(i, curr, prev, next, match); 274 | views[i].className = "step " + cls; 275 | 276 | // if steps have different sizes, recalc top position and set style 277 | if (trackTopPos) { 278 | var p = cls === prev ? roll.steps[i].size * -1 : cls === next ? viewportHeight : 0; 279 | views[i].style.top = p + "px"; 280 | } 281 | } 282 | }; 283 | } 284 | 285 | /** 286 | * Static method to create a Roll instance with DOM elements 287 | * @param viewPortID id of viewport element, which is the parent of the viewPane. eg, "#viewport" 288 | * @param viewPaneID id of view pane element, eg, "#pane" 289 | * @param viewBox id of view box element, which is the parent the viewClass elements. eg, "#steps" 290 | * @param viewClass id of each step or slide element, eg, ".step" 291 | * @param pad optional padding between steps. Defaults to 0. 292 | * @returns the roll instance which you can listen for "step" and "roll" event via `roll.on(...)` 293 | */ 294 | }, { 295 | key: "DOM", 296 | value: function DOM(viewPortID, viewPaneID, viewBoxID, viewClass) { 297 | var pad = arguments.length <= 4 || arguments[4] === undefined ? 0 : arguments[4]; 298 | 299 | var viewport = document.querySelector(viewPortID); 300 | var viewpane = viewport.querySelector(viewPaneID); 301 | var viewbox = document.querySelector(viewBoxID); 302 | var views = viewbox.querySelectorAll(viewClass); 303 | 304 | if (!viewport || !viewpane) throw "Cannot find " + viewPortID + " or " + viewPaneID + " element id."; 305 | if (!viewClass) throw "Cannot find " + viewClass + " element class name"; 306 | 307 | // create roll instance based on viewport element height 308 | var roll = new Roll(viewport.getBoundingClientRect().height); 309 | 310 | // add each viewClass element as a step 311 | for (var i = 0; i < views.length; i++) { 312 | var rect = views[i].getBoundingClientRect(); 313 | roll.addStep(Roll.chunk(rect.height, pad)); 314 | } 315 | 316 | // update viewpane height based on steps 317 | viewpane.style.height = roll.getHeight() + "px"; 318 | 319 | // update viewbox width to account for scrollbar 320 | viewbox.style.width = viewpane.getBoundingClientRect().width + "px"; 321 | 322 | // track scroll 323 | viewport.addEventListener("scroll", function (evt) { 324 | roll.move(viewport.scrollTop); 325 | }); 326 | 327 | return roll; 328 | } 329 | }]); 330 | 331 | return Roll; 332 | })(EventEmitter); 333 | 334 | exports["default"] = Roll; 335 | 336 | if (window) window.Roll = Roll; 337 | module.exports = exports["default"]; -------------------------------------------------------------------------------- /dist/roll.js: -------------------------------------------------------------------------------- 1 | (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o 0 && this._events[type].length > m) { 142 | this._events[type].warned = true; 143 | console.error('(node) warning: possible EventEmitter memory ' + 144 | 'leak detected. %d listeners added. ' + 145 | 'Use emitter.setMaxListeners() to increase limit.', 146 | this._events[type].length); 147 | if (typeof console.trace === 'function') { 148 | // not supported in IE 10 149 | console.trace(); 150 | } 151 | } 152 | } 153 | 154 | return this; 155 | }; 156 | 157 | EventEmitter.prototype.on = EventEmitter.prototype.addListener; 158 | 159 | EventEmitter.prototype.once = function(type, listener) { 160 | if (!isFunction(listener)) 161 | throw TypeError('listener must be a function'); 162 | 163 | var fired = false; 164 | 165 | function g() { 166 | this.removeListener(type, g); 167 | 168 | if (!fired) { 169 | fired = true; 170 | listener.apply(this, arguments); 171 | } 172 | } 173 | 174 | g.listener = listener; 175 | this.on(type, g); 176 | 177 | return this; 178 | }; 179 | 180 | // emits a 'removeListener' event iff the listener was removed 181 | EventEmitter.prototype.removeListener = function(type, listener) { 182 | var list, position, length, i; 183 | 184 | if (!isFunction(listener)) 185 | throw TypeError('listener must be a function'); 186 | 187 | if (!this._events || !this._events[type]) 188 | return this; 189 | 190 | list = this._events[type]; 191 | length = list.length; 192 | position = -1; 193 | 194 | if (list === listener || 195 | (isFunction(list.listener) && list.listener === listener)) { 196 | delete this._events[type]; 197 | if (this._events.removeListener) 198 | this.emit('removeListener', type, listener); 199 | 200 | } else if (isObject(list)) { 201 | for (i = length; i-- > 0;) { 202 | if (list[i] === listener || 203 | (list[i].listener && list[i].listener === listener)) { 204 | position = i; 205 | break; 206 | } 207 | } 208 | 209 | if (position < 0) 210 | return this; 211 | 212 | if (list.length === 1) { 213 | list.length = 0; 214 | delete this._events[type]; 215 | } else { 216 | list.splice(position, 1); 217 | } 218 | 219 | if (this._events.removeListener) 220 | this.emit('removeListener', type, listener); 221 | } 222 | 223 | return this; 224 | }; 225 | 226 | EventEmitter.prototype.removeAllListeners = function(type) { 227 | var key, listeners; 228 | 229 | if (!this._events) 230 | return this; 231 | 232 | // not listening for removeListener, no need to emit 233 | if (!this._events.removeListener) { 234 | if (arguments.length === 0) 235 | this._events = {}; 236 | else if (this._events[type]) 237 | delete this._events[type]; 238 | return this; 239 | } 240 | 241 | // emit removeListener for all listeners on all events 242 | if (arguments.length === 0) { 243 | for (key in this._events) { 244 | if (key === 'removeListener') continue; 245 | this.removeAllListeners(key); 246 | } 247 | this.removeAllListeners('removeListener'); 248 | this._events = {}; 249 | return this; 250 | } 251 | 252 | listeners = this._events[type]; 253 | 254 | if (isFunction(listeners)) { 255 | this.removeListener(type, listeners); 256 | } else { 257 | // LIFO order 258 | while (listeners.length) 259 | this.removeListener(type, listeners[listeners.length - 1]); 260 | } 261 | delete this._events[type]; 262 | 263 | return this; 264 | }; 265 | 266 | EventEmitter.prototype.listeners = function(type) { 267 | var ret; 268 | if (!this._events || !this._events[type]) 269 | ret = []; 270 | else if (isFunction(this._events[type])) 271 | ret = [this._events[type]]; 272 | else 273 | ret = this._events[type].slice(); 274 | return ret; 275 | }; 276 | 277 | EventEmitter.listenerCount = function(emitter, type) { 278 | var ret; 279 | if (!emitter._events || !emitter._events[type]) 280 | ret = 0; 281 | else if (isFunction(emitter._events[type])) 282 | ret = 1; 283 | else 284 | ret = emitter._events[type].length; 285 | return ret; 286 | }; 287 | 288 | function isFunction(arg) { 289 | return typeof arg === 'function'; 290 | } 291 | 292 | function isNumber(arg) { 293 | return typeof arg === 'number'; 294 | } 295 | 296 | function isObject(arg) { 297 | return typeof arg === 'object' && arg !== null; 298 | } 299 | 300 | function isUndefined(arg) { 301 | return arg === void 0; 302 | } 303 | 304 | },{}],2:[function(require,module,exports){ 305 | "use strict"; 306 | 307 | Object.defineProperty(exports, "__esModule", { 308 | value: true 309 | }); 310 | 311 | var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); 312 | 313 | var _get = function get(_x13, _x14, _x15) { var _again = true; _function: while (_again) { var object = _x13, property = _x14, receiver = _x15; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x13 = parent; _x14 = property; _x15 = receiver; _again = true; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; 314 | 315 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } 316 | 317 | function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } 318 | 319 | var EventEmitter = require('events').EventEmitter; 320 | 321 | /** 322 | * Roll simply keep tracks of steps' positions inside a viewport. 323 | * Apart from the static helper functions and the `scroll` function, a roll instance doesn't depend on DOM manipulation. 324 | * That means you can use a Roll instance in contexts other than DOM. 325 | */ 326 | 327 | var Roll = (function (_EventEmitter) { 328 | _inherits(Roll, _EventEmitter); 329 | 330 | /** 331 | * Create a new Roll. 332 | * @param viewSize viewport size (single dimension) 333 | */ 334 | 335 | function Roll(viewSize) { 336 | _classCallCheck(this, Roll); 337 | 338 | _get(Object.getPrototypeOf(Roll.prototype), "constructor", this).call(this); 339 | 340 | this.viewportSize = viewSize; 341 | this.paneSize = 0; 342 | 343 | // store the steps object {y1, y2, size, pad}, See Roll.chunk 344 | this.steps = []; 345 | 346 | this.pos = 0; // current position 347 | this.current = 0; // current step 348 | this.last = -1; // last step 349 | 350 | this.movingInterval = -1; 351 | } 352 | 353 | /** 354 | * Add a step object. You can also use Roll.chunk() static helper function to create a step object easily. 355 | * @param s an object with {p1, p2, size, pad} properties, or an array of steps object 356 | * @returns {Roll} 357 | */ 358 | 359 | _createClass(Roll, [{ 360 | key: "addStep", 361 | value: function addStep(s) { 362 | 363 | if (!Array.isArray(s)) { 364 | s = [s]; 365 | } 366 | 367 | // get last recorded step 368 | var d = s[0].p1; 369 | if (this.steps.length > 0) { 370 | var last = this.steps[this.steps.length - 1]; 371 | d = last.p2 + last.pad; 372 | } 373 | 374 | // append new steps 375 | for (var i = 0; i < s.length; i++) { 376 | s[i].p1 = d; 377 | s[i].p2 = s[i].p1 + s[i].size; 378 | d = s[i].p2 + s[i].pad; 379 | this.steps.push(s[i]); 380 | } 381 | 382 | // recalculate pane size 383 | this.getHeight(true); 384 | 385 | return this; 386 | } 387 | 388 | /** 389 | * Get step by index 390 | * @param index 391 | */ 392 | }, { 393 | key: "getStepAt", 394 | value: function getStepAt(index) { 395 | return this.steps[Math.max(0, Math.min(this.steps.length - 1, index))]; 396 | } 397 | 398 | /** 399 | * Calculate and return current step. When padding > 0, step will be -1 when current progress is on the padding area. This allows you to check progress against padding. 400 | * @returns {number} 401 | */ 402 | }, { 403 | key: "getStep", 404 | value: function getStep() { 405 | for (var i = 0; i < this.steps.length; i++) { 406 | var st = this.steps[i]; 407 | if (st.p1 >= -this.viewportSize && st.p2 <= st.size) { 408 | this.current = i; 409 | return i; 410 | } 411 | } 412 | return -1; 413 | } 414 | 415 | /** 416 | * Get current progress within the current step 417 | * @returns 0-1 if step.pad is 0. Otherwise it will range from negative to positive. 418 | */ 419 | }, { 420 | key: "getStepProgress", 421 | value: function getStepProgress() { 422 | var curr = this.steps[this.current]; 423 | return 1 - curr.p2 / curr.size; 424 | } 425 | 426 | /** 427 | * Get current position 428 | * @returns {number|*} 429 | */ 430 | }, { 431 | key: "getPosition", 432 | value: function getPosition() { 433 | return this.pos; 434 | } 435 | 436 | /** 437 | * Get total height of the pane (including padding) 438 | * @returns {*} 439 | */ 440 | }, { 441 | key: "getHeight", 442 | value: function getHeight() { 443 | var recalc = arguments.length <= 0 || arguments[0] === undefined ? false : arguments[0]; 444 | 445 | if (recalc) this.paneSize = this.steps.reduce(function (a, b) { 446 | return a + b.size + b.pad; 447 | }, 0); 448 | return this.paneSize; 449 | } 450 | 451 | /** 452 | * Get viewport's height (same as this.viewportSize) 453 | * @returns {*} 454 | */ 455 | }, { 456 | key: "getViewportHeight", 457 | value: function getViewportHeight() { 458 | return this.viewportSize; 459 | } 460 | 461 | /** 462 | * Move the roll. This will emit two events `roll(step, currProgress, currPosition, totalProgress)` and `step(curr, last)` 463 | * @param pos new position 464 | * @returns {Roll} 465 | */ 466 | }, { 467 | key: "move", 468 | value: function move(pos) { 469 | var last = this.pos; 470 | this.pos = -pos; 471 | var diff = this.pos - last; 472 | 473 | for (var i = 0; i < this.steps.length; i++) { 474 | var s = this.steps[i]; 475 | s.p1 += diff; 476 | s.p2 = s.p1 + s.size; 477 | } 478 | 479 | var curr = this.getStep(); 480 | var progress = this.getStepProgress(); 481 | this.emit("roll", curr, progress, pos, pos / (this.paneSize - this.viewportSize)); 482 | 483 | if (curr != this.last && curr >= 0) { 484 | this.emit("step", curr, this.last, this.viewportSize); 485 | this.last = curr; 486 | } 487 | 488 | return this; 489 | } 490 | 491 | /** 492 | * Animated scrolling a DOM element 493 | * @param index step index 494 | * @param scrollPane a DOM element with scrolling (overflow-y). 495 | * @param speed optional speed of animated scroll. Defaults to 0.1. Larger is faster 496 | * @param isVertical optional boolean to indicate horizontal or vertical scroll 497 | */ 498 | }, { 499 | key: "scroll", 500 | value: function scroll(index, scrollPane) { 501 | var _this = this; 502 | 503 | var speed = arguments.length <= 2 || arguments[2] === undefined ? 0.1 : arguments[2]; 504 | var isVertical = arguments.length <= 3 || arguments[3] === undefined ? true : arguments[3]; 505 | 506 | if (!scrollPane || scrollPane.scrollTop == null) throw "scrollPane parameter requires a DOM element with scrollTop property"; 507 | clearInterval(this.movingInterval); 508 | var _temp = Number.NEGATIVE_INFINITY; 509 | var dir = isVertical ? "scrollTop" : "scrollLeft"; 510 | 511 | this.movingInterval = setInterval(function () { 512 | var target = _this.getStepAt(index); 513 | var d = (target.p1 + target.size / 4) * speed; 514 | scrollPane[dir] += d; 515 | if (Math.abs(d) < 1 || _temp === scrollPane[dir]) clearInterval(_this.movingInterval); 516 | _temp = scrollPane[dir]; 517 | }, 17); 518 | } 519 | 520 | /** 521 | * A convenient static function to create a step object 522 | * @param size chunk size 523 | * @param pad optional padding (default to 0) 524 | * @returns {{p1: number, p2: *, size: *, pad: number}} 525 | */ 526 | }], [{ 527 | key: "chunk", 528 | value: function chunk(size) { 529 | var pad = arguments.length <= 1 || arguments[1] === undefined ? 0 : arguments[1]; 530 | 531 | return { 532 | p1: 0, 533 | p2: size, 534 | size: size, 535 | pad: pad 536 | }; 537 | } 538 | 539 | /** 540 | * A convenient static function to compare a step with current step, and transform it to a name 541 | * @param step the step to check 542 | * @param currStep current step 543 | * @param prev optional class name for step is < currStep. Defaults to "prev" 544 | * @param next optional class name for step is > currStep. Defaults to "next" 545 | * @param match optional class name for step = currStep. Defaults to "curr" 546 | * @returns {string} 547 | */ 548 | }, { 549 | key: "stepName", 550 | value: function stepName(step, currStep) { 551 | var prev = arguments.length <= 2 || arguments[2] === undefined ? "prev" : arguments[2]; 552 | var next = arguments.length <= 3 || arguments[3] === undefined ? "next" : arguments[3]; 553 | var match = arguments.length <= 4 || arguments[4] === undefined ? "curr" : arguments[4]; 554 | 555 | return step === currStep ? match : step < currStep ? prev : next; 556 | } 557 | 558 | /** 559 | * Static helper to get a handle function for Roll's "step" event. The handler function will add class names to each step element based on current step value. 560 | * @param roll a Roll instance 561 | * @param views a list of DOM elements which are the steps 562 | * @param prev optional class name for step is < currStep. Defaults to "prev" 563 | * @param next optional class name for step is > currStep. Defaults to "next" 564 | * @param match optional class name for step = currStep. Defaults to "curr" 565 | * @returns {Function} 566 | */ 567 | }, { 568 | key: "stepHandler", 569 | value: function stepHandler(roll, views) { 570 | var prev = arguments.length <= 2 || arguments[2] === undefined ? "prev" : arguments[2]; 571 | var next = arguments.length <= 3 || arguments[3] === undefined ? "next" : arguments[3]; 572 | var match = arguments.length <= 4 || arguments[4] === undefined ? "curr" : arguments[4]; 573 | var trackTopPos = arguments.length <= 5 || arguments[5] === undefined ? false : arguments[5]; 574 | 575 | return function (curr, last, viewportHeight) { 576 | for (var i = 0; i < roll.steps.length; i++) { 577 | var cls = Roll.stepName(i, curr, prev, next, match); 578 | views[i].className = "step " + cls; 579 | 580 | // if steps have different sizes, recalc top position and set style 581 | if (trackTopPos) { 582 | var p = cls === prev ? roll.steps[i].size * -1 : cls === next ? viewportHeight : 0; 583 | views[i].style.top = p + "px"; 584 | } 585 | } 586 | }; 587 | } 588 | 589 | /** 590 | * Static method to create a Roll instance with DOM elements 591 | * @param viewPortID id of viewport element, which is the parent of the viewPane. eg, "#viewport" 592 | * @param viewPaneID id of view pane element, eg, "#pane" 593 | * @param viewBox id of view box element, which is the parent the viewClass elements. eg, "#steps" 594 | * @param viewClass id of each step or slide element, eg, ".step" 595 | * @param pad optional padding between steps. Defaults to 0. 596 | * @returns the roll instance which you can listen for "step" and "roll" event via `roll.on(...)` 597 | */ 598 | }, { 599 | key: "DOM", 600 | value: function DOM(viewPortID, viewPaneID, viewBoxID, viewClass) { 601 | var pad = arguments.length <= 4 || arguments[4] === undefined ? 0 : arguments[4]; 602 | 603 | var viewport = document.querySelector(viewPortID); 604 | var viewpane = viewport.querySelector(viewPaneID); 605 | var viewbox = document.querySelector(viewBoxID); 606 | var views = viewbox.querySelectorAll(viewClass); 607 | 608 | if (!viewport || !viewpane) throw "Cannot find " + viewPortID + " or " + viewPaneID + " element id."; 609 | if (!viewClass) throw "Cannot find " + viewClass + " element class name"; 610 | 611 | // create roll instance based on viewport element height 612 | var roll = new Roll(viewport.getBoundingClientRect().height); 613 | 614 | // add each viewClass element as a step 615 | for (var i = 0; i < views.length; i++) { 616 | var rect = views[i].getBoundingClientRect(); 617 | roll.addStep(Roll.chunk(rect.height, pad)); 618 | } 619 | 620 | // update viewpane height based on steps 621 | viewpane.style.height = roll.getHeight() + "px"; 622 | 623 | // update viewbox width to account for scrollbar 624 | viewbox.style.width = viewpane.getBoundingClientRect().width + "px"; 625 | 626 | // track scroll 627 | viewport.addEventListener("scroll", function (evt) { 628 | roll.move(viewport.scrollTop); 629 | }); 630 | 631 | return roll; 632 | } 633 | }]); 634 | 635 | return Roll; 636 | })(EventEmitter); 637 | 638 | exports["default"] = Roll; 639 | module.exports = exports["default"]; 640 | 641 | },{"events":1}],3:[function(require,module,exports){ 642 | "use strict"; 643 | 644 | var Roll = require("./roll.js"); 645 | 646 | if (window) window.Roll = Roll; 647 | 648 | },{"./roll.js":2}]},{},[3]) 649 | 650 | 651 | //# sourceMappingURL=roll.js.map 652 | -------------------------------------------------------------------------------- /dist/roll.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"sources":["node_modules/browserify/node_modules/browser-pack/_prelude.js","node_modules/browserify/node_modules/events/events.js","src/roll.js","src/roll_standalone.js"],"names":[],"mappings":"AAAA;ACAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;AC7SA,IAAI,YAAY,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC,YAAY,CAAC;;;;;;;;IAO7B,IAAI;YAAJ,IAAI;;;;;;;AAMZ,WANQ,IAAI,CAMV,QAAQ,EAAG;0BANL,IAAI;;AAOrB,+BAPiB,IAAI,6CAOb;;AAER,QAAI,CAAC,YAAY,GAAG,QAAQ,CAAC;AAC7B,QAAI,CAAC,QAAQ,GAAG,CAAC,CAAC;;;AAGlB,QAAI,CAAC,KAAK,GAAG,EAAE,CAAC;;AAEhB,QAAI,CAAC,GAAG,GAAG,CAAC,CAAC;AACb,QAAI,CAAC,OAAO,GAAG,CAAC,CAAC;AACjB,QAAI,CAAC,IAAI,GAAG,CAAC,CAAC,CAAC;;AAEf,QAAI,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC;GAC1B;;;;;;;;eApBkB,IAAI;;WA4BhB,iBAAC,CAAC,EAAE;;AAET,UAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE;AACrB,SAAC,GAAG,CAAC,CAAC,CAAC,CAAC;OACT;;;AAGD,UAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAChB,UAAI,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAG;AAC1B,YAAI,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,GAAC,CAAC,CAAC,CAAC;AAC3C,SAAC,GAAG,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC;OACxB;;;AAGD,WAAK,IAAI,CAAC,GAAC,CAAC,EAAE,CAAC,GAAC,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AAC7B,SAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AACZ,SAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC9B,SAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;AACvB,YAAI,CAAC,KAAK,CAAC,IAAI,CAAE,CAAC,CAAC,CAAC,CAAC,CAAE,CAAC;OACzB;;;AAGD,UAAI,CAAC,SAAS,CAAE,IAAI,CAAE,CAAC;;AAEvB,aAAO,IAAI,CAAC;KACb;;;;;;;;WAOQ,mBAAE,KAAK,EAAG;AACjB,aAAO,IAAI,CAAC,KAAK,CAAE,IAAI,CAAC,GAAG,CAAE,CAAC,EAAE,IAAI,CAAC,GAAG,CAAE,IAAI,CAAC,KAAK,CAAC,MAAM,GAAC,CAAC,EAAE,KAAK,CAAC,CAAE,CAAC,CAAC;KAC1E;;;;;;;;WAOM,mBAAG;AACR,WAAK,IAAI,CAAC,GAAC,CAAC,EAAE,CAAC,GAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACtC,YAAI,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACvB,YAAI,EAAE,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,IAAI,EAAG;AACpD,cAAI,CAAC,OAAO,GAAG,CAAC,CAAC;AACjB,iBAAO,CAAC,CAAC;SACV;OACF;AACD,aAAO,CAAC,CAAC,CAAC;KACX;;;;;;;;WAMc,2BAAG;AAChB,UAAI,IAAI,GAAG,IAAI,CAAC,KAAK,CAAE,IAAI,CAAC,OAAO,CAAE,CAAC;AACtC,aAAO,CAAC,GAAI,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC,IAAI,AAAC,CAAC;KAClC;;;;;;;;WAOU,uBAAG;AACZ,aAAO,IAAI,CAAC,GAAG,CAAC;KACjB;;;;;;;;WAMQ,qBAAmB;UAAjB,MAAM,yDAAG,KAAK;;AACvB,UAAI,MAAM,EAAG,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,CAAE,UAAC,CAAC,EAAC,CAAC;eAAK,CAAC,GAAC,CAAC,CAAC,IAAI,GAAC,CAAC,CAAC,GAAG;OAAA,EAAE,CAAC,CAAE,CAAC;AAC7E,aAAO,IAAI,CAAC,QAAQ,CAAC;KACtB;;;;;;;;WAMgB,6BAAG;AAClB,aAAO,IAAI,CAAC,YAAY,CAAC;KAC1B;;;;;;;;;WAQG,cAAE,GAAG,EAAG;AACV,UAAI,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC;AACpB,UAAI,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC;AAChB,UAAI,IAAI,GAAG,IAAI,CAAC,GAAG,GAAG,IAAI,CAAC;;AAE3B,WAAK,IAAI,CAAC,GAAC,CAAC,EAAE,CAAC,GAAC,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACtC,YAAI,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACtB,SAAC,CAAC,EAAE,IAAI,IAAI,CAAC;AACb,SAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,IAAI,CAAC;OACtB;;AAED,UAAI,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;AAC1B,UAAI,QAAQ,GAAG,IAAI,CAAC,eAAe,EAAE,CAAC;AACtC,UAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,GAAG,IAAE,IAAI,CAAC,QAAQ,GAAC,IAAI,CAAC,YAAY,CAAA,AAAC,CAAE,CAAC;;AAE/E,UAAI,IAAI,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,IAAI,CAAC,EAAE;AAClC,YAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,YAAY,CAAE,CAAC;AACvD,YAAI,CAAC,IAAI,GAAG,IAAI,CAAC;OAClB;;AAED,aAAO,IAAI,CAAC;KACb;;;;;;;;;;;WAUK,gBAAE,KAAK,EAAE,UAAU,EAA8B;;;UAA5B,KAAK,yDAAC,GAAG;UAAE,UAAU,yDAAC,IAAI;;AACnD,UAAI,CAAC,UAAU,IAAI,UAAU,CAAC,SAAS,IAAI,IAAI,EAAE,MAAM,qEAAqE,CAAC;AAC7H,mBAAa,CAAE,IAAI,CAAC,cAAc,CAAE,CAAC;AACrC,UAAI,KAAK,GAAG,MAAM,CAAC,iBAAiB,CAAC;AACrC,UAAI,GAAG,GAAG,AAAC,UAAU,GAAI,WAAW,GAAG,YAAY,CAAC;;AAEpD,UAAI,CAAC,cAAc,GAAG,WAAW,CAAE,YAAM;AACvC,YAAI,MAAM,GAAG,MAAK,SAAS,CAAC,KAAK,CAAC,CAAC;AACnC,YAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,MAAM,CAAC,IAAI,GAAC,CAAC,CAAA,GAAI,KAAK,CAAC;AAC5C,kBAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;AACrB,YAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,GAAC,CAAC,IAAI,KAAK,KAAK,UAAU,CAAC,GAAG,CAAC,EAAE,aAAa,CAAC,MAAK,cAAc,CAAC,CAAC;AACnF,aAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC;OACzB,EAAE,EAAE,CAAC,CAAC;KACR;;;;;;;;;;WASW,eAAE,IAAI,EAAS;UAAP,GAAG,yDAAC,CAAC;;AACvB,aAAO;AACL,UAAE,EAAE,CAAC;AACL,UAAE,EAAE,IAAI;AACR,YAAI,EAAE,IAAI;AACV,WAAG,EAAE,GAAG;OACT,CAAA;KACF;;;;;;;;;;;;;WAYc,kBAAE,IAAI,EAAE,QAAQ,EAA0C;UAAxC,IAAI,yDAAC,MAAM;UAAE,IAAI,yDAAC,MAAM;UAAE,KAAK,yDAAC,MAAM;;AACrE,aAAO,AAAC,IAAI,KAAK,QAAQ,GAAI,KAAK,GAAK,AAAC,IAAI,GAAG,QAAQ,GAAI,IAAI,GAAG,IAAI,AAAE,CAAC;KAC1E;;;;;;;;;;;;;WAYiB,qBAAE,IAAI,EAAE,KAAK,EAA6D;UAA3D,IAAI,yDAAC,MAAM;UAAE,IAAI,yDAAC,MAAM;UAAE,KAAK,yDAAC,MAAM;UAAE,WAAW,yDAAC,KAAK;;AACxF,aAAO,UAAW,IAAI,EAAE,IAAI,EAAE,cAAc,EAAG;AAC7C,aAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AAC1C,cAAI,GAAG,GAAG,IAAI,CAAC,QAAQ,CAAE,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,CAAE,CAAC;AACtD,eAAK,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,OAAO,GAAG,GAAG,CAAC;;;AAGnC,cAAI,WAAW,EAAE;AACf,gBAAI,CAAC,GAAG,AAAC,GAAG,KAAG,IAAI,GAAK,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,GAAI,AAAC,GAAG,KAAG,IAAI,GAAI,cAAc,GAAG,CAAC,AAAC,CAAC;AACtF,iBAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,GAAC,IAAI,CAAC;WAC7B;SACF;OACF,CAAA;KACF;;;;;;;;;;;;;WAYS,aAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,EAAU;UAAR,GAAG,yDAAC,CAAC;;AAE7D,UAAI,QAAQ,GAAG,QAAQ,CAAC,aAAa,CAAE,UAAU,CAAE,CAAC;AACpD,UAAI,QAAQ,GAAG,QAAQ,CAAC,aAAa,CAAE,UAAU,CAAE,CAAC;AACpD,UAAI,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAE,SAAS,CAAE,CAAC;AAClD,UAAI,KAAK,GAAG,OAAO,CAAC,gBAAgB,CAAE,SAAS,CAAE,CAAC;;AAElD,UAAI,CAAC,QAAQ,IAAI,CAAC,QAAQ,EAAE,uBAAqB,UAAU,YAAO,UAAU,kBAAc;AAC1F,UAAI,CAAC,SAAS,EAAE,uBAAqB,SAAS,yBAAsB;;;AAGpE,UAAI,IAAI,GAAG,IAAI,IAAI,CAAE,QAAQ,CAAC,qBAAqB,EAAE,CAAC,MAAM,CAAE,CAAC;;;AAG/D,WAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACrC,YAAI,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,qBAAqB,EAAE,CAAC;AAC5C,YAAI,CAAC,OAAO,CAAE,IAAI,CAAC,KAAK,CAAE,IAAI,CAAC,MAAM,EAAE,GAAG,CAAE,CAAE,CAAC;OAChD;;;AAGD,cAAQ,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,GAAC,IAAI,CAAC;;;AAG9C,aAAO,CAAC,KAAK,CAAC,KAAK,GAAG,QAAQ,CAAC,qBAAqB,EAAE,CAAC,KAAK,GAAC,IAAI,CAAC;;;AAGlE,cAAQ,CAAC,gBAAgB,CAAC,QAAQ,EAAE,UAAS,GAAG,EAAE;AAChD,YAAI,CAAC,IAAI,CAAE,QAAQ,CAAC,SAAS,CAAE,CAAC;OACjC,CAAC,CAAC;;AAEH,aAAO,IAAI,CAAC;KACb;;;SAvQkB,IAAI;GAAS,YAAY;;qBAAzB,IAAI;;;;;;ACPzB,IAAI,IAAI,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;;AAEhC,IAAI,MAAM,EAAE,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC","file":"roll.js","sourceRoot":"/source/","sourcesContent":["(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require==\"function\"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error(\"Cannot find module '\"+o+\"'\");throw f.code=\"MODULE_NOT_FOUND\",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require==\"function\"&&require;for(var o=0;o 0 && this._events[type].length > m) {\n this._events[type].warned = true;\n console.error('(node) warning: possible EventEmitter memory ' +\n 'leak detected. %d listeners added. ' +\n 'Use emitter.setMaxListeners() to increase limit.',\n this._events[type].length);\n if (typeof console.trace === 'function') {\n // not supported in IE 10\n console.trace();\n }\n }\n }\n\n return this;\n};\n\nEventEmitter.prototype.on = EventEmitter.prototype.addListener;\n\nEventEmitter.prototype.once = function(type, listener) {\n if (!isFunction(listener))\n throw TypeError('listener must be a function');\n\n var fired = false;\n\n function g() {\n this.removeListener(type, g);\n\n if (!fired) {\n fired = true;\n listener.apply(this, arguments);\n }\n }\n\n g.listener = listener;\n this.on(type, g);\n\n return this;\n};\n\n// emits a 'removeListener' event iff the listener was removed\nEventEmitter.prototype.removeListener = function(type, listener) {\n var list, position, length, i;\n\n if (!isFunction(listener))\n throw TypeError('listener must be a function');\n\n if (!this._events || !this._events[type])\n return this;\n\n list = this._events[type];\n length = list.length;\n position = -1;\n\n if (list === listener ||\n (isFunction(list.listener) && list.listener === listener)) {\n delete this._events[type];\n if (this._events.removeListener)\n this.emit('removeListener', type, listener);\n\n } else if (isObject(list)) {\n for (i = length; i-- > 0;) {\n if (list[i] === listener ||\n (list[i].listener && list[i].listener === listener)) {\n position = i;\n break;\n }\n }\n\n if (position < 0)\n return this;\n\n if (list.length === 1) {\n list.length = 0;\n delete this._events[type];\n } else {\n list.splice(position, 1);\n }\n\n if (this._events.removeListener)\n this.emit('removeListener', type, listener);\n }\n\n return this;\n};\n\nEventEmitter.prototype.removeAllListeners = function(type) {\n var key, listeners;\n\n if (!this._events)\n return this;\n\n // not listening for removeListener, no need to emit\n if (!this._events.removeListener) {\n if (arguments.length === 0)\n this._events = {};\n else if (this._events[type])\n delete this._events[type];\n return this;\n }\n\n // emit removeListener for all listeners on all events\n if (arguments.length === 0) {\n for (key in this._events) {\n if (key === 'removeListener') continue;\n this.removeAllListeners(key);\n }\n this.removeAllListeners('removeListener');\n this._events = {};\n return this;\n }\n\n listeners = this._events[type];\n\n if (isFunction(listeners)) {\n this.removeListener(type, listeners);\n } else {\n // LIFO order\n while (listeners.length)\n this.removeListener(type, listeners[listeners.length - 1]);\n }\n delete this._events[type];\n\n return this;\n};\n\nEventEmitter.prototype.listeners = function(type) {\n var ret;\n if (!this._events || !this._events[type])\n ret = [];\n else if (isFunction(this._events[type]))\n ret = [this._events[type]];\n else\n ret = this._events[type].slice();\n return ret;\n};\n\nEventEmitter.listenerCount = function(emitter, type) {\n var ret;\n if (!emitter._events || !emitter._events[type])\n ret = 0;\n else if (isFunction(emitter._events[type]))\n ret = 1;\n else\n ret = emitter._events[type].length;\n return ret;\n};\n\nfunction isFunction(arg) {\n return typeof arg === 'function';\n}\n\nfunction isNumber(arg) {\n return typeof arg === 'number';\n}\n\nfunction isObject(arg) {\n return typeof arg === 'object' && arg !== null;\n}\n\nfunction isUndefined(arg) {\n return arg === void 0;\n}\n","var EventEmitter = require('events').EventEmitter;\r\n\r\n/**\r\n * Roll simply keep tracks of steps' positions inside a viewport.\r\n * Apart from the static helper functions and the `scroll` function, a roll instance doesn't depend on DOM manipulation.\r\n * That means you can use a Roll instance in contexts other than DOM.\r\n */\r\nexport default class Roll extends EventEmitter {\r\n\r\n /**\r\n * Create a new Roll.\r\n * @param viewSize viewport size (single dimension)\r\n */\r\n constructor( viewSize ) {\r\n super();\r\n\r\n this.viewportSize = viewSize;\r\n this.paneSize = 0;\r\n\r\n // store the steps object {y1, y2, size, pad}, See Roll.chunk\r\n this.steps = [];\r\n\r\n this.pos = 0; // current position\r\n this.current = 0; // current step\r\n this.last = -1; // last step\r\n\r\n this.movingInterval = -1;\r\n }\r\n\r\n\r\n /**\r\n * Add a step object. You can also use Roll.chunk() static helper function to create a step object easily.\r\n * @param s an object with {p1, p2, size, pad} properties, or an array of steps object\r\n * @returns {Roll}\r\n */\r\n addStep(s) {\r\n\r\n if (!Array.isArray(s)) {\r\n s = [s];\r\n }\r\n\r\n // get last recorded step\r\n var d = s[0].p1;\r\n if (this.steps.length > 0 ) {\r\n var last = this.steps[this.steps.length-1];\r\n d = last.p2 + last.pad;\r\n }\r\n\r\n // append new steps\r\n for (var i=0; i 0, step will be -1 when current progress is on the padding area. This allows you to check progress against padding.\r\n * @returns {number}\r\n */\r\n getStep() {\r\n for (var i=0; i= -this.viewportSize && st.p2 <= st.size ) {\r\n this.current = i;\r\n return i;\r\n }\r\n }\r\n return -1;\r\n }\r\n\r\n /**\r\n * Get current progress within the current step\r\n * @returns 0-1 if step.pad is 0. Otherwise it will range from negative to positive.\r\n */\r\n getStepProgress() {\r\n var curr = this.steps[ this.current ];\r\n return 1 - (curr.p2 / curr.size);\r\n }\r\n\r\n\r\n /**\r\n * Get current position\r\n * @returns {number|*}\r\n */\r\n getPosition() {\r\n return this.pos;\r\n }\r\n\r\n /**\r\n * Get total height of the pane (including padding)\r\n * @returns {*}\r\n */\r\n getHeight( recalc = false ) {\r\n if (recalc ) this.paneSize = this.steps.reduce( (a,b) => a+b.size+b.pad, 0 );\r\n return this.paneSize;\r\n }\r\n\r\n /**\r\n * Get viewport's height (same as this.viewportSize)\r\n * @returns {*}\r\n */\r\n getViewportHeight() {\r\n return this.viewportSize;\r\n }\r\n\r\n\r\n /**\r\n * Move the roll. This will emit two events `roll(step, currProgress, currPosition, totalProgress)` and `step(curr, last)`\r\n * @param pos new position\r\n * @returns {Roll}\r\n */\r\n move( pos ) {\r\n var last = this.pos;\r\n this.pos = -pos;\r\n var diff = this.pos - last;\r\n\r\n for (var i=0; i= 0) {\r\n this.emit(\"step\", curr, this.last, this.viewportSize );\r\n this.last = curr;\r\n }\r\n\r\n return this;\r\n }\r\n\r\n\r\n /**\r\n * Animated scrolling a DOM element\r\n * @param index step index\r\n * @param scrollPane a DOM element with scrolling (overflow-y).\r\n * @param speed optional speed of animated scroll. Defaults to 0.1. Larger is faster\r\n * @param isVertical optional boolean to indicate horizontal or vertical scroll\r\n */\r\n scroll( index, scrollPane, speed=0.1, isVertical=true) {\r\n if (!scrollPane || scrollPane.scrollTop == null) throw \"scrollPane parameter requires a DOM element with scrollTop property\";\r\n clearInterval( this.movingInterval );\r\n var _temp = Number.NEGATIVE_INFINITY;\r\n var dir = (isVertical) ? \"scrollTop\" : \"scrollLeft\";\r\n\r\n this.movingInterval = setInterval( () => {\r\n var target = this.getStepAt(index);\r\n var d = (target.p1 + target.size/4) * speed;\r\n scrollPane[dir] += d;\r\n if (Math.abs(d)<1 || _temp === scrollPane[dir]) clearInterval(this.movingInterval);\r\n _temp = scrollPane[dir];\r\n }, 17);\r\n }\r\n\r\n\r\n /**\r\n * A convenient static function to create a step object\r\n * @param size chunk size\r\n * @param pad optional padding (default to 0)\r\n * @returns {{p1: number, p2: *, size: *, pad: number}}\r\n */\r\n static chunk( size, pad=0) {\r\n return {\r\n p1: 0,\r\n p2: size,\r\n size: size,\r\n pad: pad\r\n }\r\n }\r\n\r\n\r\n /**\r\n * A convenient static function to compare a step with current step, and transform it to a name\r\n * @param step the step to check\r\n * @param currStep current step\r\n * @param prev optional class name for step is < currStep. Defaults to \"prev\"\r\n * @param next optional class name for step is > currStep. Defaults to \"next\"\r\n * @param match optional class name for step = currStep. Defaults to \"curr\"\r\n * @returns {string}\r\n */\r\n static stepName( step, currStep, prev=\"prev\", next=\"next\", match=\"curr\") {\r\n return (step === currStep) ? match : ( (step < currStep) ? prev : next );\r\n }\r\n\r\n\r\n /**\r\n * Static helper to get a handle function for Roll's \"step\" event. The handler function will add class names to each step element based on current step value.\r\n * @param roll a Roll instance\r\n * @param views a list of DOM elements which are the steps\r\n * @param prev optional class name for step is < currStep. Defaults to \"prev\"\r\n * @param next optional class name for step is > currStep. Defaults to \"next\"\r\n * @param match optional class name for step = currStep. Defaults to \"curr\"\r\n * @returns {Function}\r\n */\r\n static stepHandler( roll, views, prev=\"prev\", next=\"next\", match=\"curr\", trackTopPos=false) {\r\n return function ( curr, last, viewportHeight ) {\r\n for (var i = 0; i < roll.steps.length; i++) {\r\n var cls = Roll.stepName( i, curr, prev, next, match );\r\n views[i].className = \"step \" + cls;\r\n\r\n // if steps have different sizes, recalc top position and set style\r\n if (trackTopPos) {\r\n var p = (cls===prev) ? roll.steps[i].size * -1 : ((cls===next) ? viewportHeight : 0);\r\n views[i].style.top = p+\"px\";\r\n }\r\n }\r\n }\r\n }\r\n\r\n\r\n /**\r\n * Static method to create a Roll instance with DOM elements\r\n * @param viewPortID id of viewport element, which is the parent of the viewPane. eg, \"#viewport\"\r\n * @param viewPaneID id of view pane element, eg, \"#pane\"\r\n * @param viewBox id of view box element, which is the parent the viewClass elements. eg, \"#steps\"\r\n * @param viewClass id of each step or slide element, eg, \".step\"\r\n * @param pad optional padding between steps. Defaults to 0.\r\n * @returns the roll instance which you can listen for \"step\" and \"roll\" event via `roll.on(...)`\r\n */\r\n static DOM( viewPortID, viewPaneID, viewBoxID, viewClass, pad=0 ) {\r\n\r\n var viewport = document.querySelector( viewPortID );\r\n var viewpane = viewport.querySelector( viewPaneID );\r\n var viewbox = document.querySelector( viewBoxID );\r\n var views = viewbox.querySelectorAll( viewClass );\r\n\r\n if (!viewport || !viewpane) throw `Cannot find ${viewPortID} or ${viewPaneID} element id.`\r\n if (!viewClass) throw `Cannot find ${viewClass} element class name`;\r\n\r\n // create roll instance based on viewport element height\r\n var roll = new Roll( viewport.getBoundingClientRect().height );\r\n\r\n // add each viewClass element as a step\r\n for (var i = 0; i < views.length; i++) {\r\n var rect = views[i].getBoundingClientRect();\r\n roll.addStep( Roll.chunk( rect.height, pad ) );\r\n }\r\n\r\n // update viewpane height based on steps\r\n viewpane.style.height = roll.getHeight()+\"px\";\r\n\r\n // update viewbox width to account for scrollbar\r\n viewbox.style.width = viewpane.getBoundingClientRect().width+\"px\";\r\n\r\n // track scroll\r\n viewport.addEventListener(\"scroll\", function(evt) {\r\n roll.move( viewport.scrollTop );\r\n });\r\n\r\n return roll;\r\n }\r\n\r\n}\r\n","var Roll = require(\"./roll.js\");\r\n\r\nif (window) window.Roll = Roll;\r\n\r\n"]} -------------------------------------------------------------------------------- /dist/roll.min.js: -------------------------------------------------------------------------------- 1 | !function e(t,r,n){function s(o,u){if(!r[o]){if(!t[o]){var a="function"==typeof require&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var h=new Error("Cannot find module '"+o+"'");throw h.code="MODULE_NOT_FOUND",h}var l=r[o]={exports:{}};t[o][0].call(l.exports,function(e){var r=t[o][1][e];return s(r?r:e)},l,l.exports,e,t,r,n)}return r[o].exports}for(var i="function"==typeof require&&require,o=0;oe||isNaN(e))throw TypeError("n must be a positive number");return this._maxListeners=e,this},n.prototype.emit=function(e){var t,r,n,i,a,h;if(this._events||(this._events={}),"error"===e&&(!this._events.error||o(this._events.error)&&!this._events.error.length)){if(t=arguments[1],t instanceof Error)throw t;throw TypeError('Uncaught, unspecified "error" event.')}if(r=this._events[e],u(r))return!1;if(s(r))switch(arguments.length){case 1:r.call(this);break;case 2:r.call(this,arguments[1]);break;case 3:r.call(this,arguments[1],arguments[2]);break;default:for(n=arguments.length,i=new Array(n-1),a=1;n>a;a++)i[a-1]=arguments[a];r.apply(this,i)}else if(o(r)){for(n=arguments.length,i=new Array(n-1),a=1;n>a;a++)i[a-1]=arguments[a];for(h=r.slice(),n=h.length,a=0;n>a;a++)h[a].apply(this,i)}return!0},n.prototype.addListener=function(e,t){var r;if(!s(t))throw TypeError("listener must be a function");if(this._events||(this._events={}),this._events.newListener&&this.emit("newListener",e,s(t.listener)?t.listener:t),this._events[e]?o(this._events[e])?this._events[e].push(t):this._events[e]=[this._events[e],t]:this._events[e]=t,o(this._events[e])&&!this._events[e].warned){var r;r=u(this._maxListeners)?n.defaultMaxListeners:this._maxListeners,r&&r>0&&this._events[e].length>r&&(this._events[e].warned=!0,console.error("(node) warning: possible EventEmitter memory leak detected. %d listeners added. Use emitter.setMaxListeners() to increase limit.",this._events[e].length),"function"==typeof console.trace&&console.trace())}return this},n.prototype.on=n.prototype.addListener,n.prototype.once=function(e,t){function r(){this.removeListener(e,r),n||(n=!0,t.apply(this,arguments))}if(!s(t))throw TypeError("listener must be a function");var n=!1;return r.listener=t,this.on(e,r),this},n.prototype.removeListener=function(e,t){var r,n,i,u;if(!s(t))throw TypeError("listener must be a function");if(!this._events||!this._events[e])return this;if(r=this._events[e],i=r.length,n=-1,r===t||s(r.listener)&&r.listener===t)delete this._events[e],this._events.removeListener&&this.emit("removeListener",e,t);else if(o(r)){for(u=i;u-->0;)if(r[u]===t||r[u].listener&&r[u].listener===t){n=u;break}if(0>n)return this;1===r.length?(r.length=0,delete this._events[e]):r.splice(n,1),this._events.removeListener&&this.emit("removeListener",e,t)}return this},n.prototype.removeAllListeners=function(e){var t,r;if(!this._events)return this;if(!this._events.removeListener)return 0===arguments.length?this._events={}:this._events[e]&&delete this._events[e],this;if(0===arguments.length){for(t in this._events)"removeListener"!==t&&this.removeAllListeners(t);return this.removeAllListeners("removeListener"),this._events={},this}if(r=this._events[e],s(r))this.removeListener(e,r);else for(;r.length;)this.removeListener(e,r[r.length-1]);return delete this._events[e],this},n.prototype.listeners=function(e){var t;return t=this._events&&this._events[e]?s(this._events[e])?[this._events[e]]:this._events[e].slice():[]},n.listenerCount=function(e,t){var r;return r=e._events&&e._events[t]?s(e._events[t])?1:e._events[t].length:0}},{}],2:[function(e,t,r){"use strict";function n(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}function s(e,t){if("function"!=typeof t&&null!==t)throw new TypeError("Super expression must either be null or a function, not "+typeof t);e.prototype=Object.create(t&&t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}}),t&&(Object.setPrototypeOf?Object.setPrototypeOf(e,t):e.__proto__=t)}Object.defineProperty(r,"__esModule",{value:!0});var i=function(){function e(e,t){for(var r=0;r0){var r=this.steps[this.steps.length-1];t=r.p2+r.pad}for(var n=0;n=-this.viewportSize&&t.p2<=t.size)return this.current=e,e}return-1}},{key:"getStepProgress",value:function(){var e=this.steps[this.current];return 1-e.p2/e.size}},{key:"getPosition",value:function(){return this.pos}},{key:"getHeight",value:function(){var e=arguments.length<=0||void 0===arguments[0]?!1:arguments[0];return e&&(this.paneSize=this.steps.reduce(function(e,t){return e+t.size+t.pad},0)),this.paneSize}},{key:"getViewportHeight",value:function(){return this.viewportSize}},{key:"move",value:function(e){var t=this.pos;this.pos=-e;for(var r=this.pos-t,n=0;n=0&&(this.emit("step",i,this.last,this.viewportSize),this.last=i),this}},{key:"scroll",value:function(e,t){var r=this,n=arguments.length<=2||void 0===arguments[2]?.1:arguments[2],s=arguments.length<=3||void 0===arguments[3]?!0:arguments[3];if(!t||null==t.scrollTop)throw"scrollPane parameter requires a DOM element with scrollTop property";clearInterval(this.movingInterval);var i=Number.NEGATIVE_INFINITY,o=s?"scrollTop":"scrollLeft";this.movingInterval=setInterval(function(){var s=r.getStepAt(e),u=(s.p1+s.size/4)*n;t[o]+=u,(Math.abs(u)<1||i===t[o])&&clearInterval(r.movingInterval),i=t[o]},17)}}],[{key:"chunk",value:function(e){var t=arguments.length<=1||void 0===arguments[1]?0:arguments[1];return{p1:0,p2:e,size:e,pad:t}}},{key:"stepName",value:function(e,t){var r=arguments.length<=2||void 0===arguments[2]?"prev":arguments[2],n=arguments.length<=3||void 0===arguments[3]?"next":arguments[3],s=arguments.length<=4||void 0===arguments[4]?"curr":arguments[4];return e===t?s:t>e?r:n}},{key:"stepHandler",value:function(e,r){var n=arguments.length<=2||void 0===arguments[2]?"prev":arguments[2],s=arguments.length<=3||void 0===arguments[3]?"next":arguments[3],i=arguments.length<=4||void 0===arguments[4]?"curr":arguments[4],o=arguments.length<=5||void 0===arguments[5]?!1:arguments[5];return function(u,a,h){for(var l=0;l bundling...'); 51 | rebundle(); 52 | }); 53 | } 54 | 55 | rebundle(); 56 | } 57 | 58 | function watch() { 59 | return compile(true); 60 | } 61 | 62 | 63 | gulp.task('min', function() { 64 | return gulp.src( "./dist/roll.js" ) 65 | .pipe( rename('roll.min.js') ) 66 | .pipe( uglify() ) 67 | .pipe( gulp.dest( "./dist" ) ) 68 | 69 | }); 70 | 71 | gulp.task('module', function () { 72 | return gulp.src('./src/roll.js') 73 | .pipe(gulpbabel()) 74 | .pipe(gulp.dest('./dist/module')); 75 | }); 76 | 77 | gulp.task('build', function() { return compile(); }); 78 | gulp.task('watch', function() { return watch(); }); 79 | 80 | gulp.task('default', ['watch']); 81 | 82 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rolljs", 3 | "version": "0.1.2", 4 | "description": "A simple library to track scroll movements.", 5 | "author": "William Ngan", 6 | "devDependencies": { 7 | "event-stream": "^3.3.0", 8 | "gulp": "^3.8.10", 9 | "gulp-util": "^3.0.1", 10 | "gulp-concat": "^2.4.3", 11 | "gulp-rename": "^1.2.0", 12 | "gulp-insert": "^0.4.0", 13 | "gulp-uglify": "^1.0.2", 14 | "gulp-sourcemaps": "^1.5.2", 15 | "gulp-babel": "^5.2.0", 16 | "browserify": "^11.1.0", 17 | "vinyl-source-stream": "^1.1.0", 18 | "vinyl-buffer": "^1.0.0", 19 | "watchify": "^3.4.0", 20 | "babelify": "6.3.0", 21 | "events": "^1.1.0" 22 | }, 23 | "main": "dist/module/roll.js", 24 | "directories": { 25 | "doc": "docs", 26 | "test": "test" 27 | }, 28 | "dependencies": {}, 29 | "scripts": { 30 | "test": "echo \"Error: no test specified\" && exit 1" 31 | }, 32 | "repository": { 33 | "type": "git", 34 | "url": "https://github.com/williamngan/roll.git" 35 | }, 36 | "keywords": [ 37 | "animation", 38 | "scroll", 39 | "ui" 40 | ], 41 | "license": "Apache", 42 | "bugs": { 43 | "url": "https://github.com/williamngan/roll/issues" 44 | }, 45 | "homepage": "https://github.com/williamngan/roll" 46 | } 47 | -------------------------------------------------------------------------------- /src/roll.js: -------------------------------------------------------------------------------- 1 | var EventEmitter = require('events').EventEmitter; 2 | 3 | /** 4 | * Roll simply keep tracks of steps' positions inside a viewport. 5 | * Apart from the static helper functions and the `scroll` function, a roll instance doesn't depend on DOM manipulation. 6 | * That means you can use a Roll instance in contexts other than DOM. 7 | */ 8 | export default class Roll extends EventEmitter { 9 | 10 | /** 11 | * Create a new Roll. 12 | * @param viewSize viewport size (single dimension) 13 | */ 14 | constructor( viewSize ) { 15 | super(); 16 | 17 | this.viewportSize = viewSize; 18 | this.paneSize = 0; 19 | 20 | // store the steps object {y1, y2, size, pad}, See Roll.chunk 21 | this.steps = []; 22 | 23 | this.pos = 0; // current position 24 | this.current = 0; // current step 25 | this.last = -1; // last step 26 | 27 | this.movingInterval = -1; 28 | } 29 | 30 | 31 | /** 32 | * Add a step object. You can also use Roll.chunk() static helper function to create a step object easily. 33 | * @param s an object with {p1, p2, size, pad} properties, or an array of steps object 34 | * @returns {Roll} 35 | */ 36 | addStep(s) { 37 | 38 | if (!Array.isArray(s)) { 39 | s = [s]; 40 | } 41 | 42 | // get last recorded step 43 | var d = s[0].p1; 44 | if (this.steps.length > 0 ) { 45 | var last = this.steps[this.steps.length-1]; 46 | d = last.p2 + last.pad; 47 | } 48 | 49 | // append new steps 50 | for (var i=0; i 0, step will be -1 when current progress is on the padding area. This allows you to check progress against padding. 75 | * @returns {number} 76 | */ 77 | getStep() { 78 | for (var i=0; i= -this.viewportSize && st.p2 <= st.size ) { 81 | this.current = i; 82 | return i; 83 | } 84 | } 85 | return -1; 86 | } 87 | 88 | /** 89 | * Get current progress within the current step 90 | * @returns 0-1 if step.pad is 0. Otherwise it will range from negative to positive. 91 | */ 92 | getStepProgress() { 93 | var curr = this.steps[ this.current ]; 94 | return 1 - (curr.p2 / curr.size); 95 | } 96 | 97 | 98 | /** 99 | * Get current position 100 | * @returns {number|*} 101 | */ 102 | getPosition() { 103 | return this.pos; 104 | } 105 | 106 | /** 107 | * Get total height of the pane (including padding) 108 | * @returns {*} 109 | */ 110 | getHeight( recalc = false ) { 111 | if (recalc ) this.paneSize = this.steps.reduce( (a,b) => a+b.size+b.pad, 0 ); 112 | return this.paneSize; 113 | } 114 | 115 | /** 116 | * Get viewport's height (same as this.viewportSize) 117 | * @returns {*} 118 | */ 119 | getViewportHeight() { 120 | return this.viewportSize; 121 | } 122 | 123 | 124 | /** 125 | * Move the roll. This will emit two events `roll(step, currProgress, currPosition, totalProgress)` and `step(curr, last)` 126 | * @param pos new position 127 | * @returns {Roll} 128 | */ 129 | move( pos ) { 130 | var last = this.pos; 131 | this.pos = -pos; 132 | var diff = this.pos - last; 133 | 134 | for (var i=0; i= 0) { 145 | this.emit("step", curr, this.last, this.viewportSize ); 146 | this.last = curr; 147 | } 148 | 149 | return this; 150 | } 151 | 152 | 153 | /** 154 | * Animated scrolling a DOM element 155 | * @param index step index 156 | * @param scrollPane a DOM element with scrolling (overflow-y). 157 | * @param speed optional speed of animated scroll. Defaults to 0.1. Larger is faster 158 | * @param isVertical optional boolean to indicate horizontal or vertical scroll 159 | */ 160 | scroll( index, scrollPane, speed=0.1, isVertical=true) { 161 | if (!scrollPane || scrollPane.scrollTop == null) throw "scrollPane parameter requires a DOM element with scrollTop property"; 162 | clearInterval( this.movingInterval ); 163 | var _temp = Number.NEGATIVE_INFINITY; 164 | var dir = (isVertical) ? "scrollTop" : "scrollLeft"; 165 | 166 | this.movingInterval = setInterval( () => { 167 | var target = this.getStepAt(index); 168 | var d = (target.p1 + target.size/4) * speed; 169 | scrollPane[dir] += d; 170 | if (Math.abs(d)<1 || _temp === scrollPane[dir]) clearInterval(this.movingInterval); 171 | _temp = scrollPane[dir]; 172 | }, 17); 173 | } 174 | 175 | 176 | /** 177 | * A convenient static function to create a step object 178 | * @param size chunk size 179 | * @param pad optional padding (default to 0) 180 | * @returns {{p1: number, p2: *, size: *, pad: number}} 181 | */ 182 | static chunk( size, pad=0) { 183 | return { 184 | p1: 0, 185 | p2: size, 186 | size: size, 187 | pad: pad 188 | } 189 | } 190 | 191 | 192 | /** 193 | * A convenient static function to compare a step with current step, and transform it to a name 194 | * @param step the step to check 195 | * @param currStep current step 196 | * @param prev optional class name for step is < currStep. Defaults to "prev" 197 | * @param next optional class name for step is > currStep. Defaults to "next" 198 | * @param match optional class name for step = currStep. Defaults to "curr" 199 | * @returns {string} 200 | */ 201 | static stepName( step, currStep, prev="prev", next="next", match="curr") { 202 | return (step === currStep) ? match : ( (step < currStep) ? prev : next ); 203 | } 204 | 205 | 206 | /** 207 | * Static helper to get a handle function for Roll's "step" event. The handler function will add class names to each step element based on current step value. 208 | * @param roll a Roll instance 209 | * @param views a list of DOM elements which are the steps 210 | * @param prev optional class name for step is < currStep. Defaults to "prev" 211 | * @param next optional class name for step is > currStep. Defaults to "next" 212 | * @param match optional class name for step = currStep. Defaults to "curr" 213 | * @returns {Function} 214 | */ 215 | static stepHandler( roll, views, prev="prev", next="next", match="curr", trackTopPos=false) { 216 | return function ( curr, last, viewportHeight ) { 217 | for (var i = 0; i < roll.steps.length; i++) { 218 | var cls = Roll.stepName( i, curr, prev, next, match ); 219 | views[i].className = "step " + cls; 220 | 221 | // if steps have different sizes, recalc top position and set style 222 | if (trackTopPos) { 223 | var p = (cls===prev) ? roll.steps[i].size * -1 : ((cls===next) ? viewportHeight : 0); 224 | views[i].style.top = p+"px"; 225 | } 226 | } 227 | } 228 | } 229 | 230 | 231 | /** 232 | * Static method to create a Roll instance with DOM elements 233 | * @param viewPortID id of viewport element, which is the parent of the viewPane. eg, "#viewport" 234 | * @param viewPaneID id of view pane element, eg, "#pane" 235 | * @param viewBox id of view box element, which is the parent the viewClass elements. eg, "#steps" 236 | * @param viewClass id of each step or slide element, eg, ".step" 237 | * @param pad optional padding between steps. Defaults to 0. 238 | * @returns the roll instance which you can listen for "step" and "roll" event via `roll.on(...)` 239 | */ 240 | static DOM( viewPortID, viewPaneID, viewBoxID, viewClass, pad=0 ) { 241 | 242 | var viewport = document.querySelector( viewPortID ); 243 | var viewpane = viewport.querySelector( viewPaneID ); 244 | var viewbox = document.querySelector( viewBoxID ); 245 | var views = viewbox.querySelectorAll( viewClass ); 246 | 247 | if (!viewport || !viewpane) throw `Cannot find ${viewPortID} or ${viewPaneID} element id.` 248 | if (!viewClass) throw `Cannot find ${viewClass} element class name`; 249 | 250 | // create roll instance based on viewport element height 251 | var roll = new Roll( viewport.getBoundingClientRect().height ); 252 | 253 | // add each viewClass element as a step 254 | for (var i = 0; i < views.length; i++) { 255 | var rect = views[i].getBoundingClientRect(); 256 | roll.addStep( Roll.chunk( rect.height, pad ) ); 257 | } 258 | 259 | // update viewpane height based on steps 260 | viewpane.style.height = roll.getHeight()+"px"; 261 | 262 | // update viewbox width to account for scrollbar 263 | viewbox.style.width = viewpane.getBoundingClientRect().width+"px"; 264 | 265 | // track scroll 266 | viewport.addEventListener("scroll", function(evt) { 267 | roll.move( viewport.scrollTop ); 268 | }); 269 | 270 | return roll; 271 | } 272 | 273 | } 274 | -------------------------------------------------------------------------------- /src/roll_standalone.js: -------------------------------------------------------------------------------- 1 | var Roll = require("./roll.js"); 2 | 3 | if (window) window.Roll = Roll; 4 | 5 | --------------------------------------------------------------------------------