github.com/jincm/wesharechain@v0.0.0-20210122032815-1537409ce26a/app/script/swipe.js (about) 1 /* 2 * Swipe 2.0 3 * 4 * Brad Birdsall 5 * Copyright 2013, MIT License 6 * 7 */ 8 9 function Swipe(container, options) { 10 11 "use strict"; 12 13 // utilities 14 var noop = function () { 15 }; // simple no operation function 16 var offloadFn = function (fn) { 17 setTimeout(fn || noop, 0) 18 }; // offload a functions execution 19 20 // check browser capabilities 21 var browser = { 22 addEventListener: !!window.addEventListener, 23 touch: ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch, 24 transitions: (function (temp) { 25 var props = ['transitionProperty', 'WebkitTransition', 'MozTransition', 'OTransition', 'msTransition']; 26 for (var i in props) if (temp.style[ props[i] ] !== undefined) return true; 27 return false; 28 })(document.createElement('swipe')) 29 }; 30 31 // quit if no root element 32 if (!container) return; 33 var element = container.children[0]; 34 var slides, slidePos, width, length; 35 options = options || {}; 36 var index = parseInt(options.startSlide, 10) || 0; 37 var speed = options.speed || 300; 38 options.continuous = options.continuous !== undefined ? options.continuous : true; 39 40 function setup() { 41 42 // cache slides 43 slides = element.children; 44 length = slides.length; 45 46 // set continuous to false if only one slide 47 if (slides.length < 2) options.continuous = false; 48 49 //special case if two slides 50 if (browser.transitions && options.continuous && slides.length < 3) { 51 element.appendChild(slides[0].cloneNode(true)); 52 element.appendChild(element.children[1].cloneNode(true)); 53 slides = element.children; 54 } 55 56 // create an array to store current positions of each slide 57 slidePos = new Array(slides.length); 58 59 // determine width of each slide 60 width = container.getBoundingClientRect().width || container.offsetWidth; 61 62 element.style.width = (slides.length * width) + 'px'; 63 64 // stack elements 65 var pos = slides.length; 66 while (pos--) { 67 68 var slide = slides[pos]; 69 70 slide.style.width = width + 'px'; 71 slide.setAttribute('data-index', pos); 72 73 if (browser.transitions) { 74 slide.style.left = (pos * -width) + 'px'; 75 move(pos, index > pos ? -width : (index < pos ? width : 0), 0); 76 } 77 78 } 79 80 // reposition elements before and after index 81 if (options.continuous && browser.transitions) { 82 move(circle(index - 1), -width, 0); 83 move(circle(index + 1), width, 0); 84 } 85 86 if (!browser.transitions) element.style.left = (index * -width) + 'px'; 87 88 container.style.visibility = 'visible'; 89 90 } 91 92 function prev() { 93 94 if (options.continuous) slide(index - 1); 95 else if (index) slide(index - 1); 96 97 } 98 99 function next() { 100 if (options.continuous) slide(index + 1); 101 else if (index < slides.length - 1) slide(index + 1); 102 103 } 104 105 function circle(index) { 106 107 // a simple positive modulo using slides.length 108 return (slides.length + (index % slides.length)) % slides.length; 109 110 } 111 112 function slide(to, slideSpeed) { 113 114 // do nothing if already on requested slide 115 if (index == to) return; 116 117 if (browser.transitions) { 118 119 var direction = Math.abs(index - to) / (index - to); // 1: backward, -1: forward 120 121 // get the actual position of the slide 122 if (options.continuous) { 123 var natural_direction = direction; 124 direction = -slidePos[circle(to)] / width; 125 126 // if going forward but to < index, use to = slides.length + to 127 // if going backward but to > index, use to = -slides.length + to 128 if (direction !== natural_direction) to = -direction * slides.length + to; 129 130 } 131 132 var diff = Math.abs(index - to) - 1; 133 134 // move all the slides between index and to in the right direction 135 while (diff--) move(circle((to > index ? to : index) - diff - 1), width * direction, 0); 136 137 to = circle(to); 138 139 move(index, width * direction, slideSpeed || speed); 140 move(to, 0, slideSpeed || speed); 141 142 if (options.continuous) move(circle(to - direction), -(width * direction), 0); // we need to get the next in place 143 144 } else { 145 146 to = circle(to); 147 animate(index * -width, to * -width, slideSpeed || speed); 148 //no fallback for a circular continuous if the browser does not accept transitions 149 } 150 151 index = to; 152 offloadFn(options.callback && options.callback(index, slides[index])); 153 } 154 155 function move(index, dist, speed) { 156 157 translate(index, dist, speed); 158 slidePos[index] = dist; 159 160 } 161 162 function translate(index, dist, speed) { 163 164 var slide = slides[index]; 165 var style = slide && slide.style; 166 167 if (!style) return; 168 169 style.webkitTransitionDuration = 170 style.MozTransitionDuration = 171 style.msTransitionDuration = 172 style.OTransitionDuration = 173 style.transitionDuration = speed + 'ms'; 174 175 style.webkitTransform = 'translate(' + dist + 'px,0)' + 'translateZ(0)'; 176 style.msTransform = 177 style.MozTransform = 178 style.OTransform = 'translateX(' + dist + 'px)'; 179 180 } 181 182 function animate(from, to, speed) { 183 184 // if not an animation, just reposition 185 if (!speed) { 186 187 element.style.left = to + 'px'; 188 return; 189 190 } 191 192 var start = +new Date; 193 194 var timer = setInterval(function () { 195 196 var timeElap = +new Date - start; 197 198 if (timeElap > speed) { 199 200 element.style.left = to + 'px'; 201 202 if (delay) begin(); 203 204 options.transitionEnd && options.transitionEnd.call(event, index, slides[index]); 205 206 clearInterval(timer); 207 return; 208 209 } 210 211 element.style.left = (( (to - from) * (Math.floor((timeElap / speed) * 100) / 100) ) + from) + 'px'; 212 213 }, 4); 214 215 } 216 217 // setup auto slideshow 218 var delay = options.auto || 0; 219 var interval; 220 221 function begin() { 222 223 interval = setTimeout(next, delay); 224 225 } 226 227 function stop() { 228 229 delay = 0; 230 clearTimeout(interval); 231 232 } 233 234 235 // setup initial vars 236 var start = {}; 237 var delta = {}; 238 var isScrolling; 239 240 // setup event capturing 241 var events = { 242 243 handleEvent: function (event) { 244 245 switch (event.type) { 246 case 'touchstart': 247 this.start(event); 248 break; 249 case 'touchmove': 250 this.move(event); 251 break; 252 case 'touchend': 253 this.end(event); 254 break; //modify by Alon Zhang 255 // case 'touchend': offloadFn(this.end(event)); break; 256 case 'webkitTransitionEnd': 257 case 'msTransitionEnd': 258 case 'oTransitionEnd': 259 case 'otransitionend': 260 case 'transitionend': 261 offloadFn(this.transitionEnd(event)); 262 break; 263 case 'resize': 264 offloadFn(setup); 265 break; 266 } 267 268 if (options.stopPropagation) event.stopPropagation(); 269 270 }, 271 start: function (event) { 272 273 var touches = event.touches[0]; 274 275 // measure start values 276 start = { 277 278 // get initial touch coords 279 x: touches.pageX, 280 y: touches.pageY, 281 282 // store time to determine touch duration 283 time: +new Date 284 285 }; 286 287 // used for testing first move event 288 isScrolling = undefined; 289 290 // reset delta and end measurements 291 delta = {}; 292 293 // attach touchmove and touchend listeners 294 element.addEventListener('touchmove', this, false); 295 element.addEventListener('touchend', this, false); 296 297 }, 298 move: function (event) { 299 300 // ensure swiping with one touch and not pinching 301 if (event.touches.length > 1 || event.scale && event.scale !== 1) return 302 303 if (options.disableScroll) event.preventDefault(); 304 305 var touches = event.touches[0]; 306 307 // measure change in x and y 308 delta = { 309 x: touches.pageX - start.x, 310 y: touches.pageY - start.y 311 } 312 313 // determine if scrolling test has run - one time test 314 if (typeof isScrolling == 'undefined') { 315 isScrolling = !!( isScrolling || Math.abs(delta.x) < Math.abs(delta.y) ); 316 } 317 318 // if user is not trying to scroll vertically 319 if (!isScrolling) { 320 321 // prevent native scrolling 322 event.preventDefault(); 323 324 // stop slideshow 325 stop(); 326 327 // increase resistance if first or last slide 328 if (options.continuous) { // we don't add resistance at the end 329 330 translate(circle(index - 1), delta.x + slidePos[circle(index - 1)], 0); 331 translate(index, delta.x + slidePos[index], 0); 332 translate(circle(index + 1), delta.x + slidePos[circle(index + 1)], 0); 333 334 } else { 335 336 delta.x = 337 delta.x / 338 ( (!index && delta.x > 0 // if first slide and sliding left 339 || index == slides.length - 1 // or if last slide and sliding right 340 && delta.x < 0 // and if sliding at all 341 ) ? 342 ( Math.abs(delta.x) / width + 1 ) // determine resistance level 343 : 1 ); // no resistance if false 344 345 // translate 1:1 346 translate(index - 1, delta.x + slidePos[index - 1], 0); 347 translate(index, delta.x + slidePos[index], 0); 348 translate(index + 1, delta.x + slidePos[index + 1], 0); 349 } 350 351 } 352 353 }, 354 end: function (event) { 355 356 // measure duration 357 var duration = +new Date - start.time; 358 359 // determine if slide attempt triggers next/prev slide 360 var isValidSlide = 361 Number(duration) < 250 // if slide duration is less than 250ms 362 && Math.abs(delta.x) > 20 // and if slide amt is greater than 20px 363 || Math.abs(delta.x) > width / 2; // or if slide amt is greater than half the width 364 365 // determine if slide attempt is past start and end 366 var isPastBounds = 367 !index && delta.x > 0 // if first slide and slide amt is greater than 0 368 || index == slides.length - 1 && delta.x < 0; // or if last slide and slide amt is less than 0 369 370 if (options.continuous) isPastBounds = false; 371 372 // determine direction of swipe (true:right, false:left) 373 var direction = delta.x < 0; 374 375 // if not scrolling vertically 376 if (!isScrolling) { 377 378 if (isValidSlide && !isPastBounds) { 379 380 if (direction) { 381 382 if (options.continuous) { // we need to get the next in this direction in place 383 384 move(circle(index - 1), -width, 0); 385 move(circle(index + 2), width, 0); 386 387 } else { 388 move(index - 1, -width, 0); 389 } 390 391 move(index, slidePos[index] - width, speed); 392 move(circle(index + 1), slidePos[circle(index + 1)] - width, speed); 393 index = circle(index + 1); 394 395 } else { 396 if (options.continuous) { // we need to get the next in this direction in place 397 398 move(circle(index + 1), width, 0); 399 move(circle(index - 2), -width, 0); 400 401 } else { 402 move(index + 1, width, 0); 403 } 404 405 move(index, slidePos[index] + width, speed); 406 move(circle(index - 1), slidePos[circle(index - 1)] + width, speed); 407 index = circle(index - 1); 408 409 } 410 411 options.callback && options.callback(index, slides[index]); 412 413 } else { 414 415 if (options.continuous) { 416 417 move(circle(index - 1), -width, speed); 418 move(index, 0, speed); 419 move(circle(index + 1), width, speed); 420 421 } else { 422 423 move(index - 1, -width, speed); 424 move(index, 0, speed); 425 move(index + 1, width, speed); 426 } 427 428 } 429 430 } 431 432 // kill touchmove and touchend event listeners until touchstart called again 433 element.removeEventListener('touchmove', events, false); 434 element.removeEventListener('touchend', events, false); 435 436 //add by Alon Zhang 437 //resume slide 438 delay = options.auto; 439 440 }, 441 transitionEnd: function (event) { 442 443 if (parseInt(event.target.getAttribute('data-index'), 10) == index) { 444 445 if (delay) begin(); 446 447 options.transitionEnd && options.transitionEnd.call(event, index, slides[index]); 448 449 } 450 451 } 452 453 } 454 455 // trigger setup 456 setup(); 457 458 // start auto slideshow if applicable 459 if (delay) begin(); 460 461 462 // add event listeners 463 if (browser.addEventListener) { 464 465 // set touchstart event on element 466 if (browser.touch) element.addEventListener('touchstart', events, false); 467 468 if (browser.transitions) { 469 element.addEventListener('webkitTransitionEnd', events, false); 470 element.addEventListener('msTransitionEnd', events, false); 471 element.addEventListener('oTransitionEnd', events, false); 472 element.addEventListener('otransitionend', events, false); 473 element.addEventListener('transitionend', events, false); 474 } 475 476 // set resize event on window 477 window.addEventListener('resize', events, false); 478 479 } else { 480 481 window.onresize = function () { 482 setup() 483 }; // to play nice with old IE 484 485 } 486 487 // expose the Swipe API 488 return { 489 setup: function () { 490 491 setup(); 492 493 }, 494 slide: function (to, speed) { 495 496 // cancel slideshow 497 stop(); 498 499 slide(to, speed); 500 501 }, 502 prev: function () { 503 504 // cancel slideshow 505 stop(); 506 507 prev(); 508 509 }, 510 next: function () { 511 512 // cancel slideshow 513 stop(); 514 515 next(); 516 517 }, 518 stop: function () { 519 520 // cancel slideshow 521 stop(); 522 523 }, 524 getPos: function () { 525 526 // return current index position 527 return index; 528 529 }, 530 getNumSlides: function () { 531 532 // return total number of slides 533 return length; 534 }, 535 kill: function () { 536 537 // cancel slideshow 538 stop(); 539 540 // reset element 541 element.style.width = ''; 542 element.style.left = ''; 543 544 // reset slides 545 var pos = slides.length; 546 while (pos--) { 547 548 var slide = slides[pos]; 549 slide.style.width = ''; 550 slide.style.left = ''; 551 552 if (browser.transitions) translate(pos, 0, 0); 553 554 } 555 556 // removed event listeners 557 if (browser.addEventListener) { 558 559 // remove current event listeners 560 element.removeEventListener('touchstart', events, false); 561 element.removeEventListener('webkitTransitionEnd', events, false); 562 element.removeEventListener('msTransitionEnd', events, false); 563 element.removeEventListener('oTransitionEnd', events, false); 564 element.removeEventListener('otransitionend', events, false); 565 element.removeEventListener('transitionend', events, false); 566 window.removeEventListener('resize', events, false); 567 568 } 569 else { 570 571 window.onresize = null; 572 573 } 574 575 } 576 } 577 578 } 579 580 581 if (window.jQuery || window.Zepto) { 582 (function ($) { 583 $.fn.Swipe = function (params) { 584 return this.each(function () { 585 $(this).data('Swipe', new Swipe($(this)[0], params)); 586 }); 587 } 588 })(window.jQuery || window.Zepto) 589 }