├── .eslintignore ├── .eslintrc.json ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── demo ├── client.js ├── index.html └── style.css ├── package.json ├── spring.test.js ├── spring.ts ├── tsconfig.json └── webpack.config.js /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "root": true, 3 | "plugins": ["prettier"], 4 | "extends": ["eslint:recommended", "prettier"], 5 | "env": { 6 | "browser": true, 7 | "es6": true, 8 | "node": true 9 | }, 10 | "parser": "babel-eslint", 11 | "parserOptions": { 12 | "ecmaVersion": 6, 13 | "ecmaFeatures": { 14 | "classes": true 15 | }, 16 | "sourceType": "module" 17 | }, 18 | "rules": { 19 | "prettier/prettier": [ 20 | "warn", 21 | { 22 | "endOfLine": "auto", 23 | "singleQuote": true 24 | } 25 | ], 26 | "strict": 0, 27 | "eqeqeq": 1, 28 | "no-console": 1, 29 | "no-irregular-whitespace": 1, 30 | "no-use-before-define": [2, "nofunc"], 31 | "no-unused-vars": [ 32 | 1, 33 | { 34 | "vars": "all", 35 | "args": "after-used", 36 | "argsIgnorePattern": "(^_|^next$)" 37 | } 38 | ], 39 | "arrow-parens": [1, "as-needed"], 40 | "no-dupe-keys": 2, 41 | "no-dupe-args": 2, 42 | "semi": 1 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /spring.js 4 | /spring.d.ts 5 | yarn.lock 6 | package-lock.json -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | package.json -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 'lts/*' 3 | cache: yarn 4 | install: yarn 5 | script: 6 | - yarn build 7 | - yarn test:ci 8 | deploy: 9 | provider: npm 10 | email: $NPM_EMAIL 11 | api_key: $NPM_TOKEN 12 | on: 13 | branch: master 14 | skip_cleanup: true 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 1.0.0 4 | 5 | - No breaking changes (it seemed about time to properly semver this) 6 | - Adds `onRest` callback 7 | 8 | ## 0.3.2 9 | 10 | - Fixes typos and updates things in README.md 11 | 12 | ## 0.3.1 13 | 14 | - Adds repo field to `package.json` 15 | 16 | ## 0.3.0 17 | 18 | - Fixes fast, consecutive updates animating way too fast 19 | - BREAKING: Inverts the magnitude of `precision`. Larger values now give greater precision. Default is now at `100` (which is more precise in practice than before). To convert old precision values, divide one by the old value: 20 | 21 | ``` 22 | const oldValue = 0.001; 23 | const newValue = 1 / oldValue; // 100 24 | ``` 25 | 26 | ## 0.2.1 27 | 28 | - Fixes missing files! 29 | 30 | ## 0.2.0 31 | 32 | - Refactors from class to function. `new` keyword is no longer necessary. 33 | - Tinier! Now 2kB (600B gzipped), instead 4kB (1kB gzipped). 34 | - Adds typescript definition file. 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Asbjørn Hegdahl 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 | # tiny-spring 2 | 3 | [](https://npmjs.com/package/tiny-spring) 4 |  5 |  6 | 7 | Simple spring dynamics for physics-driven animations. Also it's tiny (2kB before minification and gzip). 8 | 9 | ``` 10 | npm install tiny-spring 11 | ``` 12 | 13 | This small library helps you interpolate values using [spring dynamics](https://en.wikipedia.org/wiki/Harmonic_oscillator). There's no DOM-stuff or framework specifics included so you can use it with whatever framework or library you want. 14 | 15 | If you're using `React.js`, there is [use-spring-effect](https://www.npmjs.com/package/use-spring-effect) which uses `tiny-spring` internally. 16 | 17 | ## Browser support 18 | 19 | `tiny-spring` uses `requestAnimationFrame`, so if you need to support older browsers make sure you use a polyfill. 20 | 21 | ## TLDR usage 22 | 23 | ```js 24 | import Spring from 'tiny-spring'; 25 | 26 | const spring = Spring(); 27 | const element = document.getElementById('some-id'); 28 | 29 | spring.onUpdate(val => { 30 | element.style.transform = `translateX(${val}px)`; 31 | }); 32 | 33 | // Set value to 50 without animating 34 | spring.setValue(50); 35 | 36 | // Transition from current value (50) to 100 37 | spring.transitionTo(100); 38 | 39 | // Transition again from whatever the current value is to 200 40 | spring.transitionTo(200); 41 | 42 | // Stop animation and remove callback 43 | spring.destroy(); 44 | ``` 45 | 46 | ## API 47 | 48 | ### `Spring(initialValue: number, config: SpringConfig): spring` 49 | 50 | A factory function that returns a new `spring` object. 51 | 52 | ### `SpringConfig (object)` 53 | 54 | **`stiffness: number = 200`** 55 | 56 | Stiffness controls how "fast" your animation will be. Higher values result in faster motion. 57 | 58 | **`damping: number = 10`** 59 | 60 | Damping controls how much friction is applied to the spring. You can think of this as how "wobbly" the resulting motion is. Lower values result in more wobblyness. 61 | 62 | **`precision: number = 100`** 63 | 64 | Used to determine when to stop animating. With a precision of `0` the spring will reach its end value immediately. With really high values it might keep animating fractions of a pixel for a long time. Tweak this value if animations end abruptly or linger for too long. When tweaking this value you'll want to make big changes in order to see an effect (like adding/removing zeros). 65 | 66 | ### `spring (object)` 67 | 68 | **`onUpdate(fn: (value: number): void)`** 69 | 70 | Add a callback which will be called every time the value of the spring changes. 71 | 72 | **NOTE**: `spring` only supports one callback, so calling this method more than once results in previously attached callbacks being overwritten. 73 | 74 | ```js 75 | spring.onUpdate(value => { 76 | /* do something with value */ 77 | }); 78 | ``` 79 | 80 | **`onRest(fn: (value: number): void)`** 81 | 82 | Add a callback which will be called when the spring comes to a rest. 83 | 84 | **NOTE**: `spring` only supports one callback, so calling this method more than once results in previously attached callbacks being overwritten. 85 | 86 | ```js 87 | spring.onRest(value => { 88 | /* do something with value */ 89 | }); 90 | ``` 91 | 92 | **`setValue(value: number)`** 93 | 94 | Set the value of the spring without animating. 95 | 96 | **`transitionTo(value: number)`** 97 | 98 | Start animating to `value` from the current value of the spring. 99 | 100 | **`destroy()`** 101 | 102 | Aborts animation and removes the callback. 103 | -------------------------------------------------------------------------------- /demo/client.js: -------------------------------------------------------------------------------- 1 | import Spring from '../spring.ts'; 2 | 3 | const box = document.getElementById('box'); 4 | const button = document.getElementById('button'); 5 | 6 | const spring = Spring(100); 7 | 8 | spring.onUpdate(val => { 9 | box.style.transform = `translateX(${val}px) rotate(${val / 3}deg)`; 10 | }); 11 | 12 | spring.onRest(() => { 13 | console.log('spring completed!'); 14 | }); 15 | 16 | let value = 0; 17 | button.addEventListener('click', () => { 18 | value = value === 0 ? 300 : 0; 19 | spring.transitionTo(value); 20 | }); 21 | 22 | const box2 = document.getElementById('box2'); 23 | const spring2 = Spring(0, { stiffness: 50 }); 24 | 25 | spring2.onUpdate(val => { 26 | box2.style.transform = `translateX(${val}px)`; 27 | }); 28 | 29 | spring2.onRest(() => { 30 | console.log('spring2 completed!'); 31 | }); 32 | 33 | window.addEventListener('mousemove', e => { 34 | spring2.transitionTo(e.clientX); 35 | }); 36 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 8 |