├── .jshintrc ├── index.js ├── .gitignore ├── .babelrc ├── .editorconfig ├── .zuul.yml ├── .eslintrc ├── .travis.yml ├── scripts └── transpile.js ├── webpack.config.js ├── examples ├── no-options.html ├── default-sliding-transition.html ├── fading-transition.html ├── top-to-bottom-sliding-transition.html ├── requirejs-example.html ├── right-to-left-sliding-transition.html ├── mask-sliding-transition.html ├── pause-slide-change-example.html ├── responsive-layout-example.html ├── full-page-transition.html ├── add-class-to-current-elem-example.html ├── lightbox-integrated-example.html ├── touch-swipe-example.html ├── custom-ease-animation-example.html ├── reverse-sliding-direction-example.html └── text-content-example.html ├── bower.json ├── LICENSE ├── dist ├── simpleslider.min.js └── simpleslider.js ├── package.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── src └── simpleslider.js ├── README.md └── test ├── unit-tests.js └── functional-tests.js /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "strict": true, 3 | "indent": 2 4 | } 5 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist/simpleslider') 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | 3 | node_modules 4 | bower_components 5 | __tests__ 6 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["env"], 3 | "plugins": [ 4 | ["conditional-compilation", { 5 | "TEST": 1 6 | }] 7 | ] 8 | } 9 | 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | charset = utf-8 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | -------------------------------------------------------------------------------- /.zuul.yml: -------------------------------------------------------------------------------- 1 | name: simple-slider 2 | ui: jasmine2 3 | browsers: 4 | - name: chrome 5 | version: latest 6 | - name: firefox 7 | version: latest 8 | - name: ie 9 | version: 10..11 10 | - name: microsoftedge 11 | version: latest 12 | scripts: 13 | - "dist/simpleslider.min.js" 14 | 15 | -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "eslint-config-xo", 3 | "env": { 4 | "browser": true 5 | }, 6 | "rules": { 7 | "one-var": 0, 8 | "no-implicit-coercion": 0, 9 | "no-multi-assign": 0, 10 | "no-compare-neg-zero": 0, 11 | "capitalized-comments": 0, 12 | "max-params": 0, 13 | "radix": 0, 14 | "func-names": 0, 15 | "operator-linebreak": 0, 16 | "new-cap": 0, 17 | "indent": ["error", 2], 18 | "brace-style": 0 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '12' 4 | script: 5 | - 'if [ "$TRAVIS_PULL_REQUEST" != "false" ]; then npm run ci:pr; fi' 6 | - 'if [ "$TRAVIS_PULL_REQUEST" = "false" ]; then npm run ci; fi' 7 | env: 8 | global: 9 | - secure: "AsKT1Jdh0JFB0iWMY8NIfdqSohXRCK2qGrbjWbkLiPA9ueifNyI1Sn5SWDua6uSGuVsF2OX+XtFYMVhGOobtiC3N+bYdBX1UD2jPrNKzBIXvlsW2D/oc03HcNW4hSNVb09wS+It9eMcnplIw6dX3Q7TPhhGLrXg9ModLe74fNlI=" 10 | - secure: "evomBAnDPb3bk2DmUjLMuIeevLPHa/vCadPDQIhCMIR6WNpTBCCkiBFU0185JpDgzZ4dq7a5KWiKQORzqUbgKoRm8cXa6N7x1k55NG+DOuZSNfSJfTN1BWIQGFahCDsq/qvdFI5g49wuT4Qyp7ErQ8/dYokg4LCXpnsf+glF6VI=" 11 | -------------------------------------------------------------------------------- /scripts/transpile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const babel = require('babel-core'); 4 | const fs = require('fs'); 5 | const path = require('path'); 6 | 7 | babel.transformFile('src/simpleslider.js', { 8 | presets: ['env'], 9 | plugins: [ 10 | ['transform-es2015-modules-umd'], 11 | ['conditional-compilation', { 12 | TEST: 0 13 | }], 14 | ['remove-comments'] 15 | ] 16 | }, (err, result) => { 17 | if (err) { 18 | console.error(err); 19 | process.exit(1); 20 | } 21 | const filename = path.join(__dirname, '..', 'dist', 'simpleslider.js'); 22 | fs.writeFileSync(filename, result.code); 23 | }); 24 | 25 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: { 5 | 'unit-tests': './test/unit-tests.js' 6 | }, 7 | output: { 8 | filename: '[name].js', 9 | path: path.resolve(__dirname, '__tests__') 10 | }, 11 | module: { 12 | rules: [ 13 | { 14 | test: /\.js$/, 15 | use: { 16 | loader: 'babel-loader', 17 | options: { 18 | presets: ['env'], 19 | plugins: [ 20 | ['conditional-compilation', { 21 | TEST: 1 22 | }] 23 | ] 24 | } 25 | } 26 | } 27 | ] 28 | } 29 | }; 30 | 31 | -------------------------------------------------------------------------------- /examples/no-options.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | No options on initialization example 5 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /examples/default-sliding-transition.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Simple horizontal slider example 5 | 8 | 9 | 10 |
11 | 12 | 13 | 14 |
15 | 16 | 17 | 27 | 28 | -------------------------------------------------------------------------------- /examples/fading-transition.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Fade in/out transition example 5 | 6 | 9 | 10 |
11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 28 | 29 | -------------------------------------------------------------------------------- /examples/top-to-bottom-sliding-transition.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Vertical sliding example 5 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 28 | 29 | -------------------------------------------------------------------------------- /examples/requirejs-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | RequireJS example 5 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 |
16 | 17 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-slider", 3 | "version": "1.0.0", 4 | "description": "The 1kb JavaScript Carousel", 5 | "homepage": "https://ruyadorno.github.io/simple-slider/", 6 | "authors": [ 7 | "Ruy Adorno (http://ruyadorno.com)" 8 | ], 9 | "main": "dist/simpleslider.js", 10 | "keywords": [ 11 | "simple", 12 | "carousel", 13 | "slider", 14 | "gallery", 15 | "slideshow", 16 | "lightweight", 17 | "minimalistic", 18 | "micro", 19 | "slick", 20 | "lory", 21 | "siema", 22 | "swipe", 23 | "swiper", 24 | "flickity", 25 | "islider", 26 | "owlcarousel", 27 | "unslider", 28 | "glide", 29 | "glidejs", 30 | "javascript-carousel", 31 | "DOM", 32 | "browser" 33 | ], 34 | "license": "MIT", 35 | "ignore": [ 36 | "**/.*", 37 | "node_modules", 38 | "test", 39 | "scripts" 40 | ], 41 | "dependencies": {} 42 | } 43 | -------------------------------------------------------------------------------- /examples/right-to-left-sliding-transition.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | An example with images sliding from right to left 5 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 30 | 31 | -------------------------------------------------------------------------------- /examples/mask-sliding-transition.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Mask sliding example 5 | 8 | 9 | 10 |
11 |
12 |
13 |
14 |
15 |
16 | 17 | 18 | 28 | 29 | -------------------------------------------------------------------------------- /examples/pause-slide-change-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Pause slide change example 5 | 8 | 9 | 10 |
11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 35 | 36 | -------------------------------------------------------------------------------- /examples/responsive-layout-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Responsive layout example 5 | 13 | 14 | 15 |
16 |
17 | 18 | 19 | 20 | 21 |
22 |
23 | 24 | 25 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /examples/full-page-transition.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Slider occupying full page 5 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 34 | 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Ruy Adorno (ruyadorno.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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /examples/add-class-to-current-elem-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | No options on initialization example 5 | 11 | 12 | 13 |
14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /examples/lightbox-integrated-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Lightbox integration example 5 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 |
16 |

This example is using Prinzhorn/0 script for providing the lightbox functionality.

17 | 18 | 19 | 20 | 30 | 31 | -------------------------------------------------------------------------------- /examples/touch-swipe-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Touch swipe example 5 | 13 | 14 | 15 |
16 |
17 | 18 | 19 | 20 | 21 |
22 |

This example is using the Hammer.js lib to provide the swipe gesture support.

23 |
24 | 25 | 26 | 27 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /examples/custom-ease-animation-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Custom ease function 5 | 13 | 14 | 15 |
16 |
17 | 18 | 19 | 20 | 21 |
22 |

This example uses a Robert Penner classic ease function, for more ready-to-use functions check Easie.

23 |
24 | 25 | 26 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /examples/reverse-sliding-direction-example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Reverse sliding direction example 5 | 12 | 13 | 14 | 24 | 25 | 26 | 55 | 56 | -------------------------------------------------------------------------------- /dist/simpleslider.min.js: -------------------------------------------------------------------------------- 1 | !function(e,n){if("function"==typeof define&&define.amd)define(["exports"],n);else if("undefined"!=typeof exports)n(exports);else{var t={};n(t),e.simpleslider=t}}(this,function(e){"use strict";function q(e,n){return null==e?n:e}function C(e){return(e||[]).length}function L(e,n,t,i){return(e/=i/2)<1?t/2*e*e*e+n:t/2*((e-=2)*e*e+2)+n}Object.defineProperty(e,"__esModule",{value:!0}),e.getSlider=function(n){var a=void 0,t=void 0,i=void 0,o=void 0,r=void 0,u=q((n=n||{}).container,document.querySelector("*[data-simple-slider]")),v=q(n.prop,"left"),d=1e3*q(n.duration,.5),c=1e3*q(n.delay,3),p=q(n.unit,"%"),f=q(n.init,-100),s=q(n.show,0),l=q(n.end,100),e=n.paused,m=q(n.ease,L),y=q(n.onChange,0),h=q(n.onChangeEnd,0),x=Date.now;function I(){g()&&(t&&clearTimeout(t),function e(){i=x(),t=setTimeout(function(){i=x(),r=c,z(T()),e()},r)}())}function g(){return!e&&1=C(o)?0:e}function w(){var e=a-1;return e<0?C(o)-1:e}function E(){document.hidden?b():I()}return document.addEventListener("visibilitychange",E),function(){if(0 2 | 3 | 4 | Text content example 5 | 9 | 10 | 11 |
12 |
13 |

Lorem ipsum dolor sit amet, consectetur adipiscing elit. In ac orci aliquet, mattis ipsum in, auctor magna. In commodo elit vel elit suscipit molestie. Vivamus congue, tortor ut congue volutpat, lacus nulla posuere tortor, sit amet aliquam est nibh non dolor. Etiam egestas auctor euismod. Nam id luctus nunc. Praesent tempor lorem non magna dapibus, at laoreet lorem scelerisque. Mauris laoreet, risus ut adipiscing bibendum, nibh justo facilisis lectus, vitae pharetra elit nibh dignissim elit. Vestibulum euismod libero tellus, sed iaculis enim bibendum ut. Donec fringilla, leo in hendrerit mollis, est purus rutrum ligula, a tristique dui est eget ante.

14 |

Mauris ullamcorper dolor sit amet quam molestie, id euismod diam consectetur. Donec vel mattis dui, eget accumsan lectus. In consequat gravida metus, eget commodo erat consequat sit amet. Phasellus congue libero dui, a lacinia ipsum volutpat vitae. Suspendisse potenti. Curabitur vitae orci eu lacus tincidunt consequat a ultrices magna. Ut risus libero, consequat eget dolor sit amet, dignissim consectetur nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae

15 |
16 |
17 |

Praesent semper est elit, id luctus arcu mattis ac. Fusce a ligula nulla. Vestibulum fermentum pellentesque metus, non tincidunt lectus. Duis nec risus nibh. Curabitur mollis lacus non adipiscing cursus. Nunc rutrum, tortor eget molestie euismod, justo tellus tincidunt lectus, vitae molestie felis orci vitae leo. Nulla aliquam dui mauris, sed imperdiet eros faucibus quis. Fusce semper ligula ac velit varius pellentesque. Mauris non nisi tempus, malesuada diam in, feugiat eros. Ut commodo imperdiet quam, quis lacinia velit congue ut. Donec eu auctor magna, quis imperdiet nisl. Donec vitae sapien a nunc gravida convallis eu at ante.

18 |
19 |
20 |

Mauris ullamcorper dolor sit amet quam molestie, id euismod diam consectetur. Donec vel mattis dui, eget accumsan lectus. In consequat gravida metus, eget commodo erat consequat sit amet. Phasellus congue libero dui, a lacinia ipsum volutpat vitae. Suspendisse potenti. Curabitur vitae orci eu lacus tincidunt consequat a ultrices magna. Ut risus libero, consequat eget dolor sit amet, dignissim consectetur nunc. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae;

21 |

Praesent semper est elit, id luctus arcu mattis ac. Fusce a ligula nulla. Vestibulum fermentum pellentesque metus, non tincidunt lectus. Duis nec risus nibh. Curabitur mollis lacus non adipiscing cursus. Nunc rutrum, tortor eget molestie euismod, justo tellus tincidunt lectus, vitae molestie felis orci vitae leo. Nulla aliquam dui mauris, sed imperdiet eros faucibus quis. Fusce semper ligula ac velit varius pellentesque. Mauris non nisi tempus, malesuada diam in, feugiat eros. Ut commodo imperdiet quam, quis lacinia velit congue ut. Donec eu auctor magna, quis imperdiet nisl. Donec vitae sapien a nunc gravida convallis eu at ante.

22 |

Donec lectus purus, imperdiet ac sagittis ac, vulputate eget diam. Etiam bibendum sed arcu in hendrerit. Nulla a nunc sit amet nibh ultricies elementum eget sit amet orci. Nunc iaculis lorem a leo hendrerit auctor tempus fringilla dui. Nunc lacinia orci eget commodo porttitor. Curabitur aliquam arcu quam, vitae facilisis eros tempus nec. Vivamus blandit odio justo. Donec eu dictum leo, a aliquet eros. Sed consectetur porttitor dolor, eu dapibus justo auctor in.

23 |
24 |
25 | 26 | 27 | 32 | 33 | -------------------------------------------------------------------------------- /dist/simpleslider.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | if (typeof define === "function" && define.amd) { 3 | define(['exports'], factory); 4 | } else if (typeof exports !== "undefined") { 5 | factory(exports); 6 | } else { 7 | var mod = { 8 | exports: {} 9 | }; 10 | factory(mod.exports); 11 | global.simpleslider = mod.exports; 12 | } 13 | })(this, function (exports) { 14 | 'use strict'; 15 | 16 | Object.defineProperty(exports, "__esModule", { 17 | value: true 18 | }); 19 | function getdef(val, def) { 20 | return val == null ? def : val; 21 | } 22 | 23 | function len(arr) { 24 | return (arr || []).length; 25 | } 26 | 27 | function startSlides(containerElem, children, unit, startVal, visVal, trProp) { 28 | var style = void 0, 29 | imgs = []; 30 | 31 | if (!children) { 32 | children = containerElem.children; 33 | } 34 | 35 | var i = len(children); 36 | 37 | while (--i >= 0) { 38 | imgs[i] = children[i]; 39 | style = imgs[i].style; 40 | style.position = 'absolute'; 41 | style.top = style.left = style.zIndex = 0; 42 | style[trProp] = startVal + unit; 43 | } 44 | 45 | style[trProp] = visVal + unit; 46 | style.zIndex = 1; 47 | 48 | return imgs; 49 | } 50 | 51 | function defaultEase(time, begin, change, duration) { 52 | return (time = time / (duration / 2)) < 1 ? change / 2 * time * time * time + begin : change / 2 * ((time -= 2) * time * time + 2) + begin; 53 | } 54 | 55 | function getSlider(options) { 56 | options = options || {}; 57 | var actualIndex = void 0, 58 | interval = void 0, 59 | intervalStartTime = void 0, 60 | imgs = void 0, 61 | remainingTime = void 0; 62 | 63 | var containerElem = getdef(options.container, document.querySelector('*[data-simple-slider]')); 64 | var trProp = getdef(options.prop, 'left'); 65 | var trTime = getdef(options.duration, 0.5) * 1000; 66 | var delay = getdef(options.delay, 3) * 1000; 67 | var unit = getdef(options.unit, '%'); 68 | var startVal = getdef(options.init, -100); 69 | var visVal = getdef(options.show, 0); 70 | var endVal = getdef(options.end, 100); 71 | var paused = options.paused; 72 | var ease = getdef(options.ease, defaultEase); 73 | var onChange = getdef(options.onChange, 0); 74 | var onChangeEnd = getdef(options.onChangeEnd, 0); 75 | var now = Date.now; 76 | 77 | function reset() { 78 | if (len(containerElem.children) > 0) { 79 | var style = containerElem.style; 80 | style.position = 'relative'; 81 | style.overflow = 'hidden'; 82 | style.display = 'block'; 83 | 84 | imgs = startSlides(containerElem, options.children, unit, startVal, visVal, trProp); 85 | actualIndex = 0; 86 | remainingTime = delay; 87 | } 88 | } 89 | 90 | function setAutoPlayLoop() { 91 | intervalStartTime = now(); 92 | interval = setTimeout(function () { 93 | intervalStartTime = now(); 94 | remainingTime = delay; 95 | 96 | change(nextIndex()); 97 | 98 | setAutoPlayLoop(); 99 | }, remainingTime); 100 | } 101 | 102 | function resume() { 103 | if (isAutoPlay()) { 104 | if (interval) { 105 | clearTimeout(interval); 106 | } 107 | 108 | setAutoPlayLoop(); 109 | } 110 | } 111 | 112 | function isAutoPlay() { 113 | return !paused && len(imgs) > 1; 114 | } 115 | 116 | function pause() { 117 | if (isAutoPlay()) { 118 | remainingTime = delay - (now() - intervalStartTime); 119 | clearTimeout(interval); 120 | interval = 0; 121 | } 122 | } 123 | 124 | function reverse() { 125 | var newEndVal = startVal; 126 | startVal = endVal; 127 | endVal = newEndVal; 128 | actualIndex = Math.abs(actualIndex - (len(imgs) - 1)); 129 | imgs = imgs.reverse(); 130 | } 131 | 132 | function change(newIndex) { 133 | var count = len(imgs); 134 | while (--count >= 0) { 135 | imgs[count].style.zIndex = 1; 136 | } 137 | 138 | imgs[newIndex].style.zIndex = 3; 139 | imgs[actualIndex].style.zIndex = 2; 140 | 141 | anim(imgs[actualIndex].style, visVal, endVal, imgs[newIndex].style, startVal, visVal, trTime, 0, 0, ease); 142 | 143 | actualIndex = newIndex; 144 | 145 | if (onChange) { 146 | onChange(prevIndex(), actualIndex); 147 | } 148 | } 149 | 150 | function next() { 151 | change(nextIndex()); 152 | resume(); 153 | } 154 | 155 | function prev() { 156 | change(prevIndex()); 157 | resume(); 158 | } 159 | 160 | function nextIndex() { 161 | var newIndex = actualIndex + 1; 162 | return newIndex >= len(imgs) ? 0 : newIndex; 163 | } 164 | 165 | function prevIndex() { 166 | var newIndex = actualIndex - 1; 167 | return newIndex < 0 ? len(imgs) - 1 : newIndex; 168 | } 169 | 170 | function dispose() { 171 | clearTimeout(interval); 172 | document.removeEventListener('visibilitychange', visibilityChange); 173 | 174 | imgs = containerElem = interval = trProp = trTime = delay = startVal = endVal = paused = actualIndex = remainingTime = onChange = onChangeEnd = null; 175 | } 176 | 177 | function currentIndex() { 178 | return actualIndex; 179 | } 180 | 181 | function anim(insertElem, insertFrom, insertTo, removeElem, removeFrom, removeTo, transitionDuration, startTime, elapsedTime, easeFunc) { 182 | function setProp(elem, from, to) { 183 | elem[trProp] = easeFunc(elapsedTime - startTime, from, to - from, transitionDuration) + unit; 184 | } 185 | 186 | if (startTime > 0) { 187 | if (elapsedTime - startTime < transitionDuration) { 188 | setProp(insertElem, insertFrom, insertTo); 189 | setProp(removeElem, removeFrom, removeTo); 190 | } else { 191 | insertElem[trProp] = insertTo + unit; 192 | removeElem[trProp] = removeTo + unit; 193 | 194 | if (onChangeEnd) { 195 | onChangeEnd(actualIndex, nextIndex()); 196 | } 197 | 198 | return; 199 | } 200 | } 201 | 202 | requestAnimationFrame(function (time) { 203 | if (startTime === 0) { 204 | startTime = time; 205 | } 206 | 207 | anim(insertElem, insertFrom, insertTo, removeElem, removeFrom, removeTo, transitionDuration, startTime, time, easeFunc); 208 | }); 209 | } 210 | 211 | function visibilityChange() { 212 | if (document.hidden) { 213 | pause(); 214 | } else { 215 | resume(); 216 | } 217 | } 218 | 219 | document.addEventListener('visibilitychange', visibilityChange); 220 | reset(); 221 | 222 | if (len(imgs) > 1) { 223 | resume(); 224 | } 225 | 226 | return { 227 | currentIndex: currentIndex, 228 | pause: pause, 229 | resume: resume, 230 | nextIndex: nextIndex, 231 | prevIndex: prevIndex, 232 | next: next, 233 | prev: prev, 234 | change: change, 235 | reverse: reverse, 236 | dispose: dispose 237 | }; 238 | } 239 | 240 | exports.getSlider = getSlider; 241 | }); -------------------------------------------------------------------------------- /src/simpleslider.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function getdef(val, def) { 4 | return val == null ? def : val; // eslint-disable-line 5 | } 6 | 7 | function len(arr) { 8 | return (arr || []).length; 9 | } 10 | 11 | function startSlides(containerElem, children, unit, startVal, visVal, trProp) { 12 | let style, 13 | imgs = []; 14 | 15 | if (!children) { 16 | children = containerElem.children; 17 | } 18 | 19 | let i = len(children); 20 | 21 | while (--i >= 0) { 22 | imgs[i] = children[i]; 23 | style = imgs[i].style; 24 | style.position = 'absolute'; 25 | style.top = 26 | style.left = 27 | style.zIndex = 0; 28 | style[trProp] = startVal + unit; 29 | } 30 | 31 | style[trProp] = visVal + unit; 32 | style.zIndex = 1; 33 | 34 | return imgs; 35 | } 36 | 37 | function defaultEase(time, begin, change, duration) { 38 | return ((time = time / (duration / 2)) < 1) // eslint-disable-line 39 | ? change / 2 * time * time * time + begin // eslint-disable-line 40 | : change / 2 * ((time -= 2) * time * time + 2) + begin; // eslint-disable-line 41 | } 42 | 43 | function getSlider(options) { 44 | options = options || {}; 45 | let actualIndex, interval, intervalStartTime, imgs, remainingTime; 46 | 47 | // Get user defined options or its default values 48 | let containerElem = getdef(options.container, document.querySelector('*[data-simple-slider]')); 49 | let trProp = getdef(options.prop, 'left'); 50 | let trTime = getdef(options.duration, 0.5) * 1000; 51 | let delay = getdef(options.delay, 3) * 1000; 52 | let unit = getdef(options.unit, '%'); 53 | let startVal = getdef(options.init, -100); 54 | let visVal = getdef(options.show, 0); 55 | let endVal = getdef(options.end, 100); 56 | let paused = options.paused; 57 | let ease = getdef(options.ease, defaultEase); 58 | let onChange = getdef(options.onChange, 0); 59 | let onChangeEnd = getdef(options.onChangeEnd, 0); 60 | let now = Date.now; 61 | 62 | function reset() { 63 | if (len(containerElem.children) > 0) { 64 | let style = containerElem.style; 65 | style.position = 'relative'; 66 | style.overflow = 'hidden'; 67 | style.display = 'block'; 68 | 69 | imgs = startSlides(containerElem, options.children, unit, startVal, visVal, trProp); 70 | actualIndex = 0; 71 | remainingTime = delay; 72 | } 73 | } 74 | 75 | // Slideshow/autoPlay timing logic 76 | function setAutoPlayLoop() { 77 | intervalStartTime = now(); 78 | interval = setTimeout(() => { 79 | intervalStartTime = now(); 80 | remainingTime = delay; // resets time, used by pause/resume logic 81 | 82 | change(nextIndex()); 83 | 84 | // loops 85 | setAutoPlayLoop(); 86 | }, remainingTime); 87 | } 88 | 89 | function resume() { 90 | if (isAutoPlay()) { 91 | if (interval) { 92 | clearTimeout(interval); 93 | } 94 | 95 | setAutoPlayLoop(); 96 | } 97 | } 98 | 99 | function isAutoPlay() { 100 | return !paused && len(imgs) > 1; 101 | } 102 | 103 | function pause() { 104 | if (isAutoPlay()) { 105 | remainingTime = delay - (now() - intervalStartTime); 106 | clearTimeout(interval); 107 | interval = 0; 108 | } 109 | } 110 | 111 | function reverse() { 112 | const newEndVal = startVal; 113 | startVal = endVal; 114 | endVal = newEndVal; 115 | actualIndex = Math.abs(actualIndex - (len(imgs) - 1)); 116 | imgs = imgs.reverse(); 117 | } 118 | 119 | function change(newIndex) { 120 | let count = len(imgs); 121 | while (--count >= 0) { 122 | imgs[count].style.zIndex = 1; 123 | } 124 | 125 | imgs[newIndex].style.zIndex = 3; 126 | imgs[actualIndex].style.zIndex = 2; 127 | 128 | anim( 129 | imgs[actualIndex].style, // insertElem 130 | visVal, // insertFrom 131 | endVal, // insertTo 132 | imgs[newIndex].style, // removeElem 133 | startVal, // removeFrom 134 | visVal, // removeTo 135 | trTime, // transitionDuration 136 | 0, // startTime 137 | 0, // elapsedTime 138 | ease // easeFunc 139 | ); 140 | 141 | actualIndex = newIndex; 142 | 143 | if (onChange) { 144 | onChange(prevIndex(), actualIndex); 145 | } 146 | } 147 | 148 | function next() { 149 | change(nextIndex()); 150 | resume(); 151 | } 152 | 153 | function prev() { 154 | change(prevIndex()); 155 | resume(); 156 | } 157 | 158 | function nextIndex() { 159 | const newIndex = actualIndex + 1; 160 | return newIndex >= len(imgs) 161 | ? 0 162 | : newIndex; 163 | } 164 | 165 | function prevIndex() { 166 | const newIndex = actualIndex - 1; 167 | return newIndex < 0 168 | ? len(imgs) - 1 169 | : newIndex; 170 | } 171 | 172 | function dispose() { 173 | clearTimeout(interval); 174 | document.removeEventListener('visibilitychange', visibilityChange); 175 | 176 | imgs = 177 | containerElem = 178 | interval = 179 | trProp = 180 | trTime = 181 | delay = 182 | startVal = 183 | endVal = 184 | paused = 185 | actualIndex = 186 | remainingTime = 187 | onChange = 188 | onChangeEnd = null; 189 | } 190 | 191 | function currentIndex() { 192 | return actualIndex; 193 | } 194 | 195 | function anim(insertElem, insertFrom, insertTo, removeElem, removeFrom, removeTo, transitionDuration, startTime, elapsedTime, easeFunc) { 196 | function setProp(elem, from, to) { 197 | elem[trProp] = 198 | easeFunc(elapsedTime - startTime, from, to - from, transitionDuration) + unit; 199 | } 200 | 201 | if (startTime > 0) { 202 | if (elapsedTime - startTime < transitionDuration) { 203 | setProp(insertElem, insertFrom, insertTo); 204 | setProp(removeElem, removeFrom, removeTo); 205 | } else { 206 | insertElem[trProp] = insertTo + unit; 207 | removeElem[trProp] = removeTo + unit; 208 | 209 | if (onChangeEnd) { 210 | onChangeEnd(actualIndex, nextIndex()); 211 | } 212 | 213 | return; 214 | } 215 | } 216 | 217 | requestAnimationFrame(time => { 218 | // Starts time in the first anim iteration 219 | if (startTime === 0) { 220 | startTime = time; 221 | } 222 | 223 | anim( 224 | insertElem, 225 | insertFrom, 226 | insertTo, 227 | removeElem, 228 | removeFrom, 229 | removeTo, 230 | transitionDuration, 231 | startTime, 232 | time, 233 | easeFunc 234 | ); 235 | }); 236 | } 237 | 238 | function visibilityChange() { 239 | if (document.hidden) { 240 | pause(); 241 | } else { 242 | resume(); 243 | } 244 | } 245 | 246 | // configures visibility api handler https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API 247 | document.addEventListener('visibilitychange', visibilityChange); 248 | reset(); 249 | 250 | if (len(imgs) > 1) { 251 | resume(); 252 | } 253 | 254 | "#if TEST > 0"; // eslint-disable-line 255 | return { 256 | internalState: { 257 | getInterval: () => interval, 258 | getRemainingTime: () => remainingTime, 259 | getImgs: () => imgs, 260 | getContainerElem: () => containerElem, 261 | setActualIndex: val => { actualIndex = val; }, 262 | isAutoPlay, 263 | defaultEase, 264 | reset, 265 | trProp, 266 | trTime, 267 | delay, 268 | unit, 269 | startVal, 270 | visVal, 271 | endVal, 272 | paused, 273 | ease 274 | }, 275 | currentIndex, 276 | pause, 277 | resume, 278 | nextIndex, 279 | prevIndex, 280 | next, 281 | prev, 282 | change, 283 | reverse, 284 | dispose 285 | }; 286 | "#else"; // eslint-disable-line 287 | return { 288 | currentIndex, 289 | pause, 290 | resume, 291 | nextIndex, 292 | prevIndex, 293 | next, 294 | prev, 295 | change, 296 | reverse, 297 | dispose 298 | }; 299 | "#endif"; // eslint-disable-line 300 | } 301 | 302 | export {getSlider}; 303 | 304 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # simple-slider 2 | 3 | [![NPM version](https://badge.fury.io/js/simple-slider.svg)](https://npmjs.org/package/simple-slider) 4 | [![CDNJS version](https://img.shields.io/cdnjs/v/simple-slider.svg)](https://cdnjs.com/libraries/simple-slider) 5 | [![Build Status](https://travis-ci.org/ruyadorno/simple-slider.svg?branch=master)](https://travis-ci.org/ruyadorno/simple-slider) 6 | ![File Size: < 1.1kB gzipped](http://img.badgesize.io/ruyadorno/simple-slider/master/dist/simpleslider.min.js?compression=gzip) 7 | [![license](http://img.shields.io/badge/license-MIT-blue.svg?style=flat)](https://raw.githubusercontent.com/ruyadorno/simple-slider/master/LICENSE) 8 | [![Join the chat at https://gitter.im/simple-slider/Lobby](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/simple-slider/Lobby) 9 | 10 | https://ruyadorno.github.io/simple-slider 11 | 12 | Extremely lightweight carousel micro library. 13 | 14 | [![Build Status](https://saucelabs.com/browser-matrix/ruyadorno-3.svg)](https://saucelabs.com/beta/builds/533bdd25630b4472a05f128b0c47e91f) 15 | 16 | ## About 17 | 18 | **simple-slider** is a carousel micro library based on the [requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame) API. It makes for a highly testable implementation and less css-dependent. 19 | 20 | This package contains a framework agnostic implementation. If you are using **AngularJS** or **Polymer** there are some **simple-slider** framework-specific implementations available: 21 | 22 | - [angular-simple-slider](https://github.com/ruyadorno/angular-simple-slider) 23 | - [polymer-simple-slider](https://github.com/ruyadorno/polymer-simple-slider) 24 | 25 | 26 | ## Features 27 | 28 | - Small size, less than 1.1kb minified/gzipped 29 | - Support to [UMD](https://github.com/umdjs/umd): AMD, CommonJS and global definition 30 | - Uses [requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame) for animations 31 | - Supports [Page visibility API](https://developer.mozilla.org/en-US/docs/Web/Events/visibilitychange) to pause/resume transitions when user navigates away from the page 32 | - Accept [ease functions](https://github.com/jimjeffers/Easie/blob/master/easie.js) to customize the transition animation 33 | - Lots of ready-to-use examples available, just check the [example](./examples/) folder 34 | - Animates any numerical css property 35 | 36 | 37 | ## Install 38 | 39 | Available on **npm**: 40 | 41 | ```sh 42 | npm install --save simple-slider 43 | ``` 44 | 45 | and you can also get it on **Bower**: 46 | 47 | ```sh 48 | bower install --save simple-slider 49 | ``` 50 | 51 | ### Getting it from cdnjs 52 | 53 | This library is also available on https://cdnjs.com/ you can use it just by importing: 54 | 55 | - Minified version: https://cdnjs.cloudflare.com/ajax/libs/simple-slider/1.0.0/simpleslider.min.js 56 | - Unminified version: https://cdnjs.cloudflare.com/ajax/libs/simple-slider/1.0.0/simpleslider.js 57 | 58 | 59 | ## Usage 60 | 61 | Simply import the script in your html and call the `simpleslider.getSlider` function. 62 | 63 | ```html 64 |
65 | 66 | 67 |
68 | 69 | 72 | ``` 73 | 74 | *In this previous example we didn't specified any additional option, in this case the slider will use its default left-to-right sliding animation and the container element will be the first element containing a `data-simple-slider` attribute.* 75 | 76 | ### Usage in a CommonJS environment 77 | 78 | ```js 79 | var simpleslider = require('simple-slider'); 80 | 81 | simpleslider.getSlider(); 82 | ``` 83 | 84 | ### ES2015+ environments 85 | 86 | ```js 87 | import { getSlider } from 'simple-slider/src'; 88 | 89 | getSlider(); 90 | ``` 91 | 92 | ### RequireJS/AMD environment 93 | 94 | ```js 95 | require(['simple-slider'], function(simpleslider) { 96 | simpleslider.getSlider(); 97 | }); 98 | ``` 99 | 100 | 101 | ## Options 102 | 103 | Options are set as named properties of a single parameter accepted by the `getSlider` function, they help you customize the slider transition and how it's going to work. 104 | 105 | The main option is a `container` element, this will usually be a `
` or `
    ` containing the elements to be transitioned, keep in mind that this container should also have a defined width/height value. You can also tweak things such as the delay time between each transition, how long each transition will take, etc. 106 | 107 | ```html 108 |
    109 | 110 | 111 |
    112 | 113 | 120 | ``` 121 | 122 | ### Available Options 123 | 124 | Here is the list of available values to customize how your slider is going to work: 125 | 126 | - **container**: <[Element](https://developer.mozilla.org/en-US/docs/Web/API/Element)> The HTML element that act as a container for the slider. Defaults to `document.querySelector('*[data-simple-slider])`. 127 | - **children** <[NodeList](https://developer.mozilla.org/en-US/docs/Web/API/NodeList)/Array> A list of children to be used as slides, you can use the [querySelectorAll](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelectorAll) to have more flexibility on what children of the `container` element should be used as slides. Defaults to `container.children`. 128 | - **paused**: Controls carousel auto-transition/slideshow. If value is `true` no transition will happen. Defaults to `false`. 129 | - **prop**: Determines the css property to be animated. Defaults to `left`. 130 | - **duration**: Value setting the duration of animation transition. Defaults to `0.5`. 131 | - **delay**: Value determining the wait between each animation when auto-transition is enabled. Defaults to `3` seconds. 132 | - **init**: Initial value of slide elements when starting a transition animation. Defaults to `-100`. 133 | - **show**: The value a slide element should have when it is displayed. Defaults to `0`. 134 | - **end**: The value a slide will move to during a transition animation. Defaults to `100`. 135 | - **unit**: The css unit value to be used. Defaults to `%`. 136 | - **ease**: An ease function, you can use any of [these](https://github.com/jimjeffers/Easie/blob/master/easie.js). Defaults to `defaultEase` internal function. 137 | - **onChange**: A callback function to be invoked each time a slide changes. 138 | - **onChangeEnd**: A callback function to be invoked at the end of the slide transition 139 | 140 | ### Default values 141 | 142 | ```js 143 | { 144 | container: document.querySelector('*[data-simple-slider]'), 145 | children: container.children, 146 | paused: false, 147 | prop: 'left', 148 | duration: 0.5, 149 | delay: 3, 150 | init: -100, 151 | show: 0, 152 | end: 100, 153 | unit: '%', 154 | ease: defaultEase function, 155 | onChange: undefined, 156 | onChangeEnd: undefined 157 | } 158 | ``` 159 | 160 | 161 | ## Programmatic API 162 | 163 | Some methods are exposed by the returning value of the function allowing you to control the slider. 164 | 165 | ```html 166 |
    167 | 168 | 169 |
    170 | 171 | 186 | ``` 187 | 188 | ### Available methods: 189 | 190 | - `currentIndex()` Returns the index of the current displaying image. 191 | - `pause()` Pauses the slideshow. 192 | - `resume()` Resumes the slideshow. 193 | - `reverse()` Swaps `init` for `end` and reverses the order of slides. 194 | - `nextIndex()` Gets the index of the next slide to be shown. 195 | - `prevIndex()` Gets the index of the previous slide. 196 | - `next()` Switches displaying image to the next one. 197 | - `prev()` Switches displaying image to the previous one. 198 | - `change(index)` Changes image to a given `index` value. 199 | - `dispose()` Disposes of all internal variable references. 200 | 201 | 202 | ## More examples 203 | 204 | - [Responsive layout example](./examples/responsive-layout-example.html) 205 | - [Default slider with no options](./examples/no-options.html) 206 | - [Default slider with options](./examples/default-sliding-transition.html) 207 | - [Fading/Opacity transition](./examples/fading-transition.html) 208 | - [Full page transition](./examples/full-page-transition.html) 209 | - [Lightbox integrated](./examples/lightbox-integrated-example.html) 210 | - [Mask sliding transition](./examples/mask-sliding-transition.html) 211 | - [Pause slide](./examples/pause-slide-change-example.html) 212 | - [RequireJS usage](./examples/requirejs-example.html) 213 | - [Reverse sliding direction](./examples/reverse-sliding-direction-example.html) 214 | - [Right to left sliding transition](./examples/right-to-left-sliding-transition.html) 215 | - [Slides containing no images](./examples/text-content-example.html) 216 | - [Top to bottom transition](./examples/top-to-bottom-sliding-transition.html) 217 | - [Touch swipe example](./examples/touch-swipe-example.html) 218 | - [Custom ease animation](./examples/custom-ease-animation-example.html) 219 | 220 | 221 | ## [Documentation](https://ruyadorno.github.io/simple-slider/docs/) 222 | 223 | Extensive documentation of the options and methods can be found at the [simple-slider official documentation](https://ruyadorno.github.io/simple-slider/docs/). 224 | 225 | 226 | ## Alternatives 227 | 228 | A JavaScript carousel micro library is not a new thing (fun fact, **simple-slider** has been around [since 2013](https://github.com/ruyadorno/simple-slider/commit/1e54f82536e5e1ef047445ab705c674cff3db9ee)). 229 | 230 | I would recommend that you take a look at some of the available alternatives and decide by yourself which one better suits your needs. 231 | 232 | - [slick](https://github.com/kenwheeler/slick) 233 | - [lory](https://github.com/meandmax/lory) 234 | - [siema](https://pawelgrzybek.com/siema/) 235 | - [Swiper](https://github.com/nolimits4web/Swiper) 236 | - [iSlider](https://github.com/BE-FE/iSlider) 237 | - [Owl Carousel](https://github.com/OwlCarousel2/OwlCarousel2) 238 | - [flickity](https://github.com/metafizzy/flickity) 239 | - [swipe](https://github.com/lyfeyaj/swipe) 240 | - [unslider](https://github.com/idiot/unslider) 241 | - [Glide](https://github.com/jedrzejchalubek/glidejs) 242 | 243 | 244 | ## License 245 | 246 | MIT © [Ruy Adorno](http://ruyadorno.com) 247 | 248 | -------------------------------------------------------------------------------- /test/unit-tests.js: -------------------------------------------------------------------------------- 1 | /* eslint-env jest */ 2 | import {getSlider} from '../src/simpleslider'; 3 | import {polyfill} from 'raf'; 4 | 5 | polyfill(); 6 | 7 | describe('simple-slider', function () { 8 | 'use strict'; 9 | 10 | var testDivCount = 0; 11 | 12 | // Helper functions to create dummy elements 13 | 14 | var createEmptyDiv = function () { 15 | var newDiv = document.createElement('div'); 16 | newDiv.id = 'test-div-' + testDivCount; 17 | newDiv.style.width = '480px'; 18 | newDiv.style.height = '200px'; 19 | testDivCount++; 20 | 21 | return newDiv; 22 | }; 23 | 24 | var addChildrenDivs = function (newDiv, numChild) { 25 | var childrenNum = numChild ? numChild : Math.ceil(Math.random() * 10); 26 | while (--childrenNum >= 0) { 27 | newDiv.appendChild(document.createElement('div')); 28 | } 29 | }; 30 | 31 | var getNewDiv = function (numChild) { 32 | var newDiv = createEmptyDiv(); 33 | 34 | addChildrenDivs(newDiv, numChild); 35 | 36 | return newDiv; 37 | }; 38 | 39 | var getNewSlider = function (options, numChild) { 40 | return getSlider(Object.assign({ 41 | container: getNewDiv(numChild) 42 | }, options)); 43 | }; 44 | 45 | it('should be able to create a new instance', function () { 46 | var ss = getNewSlider(); 47 | expect(ss.toString()).toEqual('[object Object]'); 48 | 49 | ss.dispose(); 50 | }); 51 | 52 | it('default properties should match', function () { 53 | // Test default values 54 | var ss = getNewSlider(); 55 | expect(ss.internalState.paused).toEqual(undefined); 56 | expect(ss.internalState.trProp).toEqual('left'); 57 | expect(ss.internalState.trTime).toEqual(500); 58 | expect(ss.internalState.delay).toEqual(3000); 59 | expect(ss.internalState.startVal).toEqual(-100); 60 | expect(ss.internalState.visVal).toEqual(0); 61 | expect(ss.internalState.endVal).toEqual(100); 62 | expect(ss.internalState.ease).toEqual(ss.internalState.defaultEase); 63 | 64 | ss.dispose(); 65 | }); 66 | 67 | it('properties should be defined properly', function () { 68 | // Test some custom values 69 | var customEasingStub = function () { 70 | return true; 71 | }; 72 | 73 | var ss = getNewSlider({ 74 | prop: 'left', 75 | duration: 1, 76 | delay: 2, 77 | init: 300, 78 | show: 200, 79 | end: 100, 80 | paused: true, 81 | ease: customEasingStub 82 | }); 83 | expect(ss.internalState.isAutoPlay()).toEqual(false); 84 | expect(ss.internalState.trProp).toEqual('left'); 85 | expect(ss.internalState.trTime).toEqual(1000); 86 | expect(ss.internalState.delay).toEqual(2000); 87 | expect(ss.internalState.startVal).toEqual(300); 88 | expect(ss.internalState.visVal).toEqual(200); 89 | expect(ss.internalState.endVal).toEqual(100); 90 | expect(ss.internalState.ease).toEqual(customEasingStub); 91 | 92 | ss.dispose(); 93 | }); 94 | 95 | it('should work when partialy declaring properties', function () { 96 | // Partially defined values 97 | var ss = getNewSlider({ 98 | prop: 'top', 99 | init: -100, 100 | paused: true 101 | }); 102 | expect(ss.internalState.trProp).toEqual('top'); 103 | expect(ss.internalState.startVal).toEqual(-100); 104 | expect(ss.internalState.isAutoPlay()).toEqual(false); 105 | 106 | ss.dispose(); 107 | }); 108 | 109 | it('after init should contain imgs data', function () { 110 | var newDiv = getNewDiv(); 111 | var ss = getSlider({container: newDiv}); 112 | var countChildren = newDiv.children.length - 1; 113 | 114 | expect(ss.internalState.getImgs().length).toEqual(newDiv.children.length); 115 | 116 | while (countChildren >= 0) { 117 | expect(ss.internalState.getImgs()).toContain(newDiv.children[countChildren]); 118 | countChildren--; 119 | } 120 | 121 | ss.dispose(); 122 | }); 123 | 124 | it('should set initial styling on elements', function () { 125 | var ss = getNewSlider({}, 5); 126 | 127 | expect(ss.internalState.getContainerElem().style.position).toEqual('relative'); 128 | expect(ss.internalState.getContainerElem().style.overflow).toEqual('hidden'); 129 | 130 | var i = ss.internalState.getContainerElem().children.length; 131 | 132 | while (--i >= 0) { 133 | expect(ss.internalState.getImgs()[i].style.position).toEqual('absolute'); 134 | expect(ss.internalState.getImgs()[i].style.top).toEqual('0px'); 135 | 136 | // Only the first one should be on visible state 137 | if (i === 0) { 138 | expect(ss.internalState.getImgs()[i].style.left).toEqual('0%'); 139 | } else { 140 | expect(ss.internalState.getImgs()[i].style.left).toEqual('-100%'); 141 | } 142 | } 143 | 144 | ss.dispose(); 145 | }); 146 | 147 | describe('.onChange()', function () { 148 | it('should call onChange function if defined', function (done) { 149 | var ss; 150 | var callback = function () { 151 | expect(true).toBeTruthy(); 152 | 153 | ss.dispose(); 154 | 155 | done(); 156 | }; 157 | 158 | // Should also get when using show 159 | ss = getNewSlider({ 160 | onChange: callback 161 | }, 3); 162 | 163 | ss.change(2); 164 | }); 165 | 166 | it('should have prevIndex and nextIndex parameters', function (done) { 167 | var ss; 168 | var callback = function (prevIndex, nextIndex) { 169 | expect(prevIndex).toBe(0); 170 | expect(nextIndex).toBe(1); 171 | 172 | ss.dispose(); 173 | 174 | done(); 175 | }; 176 | 177 | // Should also get when using show 178 | ss = getNewSlider({ 179 | onChange: callback 180 | }, 3); 181 | 182 | ss.next(); 183 | }); 184 | }); 185 | 186 | describe('.onChangeEnd()', function () { 187 | it('should call onChangeEnd function if defined', function (done) { 188 | expect.assertions(1); 189 | var ss; 190 | var callback = function () { 191 | expect(true).toBeTruthy(); 192 | 193 | ss.dispose(); 194 | 195 | done(); 196 | }; 197 | 198 | // Should also get when using show 199 | ss = getNewSlider({ 200 | duration: 0.1, 201 | onChangeEnd: callback 202 | }, 3); 203 | 204 | ss.change(2); 205 | }); 206 | 207 | it('should have currentIndex and nextIndex parameters', function (done) { 208 | expect.assertions(2); 209 | var ss; 210 | var callback = function (currIndex, nextIndex) { 211 | expect(currIndex).toBe(1); 212 | expect(nextIndex).toBe(2); 213 | 214 | ss.dispose(); 215 | 216 | done(); 217 | }; 218 | 219 | // Should also get when using show 220 | ss = getNewSlider({ 221 | onChangeEnd: callback 222 | }, 3); 223 | 224 | ss.next(); 225 | }); 226 | }); 227 | 228 | describe('*internal .reset()', function () { 229 | it('should reset original style values', function () { 230 | var ss = getNewSlider({ 231 | paused: true 232 | }, 5); 233 | 234 | ss.change(3); 235 | 236 | ss.internalState.reset(); 237 | 238 | expect(ss.internalState.getContainerElem().style.position).toEqual('relative'); 239 | expect(ss.internalState.getContainerElem().style.overflow).toEqual('hidden'); 240 | expect(ss.internalState.getContainerElem().style.display).toEqual('block'); 241 | 242 | ss.dispose(); 243 | }); 244 | 245 | it('should start imgs control', function () { 246 | var ss = getNewSlider({ 247 | paused: true 248 | }, 5); 249 | 250 | ss.change(3); 251 | 252 | ss.internalState.reset(); 253 | 254 | expect(ss.internalState.getImgs().length).toEqual(5); 255 | 256 | ss.dispose(); 257 | }); 258 | 259 | it('should set actualIndex to 0', function () { 260 | var ss = getNewSlider({ 261 | paused: true 262 | }, 5); 263 | 264 | ss.change(3); 265 | 266 | ss.internalState.reset(); 267 | 268 | expect(ss.currentIndex()).toEqual(0); 269 | 270 | ss.dispose(); 271 | }); 272 | 273 | it('should set initial image properties values', function () { 274 | var ss = getNewSlider({ 275 | paused: true 276 | }, 5); 277 | 278 | ss.change(3); 279 | 280 | ss.internalState.reset(); 281 | 282 | expect(ss.internalState.getImgs()[0].style[ss.internalState.trProp]).toEqual(ss.internalState.visVal.toString() + ss.internalState.unit); 283 | expect(ss.internalState.getImgs()[1].style[ss.internalState.trProp]).toEqual(ss.internalState.startVal.toString() + ss.internalState.unit); 284 | 285 | ss.dispose(); 286 | }); 287 | }); 288 | 289 | describe('.next()', function () { 290 | it('should change to next slide', function () { 291 | var ss = getNewSlider({paused: true}, 5); 292 | var initialIndex = ss.currentIndex(); 293 | 294 | ss.next(); 295 | 296 | expect(ss.currentIndex()).toEqual(initialIndex + 1); 297 | 298 | ss.dispose(); 299 | }); 300 | 301 | it('should change to first slide when current slide is the last in the set', function () { 302 | var ss = getNewSlider({paused: true}, 5); 303 | 304 | ss.internalState.setActualIndex(4); 305 | 306 | ss.next(); 307 | 308 | expect(ss.currentIndex()).toEqual(0); 309 | 310 | ss.dispose(); 311 | }); 312 | }); 313 | 314 | describe('.prev()', function () { 315 | it('should change to previous slide', function () { 316 | var ss = getNewSlider({paused: true}, 5); 317 | 318 | ss.internalState.setActualIndex(1); 319 | 320 | ss.prev(); 321 | 322 | expect(ss.currentIndex()).toEqual(0); 323 | 324 | ss.dispose(); 325 | }); 326 | 327 | it('should change to last slide when current slide is the first in the set', function () { 328 | var ss = getNewSlider({paused: true}, 5); 329 | 330 | ss.prev(); 331 | 332 | expect(ss.currentIndex()).toEqual(ss.internalState.getImgs().length - 1); 333 | 334 | ss.dispose(); 335 | }); 336 | }); 337 | 338 | describe('.pause()', function () { 339 | it('should clear slide change setInterval', function () { 340 | var ss = getNewSlider({}, 5); 341 | 342 | expect(ss.internalState.getInterval()).not.toEqual(0); 343 | 344 | ss.pause(); 345 | 346 | expect(ss.internalState.getInterval()).toEqual(0); 347 | 348 | ss.dispose(); 349 | }); 350 | 351 | it('*internal should define this.remainingTime value', function () { 352 | var ss = getNewSlider({}, 5); 353 | 354 | ss.pause(); 355 | 356 | // Adds half a second of tolerances 357 | // it might be some miliseconds off sometimes 358 | expect(ss.internalState.getRemainingTime()).toBeGreaterThan(ss.internalState.delay - 500); 359 | 360 | // But should never be over the origin delay 361 | expect(ss.internalState.getRemainingTime()).toBeLessThan(ss.internalState.delay + 1); 362 | 363 | ss.dispose(); 364 | }); 365 | 366 | it('should do nothing when autoplay is disabled', function () { 367 | var ss = getNewSlider({paused: true}, 5); 368 | 369 | ss.pause(); 370 | 371 | ss.dispose(); 372 | }); 373 | }); 374 | 375 | describe('.resume()', function () { 376 | it('should re-enable slide changing setInterval', function () { 377 | var ss = getNewSlider({}, 5); 378 | 379 | ss.pause(); 380 | 381 | expect(ss.internalState.getInterval()).toEqual(0); 382 | 383 | ss.resume(); 384 | 385 | expect(ss.internalState.getInterval()).not.toEqual(0); 386 | 387 | ss.dispose(); 388 | }); 389 | 390 | it('should do nothing when autoplay is disabled', function () { 391 | var ss = getNewSlider({paused: true}, 5); 392 | 393 | ss.resume(); 394 | 395 | ss.dispose(); 396 | }); 397 | }); 398 | 399 | describe('.nextIndex()', function () { 400 | it('should return next index value on carousel', function () { 401 | var ss = getNewSlider({ 402 | paused: true 403 | }, 5); 404 | 405 | // Original value should always be zero 406 | expect(ss.currentIndex()).toEqual(0); 407 | 408 | // Next index should not increment currentIndex() value 409 | expect(ss.nextIndex()).toEqual(1); 410 | 411 | ss.dispose(); 412 | }); 413 | 414 | it('should return first item index when it is on last item', function () { 415 | var ss = getNewSlider({ 416 | paused: true 417 | }, 5); 418 | 419 | ss.change(4); 420 | 421 | expect(ss.nextIndex()).toEqual(0); 422 | 423 | ss.dispose(); 424 | }); 425 | 426 | it('should not increment currentIndex() value', function () { 427 | var ss = getNewSlider({ 428 | paused: true 429 | }, 5); 430 | 431 | // Next index should not increment currentIndex() value 432 | expect(ss.nextIndex()).toEqual(1); 433 | expect(ss.nextIndex()).toEqual(1); 434 | 435 | ss.dispose(); 436 | }); 437 | }); 438 | 439 | describe('.dispose()', function () { 440 | it('should dispose created instances', function () { 441 | var ss = getNewSlider({ 442 | prop: 'opacity' 443 | }, 5); 444 | 445 | ss.dispose(); 446 | 447 | expect(ss.internalState.getImgs()).toEqual(null); 448 | expect(ss.currentIndex()).toEqual(null); 449 | expect(ss.internalState.getInterval()).toEqual(null); 450 | expect(ss.internalState.getContainerElem()).toEqual(null); 451 | }); 452 | 453 | it('dispose should clear autoplay interval', function (done) { 454 | var ss = getNewSlider({ 455 | paused: false, 456 | prop: 'opacity' 457 | }, 5); 458 | 459 | // spy on change method 460 | spyOn(ss, 'change'); // eslint-disable-line no-undef 461 | 462 | ss.dispose(); 463 | 464 | setTimeout(function () { 465 | expect(ss.change).not.toHaveBeenCalled(); 466 | done(); 467 | }, (ss.delay) + 1); 468 | }); 469 | }); 470 | 471 | describe('.reverse()', function () { 472 | it('should reverse order of slides', function () { 473 | var ss = getNewSlider({ 474 | prop: 'opacity' 475 | }, 5); 476 | var reversedImgs = ss.internalState.getImgs().slice().reverse(); 477 | 478 | expect(ss.internalState.getImgs()).not.toEqual(reversedImgs); 479 | ss.reverse(); 480 | expect(ss.internalState.getImgs()).toEqual(reversedImgs); 481 | }); 482 | }); 483 | }); 484 | -------------------------------------------------------------------------------- /test/functional-tests.js: -------------------------------------------------------------------------------- 1 | /* eslint-env jasmine */ 2 | describe('simple-slider', function () { 3 | 'use strict'; 4 | 5 | var testDivCount = 0; 6 | 7 | // Helper functions to create dummy elements 8 | 9 | var createEmptyDiv = function () { 10 | var newDiv = document.createElement('div'); 11 | newDiv.id = 'test-div-' + testDivCount; 12 | newDiv.style.width = '500px'; 13 | newDiv.style.height = '500px'; 14 | testDivCount++; 15 | 16 | return newDiv; 17 | }; 18 | 19 | var addChildrenDivs = function (newDiv, numChild) { 20 | var childrenNum = numChild ? numChild : (Math.ceil(Math.random() * 10) + 1); 21 | var child; 22 | while (--childrenNum >= 0) { 23 | child = document.createElement('img'); 24 | child.src = '//picsum.photos/50/50?random=' + (Math.random() * 1000); 25 | child.style.width = '100%'; 26 | newDiv.appendChild(child); 27 | } 28 | }; 29 | 30 | var getNewDiv = function (numChild) { 31 | var newDiv = createEmptyDiv(); 32 | addChildrenDivs(newDiv, numChild); 33 | document.body.appendChild(newDiv); 34 | return newDiv; 35 | }; 36 | 37 | var getNewSlider = function (options, numChild) { 38 | var container = getNewDiv(numChild); 39 | options.container = container; 40 | return { 41 | slider: window.simpleslider.getSlider(options), 42 | container: container 43 | }; 44 | }; 45 | 46 | describe('slideshow animation logic', function () { 47 | it('should have correct default initial values', function () { 48 | var s = getNewSlider({}, 5); 49 | expect(s.container.children[0].style.left).toEqual('0%'); 50 | expect(s.container.children[1].style.left).toEqual('-100%'); 51 | s.slider.dispose(); 52 | }); 53 | 54 | it('should have correct custom initial values', function () { 55 | var s = getNewSlider({ 56 | prop: 'left', 57 | init: -500, 58 | show: 0, 59 | end: 500, 60 | unit: 'px' 61 | }, 5); 62 | 63 | expect(s.container.children[0].style.left).toEqual('0px'); 64 | expect(s.container.children[1].style.left).toEqual('-500px'); 65 | s.slider.dispose(); 66 | }); 67 | 68 | it('should change values correctly after default transition', function (done) { 69 | var startTime = 0; 70 | var isTransitionTested; 71 | var s = getNewSlider({}, 5); 72 | var nextIndex = s.slider.currentIndex() + 1; 73 | var timeEnoughToStartTransition = 3100; 74 | var timeEnoughToEndTransition = 3700; 75 | 76 | function testDefault(time) { 77 | if (!startTime) { 78 | startTime = time; 79 | } 80 | 81 | if ((time - startTime) > timeEnoughToEndTransition) { 82 | // Test values after finishing the first transition 83 | try { 84 | expect(s.container.children[0].style.left).toEqual('100%'); 85 | expect(s.container.children[1].style.left).toEqual('0%'); 86 | } catch (e) { 87 | console.error(e); 88 | } 89 | 90 | s.slider.dispose(); 91 | done(); 92 | return; 93 | } 94 | 95 | if (!isTransitionTested && (time - startTime) > timeEnoughToStartTransition) { 96 | // Internal index value is correct 97 | try { 98 | expect(s.slider.currentIndex()).toEqual(nextIndex); 99 | } catch (e) { 100 | console.error(e); 101 | } 102 | } 103 | 104 | requestAnimationFrame(testDefault); 105 | } 106 | 107 | requestAnimationFrame(testDefault); 108 | }); 109 | 110 | it('should change values using no options', function (done) { 111 | // set a mock element with default data-attr 112 | var nextIndex; 113 | var s = { 114 | container: getNewDiv() 115 | }; 116 | 117 | s.container.setAttribute('data-simple-slider', true); 118 | 119 | function onChange() { 120 | // Internal index value is correct 121 | try { 122 | expect(s.slider.currentIndex()).toEqual(nextIndex); 123 | } catch (e) { 124 | console.error(e); 125 | } 126 | } 127 | 128 | function onChangeEnd() { 129 | // Test values after finishing the first transition 130 | try { 131 | expect(s.container.children[0].style.left).toEqual('100%'); 132 | expect(s.container.children[1].style.left).toEqual('0%'); 133 | } catch (e) { 134 | console.error(e); 135 | } 136 | 137 | s.slider.dispose(); 138 | done(); 139 | } 140 | 141 | s.slider = window.simpleslider.getSlider({ 142 | onChange: onChange, 143 | onChangeEnd: onChangeEnd 144 | }); 145 | nextIndex = s.slider.currentIndex() + 1; 146 | }); 147 | 148 | it('should change values correctly, sliding style', function (done) { 149 | var s = getNewSlider({ 150 | prop: 'left', 151 | delay: 0.4, 152 | duration: 0.2, 153 | init: -500, 154 | show: 0, 155 | end: 500, 156 | unit: 'px' 157 | }, 5); 158 | 159 | var startTime = 0; 160 | var isTransitionTested; 161 | var nextIndex = s.slider.currentIndex() + 1; 162 | var timeEnoughToStartTransition = 500; 163 | var timeEnoughToEndTransition = 700; 164 | 165 | expect(s.container.children[0].style.left).toEqual('0px'); 166 | expect(s.container.children[1].style.left).toEqual('-500px'); 167 | 168 | function testSliding(time) { 169 | if (!startTime) { 170 | startTime = time; 171 | } 172 | 173 | if ((time - startTime) > timeEnoughToEndTransition) { 174 | // Test values after finishing the first transition 175 | try { 176 | expect(s.container.children[0].style.left).toEqual('500px'); 177 | expect(s.container.children[1].style.left).toEqual('0px'); 178 | expect(s.container.children[2].style.left).toEqual('-500px'); 179 | } catch (e) { 180 | console.error(e); 181 | } 182 | 183 | s.slider.dispose(); 184 | done(); 185 | return; 186 | } 187 | 188 | if (!isTransitionTested && (time - startTime) > timeEnoughToStartTransition) { 189 | // Internal index value is correct 190 | try { 191 | expect(s.slider.currentIndex()).toEqual(nextIndex); 192 | } catch (e) { 193 | console.error(e); 194 | } 195 | 196 | isTransitionTested = true; 197 | } 198 | 199 | requestAnimationFrame(testSliding); 200 | } 201 | 202 | requestAnimationFrame(testSliding); 203 | }); 204 | 205 | it('should change values correctly after using change function', function (done) { 206 | var s = getNewSlider({ 207 | paused: true, 208 | delay: 0.4, 209 | duration: 0.2 210 | }, 5); 211 | 212 | var startTime = 0; 213 | var isTransitionTested; 214 | var nextIndex = s.slider.currentIndex() + 1; 215 | var timeEnoughToStartTransition = 500; 216 | var timeEnoughToEndTransition = 700; 217 | 218 | s.slider.change(1); 219 | 220 | function testChange(time) { 221 | if (!startTime) { 222 | startTime = time; 223 | } 224 | 225 | if ((time - startTime) > timeEnoughToEndTransition) { 226 | // Test values after finishing the first transition 227 | try { 228 | expect(s.container.children[0].style.left).toEqual('100%'); 229 | expect(s.container.children[1].style.left).toEqual('0%'); 230 | } catch (e) { 231 | console.error(e); 232 | } 233 | 234 | s.slider.dispose(); 235 | done(); 236 | return; 237 | } 238 | 239 | if (!isTransitionTested && (time - startTime) > timeEnoughToStartTransition) { 240 | // Internal index value is correct 241 | try { 242 | expect(s.slider.currentIndex()).toEqual(nextIndex); 243 | } catch (e) { 244 | console.error(e); 245 | } 246 | } 247 | 248 | requestAnimationFrame(testChange); 249 | } 250 | 251 | requestAnimationFrame(testChange); 252 | }); 253 | 254 | it('should not change values when using paused:true option', function (done) { 255 | var s = getNewSlider({ 256 | paused: true, 257 | delay: 0.4, 258 | duration: 0.2 259 | }, 5); 260 | 261 | var startTime = 0; 262 | var isTransitionTested; 263 | var startIndex = s.slider.currentIndex(); 264 | var timeEnoughToStartTransition = 500; 265 | var timeEnoughToEndTransition = 700; 266 | 267 | function testPausedOption(time) { 268 | if (!startTime) { 269 | startTime = time; 270 | } 271 | 272 | if ((time - startTime) > timeEnoughToEndTransition) { 273 | try { 274 | expect(s.container.children[0].style.left).toEqual('0%'); 275 | expect(s.container.children[1].style.left).toEqual('-100%'); 276 | } catch (e) { 277 | console.error(e); 278 | } 279 | 280 | s.slider.dispose(); 281 | 282 | done(); 283 | return; 284 | } 285 | 286 | if (!isTransitionTested && (time - startTime) > timeEnoughToStartTransition) { 287 | // index value is correct 288 | try { 289 | expect(s.slider.currentIndex()).toEqual(startIndex); 290 | } catch (e) { 291 | console.error(e); 292 | } 293 | 294 | isTransitionTested = true; 295 | } 296 | 297 | requestAnimationFrame(testPausedOption); 298 | } 299 | 300 | requestAnimationFrame(testPausedOption); 301 | }); 302 | 303 | it('should work well with just 2 slides', function (done) { 304 | var s; 305 | var changeCount = 0; 306 | var changeEndCount = 0; 307 | var startIndex; 308 | var nextIndex; 309 | var onChange = function () { 310 | if (changeCount === 0) { 311 | expect(s.slider.currentIndex()).toEqual(nextIndex); 312 | } else if (changeCount === 1) { 313 | // Internal index value should be start value again 314 | try { 315 | expect(s.slider.currentIndex()).toEqual(startIndex); 316 | } catch (e) { 317 | console.error(e); 318 | } 319 | } else { 320 | throw Error('Unexpected onChange invokation'); 321 | } 322 | 323 | changeCount++; 324 | }; 325 | 326 | var onChangeEnd = function () { 327 | if (changeEndCount === 0) { 328 | // Ensure values have changed 329 | try { 330 | expect(s.container.children[0].style.left).toEqual('100%'); 331 | expect(s.container.children[1].style.left).toEqual('0%'); 332 | } catch (e) { 333 | console.error(e); 334 | } 335 | } else if (changeEndCount === 1) { 336 | try { 337 | // Ensure values now hold initial values again 338 | expect(s.container.children[0].style.left).toEqual('0%'); 339 | expect(s.container.children[1].style.left).toEqual('100%'); 340 | } catch (e) { 341 | console.error(e); 342 | } 343 | 344 | s.slider.dispose(); 345 | 346 | done(); 347 | } else { 348 | throw Error('Unexpected onChangeEnd invokation'); 349 | } 350 | 351 | changeEndCount++; 352 | }; 353 | 354 | s = getNewSlider({ 355 | delay: 0.2, 356 | duration: 0.1, 357 | onChange: onChange, 358 | onChangeEnd: onChangeEnd 359 | }, 2); 360 | startIndex = s.slider.currentIndex(); 361 | nextIndex = s.slider.currentIndex() + 1; 362 | 363 | // Values should have correct initial values 364 | expect(s.container.children[0].style.left).toEqual('0%'); 365 | expect(s.container.children[1].style.left).toEqual('-100%'); 366 | }); 367 | 368 | it('should not swap slides when there is only one image', function (done) { 369 | var s = getNewSlider({ 370 | delay: 0.4, 371 | duration: 0.2 372 | }, 1); 373 | 374 | var startTime = 0; 375 | var isTransitionTested; 376 | var startIndex = s.slider.currentIndex(); 377 | var timeEnoughToStartTransition = 500; 378 | var timeEnoughToEndTransition = 700; 379 | 380 | function testOneImage(time) { 381 | if (!startTime) { 382 | startTime = time; 383 | } 384 | 385 | if ((time - startTime) > timeEnoughToEndTransition) { 386 | // Ensure values paused hold initial values after time enough to have changed 387 | try { 388 | expect(s.container.children[0].style.left).toEqual('0%'); 389 | } catch (e) { 390 | console.error(e); 391 | } 392 | 393 | s.slider.dispose(); 394 | 395 | done(); 396 | return; 397 | } 398 | 399 | if (!isTransitionTested && (time - startTime) > timeEnoughToStartTransition) { 400 | // Internal index value is correct 401 | try { 402 | expect(s.slider.currentIndex()).toEqual(startIndex); 403 | } catch (e) { 404 | console.error(e); 405 | } 406 | } 407 | 408 | requestAnimationFrame(testOneImage); 409 | } 410 | 411 | requestAnimationFrame(testOneImage); 412 | }); 413 | 414 | it('should handle z-index during transition', function (done) { 415 | var s = getNewSlider({ 416 | prop: 'width', 417 | init: 0, 418 | show: 500, 419 | end: 500, 420 | unit: 'px', 421 | delay: 0.2, 422 | duration: 0.1 423 | }, 5); 424 | 425 | // Simulates the state after a full carousel round 426 | var i = s.container.children.length; 427 | while (--i >= 0) { 428 | s.container.children[i].style.zIndex = 1; 429 | } 430 | 431 | var startTime = 0; 432 | var timeEnoughToStartTransition = 320; 433 | 434 | function testZIndex(time) { 435 | if (!startTime) { 436 | startTime = time; 437 | } 438 | 439 | if ((time - startTime) > timeEnoughToStartTransition) { 440 | try { 441 | expect( 442 | parseInt(s.container.children[1].style.zIndex) 443 | ).toBeGreaterThan( 444 | parseInt(s.container.children[0].style.zIndex) 445 | ); 446 | expect( 447 | parseInt(s.container.children[0].style.zIndex) 448 | ).toBeGreaterThan( 449 | parseInt(s.container.children[4].style.zIndex) 450 | ); 451 | } catch (e) { 452 | console.error(e); 453 | } 454 | 455 | s.slider.dispose(); 456 | done(); 457 | return; 458 | } 459 | 460 | requestAnimationFrame(testZIndex); 461 | } 462 | 463 | requestAnimationFrame(testZIndex); 464 | }); 465 | 466 | it('should allow transition to lower values than visible value', function (done) { 467 | var s = getNewSlider({ 468 | prop: 'left', 469 | init: 500, 470 | show: 0, 471 | end: -500, 472 | unit: 'px', 473 | delay: 0.5, 474 | duration: 0.4 475 | }, 5); 476 | 477 | var startTime = 0; 478 | var timeEnoughToHalftransition = 700; 479 | 480 | function testVisible(time) { 481 | if (!startTime) { 482 | startTime = time; 483 | } 484 | 485 | if ((time - startTime) > timeEnoughToHalftransition) { 486 | try { 487 | // Should be somewhere in the middle of animation values 488 | expect(parseInt(s.container.children[0].style.left, 10)).toBeLessThan(0); 489 | expect(parseInt(s.container.children[0].style.left, 10)).toBeGreaterThan(-500); 490 | expect(parseInt(s.container.children[1].style.left, 10)).toBeLessThan(500); 491 | expect(parseInt(s.container.children[1].style.left, 10)).toBeGreaterThan(0); 492 | } catch (e) { 493 | console.error(e); 494 | } 495 | 496 | s.slider.dispose(); 497 | 498 | done(); 499 | return; 500 | } 501 | 502 | requestAnimationFrame(testVisible); 503 | } 504 | 505 | requestAnimationFrame(testVisible); 506 | }); 507 | 508 | it('should allow opacity remove transition', function (done) { 509 | var s = getNewSlider({ 510 | prop: 'opacity', 511 | init: 0, 512 | show: 1, 513 | end: 0, 514 | unit: '', 515 | delay: 0.5, 516 | duration: 0.4 517 | }, 5); 518 | 519 | var startTime = 0; 520 | var timeEnoughToHalftransition = 700; 521 | 522 | function testOpacity(time) { 523 | if (!startTime) { 524 | startTime = time; 525 | } 526 | 527 | if ((time - startTime) > timeEnoughToHalftransition) { 528 | try { 529 | // Should be somewhere in the middle of remove animation 530 | expect(parseFloat(s.container.children[0].style.opacity)).toBeLessThan(1); 531 | expect(parseFloat(s.container.children[0].style.opacity)).toBeGreaterThan(0); 532 | } catch (e) { 533 | console.error(e); 534 | } 535 | 536 | s.slider.dispose(); 537 | 538 | done(); 539 | return; 540 | } 541 | 542 | requestAnimationFrame(testOpacity); 543 | } 544 | 545 | requestAnimationFrame(testOpacity); 546 | }); 547 | 548 | it('should be able to pause autoplay', function (done) { 549 | var s = getNewSlider({ 550 | paused: false, 551 | delay: 0.5, 552 | duration: 0.4 553 | }, 5); 554 | 555 | var isTransitionTested; 556 | var timeEnoughToHalftransition = 750; 557 | var timeEnoughToAnotherTransition = 980; 558 | var startTime = 0; 559 | 560 | expect(s.container.children[0].style.left).toEqual('0%'); 561 | expect(s.container.children[1].style.left).toEqual('-100%'); 562 | 563 | function testPause(time) { 564 | if (!startTime) { 565 | startTime = time; 566 | } else if ((time - startTime) > timeEnoughToAnotherTransition) { 567 | // after a long enough delay, left position of slide should stay the same 568 | try { 569 | expect(s.container.children[0].style.left).toEqual('100%'); 570 | expect(s.container.children[1].style.left).toEqual('0%'); 571 | } catch (e) { 572 | console.error(e); 573 | } 574 | 575 | s.slider.dispose(); 576 | 577 | done(); 578 | return; 579 | } else if (!isTransitionTested && (time - startTime) > timeEnoughToHalftransition) { 580 | s.slider.pause(); 581 | 582 | try { 583 | expect(parseInt(s.container.children[0].style.left, 10)).toBeLessThan(100); 584 | expect(parseInt(s.container.children[0].style.left, 10)).toBeGreaterThan(0); 585 | expect(parseInt(s.container.children[1].style.left, 10)).toBeLessThan(0); 586 | expect(parseInt(s.container.children[1].style.left, 10)).toBeGreaterThan(-100); 587 | } catch (e) { 588 | console.error(e); 589 | } 590 | 591 | isTransitionTested = true; 592 | } 593 | 594 | requestAnimationFrame(testPause); 595 | } 596 | 597 | requestAnimationFrame(testPause); 598 | }); 599 | }); 600 | }); 601 | --------------------------------------------------------------------------------