├── .gitignore ├── LICENSE ├── README.md ├── gulpfile.js ├── package.json └── src ├── force.js ├── force.min.js └── force.min.js.map /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Rene Tanczos (renetanczos.com) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Force.js 2 | 3 | Finally, an easy way to animate elements and jump around on your page. 4 | 5 | It has different easing functions (listed below) and tries (by default) to use CSS-Transitions to animate elements. 6 | 7 | If the browser doesn't support transitions, force.js fallback to native javascript functions. 8 | 9 | Force.js support even older browser version with no transition support. 10 | 11 | **Go to the [force-js page](https://force-js.com) and try it out!** 12 | 13 | To use it on your page, just write this line of code in the HTML `HEAD` or on the bottom of the `BODY` tag. 14 | 15 | ``` 16 | 17 | ``` 18 | 19 | **Support for AMD and Node Module Pattern (Including browserify)** 20 | 21 | ## Get it on Bower 22 | 23 | ``` 24 | bower install force-js 25 | ``` 26 | 27 | ## Get it via cdnjs 28 | 29 | Just copy and paste it into your project. 30 | 31 | ``` 32 | 33 | ``` 34 | 35 | Go to the [force-js page on cdnjs](https://cdnjs.com/libraries/force-js). 36 | 37 | ### jQuery 38 | 39 | Force.js is 100% pure vanilla!!! 40 | 41 | But for all the coders who don't want to miss jQuery. 42 | 43 | Force.js automatically detects jQuery and extend its Objects with the **force.move()** and **force.jump()** function. 44 | 45 | So you are able to use force.js in an jQuery object. 46 | 47 | ``` 48 | $('#ball').move({left: 100px, top: 50px}, 1000); 49 | ``` 50 | 51 | *To use force.js in jQuery, write the jQuery TAG first. So force.js can extend its objects afterwards.* 52 | 53 | ## Jump 54 | 55 | Doing jumps on the page are easier than every. 56 | 57 | To automatically detect hash links on your page, just use the **force.bindHashes()** function like this. 58 | 59 | ``` 60 | force.bindHashes(); 61 | ``` 62 | 63 | But if you want to do it by yourself, use the **force.jump()** function. 64 | 65 | ``` 66 | var element = document.getElementBy('element-id'); 67 | 68 | // jump by object 69 | force.jump(element); 70 | 71 | // jump by selector 72 | force.jump('#element-id'); 73 | ``` 74 | 75 | You can use the function with additional options. 76 | 77 | ``` 78 | force.jump(target); 79 | 80 | var options = { 81 | setHash: false 82 | // if set to TRUE, it sets the hash/id value of the element in the URL 83 | 84 | duration: 500, 85 | done: function() {}, 86 | easing: 'easeInQuad', 87 | }; 88 | force.jump(target, options); 89 | ``` 90 | 91 | Or the jQuery extention. 92 | 93 | ``` 94 | $('#ball').jump(); 95 | 96 | //$('#ball').jump(options); 97 | ``` 98 | 99 | ## Move 100 | 101 | You can also animate elements with force.js. 102 | 103 | To do so, just use the **force.move()** function. 104 | 105 | ``` 106 | var element = document.getElementBy('element-id'); 107 | 108 | // jump by object 109 | force.move(element, {left: 100px, top: 50px}, 1000); 110 | 111 | // jump by selector 112 | force.move('#element-id', {left: 100px, top: 50px}, 1000); 113 | ``` 114 | 115 | You can use the function with additional options. 116 | 117 | ``` 118 | force.move(target, properties, duration, doneCallback); 119 | 120 | var options = { 121 | properties: { 122 | left: '100px' 123 | }, 124 | duration: 500, 125 | done: function() {}, 126 | easing: 'easeInQuad' 127 | }; 128 | force.move(target, options); 129 | ``` 130 | 131 | Or the jQuery extention. 132 | 133 | ``` 134 | $('#ball').move({left: 100px, top: 50px}, 1000); 135 | 136 | // $('#ball').move(options, duration, doneCallback); 137 | ``` 138 | 139 | Don't worry if you use the function multiple times. 140 | 141 | The **force.move()** function is cached! 142 | 143 | That means, the function just get executed if the previous call is done. 144 | 145 | ## Options 146 | 147 | In force.js you are able to modify everything you want to fit your needs. 148 | 149 | ``` 150 | // edit single option 151 | force.opt.cacheScrolling = true; 152 | 153 | // or use the config function and paste an object to override the old settings. 154 | force.config( { cacheScrolling: true } ); 155 | ``` 156 | 157 | #### hashLinkPattern: 'a[href*="#"]:not([href="#"])' 158 | This is a selector to find the hash links in your page by exectuting the **force.bindHashes()** function. 159 | 160 | #### frames: 60 161 | 162 | By default, force.js runs at 60 fps. 163 | 164 | You can edit this property as well. 165 | 166 | But be careful! 167 | 168 | You can increase but also decrease the performance of the page. 169 | 170 | #### moveDuration: 1000 AND jumpDuration: 1000 171 | 172 | The default duration for the **force.move()** and **force.jump()** function is **1000** ms. 173 | You can override this value inside the functions by set the duration property in the config object if you want to. 174 | 175 | #### moveEasing: 'swing' AND jumpEasing: 'swing' 176 | 177 | The default easing function is **swing** but you can change this to: 178 | - swing 179 | - easeInQuad 180 | - easeOutQuad 181 | - easeInOutQuad 182 | - easeInCubic 183 | - easeOutCubic 184 | - easeInOutCubic 185 | - easeInQuart 186 | - easeOutQuart 187 | - easeInOutQuart 188 | - easeInQuint 189 | - easeOutQuint 190 | - easeInOutQuint 191 | - easeInSine 192 | - easeOutSine 193 | - easeInOutSine 194 | - easeInExpo 195 | - easeOutExpo 196 | - easeInOutExpo 197 | - easeInCirc 198 | - easeOutCirc 199 | - easeInOutCirc 200 | - easeInElastic* 201 | - easeOutElastic* 202 | - easeInOutElastic* 203 | - easeInBack* 204 | - easeOutBack* 205 | - easeInOutBack* 206 | - easeInBounce* 207 | - easeOutBounce* 208 | - easeInOutBounce* 209 | 210 | \* This easing functions doesn't work with css transitions and use native javascript. 211 | But don't worry, force.js does it automatically for you! 212 | 213 | #### cacheJumps: true 214 | 215 | Page jumps are cached by default. 216 | 217 | That means, the next jump only animates if the previous is finished. 218 | 219 | Set it to FALSE and the jump stops immediately and starts the new jump. 220 | 221 | #### cssTransitions: true 222 | 223 | By default, force.js try to use css transitions if the browser supports it. 224 | 225 | Using transitions looks much smoother than native javascript and doesn't block the event loop. 226 | 227 | I would recommend it to let it turned ON by default. 228 | 229 | force.js manage the usage of transitions automatically if the browser doesn't support it. 230 | 231 | ### Licence 232 | Force.js is published under the MIT licence. So feel free to use, share or modify it! 233 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 2 | 'use strict'; 3 | 4 | const gulp = require('gulp'); 5 | const rename = require('gulp-rename'); 6 | const uglify = require('gulp-uglify'); 7 | const saveLicense = require('uglify-save-license'); 8 | const sourcemaps = require('gulp-sourcemaps'); 9 | 10 | 11 | gulp.task('js', () => { 12 | return gulp 13 | .src(['./src/force.js']) 14 | .pipe(sourcemaps.init()) 15 | .pipe(uglify({ 16 | output: { 17 | comments: saveLicense 18 | }, 19 | mangle: true 20 | })) 21 | .pipe(rename(function(path) { 22 | path.basename += '.min' 23 | return path; 24 | })) 25 | .pipe(sourcemaps.write('./')) 26 | .pipe(gulp.dest('./src')); 27 | }); 28 | 29 | gulp.task('default', gulp.parallel('js')); 30 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "force-js", 3 | "version": "1.0.0", 4 | "description": "Easy to use page animation lib with no dependecies", 5 | "main": "force.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/gravmatt/force-js.git" 12 | }, 13 | "keywords": [ 14 | "forcejs", 15 | "animation", 16 | "js" 17 | ], 18 | "author": "Rene Tanczos", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/gravmatt/force-js/issues" 22 | }, 23 | "homepage": "https://github.com/gravmatt/force-js#readme", 24 | "dependencies": { 25 | "gulp": "^4.0.0", 26 | "gulp-rename": "^1.4.0", 27 | "gulp-sourcemaps": "^2.6.5", 28 | "gulp-uglify": "^3.0.2", 29 | "uglify-save-license": "^0.4.1" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/force.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015 Rene Tanczos 3 | */ 4 | ;(function(window, document, undefined) { 5 | var force = function() { 6 | 'use strict'; 7 | 8 | var scrollCache = [], 9 | isAnimating = false, 10 | animationCache = [], 11 | isScrolling = false, 12 | currentJumpLoop, 13 | transitionTimeout, 14 | hashLinkElements, 15 | jsEasing = { 16 | swing: function (t, b, c, d) { 17 | // default 18 | return this.easeOutQuad(t, b, c, d); 19 | }, 20 | easeInQuad: function (t, b, c, d) { 21 | return c*(t/=d)*t + b; 22 | }, 23 | easeOutQuad: function (t, b, c, d) { 24 | return -c *(t/=d)*(t-2) + b; 25 | }, 26 | easeInOutQuad: function (t, b, c, d) { 27 | if ((t/=d/2) < 1) return c/2*t*t + b; 28 | return -c/2 * ((--t)*(t-2) - 1) + b; 29 | }, 30 | easeInCubic: function (t, b, c, d) { 31 | return c*(t/=d)*t*t + b; 32 | }, 33 | easeOutCubic: function (t, b, c, d) { 34 | return c*((t=t/d-1)*t*t + 1) + b; 35 | }, 36 | easeInOutCubic: function (t, b, c, d) { 37 | if ((t/=d/2) < 1) return c/2*t*t*t + b; 38 | return c/2*((t-=2)*t*t + 2) + b; 39 | }, 40 | easeInQuart: function (t, b, c, d) { 41 | return c*(t/=d)*t*t*t + b; 42 | }, 43 | easeOutQuart: function (t, b, c, d) { 44 | return -c * ((t=t/d-1)*t*t*t - 1) + b; 45 | }, 46 | easeInOutQuart: function (t, b, c, d) { 47 | if ((t/=d/2) < 1) return c/2*t*t*t*t + b; 48 | return -c/2 * ((t-=2)*t*t*t - 2) + b; 49 | }, 50 | easeInQuint: function (t, b, c, d) { 51 | return c*(t/=d)*t*t*t*t + b; 52 | }, 53 | easeOutQuint: function (t, b, c, d) { 54 | return c*((t=t/d-1)*t*t*t*t + 1) + b; 55 | }, 56 | easeInOutQuint: function (t, b, c, d) { 57 | if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b; 58 | return c/2*((t-=2)*t*t*t*t + 2) + b; 59 | }, 60 | easeInSine: function (t, b, c, d) { 61 | return -c * Math.cos(t/d * (Math.PI/2)) + c + b; 62 | }, 63 | easeOutSine: function (t, b, c, d) { 64 | return c * Math.sin(t/d * (Math.PI/2)) + b; 65 | }, 66 | easeInOutSine: function (t, b, c, d) { 67 | return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b; 68 | }, 69 | easeInExpo: function (t, b, c, d) { 70 | return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b; 71 | }, 72 | easeOutExpo: function (t, b, c, d) { 73 | return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b; 74 | }, 75 | easeInOutExpo: function (t, b, c, d) { 76 | if (t==0) return b; 77 | if (t==d) return b+c; 78 | if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b; 79 | return c/2 * (-Math.pow(2, -10 * --t) + 2) + b; 80 | }, 81 | easeInCirc: function (t, b, c, d) { 82 | return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b; 83 | }, 84 | easeOutCirc: function (t, b, c, d) { 85 | return c * Math.sqrt(1 - (t=t/d-1)*t) + b; 86 | }, 87 | easeInOutCirc: function (t, b, c, d) { 88 | if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b; 89 | return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b; 90 | }, 91 | easeInElastic: function (t, b, c, d) { 92 | var s=1.70158;var p=0;var a=c; 93 | if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; 94 | if (a < Math.abs(c)) { a=c; var s=p/4; } 95 | else var s = p/(2*Math.PI) * Math.asin (c/a); 96 | return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; 97 | }, 98 | easeOutElastic: function (t, b, c, d) { 99 | var s=1.70158;var p=0;var a=c; 100 | if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3; 101 | if (a < Math.abs(c)) { a=c; var s=p/4; } 102 | else var s = p/(2*Math.PI) * Math.asin (c/a); 103 | return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b; 104 | }, 105 | easeInOutElastic: function (t, b, c, d) { 106 | var s=1.70158;var p=0;var a=c; 107 | if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5); 108 | if (a < Math.abs(c)) { a=c; var s=p/4; } 109 | else var s = p/(2*Math.PI) * Math.asin (c/a); 110 | if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b; 111 | return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b; 112 | }, 113 | easeInBack: function (t, b, c, d, s) { 114 | if (s == undefined) s = 1.70158; 115 | return c*(t/=d)*t*((s+1)*t - s) + b; 116 | }, 117 | easeOutBack: function (t, b, c, d, s) { 118 | if (s == undefined) s = 1.70158; 119 | return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b; 120 | }, 121 | easeInOutBack: function (t, b, c, d, s) { 122 | if (s == undefined) s = 1.70158; 123 | if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b; 124 | return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b; 125 | }, 126 | easeInBounce: function (t, b, c, d) { 127 | return c - this.easeOutBounce (d-t, 0, c, d) + b; 128 | }, 129 | easeOutBounce: function (t, b, c, d) { 130 | if ((t/=d) < (1/2.75)) { 131 | return c*(7.5625*t*t) + b; 132 | } else if (t < (2/2.75)) { 133 | return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b; 134 | } else if (t < (2.5/2.75)) { 135 | return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b; 136 | } else { 137 | return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b; 138 | } 139 | }, 140 | easeInOutBounce: function (t, b, c, d) { 141 | if (t < d/2) return this.easeInBounce (t*2, 0, c, d) * .5 + b; 142 | return this.easeOutBounce (t*2-d, 0, c, d) * .5 + c*.5 + b; 143 | } 144 | }, 145 | 146 | cssEasing = { 147 | swing: function () { 148 | // default 149 | return this.easeOutQuad(); 150 | }, 151 | easeInQuad: function () { 152 | return '0.55, 0.085, 0.68, 0.53'; 153 | }, 154 | easeOutQuad: function () { 155 | return '0.25, 0.46, 0.45, 0.94'; 156 | }, 157 | easeInOutQuad: function () { 158 | return '0.455, 0.03, 0.515, 0.955'; 159 | }, 160 | easeInCubic: function () { 161 | return '0.55, 0.055, 0.675, 0.19'; 162 | }, 163 | easeOutCubic: function () { 164 | return '0.215, 0.61, 0.355, 1'; 165 | }, 166 | easeInOutCubic: function () { 167 | return '0.645, 0.045, 0.355, 1'; 168 | }, 169 | easeInQuart: function () { 170 | return '0.895, 0.03, 0.685, 0.22'; 171 | }, 172 | easeOutQuart: function () { 173 | return '0.165, 0.84, 0.44, 1'; 174 | }, 175 | easeInOutQuart: function () { 176 | return '0.77, 0, 0.175, 1'; 177 | }, 178 | easeInQuint: function () { 179 | return '0.755, 0.05, 0.855, 0.06'; 180 | }, 181 | easeOutQuint: function () { 182 | return '0.23, 1, 0.32, 1'; 183 | }, 184 | easeInOutQuint: function () { 185 | return '0.86, 0, 0.07, 1'; 186 | }, 187 | easeInSine: function () { 188 | return '0.47, 0, 0.745, 0.715'; 189 | }, 190 | easeOutSine: function () { 191 | return '0.39, 0.575, 0.565, 1'; 192 | }, 193 | easeInOutSine: function () { 194 | return '0.445, 0.05, 0.55, 0.95'; 195 | }, 196 | easeInExpo: function () { 197 | return '0.95, 0.05, 0.795, 0.035'; 198 | }, 199 | easeOutExpo: function () { 200 | return '0.19, 1, 0.22, 1'; 201 | }, 202 | easeInOutExpo: function () { 203 | return '1, 0, 0, 1'; 204 | }, 205 | easeInCirc: function () { 206 | return '0.6, 0.04, 0.98, 0.335'; 207 | }, 208 | easeOutCirc: function () { 209 | return '0.075, 0.82, 0.165, 1'; 210 | }, 211 | easeInOutCirc: function () { 212 | return '0.785, 0.135, 0.15, 0.86'; 213 | } 214 | }, 215 | 216 | 217 | opt = { 218 | hashLinkPattern: 'a[href*="#"]:not([href="#"])', 219 | frames: 60, 220 | valueUnitRegEx: /^([\-]{0,1}[0-9\.]+)([a-z%]{0,3})$/, 221 | moveDuration: 1000, 222 | moveEasing: 'swing', 223 | jumpDuration: 1000, 224 | scrollEasing: 'swing', 225 | cacheJumps: true, 226 | cssTransitions: true 227 | }, 228 | 229 | config = function(options) { 230 | options && Object.keys(options).forEach(function(key) { 231 | opt[key] = options[key]; 232 | }); 233 | }, 234 | 235 | hasTransitionSupport = function() { 236 | var s = document.documentElement.style; 237 | return ( 238 | s.webkitTransition !== undefined || 239 | s.MozTransition !== undefined || 240 | s.OTransition !== undefined || 241 | s.MsTransition !== undefined || 242 | s.transition !== undefined 243 | ); 244 | }, 245 | 246 | setTransition = function(element, trans) { 247 | element.style.webkitTransition !== undefined && (element.style.webkitTransition = trans); 248 | element.style.MozTransition !== undefined && (element.style.MozTransition = trans); 249 | element.style.OTransition !== undefined && (element.style.OTransition = trans); 250 | element.style.MsTransition !== undefined && (element.style.MsTransition = trans); 251 | element.style.transition !== undefined && (element.style.transition = trans); 252 | }, 253 | 254 | isStyleProperty = function(propName) { 255 | return propName in document.documentElement.style; 256 | }, 257 | 258 | /* 259 | Converts the CSS property name into a JS style name. 260 | 261 | Example: 262 | from CSS: font-family 263 | to JS: fontFamily 264 | */ 265 | toJsStyle = function(value) { 266 | return value.replace(/(\-[a-z]{1})/g, function(match) {return match.slice(-1).toUpperCase()}) 267 | }, 268 | 269 | /* 270 | @target string/object 271 | can be a selector/id or object 272 | 273 | @options object 274 | if duration or done is undefined, options contains informations about the animation 275 | but if duration and done is set, options are the style properties (target properties) 276 | to be animated. 277 | 278 | @duration number 279 | Duration of the animation. 280 | 281 | @done function 282 | will be executed after the animation finished. 283 | */ 284 | move = function(target, options, duration, done) { 285 | 286 | var callback; // executes when the animation is done 287 | 288 | if(options.isJump) { 289 | // abort the current scroll animations if caching is false and a new scroll event was started 290 | (!opt.cacheJumps && isScrolling) && (clearInterval(currentJumpLoop)); 291 | // cache the target if scrolling is active and a scrolling animation is currently running 292 | if(opt.cacheJumps && isScrolling) { 293 | scrollCache.push({target: target}); 294 | return; 295 | } 296 | isScrolling = true; 297 | 298 | callback = function() { 299 | isScrolling = false; 300 | if(opt.cacheJumps && scrollCache.length > 0) { 301 | var nextEvent = scrollCache.shift(); 302 | jump(nextEvent.target); 303 | } 304 | }; 305 | } 306 | else { 307 | // executed when its not a jump 308 | if(isAnimating) { 309 | animationCache.push({target: target, options: options, duration: duration, done: done}); 310 | return; 311 | } 312 | isAnimating = true; 313 | 314 | callback = function() { 315 | isAnimating = false; 316 | var next = animationCache.shift(); 317 | next && move(next.target, next.options, next.duration, next.done); 318 | }; 319 | } 320 | 321 | var el = (typeof target === 'string') ? document.querySelector(target) : target; 322 | var o = {}; 323 | o.properties = options.properties || options; 324 | o.duration = duration || options.duration || (options.isJump ? opt.jumpDuration : opt.moveDuration); 325 | o.done = done || options.done; 326 | o.easing = options.easing || (options.isJump ? opt.scrollEasing : opt.moveEasing); 327 | o.isJump = options.isJump; 328 | 329 | // dont care about the target property. has something to do with the jump function 330 | (typeof options.target === 'string') ? (options.target = document.querySelector(options.target)) : (o.target = options.target); 331 | 332 | var isStyle = true; 333 | Object.keys(o.properties).forEach(function(key) { 334 | isStyle = isStyleProperty(key); 335 | }); 336 | 337 | // CSS transition check 338 | if(opt.cssTransitions && isStyle && hasTransitionSupport() && o.easing in cssEasing) { 339 | //console.log('css transitions supported'); 340 | 341 | Object.keys(o.properties).forEach(function(key) { 342 | var trans = 'all ' + o.duration + 'ms cubic-bezier(' + cssEasing[o.easing]() + ')'; 343 | 344 | setTransition(el, trans); 345 | 346 | var stylename = toJsStyle(key); 347 | 348 | el.style[toJsStyle(key)] = o.properties[key]; 349 | 350 | // kill the previous transition when its not finished 351 | clearTimeout(transitionTimeout); 352 | 353 | // should remove the transition after its finished 354 | transitionTimeout = setTimeout(function() { 355 | setTransition(el, ''); 356 | callback(); 357 | o.done && o.done(); 358 | }, o.duration); 359 | }); 360 | 361 | return; 362 | } 363 | 364 | var anims = [], 365 | finished = 0; 366 | 367 | Object.keys(o.properties).forEach(function(key) { 368 | var val = o.properties[key].match(opt.valueUnitRegEx); 369 | anims.push({ 370 | style: toJsStyle(key), 371 | value: parseInt(val[1]), 372 | suffix: val[2] || '', 373 | duration: o.duration, 374 | rawValue: o.properties[key] 375 | }); 376 | }); 377 | 378 | anims.forEach(function(anim) { 379 | var currentFrame = 0, 380 | valueObj; 381 | 382 | if(isStyle) 383 | valueObj = el.style[anim.style].match(opt.valueUnitRegEx); 384 | else 385 | valueObj = (el[anim.style] + '').match(opt.valueUnitRegEx); 386 | 387 | // in case no value is set 388 | if(!valueObj) valueObj = ['0', '0', '']; 389 | 390 | var initValue = isStyle ? (parseInt(valueObj[1]) || 0) : window.scrollY, 391 | change = anim.value - (initValue || 0), 392 | currentTime = 0, 393 | timeSteps = Math.ceil(anim.duration / opt.frames), 394 | loopId; 395 | 396 | loopId = setInterval(function() { 397 | if(currentFrame < opt.frames) { 398 | var v = jsEasing[o.easing](currentTime, initValue, change, anim.duration); 399 | 400 | if(isStyle) 401 | el.style[anim.style] = v + anim.suffix; 402 | else if(anim.style in el) { 403 | if(o.target) 404 | if(anim.style == 'scrollTop') { 405 | window.scrollTo(0, v) 406 | } 407 | else { 408 | o.target[anim.style] = v + anim.suffix; 409 | } 410 | else 411 | el[anim.style] = v + anim.suffix; 412 | } 413 | else 414 | // break loop (nothing to animate) 415 | currentTime = opt.frames; 416 | } 417 | else { 418 | clearInterval(loopId); 419 | finished++; 420 | callback(); 421 | (finished === anims.length && o.done) && o.done(); 422 | } 423 | currentTime += timeSteps; 424 | currentFrame++; 425 | }, timeSteps); 426 | 427 | o.isJump && (currentJumpLoop = loopId); 428 | }); 429 | }, 430 | 431 | jump = function(target, options) { 432 | var el = (typeof target === 'string') ? document.querySelector(target) : target; 433 | var o = options || {}; 434 | 435 | move(el, { 436 | properties: { 437 | scrollTop: el.offsetTop + '' 438 | }, 439 | duration: o.duration || opt.jumpDuration, 440 | easing: o.easing || opt.scrollEasing, 441 | done: o.setHash ? function() { 442 | window.location.hash = el.id; 443 | o.done && o.done(); 444 | } : o.done, 445 | target: document.body, 446 | isJump: true 447 | }); 448 | }, 449 | 450 | // Bind all subscribe all hash link clicks on the page and animates the jump to this position 451 | bindHashes = function() { 452 | hashLinkElements = document.querySelectorAll(opt.hashLinkPattern); 453 | 454 | [].forEach.call(hashLinkElements, function(el) { 455 | el.addEventListener('click', function(ev) { 456 | if (window.location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') && window.location.hostname == this.hostname) { 457 | var target = document.getElementById(this.hash.slice(1)); 458 | target && jump(target); 459 | ev.preventDefault(); 460 | } 461 | }, false) 462 | }); 463 | }, 464 | 465 | // unbind all hash links 466 | unbindHashes = function() { 467 | [].forEach.call(hashLinkElements, function(el) { 468 | el.removeEventListener('click'); 469 | }); 470 | hashLinkElements = null; 471 | }; 472 | 473 | return { 474 | opt: opt, 475 | config: config, 476 | hasTransitionSupport: hasTransitionSupport, 477 | toJsStyle: toJsStyle, 478 | move: move, 479 | jump: jump, 480 | bindHashes: bindHashes, 481 | unbindHashes: unbindHashes, 482 | setTransition: setTransition 483 | }; 484 | }(); 485 | 486 | // extend the jquery object 487 | var jq = window.$ || window.jQuery; 488 | jq && (jq.fn.extend({ 489 | move: function (options, duration, done) { 490 | return this.each(function() { 491 | force.move(this, options, duration, done); 492 | }); 493 | }, 494 | jump: function(options) { 495 | return this.each(function() { 496 | force.jump(this, options); 497 | }); 498 | } 499 | })); 500 | 501 | if ( typeof module === "object" && module && typeof module.exports === "object" ) { 502 | // Expose force as module.exports in loaders that implement the Node 503 | // module pattern (including browserify). Do not create the global, since 504 | // the user will be storing it themselves locally, and globals are frowned 505 | // upon in the Node module world. 506 | module.exports = force; 507 | } else { 508 | // Eexpose force to the global object as usual 509 | window.force = force; 510 | 511 | // Register as a named AMD module 512 | if ( typeof define === "function" && define.amd ) { 513 | define( "force", [], function () { return force; } ); 514 | } 515 | } 516 | })(window, document); 517 | -------------------------------------------------------------------------------- /src/force.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2015 Rene Tanczos 3 | */ 4 | !function(T,Q,o){var r=function(){"use strict";var d,u,t,i=[],a=!1,s=[],I=!1,m={swing:function(t,n,e,r){return this.easeOutQuad(t,n,e,r)},easeInQuad:function(t,n,e,r){return e*(t/=r)*t+n},easeOutQuad:function(t,n,e,r){return-e*(t/=r)*(t-2)+n},easeInOutQuad:function(t,n,e,r){return(t/=r/2)<1?e/2*t*t+n:-e/2*(--t*(t-2)-1)+n},easeInCubic:function(t,n,e,r){return e*(t/=r)*t*t+n},easeOutCubic:function(t,n,e,r){return e*((t=t/r-1)*t*t+1)+n},easeInOutCubic:function(t,n,e,r){return(t/=r/2)<1?e/2*t*t*t+n:e/2*((t-=2)*t*t+2)+n},easeInQuart:function(t,n,e,r){return e*(t/=r)*t*t*t+n},easeOutQuart:function(t,n,e,r){return-e*((t=t/r-1)*t*t*t-1)+n},easeInOutQuart:function(t,n,e,r){return(t/=r/2)<1?e/2*t*t*t*t+n:-e/2*((t-=2)*t*t*t-2)+n},easeInQuint:function(t,n,e,r){return e*(t/=r)*t*t*t*t+n},easeOutQuint:function(t,n,e,r){return e*((t=t/r-1)*t*t*t*t+1)+n},easeInOutQuint:function(t,n,e,r){return(t/=r/2)<1?e/2*t*t*t*t*t+n:e/2*((t-=2)*t*t*t*t+2)+n},easeInSine:function(t,n,e,r){return-e*Math.cos(t/r*(Math.PI/2))+e+n},easeOutSine:function(t,n,e,r){return e*Math.sin(t/r*(Math.PI/2))+n},easeInOutSine:function(t,n,e,r){return-e/2*(Math.cos(Math.PI*t/r)-1)+n},easeInExpo:function(t,n,e,r){return 0==t?n:e*Math.pow(2,10*(t/r-1))+n},easeOutExpo:function(t,n,e,r){return t==r?n+e:e*(1-Math.pow(2,-10*t/r))+n},easeInOutExpo:function(t,n,e,r){return 0==t?n:t==r?n+e:(t/=r/2)<1?e/2*Math.pow(2,10*(t-1))+n:e/2*(2-Math.pow(2,-10*--t))+n},easeInCirc:function(t,n,e,r){return-e*(Math.sqrt(1-(t/=r)*t)-1)+n},easeOutCirc:function(t,n,e,r){return e*Math.sqrt(1-(t=t/r-1)*t)+n},easeInOutCirc:function(t,n,e,r){return(t/=r/2)<1?-e/2*(Math.sqrt(1-t*t)-1)+n:e/2*(Math.sqrt(1-(t-=2)*t)+1)+n},easeInElastic:function(t,n,e,r){var u=1.70158,i=0,a=e;if(0==t)return n;if(1==(t/=r))return n+e;if(i||(i=.3*r),a\n*/\n;(function(window, document, undefined) {\nvar force = function() {\n 'use strict';\n\n var scrollCache = [],\n isAnimating = false,\n animationCache = [],\n isScrolling = false,\n currentJumpLoop,\n transitionTimeout,\n hashLinkElements,\n jsEasing = {\n \tswing: function (t, b, c, d) {\n // default\n \t\treturn this.easeOutQuad(t, b, c, d);\n \t},\n \teaseInQuad: function (t, b, c, d) {\n \t\treturn c*(t/=d)*t + b;\n \t},\n \teaseOutQuad: function (t, b, c, d) {\n \t\treturn -c *(t/=d)*(t-2) + b;\n \t},\n \teaseInOutQuad: function (t, b, c, d) {\n \t\tif ((t/=d/2) < 1) return c/2*t*t + b;\n \t\treturn -c/2 * ((--t)*(t-2) - 1) + b;\n \t},\n \teaseInCubic: function (t, b, c, d) {\n \t\treturn c*(t/=d)*t*t + b;\n \t},\n \teaseOutCubic: function (t, b, c, d) {\n \t\treturn c*((t=t/d-1)*t*t + 1) + b;\n \t},\n \teaseInOutCubic: function (t, b, c, d) {\n \t\tif ((t/=d/2) < 1) return c/2*t*t*t + b;\n \t\treturn c/2*((t-=2)*t*t + 2) + b;\n \t},\n \teaseInQuart: function (t, b, c, d) {\n \t\treturn c*(t/=d)*t*t*t + b;\n \t},\n \teaseOutQuart: function (t, b, c, d) {\n \t\treturn -c * ((t=t/d-1)*t*t*t - 1) + b;\n \t},\n \teaseInOutQuart: function (t, b, c, d) {\n \t\tif ((t/=d/2) < 1) return c/2*t*t*t*t + b;\n \t\treturn -c/2 * ((t-=2)*t*t*t - 2) + b;\n \t},\n \teaseInQuint: function (t, b, c, d) {\n \t\treturn c*(t/=d)*t*t*t*t + b;\n \t},\n \teaseOutQuint: function (t, b, c, d) {\n \t\treturn c*((t=t/d-1)*t*t*t*t + 1) + b;\n \t},\n \teaseInOutQuint: function (t, b, c, d) {\n \t\tif ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;\n \t\treturn c/2*((t-=2)*t*t*t*t + 2) + b;\n \t},\n \teaseInSine: function (t, b, c, d) {\n \t\treturn -c * Math.cos(t/d * (Math.PI/2)) + c + b;\n \t},\n \teaseOutSine: function (t, b, c, d) {\n \t\treturn c * Math.sin(t/d * (Math.PI/2)) + b;\n \t},\n \teaseInOutSine: function (t, b, c, d) {\n \t\treturn -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;\n \t},\n \teaseInExpo: function (t, b, c, d) {\n \t\treturn (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;\n \t},\n \teaseOutExpo: function (t, b, c, d) {\n \t\treturn (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;\n \t},\n \teaseInOutExpo: function (t, b, c, d) {\n \t\tif (t==0) return b;\n \t\tif (t==d) return b+c;\n \t\tif ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;\n \t\treturn c/2 * (-Math.pow(2, -10 * --t) + 2) + b;\n \t},\n \teaseInCirc: function (t, b, c, d) {\n \t\treturn -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;\n \t},\n \teaseOutCirc: function (t, b, c, d) {\n \t\treturn c * Math.sqrt(1 - (t=t/d-1)*t) + b;\n \t},\n \teaseInOutCirc: function (t, b, c, d) {\n \t\tif ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;\n \t\treturn c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;\n \t},\n \teaseInElastic: function (t, b, c, d) {\n \t\tvar s=1.70158;var p=0;var a=c;\n \t\tif (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;\n \t\tif (a < Math.abs(c)) { a=c; var s=p/4; }\n \t\telse var s = p/(2*Math.PI) * Math.asin (c/a);\n \t\treturn -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;\n \t},\n \teaseOutElastic: function (t, b, c, d) {\n \t\tvar s=1.70158;var p=0;var a=c;\n \t\tif (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;\n \t\tif (a < Math.abs(c)) { a=c; var s=p/4; }\n \t\telse var s = p/(2*Math.PI) * Math.asin (c/a);\n \t\treturn a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;\n \t},\n \teaseInOutElastic: function (t, b, c, d) {\n \t\tvar s=1.70158;var p=0;var a=c;\n \t\tif (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5);\n \t\tif (a < Math.abs(c)) { a=c; var s=p/4; }\n \t\telse var s = p/(2*Math.PI) * Math.asin (c/a);\n \t\tif (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;\n \t\treturn a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;\n \t},\n \teaseInBack: function (t, b, c, d, s) {\n \t\tif (s == undefined) s = 1.70158;\n \t\treturn c*(t/=d)*t*((s+1)*t - s) + b;\n \t},\n \teaseOutBack: function (t, b, c, d, s) {\n \t\tif (s == undefined) s = 1.70158;\n \t\treturn c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;\n \t},\n \teaseInOutBack: function (t, b, c, d, s) {\n \t\tif (s == undefined) s = 1.70158;\n \t\tif ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;\n \t\treturn c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;\n \t},\n \teaseInBounce: function (t, b, c, d) {\n \t\treturn c - this.easeOutBounce (d-t, 0, c, d) + b;\n \t},\n \teaseOutBounce: function (t, b, c, d) {\n \t\tif ((t/=d) < (1/2.75)) {\n \t\t\treturn c*(7.5625*t*t) + b;\n \t\t} else if (t < (2/2.75)) {\n \t\t\treturn c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;\n \t\t} else if (t < (2.5/2.75)) {\n \t\t\treturn c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;\n \t\t} else {\n \t\t\treturn c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;\n \t\t}\n \t},\n \teaseInOutBounce: function (t, b, c, d) {\n \t\tif (t < d/2) return this.easeInBounce (t*2, 0, c, d) * .5 + b;\n \t\treturn this.easeOutBounce (t*2-d, 0, c, d) * .5 + c*.5 + b;\n \t}\n },\n\n cssEasing = {\n \tswing: function () {\n // default\n \t\treturn this.easeOutQuad();\n \t},\n \teaseInQuad: function () {\n \t\treturn '0.55, 0.085, 0.68, 0.53';\n \t},\n \teaseOutQuad: function () {\n \t\treturn '0.25, 0.46, 0.45, 0.94';\n \t},\n \teaseInOutQuad: function () {\n \t\treturn '0.455, 0.03, 0.515, 0.955';\n \t},\n \teaseInCubic: function () {\n \t\treturn '0.55, 0.055, 0.675, 0.19';\n \t},\n \teaseOutCubic: function () {\n \t\treturn '0.215, 0.61, 0.355, 1';\n \t},\n \teaseInOutCubic: function () {\n \t\treturn '0.645, 0.045, 0.355, 1';\n \t},\n \teaseInQuart: function () {\n \t\treturn '0.895, 0.03, 0.685, 0.22';\n \t},\n \teaseOutQuart: function () {\n \t\treturn '0.165, 0.84, 0.44, 1';\n \t},\n \teaseInOutQuart: function () {\n \t\treturn '0.77, 0, 0.175, 1';\n \t},\n \teaseInQuint: function () {\n \t\treturn '0.755, 0.05, 0.855, 0.06';\n \t},\n \teaseOutQuint: function () {\n \t\treturn '0.23, 1, 0.32, 1';\n \t},\n \teaseInOutQuint: function () {\n \t\treturn '0.86, 0, 0.07, 1';\n \t},\n \teaseInSine: function () {\n \t\treturn '0.47, 0, 0.745, 0.715';\n \t},\n \teaseOutSine: function () {\n \t\treturn '0.39, 0.575, 0.565, 1';\n \t},\n \teaseInOutSine: function () {\n \t\treturn '0.445, 0.05, 0.55, 0.95';\n \t},\n \teaseInExpo: function () {\n \t\treturn '0.95, 0.05, 0.795, 0.035';\n \t},\n \teaseOutExpo: function () {\n \t\treturn '0.19, 1, 0.22, 1';\n \t},\n \teaseInOutExpo: function () {\n \t\treturn '1, 0, 0, 1';\n \t},\n \teaseInCirc: function () {\n \t\treturn '0.6, 0.04, 0.98, 0.335';\n \t},\n \teaseOutCirc: function () {\n \t\treturn '0.075, 0.82, 0.165, 1';\n \t},\n \teaseInOutCirc: function () {\n \t\treturn '0.785, 0.135, 0.15, 0.86';\n \t}\n },\n\n\n opt = {\n hashLinkPattern: 'a[href*=\"#\"]:not([href=\"#\"])',\n frames: 60,\n valueUnitRegEx: /^([\\-]{0,1}[0-9\\.]+)([a-z%]{0,3})$/,\n moveDuration: 1000,\n moveEasing: 'swing',\n jumpDuration: 1000,\n scrollEasing: 'swing',\n cacheJumps: true,\n cssTransitions: true\n },\n\n config = function(options) {\n options && Object.keys(options).forEach(function(key) {\n opt[key] = options[key];\n });\n },\n\n hasTransitionSupport = function() {\n var s = document.documentElement.style;\n return (\n s.webkitTransition !== undefined ||\n s.MozTransition !== undefined ||\n s.OTransition !== undefined ||\n s.MsTransition !== undefined ||\n s.transition !== undefined\n );\n },\n\n setTransition = function(element, trans) {\n element.style.webkitTransition !== undefined && (element.style.webkitTransition = trans);\n element.style.MozTransition !== undefined && (element.style.MozTransition = trans);\n element.style.OTransition !== undefined && (element.style.OTransition = trans);\n element.style.MsTransition !== undefined && (element.style.MsTransition = trans);\n element.style.transition !== undefined && (element.style.transition = trans);\n },\n\n isStyleProperty = function(propName) {\n return propName in document.documentElement.style;\n },\n\n /*\n Converts the CSS property name into a JS style name.\n\n Example:\n from CSS: font-family\n to JS: fontFamily\n */\n toJsStyle = function(value) {\n return value.replace(/(\\-[a-z]{1})/g, function(match) {return match.slice(-1).toUpperCase()})\n },\n\n /*\n @target string/object\n can be a selector/id or object\n\n @options object\n if duration or done is undefined, options contains informations about the animation\n but if duration and done is set, options are the style properties (target properties)\n to be animated.\n\n @duration number\n Duration of the animation.\n\n @done function\n will be executed after the animation finished.\n */\n move = function(target, options, duration, done) {\n\n var callback; // executes when the animation is done\n\n if(options.isJump) {\n // abort the current scroll animations if caching is false and a new scroll event was started\n (!opt.cacheJumps && isScrolling) && (clearInterval(currentJumpLoop));\n // cache the target if scrolling is active and a scrolling animation is currently running\n if(opt.cacheJumps && isScrolling) {\n scrollCache.push({target: target});\n return;\n }\n isScrolling = true;\n\n callback = function() {\n isScrolling = false;\n if(opt.cacheJumps && scrollCache.length > 0) {\n var nextEvent = scrollCache.shift();\n jump(nextEvent.target);\n }\n };\n }\n else {\n // executed when its not a jump\n if(isAnimating) {\n animationCache.push({target: target, options: options, duration: duration, done: done});\n return;\n }\n isAnimating = true;\n\n callback = function() {\n isAnimating = false;\n var next = animationCache.shift();\n next && move(next.target, next.options, next.duration, next.done);\n };\n }\n\n var el = (typeof target === 'string') ? document.querySelector(target) : target;\n var o = {};\n o.properties = options.properties || options;\n o.duration = duration || options.duration || (options.isJump ? opt.jumpDuration : opt.moveDuration);\n o.done = done || options.done;\n o.easing = options.easing || (options.isJump ? opt.scrollEasing : opt.moveEasing);\n o.isJump = options.isJump;\n\n // dont care about the target property. has something to do with the jump function\n (typeof options.target === 'string') ? (options.target = document.querySelector(options.target)) : (o.target = options.target);\n\n var isStyle = true;\n Object.keys(o.properties).forEach(function(key) {\n isStyle = isStyleProperty(key);\n });\n\n // CSS transition check\n if(opt.cssTransitions && isStyle && hasTransitionSupport() && o.easing in cssEasing) {\n //console.log('css transitions supported');\n\n Object.keys(o.properties).forEach(function(key) {\n var trans = 'all ' + o.duration + 'ms cubic-bezier(' + cssEasing[o.easing]() + ')';\n\n setTransition(el, trans);\n\n var stylename = toJsStyle(key);\n\n el.style[toJsStyle(key)] = o.properties[key];\n\n // kill the previous transition when its not finished\n clearTimeout(transitionTimeout);\n\n // should remove the transition after its finished\n transitionTimeout = setTimeout(function() {\n setTransition(el, '');\n callback();\n o.done && o.done();\n }, o.duration);\n });\n\n return;\n }\n\n var anims = [],\n finished = 0;\n\n Object.keys(o.properties).forEach(function(key) {\n var val = o.properties[key].match(opt.valueUnitRegEx);\n anims.push({\n style: toJsStyle(key),\n value: parseInt(val[1]),\n suffix: val[2] || '',\n duration: o.duration,\n rawValue: o.properties[key]\n });\n });\n\n anims.forEach(function(anim) {\n var currentFrame = 0,\n valueObj;\n\n if(isStyle)\n valueObj = el.style[anim.style].match(opt.valueUnitRegEx);\n else\n valueObj = (el[anim.style] + '').match(opt.valueUnitRegEx);\n\n // in case no value is set\n if(!valueObj) valueObj = ['0', '0', ''];\n\n var initValue = isStyle ? (parseInt(valueObj[1]) || 0) : window.scrollY,\n change = anim.value - (initValue || 0),\n currentTime = 0,\n timeSteps = Math.ceil(anim.duration / opt.frames),\n loopId;\n\n loopId = setInterval(function() {\n if(currentFrame < opt.frames) {\n var v = jsEasing[o.easing](currentTime, initValue, change, anim.duration);\n\n if(isStyle)\n el.style[anim.style] = v + anim.suffix;\n else if(anim.style in el) {\n if(o.target)\n if(anim.style == 'scrollTop') {\n window.scrollTo(0, v)\n }\n else {\n o.target[anim.style] = v + anim.suffix;\n }\n else\n el[anim.style] = v + anim.suffix;\n }\n else\n // break loop (nothing to animate)\n currentTime = opt.frames;\n }\n else {\n clearInterval(loopId);\n finished++;\n callback();\n (finished === anims.length && o.done) && o.done();\n }\n currentTime += timeSteps;\n currentFrame++;\n }, timeSteps);\n\n o.isJump && (currentJumpLoop = loopId);\n });\n },\n\n jump = function(target, options) {\n var el = (typeof target === 'string') ? document.querySelector(target) : target;\n var o = options || {};\n\n move(el, {\n properties: {\n scrollTop: el.offsetTop + ''\n },\n duration: o.duration || opt.jumpDuration,\n easing: o.easing || opt.scrollEasing,\n done: o.setHash ? function() {\n window.location.hash = el.id;\n o.done && o.done();\n } : o.done,\n target: document.body,\n isJump: true\n });\n },\n\n // Bind all subscribe all hash link clicks on the page and animates the jump to this position\n bindHashes = function() {\n hashLinkElements = document.querySelectorAll(opt.hashLinkPattern);\n\n [].forEach.call(hashLinkElements, function(el) {\n \t\tel.addEventListener('click', function(ev) {\n \t\t\tif (window.location.pathname.replace(/^\\//, '') == this.pathname.replace(/^\\//, '') && window.location.hostname == this.hostname) {\n \t\t var target = document.getElementById(this.hash.slice(1));\n target && jump(target);\n ev.preventDefault();\n \t\t }\n \t\t}, false)\n \t});\n },\n\n // unbind all hash links\n unbindHashes = function() {\n [].forEach.call(hashLinkElements, function(el) {\n el.removeEventListener('click');\n });\n hashLinkElements = null;\n };\n\n return {\n opt: opt,\n config: config,\n hasTransitionSupport: hasTransitionSupport,\n toJsStyle: toJsStyle,\n move: move,\n jump: jump,\n bindHashes: bindHashes,\n unbindHashes: unbindHashes,\n setTransition: setTransition\n };\n }();\n\n // extend the jquery object\n var jq = window.$ || window.jQuery;\n jq && (jq.fn.extend({\n move: function (options, duration, done) {\n return this.each(function() {\n force.move(this, options, duration, done);\n });\n },\n jump: function(options) {\n return this.each(function() {\n force.jump(this, options);\n });\n }\n }));\n\n if ( typeof module === \"object\" && module && typeof module.exports === \"object\" ) {\n \t// Expose force as module.exports in loaders that implement the Node\n \t// module pattern (including browserify). Do not create the global, since\n \t// the user will be storing it themselves locally, and globals are frowned\n \t// upon in the Node module world.\n \tmodule.exports = force;\n } else {\n \t// Eexpose force to the global object as usual\n \twindow.force = force;\n\n \t// Register as a named AMD module\n \tif ( typeof define === \"function\" && define.amd ) {\n \t\tdefine( \"force\", [], function () { return force; } );\n \t}\n }\n})(window, document);\n"]} --------------------------------------------------------------------------------