|
111 | 111 |
|
112 | 112 | // CHECK SUPPORT |
113 | 113 | var body = document.body; |
114 | | - |
115 | | - var ua = navigator.userAgent.toLowerCase(); |
116 | 114 | var impressSupported = |
117 | 115 |
|
118 | 116 | // Browser should support CSS 3D transtorms |
119 | 117 | ( pfx( "perspective" ) !== null ) && |
120 | 118 |
|
121 | | - // Browser should support `classList` and `dataset` APIs |
| 119 | + // And `classList` and `dataset` APIs |
122 | 120 | ( body.classList ) && |
123 | | - ( body.dataset ) && |
124 | | - |
125 | | - // But some mobile devices need to be blacklisted, |
126 | | - // because their CSS 3D support or hardware is not |
127 | | - // good enough to run impress.js properly, sorry... |
128 | | - ( ua.search( /(iphone)|(ipod)|(android)/ ) === -1 ); |
| 121 | + ( body.dataset ); |
129 | 122 |
|
130 | 123 | if ( !impressSupported ) { |
131 | 124 |
|
|
175 | 168 | goto: empty, |
176 | 169 | prev: empty, |
177 | 170 | next: empty, |
| 171 | + swipe: empty, |
178 | 172 | tear: empty, |
179 | 173 | lib: {} |
180 | 174 | }; |
|
583 | 577 | return goto( next, undefined, "next", origEvent ); |
584 | 578 | }; |
585 | 579 |
|
| 580 | + // Swipe for touch devices by @and3rson. |
| 581 | + // Below we extend the api to control the animation between the currently |
| 582 | + // active step and a presumed next/prev step. See touch plugin for |
| 583 | + // an example of using this api. |
| 584 | + |
| 585 | + // Helper function |
| 586 | + var interpolate = function( a, b, k ) { |
| 587 | + return a + ( b - a ) * k; |
| 588 | + }; |
| 589 | + |
| 590 | + // Animate a swipe. |
| 591 | + // |
| 592 | + // Pct is a value between -1.0 and +1.0, designating the current length |
| 593 | + // of the swipe. |
| 594 | + // |
| 595 | + // If pct is negative, swipe towards the next() step, if positive, |
| 596 | + // towards the prev() step. |
| 597 | + // |
| 598 | + // Note that pre-stepleave plugins such as goto can mess with what is a |
| 599 | + // next() and prev() step, so we need to trigger the pre-stepleave event |
| 600 | + // here, even if a swipe doesn't guarantee that the transition will |
| 601 | + // actually happen. |
| 602 | + // |
| 603 | + // Calling swipe(), with any value of pct, won't in itself cause a |
| 604 | + // transition to happen, this is just to animate the swipe. Once the |
| 605 | + // transition is committed - such as at a touchend event - caller is |
| 606 | + // responsible for also calling prev()/next() as appropriate. |
| 607 | + var swipe = function( pct ) { |
| 608 | + if ( Math.abs( pct ) > 1 ) { |
| 609 | + return; |
| 610 | + } |
| 611 | + |
| 612 | + // Prepare & execute the preStepLeave event |
| 613 | + var event = { target: activeStep, detail: {} }; |
| 614 | + event.detail.swipe = pct; |
| 615 | + |
| 616 | + // Will be ignored within swipe animation, but just in case a plugin wants to read this, |
| 617 | + // humor them |
| 618 | + event.detail.transitionDuration = config.transitionDuration; |
| 619 | + var idx; // Needed by jshint |
| 620 | + if ( pct < 0 ) { |
| 621 | + idx = steps.indexOf( activeStep ) + 1; |
| 622 | + event.detail.next = idx < steps.length ? steps[ idx ] : steps[ 0 ]; |
| 623 | + event.detail.reason = "next"; |
| 624 | + } else if ( pct > 0 ) { |
| 625 | + idx = steps.indexOf( activeStep ) - 1; |
| 626 | + event.detail.next = idx >= 0 ? steps[ idx ] : steps[ steps.length - 1 ]; |
| 627 | + event.detail.reason = "prev"; |
| 628 | + } else { |
| 629 | + |
| 630 | + // No move |
| 631 | + return; |
| 632 | + } |
| 633 | + if ( execPreStepLeavePlugins( event ) === false ) { |
| 634 | + |
| 635 | + // If a preStepLeave plugin wants to abort the transition, don't animate a swipe |
| 636 | + // For stop, this is probably ok. For substep, the plugin it self might want to do |
| 637 | + // some animation, but that's not the current implementation. |
| 638 | + return false; |
| 639 | + } |
| 640 | + var nextElement = event.detail.next; |
| 641 | + |
| 642 | + var nextStep = stepsData[ "impress-" + nextElement.id ]; |
| 643 | + |
| 644 | + // If the same step is re-selected, force computing window scaling, |
| 645 | + var nextScale = nextStep.scale * windowScale; |
| 646 | + var k = Math.abs( pct ); |
| 647 | + |
| 648 | + var interpolatedStep = { |
| 649 | + translate: { |
| 650 | + x: interpolate( currentState.translate.x, -nextStep.translate.x, k ), |
| 651 | + y: interpolate( currentState.translate.y, -nextStep.translate.y, k ), |
| 652 | + z: interpolate( currentState.translate.z, -nextStep.translate.z, k ) |
| 653 | + }, |
| 654 | + rotate: { |
| 655 | + x: interpolate( currentState.rotate.x, -nextStep.rotate.x, k ), |
| 656 | + y: interpolate( currentState.rotate.y, -nextStep.rotate.y, k ), |
| 657 | + z: interpolate( currentState.rotate.z, -nextStep.rotate.z, k ), |
| 658 | + |
| 659 | + // Unfortunately there's a discontinuity if rotation order changes. Nothing I |
| 660 | + // can do about it? |
| 661 | + order: k < 0.7 ? currentState.rotate.order : nextStep.rotate.order |
| 662 | + }, |
| 663 | + scale: interpolate( currentState.scale, nextScale, k ) |
| 664 | + }; |
| 665 | + |
| 666 | + css( root, { |
| 667 | + |
| 668 | + // To keep the perspective look similar for different scales |
| 669 | + // we need to 'scale' the perspective, too |
| 670 | + perspective: config.perspective / interpolatedStep.scale + "px", |
| 671 | + transform: scale( interpolatedStep.scale ), |
| 672 | + transitionDuration: "0ms", |
| 673 | + transitionDelay: "0ms" |
| 674 | + } ); |
| 675 | + |
| 676 | + css( canvas, { |
| 677 | + transform: rotate( interpolatedStep.rotate, true ) + |
| 678 | + translate( interpolatedStep.translate ), |
| 679 | + transitionDuration: "0ms", |
| 680 | + transitionDelay: "0ms" |
| 681 | + } ); |
| 682 | + }; |
| 683 | + |
586 | 684 | // Teardown impress |
587 | 685 | // Resets the DOM to the state it was before impress().init() was called. |
588 | 686 | // (If you called impress(rootId).init() for multiple different rootId's, then you must |
|
666 | 764 | goto: goto, |
667 | 765 | next: next, |
668 | 766 | prev: prev, |
| 767 | + swipe: swipe, |
669 | 768 | tear: tear, |
670 | 769 | lib: lib |
671 | 770 | } ); |
|
1701 | 1800 |
|
1702 | 1801 | } )( document, window ); |
1703 | 1802 |
|
| 1803 | + |
| 1804 | +/** |
| 1805 | + * Support for swipe and tap on touch devices |
| 1806 | + * |
| 1807 | + * This plugin implements navigation for plugin devices, via swiping left/right, |
| 1808 | + * or tapping on the left/right edges of the screen. |
| 1809 | + * |
| 1810 | + * |
| 1811 | + * |
| 1812 | + * Copyright 2015: Andrew Dunai (@and3rson) |
| 1813 | + * Modified to a plugin, 2016: Henrik Ingo (@henrikingo) |
| 1814 | + * |
| 1815 | + * MIT License |
| 1816 | + */ |
| 1817 | +/* global document, window */ |
| 1818 | +( function( document, window ) { |
| 1819 | + "use strict"; |
| 1820 | + |
| 1821 | + // Touch handler to detect swiping left and right based on window size. |
| 1822 | + // If the difference in X change is bigger than 1/20 of the screen width, |
| 1823 | + // we simply call an appropriate API function to complete the transition. |
| 1824 | + var startX = 0; |
| 1825 | + var lastX = 0; |
| 1826 | + var lastDX = 0; |
| 1827 | + var threshold = window.innerWidth / 20; |
| 1828 | + |
| 1829 | + document.addEventListener( "touchstart", function( event ) { |
| 1830 | + lastX = startX = event.touches[ 0 ].clientX; |
| 1831 | + } ); |
| 1832 | + |
| 1833 | + document.addEventListener( "touchmove", function( event ) { |
| 1834 | + var x = event.touches[ 0 ].clientX; |
| 1835 | + var diff = x - startX; |
| 1836 | + |
| 1837 | + // To be used in touchend |
| 1838 | + lastDX = lastX - x; |
| 1839 | + lastX = x; |
| 1840 | + |
| 1841 | + window.impress().swipe( diff / window.innerWidth ); |
| 1842 | + } ); |
| 1843 | + |
| 1844 | + document.addEventListener( "touchend", function() { |
| 1845 | + var totalDiff = lastX - startX; |
| 1846 | + if ( Math.abs( totalDiff ) > window.innerWidth / 5 && ( totalDiff * lastDX ) <= 0 ) { |
| 1847 | + if ( totalDiff > window.innerWidth / 5 && lastDX <= 0 ) { |
| 1848 | + window.impress().prev(); |
| 1849 | + } else if ( totalDiff < -window.innerWidth / 5 && lastDX >= 0 ) { |
| 1850 | + window.impress().next(); |
| 1851 | + } |
| 1852 | + } else if ( Math.abs( lastDX ) > threshold ) { |
| 1853 | + if ( lastDX < -threshold ) { |
| 1854 | + window.impress().prev(); |
| 1855 | + } else if ( lastDX > threshold ) { |
| 1856 | + window.impress().next(); |
| 1857 | + } |
| 1858 | + } else { |
| 1859 | + |
| 1860 | + // No movement - move (back) to the current slide |
| 1861 | + window.impress().goto( document.querySelector( "#impress .step.active" ) ); |
| 1862 | + } |
| 1863 | + } ); |
| 1864 | + |
| 1865 | + document.addEventListener( "touchcancel", function() { |
| 1866 | + |
| 1867 | + // Move (back) to the current slide |
| 1868 | + window.impress().goto( document.querySelector( "#impress .step.active" ) ); |
| 1869 | + } ); |
| 1870 | + |
| 1871 | +} )( document, window ); |
0 commit comments