├── .gitignore ├── LICENSE ├── README.md ├── examples ├── index.js └── package.json ├── lib └── rx.easing.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | npm-debug.log 15 | node_modules -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013 Paul Taylor 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | 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, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | rx-js-easing 2 | ============ 3 | 4 | Robert Penner's easing functions as Observables. 5 | 6 | ```javascript 7 | 8 | // All easing functions take a begin value, end value, and duration (in ms). 9 | // A few take extra arguments, which I've documented 10 | 11 | Observable.backIn = function(begin, end, duration, overshoot) {}; 12 | Observable.backOut = function(begin, end, duration, overshoot) {}; 13 | Observable.backInOut = function(begin, end, duration, overshoot) {}; 14 | Observable.bounceIn = function(begin, end, duration) {}; 15 | Observable.bounceOut = function(begin, end, duration) {}; 16 | Observable.bounceInOut = function(begin, end, duration) {}; 17 | Observable.circIn = function(begin, end, duration) {}; 18 | Observable.circOut = function(begin, end, duration) {}; 19 | Observable.circInOut = function(begin, end, duration) {}; 20 | Observable.cubicIn = function(begin, end, duration) {}; 21 | Observable.cubicOut = function(begin, end, duration) {}; 22 | Observable.cubicInOut = function(begin, end, duration) {}; 23 | Observable.elasticOut = function(begin, end, duration, amplitude, period) {}; 24 | Observable.elasticIn = function(begin, end, duration, amplitude, period) {}; 25 | Observable.elasticInOut = function(begin, end, duration, amplitude, period) {}; 26 | Observable.expoIn = function(begin, end, duration) {}; 27 | Observable.expoOut = function(begin, end, duration) {}; 28 | Observable.expoInOut = function(begin, end, duration) {}; 29 | Observable.linear = function(begin, end, duration) {}; 30 | Observable.quadIn = function(begin, end, duration) {}; 31 | Observable.quadOut = function(begin, end, duration) {}; 32 | Observable.quadInOut = function(begin, end, duration) {}; 33 | Observable.quartIn = function(begin, end, duration) {}; 34 | Observable.quartOut = function(begin, end, duration) {}; 35 | Observable.quartInOut = function(begin, end, duration) {}; 36 | Observable.quintIn = function(begin, end, duration) {}; 37 | Observable.quintOut = function(begin, end, duration) {}; 38 | Observable.quintInOut = function(begin, end, duration) {}; 39 | Observable.sineIn = function(begin, end, duration) {}; 40 | Observable.sineOut = function(begin, end, duration) {}; 41 | Observable.sineInOut = function(begin, end, duration) {}; 42 | Observable.circleIn = function(begin, end, duration) {}; 43 | Observable.circleOut = function(begin, end, duration) {}; 44 | Observable.circleInOut = function(begin, end, duration) {}; 45 | Observable.linearNone = function(begin, end, duration) {}; 46 | Observable.linearIn = function(begin, end, duration) {}; 47 | Observable.linearOut = function(begin, end, duration) {}; 48 | Observable.linearInOut = function(begin, end, duration) {}; 49 | 50 | ``` 51 | -------------------------------------------------------------------------------- /examples/index.js: -------------------------------------------------------------------------------- 1 | var $ = require("jquery"), 2 | transform_property = require("transform-property"), 3 | translate = function(el, x, y, z) { 4 | el.style[transform_property] = "translate3d(" + 5 | (x || 0) + "px, " + 6 | (y || 0) + "px, " + 7 | (z || 0) + "px)"; 8 | return el; 9 | }, 10 | Rx = require("rx") && 11 | require("rx-dom") && 12 | require("rx-jquery") && 13 | require("rxjs-gestures") && 14 | require("../lib/rx.easing"), 15 | 16 | animationTime = 650 /*ms*/; 17 | 18 | $(window) 19 | .loadAsObservable() 20 | .map(function() { 21 | 22 | $("body").append("

tap anywhere to animate the box

"); 23 | 24 | return $("
").css({ 25 | "width": "200px", 26 | "height": "200px", 27 | "border": "1px solid #333", 28 | "border-radius": "5px", 29 | "position": "absolute", 30 | "top": "50%", 31 | "left": "50%", 32 | "margin-top": "-100px", 33 | "margin-left": "-100px" 34 | }).appendTo("body"); 35 | }) 36 | .flatMap(function(box) { 37 | return $(window) 38 | .tapAsObservable() 39 | .repeat() 40 | .flatMapLatest(function(tap) { 41 | 42 | var boxW = box.width(), 43 | boxH = box.height(), 44 | offsetX = (($(window).width() - boxW) * 0.5), 45 | offsetY = (($(window).height() - boxH) * 0.5), 46 | coords = box.offset(), 47 | startX = coords.left - offsetX, 48 | startY = coords.top - offsetY, 49 | endX = tap.global.x - offsetX - (boxW * 0.5), 50 | endY = tap.global.y - offsetY - (boxH * 0.5); 51 | 52 | // Animate the box coordinates in parallel: 53 | return Rx.Observable.zip( 54 | Rx.Observable.quadOut(startX, endX, animationTime), 55 | Rx.Observable.quadOut(startY, endY, animationTime), 56 | translate.bind(null, box[0]) 57 | ); 58 | 59 | // Animate the box coordinates in series: 60 | return Rx.Observable.concat( 61 | Rx.Observable 62 | .quadOut(startX, endX, animationTime * 0.5) 63 | .map(function(x) { return { x: x } }), 64 | Rx.Observable 65 | .quadOut(startY, endY, animationTime * 0.5) 66 | .map(function(y) { return { y: y }; }) 67 | ).scan({x: startX, y: startY}, function(coords, newCoords) { 68 | for(var prop in newCoords) { 69 | coords[prop] = newCoords[prop]; 70 | } 71 | translate(box[0], coords.x, coords.y); 72 | return coords; 73 | }); 74 | }); 75 | }) 76 | .publish().connect(); 77 | -------------------------------------------------------------------------------- /examples/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rxjs-easing-examples", 3 | "version": "1.0.0", 4 | "description": "Examples that use the rxjs-easing Observables.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "beefy index.js --debug=false" 9 | }, 10 | "keywords": [ 11 | "RxJS", 12 | "Easing", 13 | "Streams", 14 | "Observables" 15 | ], 16 | "author": "Paul Taylor ", 17 | "license": "MIT", 18 | "dependencies": { 19 | "beefy": "^2.1.5", 20 | "browserify": "^9.0.8", 21 | "jquery": "^2.1.3", 22 | "rx": "^2.5.2", 23 | "rx-dom": "^5.0.3", 24 | "rx-jquery": "^1.1.7", 25 | "rxjs-gestures": "^0.1.0", 26 | "transform-property": "0.0.1" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/rx.easing.js: -------------------------------------------------------------------------------- 1 | var Rx = require('rx'); 2 | 3 | var Observable = Rx.Observable; 4 | var observableProto = Rx.Observable.prototype; 5 | 6 | var frame_rate = 30; 7 | var interval_len = 1000/frame_rate; 8 | 9 | var timer = Observable 10 | .timer(0, interval_len, Rx.Scheduler.requestAnimationFrame || Rx.Scheduler.timeout) 11 | .select(function(i) { 12 | return i * interval_len; 13 | }); 14 | 15 | Observable.backIn = make_ease_observable(curry_ease_time(backIn)); 16 | Observable.backOut = make_ease_observable(curry_ease_time(backOut)); 17 | Observable.backInOut = make_ease_observable(curry_ease_time(backInOut)); 18 | Observable.bounceIn = make_ease_observable(curry_ease_time(bounceIn)); 19 | Observable.bounceOut = make_ease_observable(curry_ease_time(bounceOut)); 20 | Observable.bounceInOut = make_ease_observable(curry_ease_time(bounceInOut)); 21 | Observable.circIn = make_ease_observable(curry_ease_time(circIn)); 22 | Observable.circOut = make_ease_observable(curry_ease_time(circOut)); 23 | Observable.circInOut = make_ease_observable(curry_ease_time(circInOut)); 24 | Observable.cubicIn = make_ease_observable(curry_ease_time(cubicIn)); 25 | Observable.cubicOut = make_ease_observable(curry_ease_time(cubicOut)); 26 | Observable.cubicInOut = make_ease_observable(curry_ease_time(cubicInOut)); 27 | Observable.elasticOut = make_ease_observable(curry_ease_time(elasticOut)); 28 | Observable.elasticIn = make_ease_observable(curry_ease_time(elasticIn)); 29 | Observable.elasticInOut = make_ease_observable(curry_ease_time(elasticInOut)); 30 | Observable.expoIn = make_ease_observable(curry_ease_time(expoIn)); 31 | Observable.expoOut = make_ease_observable(curry_ease_time(expoOut)); 32 | Observable.expoInOut = make_ease_observable(curry_ease_time(expoInOut)); 33 | Observable.linear = make_ease_observable(curry_ease_time(linear)); 34 | Observable.quadIn = make_ease_observable(curry_ease_time(quadIn)); 35 | Observable.quadOut = make_ease_observable(curry_ease_time(quadOut)); 36 | Observable.quadInOut = make_ease_observable(curry_ease_time(quadInOut)); 37 | Observable.quartIn = make_ease_observable(curry_ease_time(quartIn)); 38 | Observable.quartOut = make_ease_observable(curry_ease_time(quartOut)); 39 | Observable.quartInOut = make_ease_observable(curry_ease_time(quartInOut)); 40 | Observable.quintIn = make_ease_observable(curry_ease_time(quintIn)); 41 | Observable.quintOut = make_ease_observable(curry_ease_time(quintOut)); 42 | Observable.quintInOut = make_ease_observable(curry_ease_time(quintInOut)); 43 | Observable.sineIn = make_ease_observable(curry_ease_time(sineIn)); 44 | Observable.sineOut = make_ease_observable(curry_ease_time(sineOut)); 45 | Observable.sineInOut = make_ease_observable(curry_ease_time(sineInOut)); 46 | Observable.circleIn = Observable.circIn; 47 | Observable.circleOut = Observable.circOut; 48 | Observable.circleInOut = Observable.circInOut; 49 | Observable.linearNone = Observable.linear; 50 | Observable.linearIn = Observable.linear; 51 | Observable.linearOut = Observable.linear; 52 | Observable.linearInOut = Observable.linear; 53 | 54 | module.exports = Rx; 55 | 56 | function make_ease_observable(easing_func) { 57 | 58 | return function(begin, end, duration) { 59 | 60 | var args = Array.prototype.slice.call(arguments); 61 | 62 | // The easing functions use the difference 63 | // between the start and end values. 64 | args[1] = end - begin; 65 | 66 | return timer 67 | .takeWithTime(duration) 68 | .select(easing_func.apply(null, args)) 69 | .concat(Observable.returnValue(end)); 70 | } 71 | } 72 | 73 | function curry_ease_time(easing_func) { 74 | 75 | return function() { 76 | 77 | var args = Array.prototype.slice.call(arguments); 78 | 79 | return function(time) { 80 | return easing_func.apply(null, [time].concat(args)); 81 | } 82 | } 83 | } 84 | 85 | // Shamlessly pulled from Jim Jeffers's Easie: https://github.com/jimjeffers/Easie 86 | // who in turn pulled it from Robert Penner's easing equations: http://robertpenner.com/easing/ 87 | // Thanks you guys <3. 88 | 89 | function backIn(time, begin, change, duration, overshoot) { 90 | if (overshoot == null) overshoot = 1.70158; 91 | 92 | return change * (time /= duration) * time * ((overshoot + 1) * time - overshoot) + begin; 93 | } 94 | 95 | function backOut(time, begin, change, duration, overshoot) { 96 | if (overshoot == null) overshoot = 1.70158; 97 | 98 | return change * ((time = time / duration - 1) * time * ((overshoot + 1) * time + overshoot) + 1) + begin; 99 | } 100 | 101 | function backInOut(time, begin, change, duration, overshoot) { 102 | if (overshoot == null) overshoot = 1.70158; 103 | 104 | if ((time = time / (duration / 2)) < 1) { 105 | return change / 2 * (time * time * (((overshoot *= 1.525) + 1) * time - overshoot)) + begin; 106 | } else { 107 | return change / 2 * ((time -= 2) * time * (((overshoot *= 1.525) + 1) * time + overshoot) + 2) + begin; 108 | } 109 | } 110 | 111 | function bounceOut(time, begin, change, duration) { 112 | if ((time /= duration) < 1 / 2.75) { 113 | return change * (7.5625 * time * time) + begin; 114 | } else if (time < 2 / 2.75) { 115 | return change * (7.5625 * (time -= 1.5 / 2.75) * time + 0.75) + begin; 116 | } else if (time < 2.5 / 2.75) { 117 | return change * (7.5625 * (time -= 2.25 / 2.75) * time + 0.9375) + begin; 118 | } else { 119 | return change * (7.5625 * (time -= 2.625 / 2.75) * time + 0.984375) + begin; 120 | } 121 | } 122 | 123 | function bounceIn(time, begin, change, duration) { 124 | return change - bounceOut(duration - time, 0, change, duration) + begin; 125 | } 126 | 127 | function bounceInOut(time, begin, change, duration) { 128 | if (time < duration / 2) { 129 | return bounceIn(time * 2, 0, change, duration) * 0.5 + begin; 130 | } else { 131 | return bounceOut(time * 2 - duration, 0, change, duration) * 0.5 + change * 0.5 + begin; 132 | } 133 | } 134 | 135 | function circIn(time, begin, change, duration) { 136 | return -change * (Math.sqrt(1 - (time = time / duration) * time) - 1) + begin; 137 | } 138 | 139 | function circOut(time, begin, change, duration) { 140 | return change * Math.sqrt(1 - (time = time / duration - 1) * time) + begin; 141 | } 142 | 143 | function circInOut(time, begin, change, duration) { 144 | if ((time = time / (duration / 2)) < 1) { 145 | return -change / 2 * (Math.sqrt(1 - time * time) - 1) + begin; 146 | } else { 147 | return change / 2 * (Math.sqrt(1 - (time -= 2) * time) + 1) + begin; 148 | } 149 | } 150 | 151 | function cubicIn(time, begin, change, duration) { 152 | return change * (time /= duration) * time * time + begin; 153 | } 154 | 155 | function cubicOut(time, begin, change, duration) { 156 | return change * ((time = time / duration - 1) * time * time + 1) + begin; 157 | } 158 | 159 | function cubicInOut(time, begin, change, duration) { 160 | if ((time = time / (duration / 2)) < 1) { 161 | return change / 2 * time * time * time + begin; 162 | } else { 163 | return change / 2 * ((time -= 2) * time * time + 2) + begin; 164 | } 165 | } 166 | 167 | function elasticOut(time, begin, change, duration, amplitude, period) { 168 | var overshoot; 169 | if (amplitude == null) { 170 | amplitude = null; 171 | } 172 | if (period == null) { 173 | period = null; 174 | } 175 | if (time === 0) { 176 | return begin; 177 | } else if ((time = time / duration) === 1) { 178 | return begin + change; 179 | } else { 180 | if (!(period != null)) { 181 | period = duration * 0.3; 182 | } 183 | if (!(amplitude != null) || amplitude < Math.abs(change)) { 184 | amplitude = change; 185 | overshoot = period / 4; 186 | } else { 187 | overshoot = period / (2 * Math.PI) * Math.asin(change / amplitude); 188 | } 189 | return (amplitude * Math.pow(2, -10 * time)) * Math.sin((time * duration - overshoot) * (2 * Math.PI) / period) + change + begin; 190 | } 191 | } 192 | 193 | function elasticIn(time, begin, change, duration, amplitude, period) { 194 | var overshoot; 195 | if (amplitude == null) { 196 | amplitude = null; 197 | } 198 | if (period == null) { 199 | period = null; 200 | } 201 | if (time === 0) { 202 | return begin; 203 | } else if ((time = time / duration) === 1) { 204 | return begin + change; 205 | } else { 206 | if (!(period != null)) { 207 | period = duration * 0.3; 208 | } 209 | if (!(amplitude != null) || amplitude < Math.abs(change)) { 210 | amplitude = change; 211 | overshoot = period / 4; 212 | } else { 213 | overshoot = period / (2 * Math.PI) * Math.asin(change / amplitude); 214 | } 215 | time -= 1; 216 | return -(amplitude * Math.pow(2, 10 * time)) * Math.sin((time * duration - overshoot) * (2 * Math.PI) / period) + begin; 217 | } 218 | } 219 | 220 | function elasticInOut(time, begin, change, duration, amplitude, period) { 221 | var overshoot; 222 | if (amplitude == null) { 223 | amplitude = null; 224 | } 225 | if (period == null) { 226 | period = null; 227 | } 228 | if (time === 0) { 229 | return begin; 230 | } else if ((time = time / (duration / 2)) === 2) { 231 | return begin + change; 232 | } else { 233 | if (!(period != null)) { 234 | period = duration * (0.3 * 1.5); 235 | } 236 | if (!(amplitude != null) || amplitude < Math.abs(change)) { 237 | amplitude = change; 238 | overshoot = period / 4; 239 | } else { 240 | overshoot = period / (2 * Math.PI) * Math.asin(change / amplitude); 241 | } 242 | if (time < 1) { 243 | return -0.5 * (amplitude * Math.pow(2, 10 * (time -= 1))) * Math.sin((time * duration - overshoot) * ((2 * Math.PI) / period)) + begin; 244 | } else { 245 | return amplitude * Math.pow(2, -10 * (time -= 1)) * Math.sin((time * duration - overshoot) * (2 * Math.PI) / period) + change + begin; 246 | } 247 | } 248 | } 249 | 250 | function expoIn(time, begin, change, duration) { 251 | if (time === 0) { 252 | return begin; 253 | } 254 | return change * Math.pow(2, 10 * (time / duration - 1)) + begin; 255 | } 256 | 257 | 258 | function expoOut(time, begin, change, duration) { 259 | if (time === duration) { 260 | return begin + change; 261 | } 262 | return change * (-Math.pow(2, -10 * time / duration) + 1) + begin; 263 | } 264 | 265 | function expoInOut(time, begin, change, duration) { 266 | if (time === 0) { 267 | return begin; 268 | } else if (time === duration) { 269 | return begin + change; 270 | } else if ((time = time / (duration / 2)) < 1) { 271 | return change / 2 * Math.pow(2, 10 * (time - 1)) + begin; 272 | } else { 273 | return change / 2 * (-Math.pow(2, -10 * (time - 1)) + 2) + begin; 274 | } 275 | } 276 | 277 | function linear(time, begin, change, duration) { 278 | return change * time / duration + begin; 279 | } 280 | 281 | function quadIn(time, begin, change, duration) { 282 | return change * (time = time / duration) * time + begin; 283 | } 284 | 285 | function quadOut(time, begin, change, duration) { 286 | return -change * (time = time / duration) * (time - 2) + begin; 287 | } 288 | 289 | function quadInOut(time, begin, change, duration) { 290 | if ((time = time / (duration / 2)) < 1) { 291 | return change / 2 * time * time + begin; 292 | } else { 293 | return -change / 2 * ((time -= 1) * (time - 2) - 1) + begin; 294 | } 295 | } 296 | 297 | function quartIn(time, begin, change, duration) { 298 | return change * (time = time / duration) * time * time * time + begin; 299 | } 300 | 301 | function quartOut(time, begin, change, duration) { 302 | return -change * ((time = time / duration - 1) * time * time * time - 1) + begin; 303 | } 304 | 305 | function quartInOut(time, begin, change, duration) { 306 | if ((time = time / (duration / 2)) < 1) { 307 | return change / 2 * time * time * time * time + begin; 308 | } else { 309 | return -change / 2 * ((time -= 2) * time * time * time - 2) + begin; 310 | } 311 | } 312 | 313 | function quintIn(time, begin, change, duration) { 314 | return change * (time = time / duration) * time * time * time * time + begin; 315 | } 316 | 317 | function quintOut(time, begin, change, duration) { 318 | return change * ((time = time / duration - 1) * time * time * time * time + 1) + begin; 319 | } 320 | 321 | function quintInOut(time, begin, change, duration) { 322 | if ((time = time / (duration / 2)) < 1) { 323 | return change / 2 * time * time * time * time * time + begin; 324 | } else { 325 | return change / 2 * ((time -= 2) * time * time * time * time + 2) + begin; 326 | } 327 | } 328 | 329 | function sineIn(time, begin, change, duration) { 330 | return -change * Math.cos(time / duration * (Math.PI / 2)) + change + begin; 331 | } 332 | 333 | function sineOut(time, begin, change, duration) { 334 | return change * Math.sin(time / duration * (Math.PI / 2)) + begin; 335 | } 336 | 337 | function sineInOut(time, begin, change, duration) { 338 | return -change / 2 * (Math.cos(Math.PI * time / duration) - 1) + begin; 339 | } 340 | 341 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rxjs-easing", 3 | "version": "0.0.5", 4 | "description": "Robert Penner's easing functions as Observables.", 5 | "main": "lib/rx.easing.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git://github.com/trxcllnt/rx-js-easing.git" 12 | }, 13 | "keywords": [ 14 | "rx", 15 | "rxjs", 16 | "easing" 17 | ], 18 | "dependencies": { 19 | "rx": "*", 20 | "rx-dom": "*" 21 | }, 22 | "author": "Paul Taylor ", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/trxcllnt/rxjs-easing/issues" 26 | } 27 | } 28 | --------------------------------------------------------------------------------