├── .babelrc ├── .editorconfig ├── .eslintrc ├── .gitignore ├── .nvmrc ├── .travis.yml ├── LICENSE ├── README.md ├── dist ├── sal.css └── sal.js ├── index.d.ts ├── index.html ├── package.json ├── postcss.config.js ├── src ├── sal.js └── sal.scss ├── test ├── config │ └── browser.js ├── fixtures │ ├── default.html │ ├── disabled.html │ ├── repeat-once-attr.html │ ├── repeat.html │ ├── reset.html │ └── update.html ├── index.test.js └── mocks │ └── styleMock.js ├── webpack.config.js ├── website ├── images │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── apple-touch-icon.png │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── favicon.ico │ ├── logo.svg │ └── manifest.json ├── styles.css └── template │ ├── index.pug │ └── shape.pug └── yarn.lock /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "useBuiltIns": "entry", 7 | "corejs": 3 8 | } 9 | ] 10 | ], 11 | "env": { 12 | "test": { 13 | "presets": [ 14 | [ 15 | "@babel/preset-env", 16 | { 17 | "targets": { 18 | "node": "current" 19 | } 20 | } 21 | ] 22 | ], 23 | "plugins": [ 24 | "@babel/transform-runtime" 25 | ] 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "airbnb-base", 3 | "parser": "babel-eslint", 4 | "parserOptions": { 5 | "ecmaVersion": 6 6 | }, 7 | "rules": { 8 | "no-unused-vars": 2, 9 | "max-len": [2, 80, 2], 10 | "no-console": 1, 11 | "prefer-arrow-callback": 2, 12 | "func-style": [ 13 | 2, 14 | "expression", 15 | { 16 | "allowArrowFunctions": true 17 | } 18 | ], 19 | "arrow-body-style": [2, "as-needed"], 20 | "array-bracket-spacing": ["error", "never"], 21 | "computed-property-spacing": ["error", "never"], 22 | "no-duplicate-imports": "error", 23 | "object-curly-spacing": ["error", "always"] 24 | }, 25 | "env": { 26 | "node": true, 27 | "browser": true, 28 | "es6": true, 29 | "jest": true 30 | }, 31 | "globals": { 32 | "process": "readonly", 33 | "module": "readonly" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Numerous always-ignore extensions 2 | *.diff 3 | *.err 4 | *.log 5 | *.orig 6 | *.rej 7 | *.swo 8 | *.swp 9 | *.vi 10 | *.zip 11 | *~ 12 | 13 | # OS or Editor folders 14 | ._* 15 | .cache 16 | .DS_Store 17 | .idea 18 | .project 19 | .settings 20 | .tmproj 21 | *.esproj 22 | *.sublime-project 23 | *.sublime-workspace 24 | nbproject 25 | Thumbs.db 26 | 27 | # Folders to ignore 28 | bower_components 29 | node_modules 30 | 31 | # Dev files to ignore 32 | dist/*.map 33 | 34 | # waiting for https://github.com/GoogleChromeLabs/size-plugin/issues/28 35 | size-plugin.json 36 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | lts/erbium 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - lts/erbium 4 | script: 5 | - npm run build 6 | - npm run test 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Mirosław Ciastek 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 | # Sal [![npm version](https://badge.fury.io/js/sal.js.svg)](https://www.npmjs.com/package/sal.js) [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/mciastek/sal/blob/master/LICENSE) [![Build Status](https://travis-ci.com/mciastek/sal.svg?branch=master)](https://travis-ci.com/mciastek/sal) 2 | 3 | Performance focused, lightweight (less than **2.8 kb**) scroll animation library, written in vanilla JavaScript. No dependencies! 4 | 5 | **Sal** (_Scroll Animation Library_) was created to provide a performant and lightweight solution for animating elements on scroll. It's based on the [Intersection Observer](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API), which gives amazing performance in terms of checking the element's presence in the viewport. 6 | 7 | **Note:** Intersection Observer API is an experimental technology so be sure to consult the [browser compatibility table](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#Browser_compatibility) and consider using a [polyfill](https://github.com/w3c/IntersectionObserver/tree/master/polyfill). 8 | 9 | ## Table of Contents 10 | - [Install](#install) 11 | - [Usage](#usage) 12 | - [Animations](#animations) 13 | - [Options](#options) 14 | - [API](#api) 15 | - [Events](#events) 16 | - [Misc](#misc) 17 | - [License](#license) 18 | 19 | ## Install 20 | 21 | ```sh 22 | # Usage with NPM 23 | $ npm install --save sal.js 24 | 25 | # and with Yarn 26 | $ yarn add sal.js 27 | ``` 28 | 29 | Load it with your favorite module loader or use as a global variable 30 | 31 | ```js 32 | // ES6 modules 33 | import sal from 'sal.js' 34 | 35 | // CommonJS modules 36 | var sal = require('sal.js') 37 | ``` 38 | 39 | And remember to add styles 40 | 41 | ```scss 42 | // Webpack 43 | @import '~sal.js/sal.css'; 44 | 45 | // Other 46 | @import './node_modules/sal.js/dist/sal.css'; 47 | ``` 48 | 49 | ## Usage 50 | 51 | In HTML, add a `data-sal` attribute with the animation name as value, e.g.: 52 | 53 | ```html 54 |
55 | ``` 56 | 57 | Then simply initialize Sal in your script file: 58 | 59 | ```js 60 | sal(); 61 | ``` 62 | 63 | It will look for all elements with a `data-sal` attribute and launch their animation when in viewport. 64 | 65 | ## Animations 66 | In **sal.js** you can easily change animation's options, by adding a proper `data` attribute: 67 | - `data-sal-duration` - changes duration of the animation (from 200 to 2000 ms) 68 | - `data-sal-delay` - adds delay to the animation (from 5 to 1000 ms) 69 | - `data-sal-easing` - sets easing for the animation (see [easings.net](https://easings.net/) for reference) 70 | 71 | For example: 72 | ```html 73 |
78 | ``` 79 | 80 | The library supports several animations: 81 | - `fade` 82 | - `slide-up` 83 | - `slide-down` 84 | - `slide-left` 85 | - `slide-right` 86 | - `zoom-in` 87 | - `zoom-out` 88 | - `flip-up` 89 | - `flip-down` 90 | - `flip-left` 91 | - `flip-right` 92 | 93 | ### Duration and delay 94 | 95 | Additionaly, when you want to customise animation's properties - `duration`, `delay` and `easing`, you can use [CSS variables](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties) to set any value you want. See the following example: 96 | 97 | ```html 98 |
102 | ``` 103 | 104 | Supported custom properties: 105 | - `--sal-duration` 106 | - `--sal-delay` 107 | - `--sal-easing` 108 | 109 | Remember, that you can use only data attributes (e.g. `data-sal-delay`) or CSS custom properties (e.g. `--sal-delay`). Data attributes have precedence over CSS custom properties. 110 | 111 | ### Repeating animation 112 | 113 | By default every animation is played once. You can change it by setting `once` option to `false` (see [Options](#options)). What's more, it's possible to override this option for an animated element by adding one of the following attributes: 114 | - `data-sal-repeat` - forces animation replay 115 | - `data-sal-once` - plays animation once 116 | 117 | ## Options 118 | 119 | | Property | Type | Description | Default | 120 | |---------------------------|-------------|---------------|---------| 121 | | `threshold` | Number | Percentage of an element's area that needs to be visible to launch animation (see [docs](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/thresholds)) | `0.5` | 122 | | `once` | Boolean | Defines if animation needs to be launched once. Can be overridden, see [Repeating Animation](#repeating-animation). | `true` | 123 | | `disabled` | Boolean or Function | Flag (or a function returning boolean) for disabling animations | `false` | 124 | 125 | You can set options during Sal's initialization, e.g.: 126 | 127 | ```js 128 | sal({ 129 | threshold: 1, 130 | once: false, 131 | }); 132 | ``` 133 | 134 | ### Advanced options 135 | 136 | | Property | Type | Description | Default | 137 | |---------------------------|-------------|---------------|---------| 138 | | `root` | Element or null | The element that is used as the viewport for checking visibility of the target (see [docs](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/root)) | `window` | 139 | | `selector` | String | Selector of the elements to be animated | `[data-sal]` | 140 | | `animateClassName` | String | Class name which triggers animation | `sal-animate` | 141 | | `disabledClassName` | String | Class name which defines the disabled state | `sal-disabled` | 142 | | `rootMargin` | String | Corresponds to root's bounding box margin (see [docs](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/rootMargin)) | `0% 50%` | 143 | | `enterEventName` | String | Enter event name (see [Events](#events)) | `sal:in` | 144 | | `exitEventName` | String | Exit event name (see [Events](#events)) | `sal:out` | 145 | 146 | ## API 147 | 148 | | Method name | Description | 149 | |---------------------------|-------------| 150 | | `enable` | Enables animations | 151 | | `disable` | Disables animations | 152 | | `reset` | Resets instance and allows to pass new options (see [Options](#options)) | 153 | | `update` | Updates observer with new elements to animated. Useful for dynamically injected HTML. | 154 | 155 | Public methods are available after Sal's initialization: 156 | 157 | ```js 158 | const scrollAnimations = sal(); 159 | 160 | scrollAnimations.disable(); 161 | ``` 162 | 163 | ### Changing options after intialization 164 | 165 | If you want to change Sal's options once it's been initialized, you should use `reset` method, that allows you to pass new set of options. It can be useful, when you would like to provide different options for specific viewport sizes. 166 | 167 | ```js 168 | const scrollAnimations = sal(); 169 | 170 | // Provide new options 171 | scrollAnimations.reset({ 172 | selector: 'animated-element', 173 | once: true, 174 | }); 175 | ``` 176 | 177 | ## Events 178 | 179 | This library supports events, fired when element is entering or exiting viewport (they are named `sal:in` and `sal:out` by default). Property `detail` is [IntersectionObserverEntry](https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserverEntry) object. 180 | 181 | 182 | You can attach listener to specific element. 183 | 184 | ```js 185 | // Get element with ".animated" class, which has "data-sal" attribute 186 | const element = document.querySelector('.animated'); 187 | 188 | element.addEventListener('sal:in', ({ detail }) => { 189 | console.log('entering', detail.target); 190 | }); 191 | ``` 192 | 193 | or to the whole document 194 | 195 | ```js 196 | document.addEventListener('sal:out', ({ detail }) => { 197 | console.log('exiting', detail.target); 198 | }); 199 | ``` 200 | 201 | **Note:** This library uses [Custom Event](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) to trigger events on animated elements. Check the [compatibility table](https://caniuse.com/#search=custom%20event) to know if your browser supports it and use a [polyfill](https://github.com/kumarharsh/custom-event-polyfill) if needed. 202 | 203 | ## Misc 204 | 205 | ### No-JS support 206 | 207 | If you aim to support users that don't allow sites to use JavaScript, you should consider disabling animations' styles in the first place. You can use `