├── .nvmrc ├── CNAME ├── .gitignore ├── icons ├── cnn.png ├── htc.png ├── nhl.png ├── airbnb.png ├── snapguide.png └── thinkgeek.png ├── left-arrow.png ├── right-arrow.png ├── swipe_bullet.png ├── component.json ├── Gruntfile.js ├── package.json ├── LICENSE.md ├── style.css ├── index.html ├── README.md └── swipe.js /.nvmrc: -------------------------------------------------------------------------------- 1 | 6 -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | -------------------------------------------------------------------------------- /icons/cnn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tes/Swipe/master/icons/cnn.png -------------------------------------------------------------------------------- /icons/htc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tes/Swipe/master/icons/htc.png -------------------------------------------------------------------------------- /icons/nhl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tes/Swipe/master/icons/nhl.png -------------------------------------------------------------------------------- /left-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tes/Swipe/master/left-arrow.png -------------------------------------------------------------------------------- /right-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tes/Swipe/master/right-arrow.png -------------------------------------------------------------------------------- /icons/airbnb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tes/Swipe/master/icons/airbnb.png -------------------------------------------------------------------------------- /swipe_bullet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tes/Swipe/master/swipe_bullet.png -------------------------------------------------------------------------------- /icons/snapguide.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tes/Swipe/master/icons/snapguide.png -------------------------------------------------------------------------------- /icons/thinkgeek.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tes/Swipe/master/icons/thinkgeek.png -------------------------------------------------------------------------------- /component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "swipe-js", 3 | "version": "2.0.0", 4 | "main": ["./swipe.js"], 5 | "author": "Brad Birdsall", 6 | "license": "MIT", 7 | "ignore": [ 8 | "**/*.html", 9 | "**/*.css", 10 | "Gruntfile.js" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /*global module */ 2 | module.exports = function( grunt ) { 3 | 4 | 'use strict'; 5 | 6 | grunt.initConfig({ 7 | 8 | uglify: { 9 | options: { 10 | mangle: { 11 | except: ['Swipe'] 12 | } 13 | }, 14 | dist: { 15 | files: { 16 | 'build/swipe.min.js': 'swipe.js' 17 | } 18 | } 19 | } 20 | 21 | }); 22 | 23 | // build 24 | grunt.loadNpmTasks('grunt-contrib-uglify'); 25 | grunt.registerTask('build', 'uglify'); 26 | grunt.registerTask('default', 'build'); 27 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tes-swipe-js", 3 | "version": "0.0.2", 4 | "main": "./swipe.js", 5 | "dependencies": { 6 | "jquery": "^3.4.0" 7 | }, 8 | "devDependencies": { 9 | "grunt": "~0.4.5", 10 | "grunt-contrib-uglify": "~0.8.0" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/tes/Swipe" 15 | }, 16 | "keywords": [ 17 | "ecosystem:jquery", 18 | "jquery-plugin", 19 | "swipe", 20 | "slider", 21 | "touch" 22 | ], 23 | "engines": { 24 | "node": ">=0.8.0" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Brad Birdsall and Xiuyu Li 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. -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, del, dfn, em, img, ins, kbd, q, samp, small, strong, b, i, dl, dt, dd, ol, ul, li, fieldset, form, label, table, tbody, tfoot, thead, tr, th, td, article, aside, footer, header, nav, section { 2 | margin:0; 3 | padding:0; 4 | border:0; 5 | outline:0; 6 | font-size:100%; 7 | vertical-align:baseline; 8 | background:transparent; 9 | } 10 | body { 11 | -webkit-text-size-adjust:none; 12 | font-family:sans-serif; 13 | min-height:416px; 14 | } 15 | h1 { 16 | font-size:33px; 17 | margin:50px 0 15px; 18 | text-align:center; 19 | color:#212121; 20 | } 21 | h2 { 22 | font-size:14px; 23 | font-weight:bold; 24 | color:#3c3c3c; 25 | margin:20px 10px 10px; 26 | } 27 | small { 28 | margin:0 10px 30px; 29 | display:block; 30 | font-size:12px; 31 | } 32 | a { 33 | margin:0 0 0 10px; 34 | font-size:12px; 35 | color:#3c3c3c; 36 | } 37 | 38 | 39 | html, body { 40 | background: #f3f3f3; 41 | } 42 | 43 | #console { 44 | font-size: 12px; 45 | font-family:"Inconsolata", "Monaco", "Consolas", "Andale Mono", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace; 46 | color: #999; 47 | line-height: 18px; 48 | margin-top: 20px; 49 | max-height: 150px; 50 | overflow: auto; 51 | } 52 | 53 | #mySwipe div b { 54 | display:block; 55 | font-weight:bold; 56 | color:#14ADE5; 57 | font-size:20px; 58 | text-align:center; 59 | margin:10px; 60 | padding:100px 10px; 61 | box-shadow: 0 1px #EBEBEB; 62 | background: #fff; 63 | border-radius: 3px; 64 | border: 1px solid; 65 | border-color: #E5E5E5 #D3D3D3 #B9C1C6; 66 | } 67 | 68 | 69 | 70 | /*************************** SWIPE NAVIGATION ********************/ 71 | 72 | #swipe_direction_nav { 73 | display: table; 74 | margin: 0 auto; 75 | position: relative; 76 | text-align: center; 77 | } 78 | 79 | #swipe_direction_nav a { 80 | display: inline-block; 81 | width: 9px; 82 | height: 18px; 83 | text-indent: -9999px; 84 | margin: 0 30px; 85 | } 86 | 87 | #swipe_direction_nav .swipe-slider-next { 88 | background-position: 100% 0; 89 | } 90 | 91 | #swipe_bullet_wrapper { 92 | text-align: center; 93 | z-index: 10; 94 | float:left; 95 | } 96 | 97 | #swipe_bullet_wrapper a { 98 | display: inline-block; 99 | width: 14px; 100 | height: 14px; 101 | background: url('swipe_bullet.png') no-repeat 0 0; 102 | text-indent: -9999px; 103 | margin: 0 2px; 104 | background-position: 0 55%; 105 | } 106 | #swipe_bullet_wrapper a.active { 107 | background-position: 0 0; 108 | } 109 | 110 | .swipe-slider-prev { 111 | background-image: url("left-arrow.png"); 112 | float:left; 113 | } 114 | 115 | .swipe-slider-next { 116 | background-image: url("right-arrow.png"); 117 | float:left; 118 | } 119 | 120 | /*************************** SWIPE NAVIGATION ********************/ -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Swipe 2 5 | 6 | 7 | 8 | 30 | 31 | 32 | 33 | 34 |

Swipe 2

35 | 36 |
37 |
38 |
0
39 |
1
40 |
2
41 |
3
42 |
4
43 |
5
44 |
6
45 |
7
46 |
8
47 |
9
48 |
10
49 |
11
50 |
12
51 |
13
52 |
14
53 |
15
54 |
16
55 |
17
56 |
18
57 |
19
58 |
20
59 |
60 | 61 |
62 | 63 | 64 |
65 | Prev 66 |
67 | Next 68 |
69 | 70 | 71 | 72 |
73 | 74 | 75 | 76 | 77 |
78 | 79 |
80 |
Pure Javascript
81 |
 82 |     var element = document.getElementById('mySwipe');
 83 |     window.mySwipe = Swipe(element, {
 84 |       startSlide: 0,
 85 |       auto: 3000,
 86 |       autoRestart: true,
 87 |       continuous: true,
 88 |       disableScroll: true,
 89 |       stopPropagation: true,
 90 |       callback: function(index, element) {},
 91 |       transitionEnd: function(index, element) {}
 92 |     });
 93 |   
94 |
95 | 96 | 97 |
98 |
With jQuery or Zepto
99 |
100 |     window.mySwipe = $('#mySwipe').Swipe().data('Swipe');
101 |   
102 |
103 | 104 | 105 | 106 | 107 | 131 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Swipe 2 | 3 | Swipe is the most accurate touch slider and extremely lightweight (only 5kb for minified version) 4 | 5 | ## Note 6 | 7 | Swipe is originally created by **[Brad Birdsall](https://github.com/thebird)** who seems not maintainning it, and this version is maintain by **[Xiuyu Li](https://github.com/nickleefly)** with new features and bugfix. 8 | 9 | ## Install 10 | 11 | You can directly install this package via Bower: `npm install swipe-js` 12 | 13 | 14 | ## Demo 15 | 16 | See [online example](http://nickleefly.github.io/swipe/) 17 | 18 | ## Usage 19 | Swipe only needs to follow a simple pattern. Here is an example: 20 | 21 | ``` html 22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | ``` 30 | 31 | Above is the initial required structure– a series of elements wrapped in two containers. Place any content you want within the items. The containing div will need to be passed to the Swipe function like so: 32 | 33 | ``` js 34 | window.mySwipe = Swipe(document.getElementById('slider')); 35 | ``` 36 | 37 | I always place this at the bottom of the page, externally, to verify the page is ready. 38 | 39 | Also Swipe needs just a few styles added to your stylesheet: 40 | 41 | ``` css 42 | .swipe { 43 | overflow: hidden; 44 | visibility: hidden; 45 | position: relative; 46 | } 47 | .swipe-wrap { 48 | overflow: hidden; 49 | position: relative; 50 | } 51 | .swipe-wrap > div { 52 | float:left; 53 | width:100%; 54 | position: relative; 55 | } 56 | ``` 57 | 58 | ## Swipe now supports bullets and arrow navigation 59 | 60 | If you want to add arrows and bullets to your swipe wonder you would do something similar to this, but you can structure it as you like, this is just an example, but you can see in action if you open the index.html file: 61 | 62 | ``` html 63 |
64 | Prev 65 |
66 | Next 67 |
68 | ``` 69 | 70 | Also when you click on a bullet Swipe will slide to that specific section. Neat! 71 | 72 | Go ahead and give the demo a try and see how amazing Swipe is. 73 | 74 | Built-in bullet support and arrow navigation contributed by [Alex Budin](https://github.com/alex-b). 75 | 76 | 77 | 78 | ## Config Options 79 | 80 | Swipe can take an optional second parameter– an object of key/value settings: 81 | 82 | - **startSlide** Integer *(default:0)* - index position Swipe should start at 83 | 84 | - **speed** Integer *(default:300)* - speed of prev and next transitions in milliseconds. 85 | 86 | - **auto** Integer - begin with auto slideshow (time in milliseconds between slides) 87 | 88 | - **continuous** Boolean *(default:true)* - create an infinite feel with no endpoints 89 | 90 | - **disableScroll** Boolean *(default:false)* - stop any touches on this container from scrolling the page 91 | 92 | - **stopPropagation** Boolean *(default:false)* - stop event propagation 93 | 94 | - **callback** Function - runs at slide change. 95 | 96 | - **transitionEnd** Function - runs at the end slide transition. 97 | 98 | - **btnNextId** Function - the ID of the element that you want to trigger the next slide. 99 | 100 | - **btnPrevId** Function - the ID of the element that you want to trigger the previous slide. 101 | 102 | - **bulletWrapperId** Function - the wrapper that will contain the bullets related to your slides (you can style it at your free will in your CSS). 103 | 104 | - **bulletClass** Function - the CSS class that you have created for your bullet, if not specified a default 'swipe-bullet' will be used. 105 | 106 | - **bulletActiveClass** Function - the CSS class that represents the active state of your bullet, if not specified a default 'active' class will be used. 107 | 108 | ### Example 109 | 110 | ``` js 111 | 112 | window.mySwipe = new Swipe(document.getElementById('slider'), { 113 | startSlide: 2, 114 | speed: 400, 115 | auto: 3000, 116 | continuous: true, 117 | disableScroll: false, 118 | stopPropagation: false, 119 | callback: function(index, elem) {}, 120 | transitionEnd: function(index, elem) {}, 121 | btnNextId : 'swipe_next', 122 | btnPrevId : 'swipe_prev', 123 | bulletWrapperId : 'swipe_bullet_wrapper', 124 | bulletClass : 'swipe-bullet', 125 | bulletActiveClass : 'active', 126 | }); 127 | 128 | ``` 129 | 130 | ## Swipe API 131 | 132 | Swipe exposes a few functions that can be useful for script control of your slider. 133 | 134 | `prev()` slide to prev 135 | 136 | `next()` slide to next 137 | 138 | `getPos()` returns current slide index position 139 | 140 | `getNumSlides()` returns the total amount of slides 141 | 142 | `slide(index, duration)` slide to set index position (duration: speed of transition in milliseconds) 143 | 144 | `restart()` restart the auto slide 145 | 146 | `stop()` stop the auto slide 147 | 148 | `kill()` remove swipe totally 149 | 150 | ## Browser Support 151 | Swipe is now compatible with all browsers, including IE7+. Swipe works best on devices that support CSS transforms and touch, but can be used without these as well. A few helper methods determine touch and CSS transition support and choose the proper animation methods accordingly. 152 | 153 | ## Who's using Swipe 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | Shoot me a [note](mailto:nickleefly@gmail.com) if you want your logo here 162 | 163 | ## License 164 | Copyright (c) 2015 Brad Birdsall Licensed under the [The MIT License (MIT)](http://opensource.org/licenses/MIT). 165 | -------------------------------------------------------------------------------- /swipe.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Swipe 2.0 3 | * 4 | * Brad Birdsall and Xiuyu Li 5 | * Copyright 2015, MIT License 6 | * 7 | */ 8 | 9 | function Swipe(container, options) { 10 | 11 | "use strict"; 12 | 13 | // utilities 14 | var noop = function() {}; // simple no operation function 15 | var offloadFn = function(fn) { setTimeout(fn || noop, 0) }; // offload a functions execution 16 | 17 | // check browser capabilities 18 | var browser = { 19 | addEventListener: !!window.addEventListener, 20 | touch: ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch, 21 | transitions: (function(temp) { 22 | var props = ['transitionProperty', 'WebkitTransition', 'MozTransition', 'OTransition', 'msTransition']; 23 | for ( var i in props ) if (temp.style[ props[i] ] !== undefined) return true; 24 | return false; 25 | })(document.createElement('swipe')) 26 | }; 27 | 28 | // quit if no root element 29 | if (!container) return; 30 | var element = container.children[0]; 31 | var slides, slidePos, width, length; 32 | options = options || {}; 33 | options.bulletWrapperClass = options.bulletWrapperClass ? '.' + options.bulletWrapperClass : ''; 34 | var bulletWrapper = options.bulletWrapperClass || null; 35 | var bulletClass = options.bulletClass || 'swipe-bullet'; 36 | var activeBulletClass = options.bulletActiveClass || 'active'; 37 | var index = parseInt(options.startSlide, 10) || 0; 38 | var position = index; 39 | var clonedSlides = false; 40 | var speed = options.speed || 300; 41 | options.continuous = options.continuous !== undefined ? options.continuous : true; 42 | 43 | // AutoRestart option: auto restart slideshow after user's touch event 44 | options.autoRestart = options.autoRestart !== undefined ? options.autoRestart : true; 45 | 46 | //add a css class to a dom element 47 | function addClassToElem(elem,value){ 48 | var rspaces = /\s+/; 49 | var classNames = (value || "").split( rspaces ); 50 | var className = " " + elem.className + " ", 51 | setClass = elem.className; 52 | for ( var c = 0, cl = classNames.length; c < cl; c++ ) { 53 | 54 | if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) { 55 | setClass += " " + classNames[c]; 56 | } 57 | 58 | } 59 | elem.className = setClass.replace(/^\s+|\s+$/g,''); //trim 60 | } 61 | 62 | //remove a css class from a dom element 63 | function removeClassFromElem(elem,value){ 64 | var rspaces = /\s+/; 65 | var rclass = /[\n\t]/g 66 | var classNames = (value || "").split( rspaces ); 67 | var className = (" " + elem.className + " ").replace(rclass, " "); 68 | 69 | for ( var c = 0, cl = classNames.length; c < cl; c++ ) { 70 | className = className.replace(" " + classNames[c] + " ", " "); 71 | } 72 | 73 | elem.className = className.replace(/^\s+|\s+$/g,''); //trim 74 | } 75 | 76 | 77 | function addEventHandler(elem,eventType,handler) { 78 | if (elem.addEventListener) 79 | elem.addEventListener (eventType,handler,false); 80 | else if (elem.attachEvent) 81 | elem.attachEvent ('on'+eventType,handler); 82 | } 83 | 84 | // get the target of a click event, we need this so we have support for multiple browsers 85 | function getTarget(e) { 86 | var clickTarget; 87 | if (e.target) clickTarget = e.target; 88 | else if (e.srcElement) clickTarget = e.srcElement; 89 | if (clickTarget.nodeType == 3) // defeat Safari bug 90 | clickTarget = clickTarget.parentNode; 91 | 92 | return clickTarget; 93 | } 94 | 95 | function setup() { 96 | 97 | // cache slides 98 | slides = element.children; 99 | length = slides.length; 100 | 101 | // set continuous to false if only one slide 102 | if (slides.length < 2) options.continuous = false; 103 | 104 | //special case if two slides 105 | if (browser.transitions && options.continuous && slides.length < 3) { 106 | element.appendChild(slides[0].cloneNode(true)); 107 | element.appendChild(element.children[1].cloneNode(true)); 108 | slides = element.children; 109 | clonedSlides = true; 110 | } 111 | 112 | // create an array to store current positions of each slide 113 | slidePos = new Array(slides.length); 114 | 115 | // determine width of each slide 116 | width = container.getBoundingClientRect().width || container.offsetWidth; 117 | 118 | element.style.width = (slides.length * width) + 'px'; 119 | 120 | // stack elements 121 | var pos = slides.length; 122 | 123 | if (bulletWrapper != null) { 124 | 125 | var bulletWrapperObj = container.querySelector(bulletWrapper); 126 | var bulletInc = 0; 127 | bulletWrapperObj.innerHTML = ''; 128 | 129 | } 130 | 131 | while(pos--) { 132 | 133 | var slide = slides[pos]; 134 | 135 | slide.style.width = width + 'px'; 136 | slide.setAttribute('data-index', pos); 137 | 138 | if (clonedSlides === false || pos <= 1) { 139 | if (bulletWrapperObj) { 140 | bulletWrapperObj.innerHTML += '' + bulletInc + ''; 141 | bulletInc++; 142 | } 143 | } 144 | 145 | if (browser.transitions) { 146 | slide.style.left = (pos * -width) + 'px'; 147 | move(pos, index > pos ? -width : (index < pos ? width : 0), 0); 148 | } 149 | 150 | } 151 | 152 | // reposition elements before and after index 153 | if (options.continuous && browser.transitions) { 154 | move(circle(index-1), -width, 0); 155 | move(circle(index+1), width, 0); 156 | } 157 | 158 | if (!browser.transitions) element.style.left = (index * -width) + 'px'; 159 | 160 | container.style.visibility = 'visible'; 161 | container.className = container.className + ' initialised'; 162 | 163 | // Need to highlight the bullet assigned to the current slide 164 | // the setup function is called on window resize so we cant highlight the first bullet but the current index 165 | hightlightCurrentBullet(index); 166 | } 167 | 168 | function hightlightCurrentBullet(to){ 169 | if (bulletWrapper){ 170 | 171 | var bulletWrapperObj = container.querySelector(bulletWrapper); 172 | var childObj = bulletWrapperObj.childNodes; 173 | 174 | for (var i=0;i index, use to = -slides.length + to 218 | if (direction !== natural_direction) to = -direction * slides.length + to; 219 | 220 | } 221 | 222 | var diff = Math.abs(index-to) - 1; 223 | 224 | // move all the slides between index and to in the right direction 225 | while (diff--) move( circle((to > index ? to : index) - diff - 1), width * direction, 0); 226 | 227 | to = circle(to); 228 | 229 | move(index, width * direction, slideSpeed || speed); 230 | move(to, 0, slideSpeed || speed); 231 | 232 | if (options.continuous) move(circle(to - direction), -(width * direction), 0); // we need to get the next in place 233 | 234 | } else { 235 | 236 | to = circle(to); 237 | animate(index * -width, to * -width, slideSpeed || speed); 238 | //no fallback for a circular continuous if the browser does not accept transitions 239 | } 240 | 241 | index = to; 242 | 243 | position = clonedSlides ? (index % 2) : index; 244 | 245 | // Highlighting the current slide bullet 246 | hightlightCurrentBullet(position); 247 | 248 | offloadFn(options.callback && options.callback(position, slides[index])); 249 | } 250 | 251 | function move(index, dist, speed) { 252 | 253 | translate(index, dist, speed); 254 | slidePos[index] = dist; 255 | 256 | } 257 | 258 | function translate(index, dist, speed) { 259 | 260 | var slide = slides[index]; 261 | var style = slide && slide.style; 262 | 263 | if (!style) return; 264 | 265 | style.webkitTransitionDuration = 266 | style.MozTransitionDuration = 267 | style.msTransitionDuration = 268 | style.OTransitionDuration = 269 | style.transitionDuration = speed + 'ms'; 270 | 271 | style.webkitTransform = 'translate(' + dist + 'px,0)' + 'translateZ(0)'; 272 | style.msTransform = 273 | style.MozTransform = 274 | style.OTransform = 'translateX(' + dist + 'px)'; 275 | 276 | } 277 | 278 | function animate(from, to, speed) { 279 | 280 | // if not an animation, just reposition 281 | if (!speed) { 282 | 283 | element.style.left = to + 'px'; 284 | return; 285 | 286 | } 287 | 288 | var start = +new Date; 289 | 290 | var timer = setInterval(function() { 291 | 292 | var timeElap = +new Date - start; 293 | 294 | if (timeElap > speed) { 295 | 296 | element.style.left = to + 'px'; 297 | 298 | if (delay) begin(); 299 | 300 | options.transitionEnd && options.transitionEnd.call(event, position, slides[index]); 301 | 302 | clearInterval(timer); 303 | return; 304 | 305 | } 306 | 307 | element.style.left = (( (to - from) * (Math.floor((timeElap / speed) * 100) / 100) ) + from) + 'px'; 308 | 309 | }, 4); 310 | 311 | } 312 | 313 | // setup auto slideshow 314 | var delay = options.auto || 0; 315 | var interval; 316 | 317 | function begin() { 318 | 319 | interval = setTimeout(next, delay); 320 | 321 | } 322 | 323 | function stop() { 324 | 325 | delay = 0; 326 | clearTimeout(interval); 327 | 328 | } 329 | 330 | function restart() { 331 | stop(); 332 | delay = options.auto || 0; 333 | begin(); 334 | } 335 | 336 | // setup initial vars 337 | var start = {}; 338 | var delta = {}; 339 | var isScrolling; 340 | 341 | // setup event capturing 342 | var events = { 343 | 344 | handleEvent: function(event) { 345 | 346 | switch (event.type) { 347 | case 'touchstart': this.start(event); break; 348 | case 'touchmove': this.move(event); break; 349 | case 'touchend': offloadFn(this.end(event)); break; 350 | case 'webkitTransitionEnd': 351 | case 'msTransitionEnd': 352 | case 'oTransitionEnd': 353 | case 'otransitionend': 354 | case 'transitionend': offloadFn(this.transitionEnd(event)); break; 355 | case 'resize': offloadFn(setup); break; 356 | } 357 | 358 | if (options.stopPropagation) event.stopPropagation(); 359 | 360 | }, 361 | start: function(event) { 362 | 363 | var touches = event.touches[0]; 364 | 365 | // measure start values 366 | start = { 367 | 368 | // get initial touch coords 369 | x: touches.pageX, 370 | y: touches.pageY, 371 | 372 | // store time to determine touch duration 373 | time: +new Date 374 | 375 | }; 376 | 377 | // used for testing first move event 378 | isScrolling = undefined; 379 | 380 | // reset delta and end measurements 381 | delta = {}; 382 | 383 | // attach touchmove and touchend listeners 384 | element.addEventListener('touchmove', this, false); 385 | element.addEventListener('touchend', this, false); 386 | 387 | }, 388 | move: function(event) { 389 | 390 | // ensure swiping with one touch and not pinching 391 | if ( event.touches.length > 1 || event.scale && event.scale !== 1) return 392 | 393 | if (options.disableScroll) event.preventDefault(); 394 | 395 | var touches = event.touches[0]; 396 | 397 | // measure change in x and y 398 | delta = { 399 | x: touches.pageX - start.x, 400 | y: touches.pageY - start.y 401 | }; 402 | 403 | // determine if scrolling test has run - one time test 404 | if ( typeof isScrolling === 'undefined') { 405 | isScrolling = !!( isScrolling || Math.abs(delta.x) < Math.abs(delta.y) ); 406 | } 407 | 408 | // if user is not trying to scroll vertically 409 | if (!isScrolling) { 410 | 411 | // prevent native scrolling 412 | event.preventDefault(); 413 | 414 | // stop slideshow 415 | stop(); 416 | 417 | // increase resistance if first or last slide 418 | if (options.continuous) { // we don't add resistance at the end 419 | 420 | translate(circle(index-1), delta.x + slidePos[circle(index-1)], 0); 421 | translate(index, delta.x + slidePos[index], 0); 422 | translate(circle(index+1), delta.x + slidePos[circle(index+1)], 0); 423 | 424 | } else { 425 | 426 | delta.x = 427 | delta.x / 428 | ( (!index && delta.x > 0 // if first slide and sliding left 429 | || index == slides.length - 1 // or if last slide and sliding right 430 | && delta.x < 0 // and if sliding at all 431 | ) ? 432 | ( Math.abs(delta.x) / width + 1 ) // determine resistance level 433 | : 1 ); // no resistance if false 434 | 435 | // translate 1:1 436 | translate(index-1, delta.x + slidePos[index-1], 0); 437 | translate(index, delta.x + slidePos[index], 0); 438 | translate(index+1, delta.x + slidePos[index+1], 0); 439 | } 440 | 441 | } 442 | 443 | }, 444 | end: function(event) { 445 | 446 | // measure duration 447 | var duration = +new Date - start.time; 448 | 449 | // determine if slide attempt triggers next/prev slide 450 | var isValidSlide = 451 | Number(duration) < 250 // if slide duration is less than 250ms 452 | && Math.abs(delta.x) > 20 // and if slide amt is greater than 20px 453 | || Math.abs(delta.x) > width/2; // or if slide amt is greater than half the width 454 | 455 | // determine if slide attempt is past start and end 456 | var isPastBounds = 457 | !index && delta.x > 0 // if first slide and slide amt is greater than 0 458 | || index == slides.length - 1 && delta.x < 0; // or if last slide and slide amt is less than 0 459 | 460 | if (options.continuous) isPastBounds = false; 461 | 462 | // determine direction of swipe (true:right, false:left) 463 | var direction = delta.x < 0; 464 | 465 | // if not scrolling vertically 466 | if (!isScrolling) { 467 | 468 | if (isValidSlide && !isPastBounds) { 469 | 470 | if (direction) { 471 | 472 | if (options.continuous) { // we need to get the next in this direction in place 473 | 474 | move(circle(index-1), -width, 0); 475 | move(circle(index+2), width, 0); 476 | 477 | } else { 478 | move(index-1, -width, 0); 479 | } 480 | 481 | move(index, slidePos[index]-width, speed); 482 | move(circle(index+1), slidePos[circle(index+1)]-width, speed); 483 | index = circle(index+1); 484 | 485 | } else { 486 | if (options.continuous) { // we need to get the next in this direction in place 487 | 488 | move(circle(index+1), width, 0); 489 | move(circle(index-2), -width, 0); 490 | 491 | } else { 492 | move(index+1, width, 0); 493 | } 494 | 495 | move(index, slidePos[index]+width, speed); 496 | move(circle(index-1), slidePos[circle(index-1)]+width, speed); 497 | index = circle(index-1); 498 | 499 | } 500 | 501 | position = clonedSlides ? (index % 2) : index; 502 | options.callback && options.callback(position, slides[index]); 503 | 504 | } else { 505 | 506 | if (options.continuous) { 507 | 508 | move(circle(index-1), -width, speed); 509 | move(index, 0, speed); 510 | move(circle(index+1), width, speed); 511 | 512 | } else { 513 | 514 | move(index-1, -width, speed); 515 | move(index, 0, speed); 516 | move(index+1, width, speed); 517 | } 518 | 519 | } 520 | 521 | } 522 | 523 | // kill touchmove and touchend event listeners until touchstart called again 524 | element.removeEventListener('touchmove', events, false) 525 | element.removeEventListener('touchend', events, false) 526 | 527 | // we need to highlight the bullet when we swipe the slide, not just on next or prev 528 | hightlightCurrentBullet(index); 529 | }, 530 | transitionEnd: function(event) { 531 | 532 | if (parseInt(event.target.getAttribute('data-index'), 10) == index) { 533 | 534 | if (delay || options.autoRestart) restart(); 535 | 536 | options.transitionEnd && options.transitionEnd.call(event, position, slides[index]); 537 | 538 | } 539 | 540 | } 541 | 542 | }; 543 | 544 | // trigger setup 545 | setup(); 546 | 547 | // start auto slideshow if applicable 548 | if (delay) begin(); 549 | 550 | 551 | // add event listeners 552 | if (browser.addEventListener) { 553 | 554 | 555 | // We need to add the listeners on the prev and next buttons if the specific DOM ids were sent when the Swipe object was instantiated 556 | // We keep them separated because a user might want to add only one button (either next or prev) 557 | if (options.btnNextClass) { 558 | 559 | var nextButton = container.querySelector(options.btnNextClass); 560 | 561 | addEventHandler(nextButton,"click",function(e){ 562 | e.preventDefault(); 563 | offloadFn(stop.call()); 564 | offloadFn(next.call()); 565 | }); 566 | 567 | } 568 | 569 | if (options.btnPrevClass) { 570 | 571 | var prevButton = container.querySelector(options.btnPrevClass); 572 | 573 | addEventHandler(prevButton,"click",function(e){ 574 | e.preventDefault(); 575 | offloadFn(stop.call()); 576 | offloadFn(prev.call()); 577 | }); 578 | 579 | } 580 | 581 | if (options.bulletWrapperClass) { 582 | 583 | var bulletWrap = container.querySelector(options.bulletWrapperClass); 584 | 585 | addEventHandler(bulletWrap,"click",function(e){ 586 | if (getTarget(e).innerHTML) { 587 | var slideNumber = parseInt(getTarget(e).innerHTML); 588 | if (!isNaN(slideNumber)) { 589 | e.preventDefault(); 590 | offloadFn(stop.call()); 591 | offloadFn(slide(slideNumber)); 592 | } 593 | } 594 | }); 595 | 596 | } 597 | 598 | 599 | // set touchstart event on element 600 | if (browser.touch) element.addEventListener('touchstart', events, false); 601 | 602 | if (browser.transitions) { 603 | element.addEventListener('webkitTransitionEnd', events, false); 604 | element.addEventListener('msTransitionEnd', events, false); 605 | element.addEventListener('oTransitionEnd', events, false); 606 | element.addEventListener('otransitionend', events, false); 607 | element.addEventListener('transitionend', events, false); 608 | } 609 | 610 | // set resize event on window 611 | window.addEventListener('resize', events, false); 612 | 613 | } else { 614 | 615 | window.onresize = function () { setup() }; // to play nice with old IE 616 | 617 | } 618 | 619 | // expose the Swipe API 620 | return { 621 | setup: function() { 622 | 623 | setup(); 624 | 625 | }, 626 | slide: function(to, speed) { 627 | 628 | // cancel slideshow 629 | stop(); 630 | 631 | slide(to, speed); 632 | 633 | }, 634 | prev: function() { 635 | 636 | // cancel slideshow 637 | stop(); 638 | 639 | prev(); 640 | 641 | }, 642 | next: function() { 643 | 644 | // cancel slideshow 645 | stop(); 646 | 647 | next(); 648 | 649 | }, 650 | restart: function() { 651 | 652 | // Restart slideshow 653 | restart(); 654 | 655 | }, 656 | stop: function() { 657 | 658 | // cancel slideshow 659 | stop(); 660 | 661 | }, 662 | getPos: function() { 663 | 664 | // return current index position 665 | return position; 666 | 667 | }, 668 | getNumSlides: function() { 669 | 670 | // return total number of slides 671 | return length; 672 | }, 673 | kill: function() { 674 | 675 | // cancel slideshow 676 | stop(); 677 | 678 | // reset element 679 | element.style.width = ''; 680 | element.style.left = ''; 681 | 682 | // reset slides 683 | var pos = slides.length; 684 | while (pos--) { 685 | 686 | var slide = slides[pos]; 687 | slide.style.width = ''; 688 | slide.style.left = ''; 689 | 690 | if (browser.transitions) translate(pos, 0, 0); 691 | 692 | } 693 | 694 | // removed event listeners 695 | if (browser.addEventListener) { 696 | 697 | // remove current event listeners 698 | element.removeEventListener('touchstart', events, false); 699 | element.removeEventListener('webkitTransitionEnd', events, false); 700 | element.removeEventListener('msTransitionEnd', events, false); 701 | element.removeEventListener('oTransitionEnd', events, false); 702 | element.removeEventListener('otransitionend', events, false); 703 | element.removeEventListener('transitionend', events, false); 704 | window.removeEventListener('resize', events, false); 705 | 706 | } 707 | else { 708 | 709 | window.onresize = null; 710 | 711 | } 712 | 713 | } 714 | } 715 | 716 | } 717 | 718 | 719 | if ( window.jQuery || window.Zepto ) { 720 | (function($) { 721 | $.fn.Swipe = function(params) { 722 | return this.each(function() { 723 | $(this).data('Swipe', new Swipe($(this)[0], params)); 724 | }); 725 | } 726 | })( window.jQuery || window.Zepto ) 727 | } 728 | 729 | if(typeof module === "object" && typeof module.exports === "object") { 730 | module.exports = Swipe; 731 | } else { 732 | window.Swipe = Swipe; 733 | } 734 | --------------------------------------------------------------------------------