├── .npmignore ├── .gitignore ├── demo ├── script.js ├── demo.css └── demo.html ├── observer.js ├── webpack.config.js ├── package.json ├── readme.md ├── test └── test.html ├── index.html ├── helpers.js ├── draggable-backup.js ├── draggable.js ├── main.css ├── dist ├── slider-x.css ├── slider-x.scss └── slider-x.js └── main.js /.npmignore: -------------------------------------------------------------------------------- 1 | # Publish only dist/ folder 2 | /* 3 | /*/ 4 | !/dist/ 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | test/ 3 | .vscode/ 4 | 5 | yarn-error.log 6 | yarn.lock -------------------------------------------------------------------------------- /demo/script.js: -------------------------------------------------------------------------------- 1 | window.addEventListener('load', function() { 2 | console.log('Document Loaded!!') 3 | 4 | const $slides = document.querySelectorAll('.my-slide div') 5 | 6 | $slides.forEach(($sl, i) => { 7 | $sl.addEventListener('click', () => {console.log(`Clicked slide ${i}`)}) 8 | }) 9 | }) -------------------------------------------------------------------------------- /observer.js: -------------------------------------------------------------------------------- 1 | // class DOMObserver { 2 | export default class DOMObserver { 3 | constructor() { 4 | this.elems = new Map(); 5 | this.watch(); 6 | } 7 | observe(elem, callback) { 8 | if (typeof callback !== 'function') { 9 | throw 'Callback must be a function' 10 | } 11 | const rect = elem.getBoundingClientRect(); 12 | this.elems.set(elem, { rect, callback }); 13 | callback() 14 | } 15 | unobserve(elem) { 16 | this.elems.delete(elem); 17 | } 18 | watch() { 19 | requestAnimationFrame(() => { 20 | this.elems.forEach((value, key) => { 21 | const nr = key.getBoundingClientRect(); 22 | if (nr.width !== value.rect.width || nr.height !== value.rect.height) { 23 | value.callback(); 24 | value.rect = nr 25 | } 26 | this.watch(); 27 | }); 28 | }); 29 | } 30 | } -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | // const TerserPlugin = require('terser-webpack-plugin') 3 | 4 | module.exports = { 5 | // devtool: 'eval', 6 | mode: 'production', 7 | entry: './main.js', 8 | output: { 9 | path: path.resolve(__dirname, 'dist'), 10 | libraryTarget: 'umd', 11 | library: 'PageFly SliderX', 12 | filename: 'slider-x.js' 13 | }, 14 | module: { 15 | rules: [ 16 | { 17 | test: /\.js$/, 18 | exclude: /node_modules/, 19 | use: { 20 | loader: 'babel-loader', 21 | options: { 22 | presets: ['@babel/preset-env'], 23 | plugins: ['transform-class-properties'] 24 | } 25 | } 26 | } 27 | ] 28 | }, 29 | // optimization: { 30 | // minimizer: [ 31 | // new TerserPlugin({ 32 | // cache: true, 33 | // parallel: true, 34 | // sourceMap: true, // Must be set to true if using source-maps in production 35 | // }), 36 | // ], 37 | // }, 38 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "slider-x", 3 | "version": "15.8.1", 4 | "description": "Simple and awesome Slider for PageFly", 5 | "main": "dist/slider-x.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/sellersmith/slider-x" 12 | }, 13 | "author": "SellerSmith Team", 14 | "license": "ISC", 15 | "bugs": { 16 | "url": "https://github.com/sellersmith/slider-x/issues" 17 | }, 18 | "homepage": "https://github.com/sellersmith/slider-x#readme", 19 | "keywords": [ 20 | "JS Library", 21 | "JS Plugin" 22 | ], 23 | "devDependencies": { 24 | "@babel/core": "^7.1.2", 25 | "@babel/preset-env": "^7.1.0", 26 | "@babel/preset-es2017": "^7.0.0-beta.53", 27 | "babel-loader": "^8.0.4", 28 | "babel-plugin-transform-class-properties": "^6.24.1", 29 | "babel-preset-env": "^1.7.0", 30 | "global": "^4.3.2", 31 | "webpack": "^4.20.2", 32 | "webpack-cli": "^3.1.2" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # 🍢 A simple `jQuery` plugin SlideShow @ [PageFly](https://apps.shopify.com/pagefly) 2 | 3 | ## Installation 4 | 5 | ```bash 6 | yarn add slider-x 7 | 8 | # or npm i slider-x 9 | ``` 10 | 11 | ## Usage 12 | 13 | Set up your markup 14 | ```html 15 |
16 |
0
17 |
1
18 |
2
19 |
3
20 |
21 | ``` 22 | 23 | Import the script and init `SliderX` 24 | ```js 25 | import 'slider-x' 26 | import 'slider-x/dist/slider-x.css' 27 | 28 | let sliderNode = document.querySelector('.your-slider-class-name') 29 | let slider = new SliderX(sliderNode [, options]) 30 | ``` 31 | 32 | ## Options 33 | ```js 34 | let defaultOptions = { 35 | slidesToShow: 1, 36 | slidesToScroll: 1, 37 | gutter: 0, 38 | autoPlay: true, 39 | autoPlayDelay: 3000, 40 | duration: 450, 41 | loop: true, 42 | draggable: true, 43 | paginationStyle: 'pagination-style-1', 44 | navStyle: 'nav-style-1', 45 | adaptiveHeight: false, 46 | height: 400, 47 | } 48 | 49 | // Style options, pick one for the `paginationStyle` and `navStyle` 50 | SliderX.styleOptions = { 51 | paginations: ['pagination-style-1', 'pagination-style-2', 'pagination-style-3', 'none'], 52 | navs: ['nav-style-1 fa-caret', 'nav-style-2 fa-angle', 'nav-style-3 fa-angle', 'nav-style-4 fa-long-arrow', 'nav-style-5 fa-long-arrow', 'none'] 53 | } 54 | ``` 55 | 56 | ## Methods 57 | 58 | ```js 59 | // Initialize slider 60 | slider.init() 61 | 62 | // Update options 63 | slider.updateOptions(newOptions) 64 | 65 | // Destroy slider 66 | slider.destroy() 67 | 68 | // Go to slider at index 69 | slider.goto(index) 70 | 71 | // Go to next slider 72 | slider.next() 73 | 74 | // Go to previous slider 75 | slider.prev() 76 | 77 | // Pause slider (if `autoPlay` is true) 78 | slider.pause() 79 | 80 | // Making it move again (after pausing it) 81 | slider.play() 82 | ``` 83 | 84 | Happy sliding 🍻 85 | -------------------------------------------------------------------------------- /test/test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Test 9 | 11 | 12 | 14 | 15 | 16 | 17 | 35 | 36 | 37 | 38 |

Final slider

39 |
40 |
0
41 |
1
42 |
2
43 |
3
44 |
3
45 |
3
46 | 47 |
48 | 49 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /demo/demo.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 0px 20px; 3 | max-width: 80%; 4 | margin: 0 auto; 5 | } 6 | 7 | .my-slide { 8 | height: 350px; 9 | /* background-image: url('https://images.unsplash.com/photo-1496325823264-7cdf2ea4f0a7?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=33dfefa08907444084b32551bcd6bef7&auto=format&fit=crop&w=1088&q=80') */ 10 | } 11 | 12 | .h-300px { 13 | height: 300px; 14 | } 15 | 16 | .h-400px { 17 | height: 400px; 18 | } 19 | 20 | .h-500px { 21 | height: 500px; 22 | } 23 | 24 | .slide-1 { 25 | background-image: url('https://images.unsplash.com/photo-1496325823264-7cdf2ea4f0a7?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=33dfefa08907444084b32551bcd6bef7&auto=format&fit=crop&w=1088&q=80') 26 | } 27 | 28 | .slide-2 { 29 | background-image: url('https://images.unsplash.com/photo-1521453159160-6b5c58c24591?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=149a5703e1ecd9f4a6b01c1f8f8280c6&auto=format&fit=crop&w=962&q=80') 30 | } 31 | 32 | .slide-3 { 33 | background-image: url('https://images.unsplash.com/photo-1520932146956-1ee4b33740fc?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=225333a710e886bf08fc3733772f2de6&auto=format&fit=crop&w=1024&q=80') 34 | } 35 | 36 | .slide-4 { 37 | background-image: url('https://images.unsplash.com/photo-1536082404951-89620d865bc8?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=708dca90dc95e788aeea39a9f7b8e58e&auto=format&fit=crop&w=1050&q=80') 38 | } 39 | 40 | .slide-5 { 41 | background-image: url('https://images.unsplash.com/photo-1527234639945-70d78416bd7d?ixlib=rb-0.3.5&s=a9ebd4e986e2f1c37d371c511d9515a4&auto=format&fit=crop&w=1919&q=80') 42 | } 43 | 44 | .h-300px { 45 | height: 300px; 46 | } 47 | 48 | .h-400px { 49 | height: 400px; 50 | } 51 | 52 | .h-200px { 53 | height: 200px; 54 | } 55 | 56 | .h-500px { 57 | height: 500px; 58 | } 59 | 60 | .test-slider-1 { 61 | height: 299px; 62 | /* background-image: url('https://images.unsplash.com/photo-1496325823264-7cdf2ea4f0a7?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=33dfefa08907444084b32551bcd6bef7&auto=format&fit=crop&w=1088&q=80') */ 63 | } 64 | 65 | .test-slider-2 { 66 | background: lightseagreen; 67 | } 68 | 69 | .pf-slider-slide { 70 | display: inline-flex; 71 | justify-content: center; 72 | align-items: center; 73 | font-size: 2em; 74 | } 75 | 76 | .child-1 { 77 | background: lightcoral; 78 | } 79 | 80 | .child-2 { 81 | background: lightseagreen; 82 | } 83 | 84 | .child-3 { 85 | background: lightgreen; 86 | } 87 | 88 | .child-4 { 89 | background: lightgray; 90 | } 91 | 92 | .child-5 { 93 | background: dodgerblue; 94 | } -------------------------------------------------------------------------------- /demo/demo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | PF Slider Demo 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |


29 | 33 |
34 |
35 |
36 |
37 |
38 | 44 | 0 45 |
46 |
1
47 |
2
48 |
3
49 |
4
50 |
5
51 |
6
52 |
7
53 |
8
54 |
9
55 |
56 |
57 |
58 |
59 | 60 |


61 |
62 |
63 | 69 | 00 70 | 71 |
72 |
11
73 |
22
74 |
33
75 |
44
76 |
77 | 78 | 79 | 80 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Final slider 9 | 10 | 11 | 12 | 69 | 70 | 71 | 72 |

Final slider

73 |
74 |
75 |
76 |
77 |
78 | 84 | 0 85 |
86 |
1
87 |
2
88 |
3
89 |
4
90 |
5
91 |
6
92 |
7
93 |
8
94 |
9
95 |
96 |
97 |
98 |
99 | 100 |


101 |
102 |
103 | 109 | 00 110 | 111 |
112 |
11
113 |
22
114 |
33
115 |
44
116 |
117 | 118 | 119 | 120 | 121 | 149 | 150 | 151 | -------------------------------------------------------------------------------- /helpers.js: -------------------------------------------------------------------------------- 1 | const prefix = 'pf' 2 | const refinePos = (arr, size) => arr.map(obj => { return { ...obj, index: obj.index % size } }) 3 | 4 | // Export all these FOLLOWING stuff before publish 5 | 6 | if (!Array.prototype.includes) { 7 | Array.prototype.includes = function(item) { 8 | return this.indexOf(item) !== -1 9 | } 10 | } 11 | 12 | 13 | export const SliderXClasses = { 14 | wrapper: `${prefix}-slider-wrapper`, 15 | inner: `${prefix}-slider-inner`, 16 | slide: `${prefix}-slider-slide`, 17 | indicators: `${prefix}-slider-pagination`, 18 | controller: `${prefix}-slider-nav`, 19 | disabledCtrl: `${prefix}-slider-nav-disabled`, 20 | nextCtrl: `${prefix}-next-nav`, 21 | prevCtrl: `${prefix}-prev-nav`, 22 | mouseEventOff: `${prefix}-slider-mouse-event-off` 23 | } 24 | 25 | // The logic is compicated! Stay tune before reading this func 26 | export const getSlidingData = (slider, direction, toIndex) => { 27 | let { totalSlide, sliderWidth, $slides } = slider 28 | totalSlide *= 3 29 | const sliderOffsetLeft = slider.el.getBoundingClientRect().left 30 | 31 | const slideWidth = getSlideWidth(slider) 32 | const { curr, slidesToShow, slidesToScroll, gutter } = slider.opts 33 | 34 | // Created this array to check if the next index is in the curr-showing-slides or not 35 | const currIndexes = [] 36 | for (let i = 0; i < slidesToShow; i++) { currIndexes.push((curr + i) % totalSlide) } 37 | 38 | let nextIndex 39 | let nextSlidesReadyPos = [], nextSlidesNewPos = [], currSlidesNewPos = [] 40 | let slidesMove 41 | 42 | if (direction === "next") { 43 | // Calculate number of slides to move 44 | if (toIndex !== undefined) { 45 | if (currIndexes.includes(toIndex)) { 46 | slidesMove = currIndexes.indexOf(toIndex) - currIndexes.indexOf(curr) 47 | } else slidesMove = slidesToShow 48 | } else slidesMove = slidesToScroll 49 | 50 | nextIndex = toIndex !== undefined ? toIndex : (curr + slidesToScroll) % totalSlide 51 | 52 | // Calculate next slides ready-position - where the next slides stay and be ready to move in 53 | let firstX 54 | if (currIndexes.includes(nextIndex)) { 55 | ///// Pausing here 56 | firstX = $slides[nextIndex].getBoundingClientRect().left - sliderOffsetLeft 57 | } else firstX = sliderWidth + gutter 58 | 59 | for (let i = 0; i < slidesToShow; i++) { 60 | const readyX = firstX + (slideWidth + gutter) * i 61 | 62 | if (!currIndexes.includes((nextIndex + i) % totalSlide)) { 63 | nextSlidesReadyPos.push({ index: nextIndex + i, readyX }) 64 | } 65 | 66 | nextSlidesNewPos.push({ index: nextIndex + i, newX: readyX - (gutter + slideWidth) * slidesMove }) 67 | } 68 | } else if (direction === "prev") { 69 | // Calculate number of slides to move 70 | if (toIndex !== undefined) { 71 | const lastIndex = (toIndex + slidesToShow - 1) % totalSlide 72 | if (currIndexes.includes(lastIndex)) { 73 | slidesMove = currIndexes.indexOf((curr + slidesToShow - 1) % totalSlide) - currIndexes.indexOf(lastIndex) 74 | } else slidesMove = slidesToShow 75 | } else slidesMove = slidesToScroll 76 | 77 | nextIndex = toIndex !== undefined ? toIndex : (totalSlide + (curr - slidesToScroll)) % totalSlide 78 | 79 | // Calculate next slides ready-position - where the next slides stay and be ready to move in 80 | let firstX // left position of the last slide in next slides 81 | if (currIndexes.includes((nextIndex + slidesToShow - 1) % totalSlide)) { 82 | firstX = $slides[(nextIndex + slidesToShow - 1) % totalSlide].getBoundingClientRect().left - sliderOffsetLeft 83 | } else firstX = -(slideWidth + gutter) 84 | 85 | for (let i = 0; i < slidesToShow; i++) { 86 | const readyX = firstX - (slideWidth + gutter) * (slidesToShow - i - 1) 87 | 88 | if (!currIndexes.includes((nextIndex + i) % totalSlide)) { 89 | nextSlidesReadyPos.push({ index: nextIndex + i, readyX }) 90 | } 91 | 92 | nextSlidesNewPos.push({ index: nextIndex + i, newX: readyX + (gutter + slideWidth) * slidesMove }) 93 | } 94 | } 95 | 96 | // Calculate new position for curr-showing-slides 97 | for (let i = 0; i < slidesToShow; i++) { 98 | const slideX = $slides[(curr + i) % totalSlide].getBoundingClientRect().left - sliderOffsetLeft 99 | let newX 100 | if (direction === 'next') newX = slideX - (gutter + slideWidth) * slidesMove 101 | else if (direction === 'prev') newX = slideX + (gutter + slideWidth) * slidesMove 102 | 103 | if (slider.moveByDrag) { 104 | const currLeft = $slides[curr % totalSlide].getBoundingClientRect().left - sliderOffsetLeft 105 | if (direction === 'prev') newX = slideX + (sliderWidth - currLeft) + gutter 106 | else if (direction === 'next') newX = slideX - (sliderWidth + currLeft) - gutter 107 | } 108 | 109 | currSlidesNewPos.push({ index: curr + i, newX }) 110 | } 111 | // debugger 112 | 113 | //////////// 114 | 115 | nextSlidesReadyPos = refinePos(nextSlidesReadyPos, totalSlide) 116 | nextSlidesNewPos = refinePos(nextSlidesNewPos, totalSlide) 117 | currSlidesNewPos = refinePos(currSlidesNewPos, totalSlide) 118 | 119 | // console.log(nextSlidesReadyPos, currSlidesNewPos, nextSlidesNewPos) 120 | 121 | return { nextIndex, nextSlidesReadyPos, currSlidesNewPos, nextSlidesNewPos } 122 | } 123 | 124 | export const getSlideWidth = slider => { 125 | const { gutter, slidesToShow } = slider.opts 126 | const wrapperWidth = slider.sliderWidth 127 | 128 | const slideWidth = (wrapperWidth - gutter * (slidesToShow - 1)) / slidesToShow 129 | return slideWidth 130 | } 131 | -------------------------------------------------------------------------------- /draggable-backup.js: -------------------------------------------------------------------------------- 1 | void function ($) { 2 | 3 | var x, y, 4 | deltaX, deltaY, 5 | offsetX, offsetY, 6 | distance, direction, 7 | velocityX, velocityY, 8 | timeStart, timeStop, timeOffset, 9 | axis, foundAxis, 10 | event, target, 11 | tapped, dragging, 12 | isTouchDevice, now, 13 | DRAG_DATA; 14 | 15 | $(window).one('touchstart', function ( e ) { 16 | isTouchDevice = true; 17 | }) 18 | $(document).ready(function () { 19 | 20 | bindEvent(document.body, 'touchstart', tap); 21 | bindEvent(document.body, 'touchmove', drag); 22 | bindEvent(document.body, 'touchend', release); 23 | bindEvent(document.body, 'touchcancel', release); 24 | 25 | bindEvent(document.body, 'mousedown', tap); 26 | bindEvent(document.body, 'mousemove', drag); 27 | bindEvent(document.body, 'mouseup', release); 28 | bindEvent(document.body, 'blur', release); 29 | 30 | $(window).on('mouseout', function ( e ) { 31 | e.toElement || release(e); 32 | }); 33 | }) 34 | 35 | function tap ( e ) { 36 | if (e.target.classList.contains('jsn-es-draggable')) { 37 | target = $(e.target); 38 | } 39 | else { 40 | var parent = findParentNode(e.target); 41 | if (parent) { 42 | target = $(parent); 43 | } 44 | else { 45 | return; 46 | } 47 | } 48 | 49 | if ( tapped ) 50 | return; 51 | 52 | now = timeStart = Date.now(); 53 | DRAG_DATA = {}; 54 | 55 | switch ( e.type ) { 56 | case 'mousedown': 57 | if (isTouchDevice) 58 | return; 59 | x = e.pageX; 60 | y = e.pageY; 61 | e.preventDefault(); 62 | break; 63 | case 'touchstart': 64 | if ( e.touches.length != 1 ) 65 | return; 66 | x = e.touches[ 0 ].clientX; 67 | y = e.touches[ 0 ].clientY; 68 | break; 69 | } 70 | deltaX = 0; 71 | deltaY = 0; 72 | offsetX = 0; 73 | offsetY = 0; 74 | foundAxis = null; 75 | distance = 0; 76 | direction = 'none'; 77 | axis = 'none'; 78 | 79 | tapped = true; 80 | } 81 | 82 | function drag ( e ) { 83 | if (!tapped) 84 | return; 85 | switch ( e.type ) { 86 | case 'mousemove': 87 | if (isTouchDevice) 88 | return; 89 | var pageX = e.pageX; 90 | var pageY = e.pageY; 91 | e.preventDefault(); 92 | break; 93 | case 'touchmove': 94 | if ( e.touches.length != 1 ) 95 | return; 96 | var pageX = e.touches[ 0 ].clientX; 97 | var pageY = e.touches[ 0 ].clientY; 98 | break; 99 | } 100 | now = Date.now(); 101 | 102 | deltaX = pageX - x; 103 | deltaY = pageY - y; 104 | offsetX += deltaX; 105 | offsetY += deltaY; 106 | x = pageX; 107 | y = pageY; 108 | 109 | if ( !foundAxis ) { 110 | if ( Math.abs(offsetX) > Math.abs(offsetY) ) { 111 | axis = 'x'; 112 | } 113 | else if ( Math.abs(offsetX) < Math.abs(offsetY) ) { 114 | axis = 'y'; 115 | } 116 | else { 117 | axis = 'none'; 118 | } 119 | distance = Math.max(Math.abs(offsetX), Math.abs(offsetY)); 120 | if ( distance > 50 ) 121 | foundAxis = true; 122 | } 123 | Math.abs(deltaX) >= Math.abs(deltaY) ? 124 | direction = deltaX > 0 ? 'right' : deltaX < 0 ? 'left' : 'none' : 125 | direction = deltaY > 0 ? 'down' : deltaY < 0 ? 'up' : 'none'; 126 | 127 | if ( !dragging ) { 128 | dragging = true; 129 | 130 | target.trigger(createEvent('es_dragstart', e ), getData()); 131 | $('body').add(target).addClass('jsn-es-draggable-dragging'); 132 | } 133 | if ( dragging ) { 134 | 135 | timeStop = Date.now(); 136 | calculateVelocity(); 137 | timeStart = Date.now(); 138 | target.trigger(createEvent('es_dragmove', e ), getData()); 139 | } 140 | } 141 | 142 | function release ( e ) { 143 | if ( !tapped ) 144 | return; 145 | switch ( e.type ) { 146 | case 'touchend': 147 | case 'touchcancel': 148 | if ( e.touches.length ) 149 | return; 150 | } 151 | tapped = false; 152 | 153 | if ( !dragging ) 154 | return 155 | 156 | timeStop = Date.now(); 157 | calculateVelocity(); 158 | 159 | dragging = false; 160 | $('body').add(target).removeClass('jsn-es-draggable-dragging'); 161 | target.trigger(createEvent('es_dragstop', e), getData()); 162 | target.removeData('jsn-es-draggable-data'); 163 | } 164 | 165 | function calculateVelocity() { 166 | timeOffset = timeStop - timeStart; 167 | velocityX = Math.abs(deltaX / timeOffset); 168 | velocityY = Math.abs(deltaY / timeOffset); 169 | } 170 | function getData () { 171 | var data = DRAG_DATA; 172 | data.direction = direction; 173 | data.axis = axis; 174 | data.deltaX = deltaX; 175 | data.deltaY = deltaY; 176 | data.moveX = offsetX; 177 | data.moveY = offsetY; 178 | data.velocityX = velocityX; 179 | data.velocityY = velocityY; 180 | 181 | DRAG_DATA = data; 182 | 183 | return data; 184 | } 185 | function bindEvent( element, event, handler, capture) { 186 | if (element.addEventListener) 187 | return element.addEventListener(event,handler,capture); 188 | if (element.attachEvent) 189 | return element.attachEvent(event,handler,capture); 190 | } 191 | function createEvent( type, e ) { 192 | var options = {}; 193 | options.originalEvent = e; 194 | options.preventDefault = e.preventDefault.bind(e); 195 | options.stopPropagation = e.stopPropagation.bind(e); 196 | return $.Event(type, options) 197 | } 198 | function findParentNode( child ) { 199 | var parent = child.parentNode, found = null; 200 | 201 | while (parent && !found && !parent.isEqualNode(document.body)) 202 | parent.classList && parent.classList.contains('jsn-es-draggable') ? 203 | found = parent: 204 | parent = parent.parentNode; 205 | 206 | return found; 207 | } 208 | 209 | }(jQuery) -------------------------------------------------------------------------------- /draggable.js: -------------------------------------------------------------------------------- 1 | void function () { 2 | 3 | var x, y, 4 | deltaX, deltaY, 5 | offsetX, offsetY, 6 | distance, direction, 7 | velocityX, velocityY, 8 | timeStart, timeStop, timeOffset, 9 | axis, foundAxis, 10 | event, target, 11 | tapped, dragging, 12 | isTouchDevice, now, 13 | DRAG_DATA; 14 | 15 | function handleTouchStart(e) { 16 | isTouchDevice = true; 17 | window.removeEventListener('touchstart', handleTouchStart) 18 | } 19 | window.addEventListener('touchstart', handleTouchStart) 20 | 21 | window.addEventListener('load', function () { 22 | bindEvent(document.body, 'touchstart', tap); 23 | bindEvent(document.body, 'touchmove', drag); 24 | bindEvent(document.body, 'touchend', release); 25 | bindEvent(document.body, 'touchcancel', release); 26 | 27 | bindEvent(document.body, 'mousedown', tap); 28 | bindEvent(document.body, 'mousemove', drag); 29 | bindEvent(document.body, 'mouseup', release); 30 | bindEvent(document.body, 'blur', release); 31 | 32 | window.addEventListener('mouseout', function (e) { 33 | e.toElement || release(e); 34 | }); 35 | }) 36 | 37 | function tap(e) { 38 | if (e.target.classList.contains('jsn-es-draggable')) { 39 | target = e.target; 40 | } 41 | else { 42 | var parent = findParentNode(e.target); 43 | if (parent) { 44 | target = parent; 45 | } 46 | else { 47 | return; 48 | } 49 | } 50 | 51 | if (tapped) 52 | return; 53 | 54 | now = timeStart = Date.now(); 55 | DRAG_DATA = {}; 56 | 57 | switch (e.type) { 58 | case 'mousedown': 59 | if (isTouchDevice) 60 | return; 61 | x = e.pageX; 62 | y = e.pageY; 63 | break; 64 | case 'touchstart': 65 | if (e.touches.length != 1) 66 | return; 67 | x = e.touches[0].clientX; 68 | y = e.touches[0].clientY; 69 | break; 70 | } 71 | deltaX = 0; 72 | deltaY = 0; 73 | offsetX = 0; 74 | offsetY = 0; 75 | foundAxis = null; 76 | distance = 0; 77 | direction = 'none'; 78 | axis = 'none'; 79 | 80 | tapped = true; 81 | } 82 | 83 | function drag(e) { 84 | if (!tapped) return; 85 | switch (e.type) { 86 | case 'mousemove': 87 | if (isTouchDevice) 88 | return; 89 | var pageX = e.pageX; 90 | var pageY = e.pageY; 91 | e.preventDefault(); 92 | break; 93 | case 'touchmove': 94 | if (e.touches.length != 1) 95 | return; 96 | var pageX = e.touches[0].clientX; 97 | var pageY = e.touches[0].clientY; 98 | break; 99 | } 100 | now = Date.now(); 101 | 102 | deltaX = pageX - x; 103 | deltaY = pageY - y; 104 | offsetX += deltaX; 105 | offsetY += deltaY; 106 | x = pageX; 107 | y = pageY; 108 | 109 | if (!foundAxis) { 110 | if (Math.abs(offsetX) > Math.abs(offsetY)) { 111 | axis = 'x'; 112 | } 113 | else if (Math.abs(offsetX) < Math.abs(offsetY)) { 114 | axis = 'y'; 115 | } 116 | else { 117 | axis = 'none'; 118 | } 119 | distance = Math.max(Math.abs(offsetX), Math.abs(offsetY)); 120 | if (distance > 50) 121 | foundAxis = true; 122 | } 123 | Math.abs(deltaX) >= Math.abs(deltaY) ? 124 | direction = deltaX > 0 ? 'right' : deltaX < 0 ? 'left' : 'none' : 125 | direction = deltaY > 0 ? 'down' : deltaY < 0 ? 'up' : 'none'; 126 | 127 | if (!dragging) { 128 | dragging = true; 129 | triggerEvent(target, createEvent('es_dragstart', e), getData()) 130 | 131 | document.body.classList.add('jsn-es-draggable-dragging') 132 | target.classList.add('jsn-es-draggable-dragging') 133 | } 134 | if (dragging) { 135 | timeStop = Date.now(); 136 | calculateVelocity(); 137 | timeStart = Date.now(); 138 | triggerEvent(target, createEvent('es_dragmove', e), getData()) 139 | } 140 | } 141 | 142 | function release(e) { 143 | if (!tapped) 144 | return; 145 | switch (e.type) { 146 | case 'touchend': 147 | case 'touchcancel': 148 | if (e.touches.length) 149 | return; 150 | } 151 | tapped = false; 152 | 153 | if (!dragging) 154 | return 155 | 156 | timeStop = Date.now(); 157 | calculateVelocity(); 158 | 159 | dragging = false; 160 | document.body.classList.remove('jsn-es-draggable-dragging'); 161 | target.classList.remove('jsn-es-draggable-dragging') 162 | triggerEvent(target, createEvent('es_dragstop', e), getData()) 163 | } 164 | 165 | function calculateVelocity() { 166 | timeOffset = timeStop - timeStart; 167 | velocityX = Math.abs(deltaX / timeOffset); 168 | velocityY = Math.abs(deltaY / timeOffset); 169 | } 170 | function getData() { 171 | var data = DRAG_DATA; 172 | data.direction = direction; 173 | data.axis = axis; 174 | data.deltaX = deltaX; 175 | data.deltaY = deltaY; 176 | data.moveX = offsetX; 177 | data.moveY = offsetY; 178 | data.velocityX = velocityX; 179 | data.velocityY = velocityY; 180 | 181 | DRAG_DATA = data; 182 | 183 | return data; 184 | } 185 | function bindEvent(element, event, handler, capture) { 186 | if (element.addEventListener) 187 | return element.addEventListener(event, handler, capture); 188 | if (element.attachEvent) 189 | return element.attachEvent(event, handler, capture); 190 | } 191 | function createEvent(type, e) { 192 | let event 193 | if (typeof(Event) === 'function') { 194 | event = new Event(type) 195 | } else { 196 | event = document.createEvent('Event') 197 | event.initEvent(type, true, true) 198 | } 199 | event.originalEvent = e 200 | event.preventDefault = e.preventDefault.bind(e) 201 | event.stopPropagation = e.stopPropagation.bind(e) 202 | return event 203 | } 204 | function triggerEvent(target, event, data) { 205 | event.data = data 206 | return (target || window).dispatchEvent(event) 207 | } 208 | function findParentNode(child) { 209 | var parent = child.parentNode, found = null; 210 | 211 | while (parent && !found && !parent.isEqualNode(document.body)) 212 | parent.classList && parent.classList.contains('jsn-es-draggable') ? 213 | found = parent : 214 | parent = parent.parentNode; 215 | 216 | return found; 217 | } 218 | 219 | }() -------------------------------------------------------------------------------- /main.css: -------------------------------------------------------------------------------- 1 | .pf-slider-mouse-event-off > * { 2 | pointer-events: none; 3 | } 4 | .pf-slider-inner { 5 | width: 100%; 6 | overflow: hidden; 7 | display: block; 8 | position: relative; 9 | } 10 | .pf-slider-slide { 11 | will-change: contents; 12 | width: 100%; 13 | position: absolute !important; 14 | -webkit-transform: translate3d(100%, 0, 0); 15 | transform: translate3d(100%, 0, 0); 16 | overflow: hidden; 17 | top: 0; 18 | } 19 | .pf-slider-wrapper { 20 | overflow: hidden; 21 | position: relative; 22 | } 23 | .pf-slider-nav { 24 | position: absolute; 25 | z-index: 999; 26 | display: block; 27 | text-decoration: none; 28 | font-size: 18px; 29 | cursor: pointer; 30 | border: unset; 31 | background: unset; 32 | } 33 | .pf-slider-nav:focus { 34 | text-decoration: none; 35 | outline: unset; 36 | } 37 | .pf-slider-nav.nav-style-1:before, 38 | .pf-slider-nav.nav-style-2:before, 39 | .pf-slider-nav.nav-style-3:before, 40 | .pf-slider-nav.nav-style-4:before, 41 | .pf-slider-nav.nav-style-5:before { 42 | font-size: 14px; 43 | color: #fff; 44 | font-family: FontAwesome; 45 | } 46 | .pf-slider-nav.nav-style-1, 47 | .pf-slider-nav.nav-style-2, 48 | .pf-slider-nav.nav-style-3, 49 | .pf-slider-nav.nav-style-4 { 50 | top: 50%; 51 | -webkit-transform: translateY(-50%); 52 | transform: translateY(-50%); 53 | } 54 | .pf-slider-nav.nav-style-1 { 55 | width: 43px; 56 | height: 43px; 57 | display: flex; 58 | align-items: center; 59 | justify-content: center; 60 | text-align: center; 61 | background-color: #171717; 62 | opacity: 0.5; 63 | border-radius: 100%; 64 | -webkit-transition: all 0.3s; 65 | transition: all 0.3s; 66 | } 67 | .pf-next-nav.nav-style-1:hover, 68 | .pf-prev-nav.nav-style-1:hover { 69 | opacity: 1; 70 | text-decoration: none; 71 | } 72 | .pf-prev-nav.nav-style-1 { 73 | left: 20px; 74 | } 75 | .pf-next-nav.nav-style-1 { 76 | right: 20px; 77 | } 78 | .pf-slider-nav.nav-style-2 { 79 | width: 46px; 80 | height: 43px; 81 | display: flex; 82 | align-items: center; 83 | justify-content: center; 84 | line-height: 43px; 85 | background-color: #030303; 86 | color: #fff; 87 | -webkit-transition: all 0.3s; 88 | transition: all 0.3s; 89 | border-radius: 3px; 90 | } 91 | .pf-next-nav.nav-style-2:hover, 92 | .pf-prev-nav.nav-style-2:hover { 93 | text-decoration: none; 94 | color: #fff; 95 | background-color: #171717; 96 | } 97 | .pf-prev-nav.nav-style-2 { 98 | left: -46px; 99 | opacity: 0; 100 | } 101 | .pf-next-nav.nav-style-2 { 102 | right: -46px; 103 | opacity: 0; 104 | } 105 | .pf-slider-wrapper:hover .pf-prev-nav.nav-style-2 { 106 | left: 20px; 107 | opacity: 1; 108 | } 109 | .pf-slider-wrapper:hover .pf-next-nav.nav-style-2 { 110 | right: 20px; 111 | opacity: 1; 112 | } 113 | .pf-slider-nav.nav-style-3 { 114 | color: #fff; 115 | padding: 20px; 116 | text-shadow: 0 0 1px #000; 117 | -webkit-transition: all 0.3s; 118 | transition: all 0.3s; 119 | } 120 | .pf-next-nav.nav-style-3:hover, 121 | .pf-prev-nav.nav-style-3:hover { 122 | text-decoration: none; 123 | } 124 | .pf-prev-nav.nav-style-3:before { 125 | font-size: 24px; 126 | } 127 | .pf-next-nav.nav-style-3:before { 128 | font-size: 24px; 129 | } 130 | .pf-prev-nav.nav-style-3 { 131 | left: 40px; 132 | opacity: 0; 133 | } 134 | .pf-next-nav.nav-style-3 { 135 | right: 40px; 136 | opacity: 0; 137 | } 138 | .pf-slider-wrapper:hover .pf-prev-nav.nav-style-3 { 139 | left: 0; 140 | opacity: 1; 141 | } 142 | .pf-slider-wrapper:hover .pf-next-nav.nav-style-3 { 143 | right: 0; 144 | opacity: 1; 145 | } 146 | .pf-slider-nav.nav-style-4 { 147 | width: 46px; 148 | height: 43px; 149 | display: flex; 150 | align-items: center; 151 | justify-content: center; 152 | line-height: 43px; 153 | background-color: #030303; 154 | color: #fff; 155 | -webkit-transition: all 0.3s; 156 | transition: all 0.3s; 157 | border-radius: 3px; 158 | } 159 | .pf-next-nav.nav-style-4:hover, 160 | .pf-prev-nav.nav-style-4:hover { 161 | text-decoration: none; 162 | color: #fff; 163 | background-color: #171717; 164 | } 165 | .pf-prev-nav.nav-style-4 { 166 | left: -46px; 167 | opacity: 0; 168 | } 169 | .pf-next-nav.nav-style-4 { 170 | right: -46px; 171 | opacity: 0; 172 | } 173 | .pf-slider-wrapper:hover .pf-prev-nav.nav-style-4 { 174 | left: 20px; 175 | opacity: 1; 176 | } 177 | .pf-slider-wrapper:hover .pf-next-nav.nav-style-4 { 178 | right: 20px; 179 | opacity: 1; 180 | } 181 | .pf-slider-nav.nav-style-5 { 182 | width: 46px; 183 | height: 43px; 184 | bottom: 5%; 185 | right: 20px; 186 | display: flex; 187 | align-items: center; 188 | justify-content: center; 189 | line-height: 43px; 190 | background-color: #030303; 191 | color: #fff; 192 | -webkit-transition: all 0.3s; 193 | transition: all 0.3s; 194 | } 195 | .pf-next-nav.nav-style-5:hover, 196 | .pf-prev-nav.nav-style-5:hover { 197 | text-decoration: none; 198 | color: #fff; 199 | background-color: #171717; 200 | } 201 | .pf-prev-nav.nav-style-5 { 202 | right: 67px; 203 | border-radius: 3px 0 0 3px; 204 | } 205 | .pf-next-nav.nav-style-5 { 206 | border-radius: 0 3px 3px 0; 207 | } 208 | .pf-slider-nav-disabled { 209 | pointer-events: none; 210 | } 211 | .pf-slider-pagination { 212 | position: absolute; 213 | bottom: 5%; 214 | left: 0; 215 | width: 100%; 216 | padding: 0; 217 | margin: 0; 218 | text-align: center; 219 | z-index: 1; 220 | } 221 | .pf-slider-pagination:hover { 222 | cursor: pointer; 223 | } 224 | .pf-slider-pagination li { 225 | display: inline-block; 226 | margin: 0 5px !important; 227 | width: 10px !important; 228 | height: 10px !important; 229 | border-radius: 50%; 230 | background: #bbb; 231 | opacity: 1; 232 | -webkit-transition: all 0.3s; 233 | transition: all 0.3s; 234 | border: 0; 235 | line-height: 0; 236 | font-size: 0; 237 | color: transparent; 238 | cursor: pointer; 239 | padding: 0; 240 | -webkit-appearance: none; 241 | } 242 | .pf-slider-pagination li.active, 243 | .pf-slider-pagination li:hover { 244 | background: #212121; 245 | } 246 | .pf-slider-pagination.pagination-style-1 li { 247 | width: 20px !important; 248 | height: 5px !important; 249 | border-radius: 0; 250 | } 251 | .pf-slider-pagination.pagination-style-2 li { 252 | position: relative; 253 | width: 8px !important; 254 | height: 8px !important; 255 | margin: 0 6px !important; 256 | background: transparent; 257 | } 258 | .pf-slider-pagination.pagination-style-2 li.active, 259 | .pf-slider-pagination.pagination-style-2 li:hover { 260 | background-color: #212121; 261 | } 262 | .pf-slider-pagination.pagination-style-2 li:after { 263 | -webkit-transition: all 0.3s; 264 | transition: all 0.3s; 265 | width: 0; 266 | height: 0; 267 | position: absolute; 268 | content: ""; 269 | left: -2px; 270 | top: -2px; 271 | width: 12px; 272 | height: 12px; 273 | background: transparent; 274 | border: 1px solid #212121; 275 | border-radius: 50%; 276 | box-sizing: border-box; 277 | } 278 | .pf-slider-pagination.pagination-style-3 li { 279 | position: relative; 280 | width: 16px !important; 281 | height: 16px !important; 282 | margin: 0 5px !important; 283 | border: 2px solid #fff; 284 | background: transparent; 285 | } 286 | .pf-slider-pagination.pagination-style-3 li.active:after { 287 | position: absolute; 288 | content: ""; 289 | top: 3px; 290 | left: 3px; 291 | right: 3px; 292 | bottom: 3px; 293 | width: 6px; 294 | border: 2px solid #fff; 295 | border-radius: 100%; 296 | } 297 | -------------------------------------------------------------------------------- /dist/slider-x.css: -------------------------------------------------------------------------------- 1 | .pf-slider-mouse-event-off > * { 2 | pointer-events: none; 3 | } 4 | 5 | .pf-slider-inner { 6 | width: 100%; 7 | overflow: hidden; 8 | display: block; 9 | position: relative; 10 | } 11 | 12 | .pf-slider-slide { 13 | will-change: contents; 14 | width: 100%; 15 | position: absolute !important; 16 | transform: translate3d(100%, 0, 0); 17 | overflow: hidden; 18 | top: 0; 19 | } 20 | 21 | .pf-slider-wrapper { 22 | overflow: hidden; 23 | position: relative; 24 | } 25 | 26 | .pf-slider-nav { 27 | position: absolute; 28 | z-index: 999; 29 | display: block; 30 | text-decoration: none; 31 | font-size: 18px; 32 | cursor: pointer; 33 | border: unset; 34 | background: unset; 35 | } 36 | 37 | .pf-slider-nav:focus { 38 | text-decoration: none; 39 | outline: unset; 40 | } 41 | 42 | .pf-slider-nav.nav-style-1::before, 43 | .pf-slider-nav.nav-style-2::before, 44 | .pf-slider-nav.nav-style-3::before, 45 | .pf-slider-nav.nav-style-4::before, 46 | .pf-slider-nav.nav-style-5::before { 47 | font-size: 14px; 48 | color: #fff; 49 | font-family: FontAwesome; 50 | } 51 | 52 | .pf-slider-nav.nav-style-1, 53 | .pf-slider-nav.nav-style-2, 54 | .pf-slider-nav.nav-style-3, 55 | .pf-slider-nav.nav-style-4{ 56 | top: 50%; 57 | transform: translateY(-50%); 58 | } 59 | 60 | /* Arrow Style 1 */ 61 | .pf-slider-nav.nav-style-1 { 62 | width: 43px; 63 | height: 43px; 64 | display: flex; 65 | align-items: center; 66 | justify-content: center; 67 | text-align: center; 68 | background-color: #171717; 69 | opacity: 0.5; 70 | border-radius: 100%; 71 | transition: all .3s; 72 | } 73 | .pf-prev-nav.nav-style-1:hover, 74 | .pf-next-nav.nav-style-1:hover { 75 | opacity: 1; 76 | text-decoration: none; 77 | } 78 | .pf-prev-nav.nav-style-1::before { 79 | content: "\f0d9"; 80 | } 81 | .pf-next-nav.nav-style-1::before { 82 | content: "\f0da"; 83 | } 84 | .pf-prev-nav.nav-style-1 { 85 | left: 20px; 86 | } 87 | .pf-next-nav.nav-style-1 { 88 | right: 20px; 89 | } 90 | 91 | /* Arrow style 2 */ 92 | .pf-slider-nav.nav-style-2 { 93 | width: 46px; 94 | height: 43px; 95 | display: flex; 96 | align-items: center; 97 | justify-content: center; 98 | line-height: 43px; 99 | background-color: #030303; 100 | color: #fff; 101 | transition: all .3s; 102 | border-radius: 3px; 103 | } 104 | .pf-prev-nav.nav-style-2:hover, 105 | .pf-next-nav.nav-style-2:hover { 106 | text-decoration: none; 107 | color: #fff; 108 | background-color: #171717; 109 | } 110 | .pf-prev-nav.nav-style-2:before { 111 | content: "\f104"; 112 | } 113 | .pf-next-nav.nav-style-2:before { 114 | content: "\f105"; 115 | } 116 | .pf-prev-nav.nav-style-2 { 117 | left: -46px; 118 | opacity: 0; 119 | } 120 | .pf-next-nav.nav-style-2 { 121 | right: -46px; 122 | opacity: 0; 123 | } 124 | .pf-slider-wrapper:hover .pf-prev-nav.nav-style-2 { 125 | left: 20px; 126 | opacity: 1; 127 | } 128 | .pf-slider-wrapper:hover .pf-next-nav.nav-style-2 { 129 | right: 20px; 130 | opacity: 1; 131 | } 132 | 133 | /* Arrow style 3 */ 134 | .pf-slider-nav.nav-style-3 { 135 | color: #fff; 136 | padding: 20px; 137 | text-shadow: 0 0 1px #000; 138 | transition: all .3s; 139 | } 140 | .pf-prev-nav.nav-style-3:hover, 141 | .pf-next-nav.nav-style-3:hover { 142 | text-decoration: none; 143 | } 144 | .pf-prev-nav.nav-style-3:before { 145 | content: "\f104"; 146 | font-size: 24px; 147 | } 148 | .pf-next-nav.nav-style-3:before { 149 | content: "\f105"; 150 | font-size: 24px; 151 | } 152 | .pf-prev-nav.nav-style-3 { 153 | left: 40px; 154 | opacity: 0; 155 | } 156 | .pf-next-nav.nav-style-3 { 157 | right: 40px; 158 | opacity: 0; 159 | } 160 | .pf-slider-wrapper:hover .pf-prev-nav.nav-style-3 { 161 | left: 0; 162 | opacity: 1; 163 | } 164 | .pf-slider-wrapper:hover .pf-next-nav.nav-style-3 { 165 | right: 0; 166 | opacity: 1; 167 | } 168 | 169 | /* Arrow style 4 */ 170 | .pf-slider-nav.nav-style-4 { 171 | width: 46px; 172 | height: 43px; 173 | display: flex; 174 | align-items: center; 175 | justify-content: center; 176 | line-height: 43px; 177 | background-color: #030303; 178 | color: #fff; 179 | transition: all .3s; 180 | border-radius: 3px; 181 | } 182 | .pf-prev-nav.nav-style-4:hover, 183 | .pf-next-nav.nav-style-4:hover { 184 | text-decoration: none; 185 | color: #fff; 186 | background-color: #171717; 187 | } 188 | .pf-prev-nav.nav-style-4:before { 189 | content: "\f177"; 190 | } 191 | .pf-next-nav.nav-style-4:before { 192 | content: "\f178"; 193 | } 194 | .pf-prev-nav.nav-style-4 { 195 | left: -46px; 196 | opacity: 0; 197 | } 198 | .pf-next-nav.nav-style-4 { 199 | right: -46px; 200 | opacity: 0; 201 | } 202 | .pf-slider-wrapper:hover .pf-prev-nav.nav-style-4 { 203 | left: 20px; 204 | opacity: 1; 205 | } 206 | .pf-slider-wrapper:hover .pf-next-nav.nav-style-4 { 207 | right: 20px; 208 | opacity: 1; 209 | } 210 | 211 | /* Arrow style 5 */ 212 | .pf-slider-nav.nav-style-5 { 213 | width: 46px; 214 | height: 43px; 215 | bottom: 5%; 216 | right: 20px; 217 | display: flex; 218 | align-items: center; 219 | justify-content: center; 220 | line-height: 43px; 221 | background-color: #030303; 222 | color: #fff; 223 | transition: all .3s; 224 | } 225 | .pf-prev-nav.nav-style-5:hover, 226 | .pf-next-nav.nav-style-5:hover { 227 | text-decoration: none; 228 | color: #fff; 229 | background-color: #171717; 230 | } 231 | .pf-prev-nav.nav-style-5:before { 232 | content: "\f177"; 233 | } 234 | .pf-next-nav.nav-style-5:before { 235 | content: "\f178"; 236 | } 237 | .pf-prev-nav.nav-style-5 { 238 | right: 67px; 239 | border-radius: 3px 0 0 3px; 240 | } 241 | .pf-next-nav.nav-style-5 { 242 | border-radius: 0 3px 3px 0; 243 | } 244 | 245 | .pf-slider-nav-disabled { 246 | pointer-events: none; 247 | } 248 | 249 | 250 | /* Pagination */ 251 | .pf-slider-pagination { 252 | position: absolute; 253 | bottom: 5%; 254 | left: 0; 255 | width: 100%; 256 | padding: 0; 257 | margin: 0; 258 | text-align: center; 259 | z-index: 1; 260 | } 261 | .pf-slider-pagination:hover { 262 | cursor: pointer; 263 | } 264 | .pf-slider-pagination li { 265 | display: inline-block; 266 | margin: 0 5px !important; 267 | width: 10px !important; 268 | height: 10px !important; 269 | border-radius: 50%; 270 | background: #bbb; 271 | opacity: 1; 272 | transition: all .3s; 273 | border: 0; 274 | line-height: 0px; 275 | font-size: 0px; 276 | color: transparent; 277 | cursor: pointer; 278 | padding: 0; 279 | -webkit-appearance: none; 280 | } 281 | .pf-slider-pagination li:hover { 282 | background: #212121; 283 | } 284 | .pf-slider-pagination li.active { 285 | background: #212121; 286 | } 287 | 288 | /* Pagination style 1 */ 289 | .pf-slider-pagination.pagination-style-1 li { 290 | width: 20px !important; 291 | height: 5px !important; 292 | border-radius: 0px; 293 | } 294 | 295 | /* Pagination style 2 */ 296 | .pf-slider-pagination.pagination-style-2 li { 297 | position: relative; 298 | width: 8px !important; 299 | height: 8px !important; 300 | margin: 0 6px !important; 301 | background: transparent; 302 | } 303 | .pf-slider-pagination.pagination-style-2 li.active, 304 | .pf-slider-pagination.pagination-style-2 li:hover { 305 | background-color: #212121; 306 | } 307 | .pf-slider-pagination.pagination-style-2 li:after { 308 | transition: all .3s; 309 | width: 0; 310 | height: 0; 311 | } 312 | .pf-slider-pagination.pagination-style-2 li:after { 313 | position: absolute; 314 | content: ''; 315 | left: -2px; 316 | top: -2px; 317 | width: 12px; 318 | height: 12px; 319 | background: transparent; 320 | border: 1px solid #212121; 321 | border-radius: 50%; 322 | box-sizing: border-box; 323 | } 324 | 325 | /* Pagination style 3 */ 326 | .pf-slider-pagination.pagination-style-3 li { 327 | position: relative; 328 | width: 16px !important; 329 | height: 16px !important; 330 | margin: 0 5px !important; 331 | border: 2px solid #ffffff; 332 | background: transparent; 333 | } 334 | .pf-slider-pagination.pagination-style-3 li.active:after { 335 | position: absolute; 336 | content: ''; 337 | top: 3px; 338 | left: 3px; 339 | right: 3px; 340 | bottom: 3px; 341 | width: 6px; 342 | border: #fff solid 2px; 343 | border-radius: 100%; 344 | } 345 | -------------------------------------------------------------------------------- /dist/slider-x.scss: -------------------------------------------------------------------------------- 1 | .pf-slider-mouse-event-off > * { 2 | pointer-events: none; 3 | } 4 | 5 | .pf-slider-inner { 6 | width: 100%; 7 | overflow: hidden; 8 | display: block; 9 | position: relative; 10 | } 11 | 12 | .pf-slider-slide { 13 | will-change: contents; 14 | width: 100%; 15 | position: absolute !important; 16 | transform: translate3d(100%, 0, 0); 17 | overflow: hidden; 18 | top: 0; 19 | } 20 | 21 | .pf-slider-wrapper { 22 | overflow: hidden; 23 | position: relative; 24 | } 25 | 26 | .pf-slider-nav { 27 | position: absolute; 28 | z-index: 999; 29 | display: block; 30 | text-decoration: none; 31 | font-size: 18px; 32 | cursor: pointer; 33 | border: unset; 34 | background: unset; 35 | } 36 | 37 | .pf-slider-nav:focus { 38 | text-decoration: none; 39 | outline: unset; 40 | } 41 | 42 | .pf-slider-nav.nav-style-1::before, 43 | .pf-slider-nav.nav-style-2::before, 44 | .pf-slider-nav.nav-style-3::before, 45 | .pf-slider-nav.nav-style-4::before, 46 | .pf-slider-nav.nav-style-5::before { 47 | font-size: 14px; 48 | color: #fff; 49 | font-family: FontAwesome; 50 | } 51 | 52 | .pf-slider-nav.nav-style-1, 53 | .pf-slider-nav.nav-style-2, 54 | .pf-slider-nav.nav-style-3, 55 | .pf-slider-nav.nav-style-4{ 56 | top: 50%; 57 | transform: translateY(-50%); 58 | } 59 | 60 | /* Arrow Style 1 */ 61 | .pf-slider-nav.nav-style-1 { 62 | width: 43px; 63 | height: 43px; 64 | display: flex; 65 | align-items: center; 66 | justify-content: center; 67 | text-align: center; 68 | background-color: #171717; 69 | opacity: 0.5; 70 | border-radius: 100%; 71 | transition: all .3s; 72 | } 73 | .pf-prev-nav.nav-style-1:hover, 74 | .pf-next-nav.nav-style-1:hover { 75 | opacity: 1; 76 | text-decoration: none; 77 | } 78 | .pf-prev-nav.nav-style-1::before { 79 | content: "\f0d9"; 80 | } 81 | .pf-next-nav.nav-style-1::before { 82 | content: "\f0da"; 83 | } 84 | .pf-prev-nav.nav-style-1 { 85 | left: 20px; 86 | } 87 | .pf-next-nav.nav-style-1 { 88 | right: 20px; 89 | } 90 | 91 | /* Arrow style 2 */ 92 | .pf-slider-nav.nav-style-2 { 93 | width: 46px; 94 | height: 43px; 95 | display: flex; 96 | align-items: center; 97 | justify-content: center; 98 | line-height: 43px; 99 | background-color: #030303; 100 | color: #fff; 101 | transition: all .3s; 102 | border-radius: 3px; 103 | } 104 | .pf-prev-nav.nav-style-2:hover, 105 | .pf-next-nav.nav-style-2:hover { 106 | text-decoration: none; 107 | color: #fff; 108 | background-color: #171717; 109 | } 110 | .pf-prev-nav.nav-style-2:before { 111 | content: "\f104"; 112 | } 113 | .pf-next-nav.nav-style-2:before { 114 | content: "\f105"; 115 | } 116 | .pf-prev-nav.nav-style-2 { 117 | left: -46px; 118 | opacity: 0; 119 | } 120 | .pf-next-nav.nav-style-2 { 121 | right: -46px; 122 | opacity: 0; 123 | } 124 | .pf-slider-wrapper:hover .pf-prev-nav.nav-style-2 { 125 | left: 20px; 126 | opacity: 1; 127 | } 128 | .pf-slider-wrapper:hover .pf-next-nav.nav-style-2 { 129 | right: 20px; 130 | opacity: 1; 131 | } 132 | 133 | /* Arrow style 3 */ 134 | .pf-slider-nav.nav-style-3 { 135 | color: #fff; 136 | padding: 20px; 137 | text-shadow: 0 0 1px #000; 138 | transition: all .3s; 139 | } 140 | .pf-prev-nav.nav-style-3:hover, 141 | .pf-next-nav.nav-style-3:hover { 142 | text-decoration: none; 143 | } 144 | .pf-prev-nav.nav-style-3:before { 145 | content: "\f104"; 146 | font-size: 24px; 147 | } 148 | .pf-next-nav.nav-style-3:before { 149 | content: "\f105"; 150 | font-size: 24px; 151 | } 152 | .pf-prev-nav.nav-style-3 { 153 | left: 40px; 154 | opacity: 0; 155 | } 156 | .pf-next-nav.nav-style-3 { 157 | right: 40px; 158 | opacity: 0; 159 | } 160 | .pf-slider-wrapper:hover .pf-prev-nav.nav-style-3 { 161 | left: 0; 162 | opacity: 1; 163 | } 164 | .pf-slider-wrapper:hover .pf-next-nav.nav-style-3 { 165 | right: 0; 166 | opacity: 1; 167 | } 168 | 169 | /* Arrow style 4 */ 170 | .pf-slider-nav.nav-style-4 { 171 | width: 46px; 172 | height: 43px; 173 | display: flex; 174 | align-items: center; 175 | justify-content: center; 176 | line-height: 43px; 177 | background-color: #030303; 178 | color: #fff; 179 | transition: all .3s; 180 | border-radius: 3px; 181 | } 182 | .pf-prev-nav.nav-style-4:hover, 183 | .pf-next-nav.nav-style-4:hover { 184 | text-decoration: none; 185 | color: #fff; 186 | background-color: #171717; 187 | } 188 | .pf-prev-nav.nav-style-4:before { 189 | content: "\f177"; 190 | } 191 | .pf-next-nav.nav-style-4:before { 192 | content: "\f178"; 193 | } 194 | .pf-prev-nav.nav-style-4 { 195 | left: -46px; 196 | opacity: 0; 197 | } 198 | .pf-next-nav.nav-style-4 { 199 | right: -46px; 200 | opacity: 0; 201 | } 202 | .pf-slider-wrapper:hover .pf-prev-nav.nav-style-4 { 203 | left: 20px; 204 | opacity: 1; 205 | } 206 | .pf-slider-wrapper:hover .pf-next-nav.nav-style-4 { 207 | right: 20px; 208 | opacity: 1; 209 | } 210 | 211 | /* Arrow style 5 */ 212 | .pf-slider-nav.nav-style-5 { 213 | width: 46px; 214 | height: 43px; 215 | bottom: 5%; 216 | right: 20px; 217 | display: flex; 218 | align-items: center; 219 | justify-content: center; 220 | line-height: 43px; 221 | background-color: #030303; 222 | color: #fff; 223 | transition: all .3s; 224 | } 225 | .pf-prev-nav.nav-style-5:hover, 226 | .pf-next-nav.nav-style-5:hover { 227 | text-decoration: none; 228 | color: #fff; 229 | background-color: #171717; 230 | } 231 | .pf-prev-nav.nav-style-5:before { 232 | content: "\f177"; 233 | } 234 | .pf-next-nav.nav-style-5:before { 235 | content: "\f178"; 236 | } 237 | .pf-prev-nav.nav-style-5 { 238 | right: 67px; 239 | border-radius: 3px 0 0 3px; 240 | } 241 | .pf-next-nav.nav-style-5 { 242 | border-radius: 0 3px 3px 0; 243 | } 244 | 245 | .pf-slider-nav-disabled { 246 | pointer-events: none; 247 | } 248 | 249 | 250 | /* Pagination */ 251 | .pf-slider-pagination { 252 | position: absolute; 253 | bottom: 5%; 254 | left: 0; 255 | width: 100%; 256 | padding: 0; 257 | margin: 0; 258 | text-align: center; 259 | z-index: 1; 260 | } 261 | .pf-slider-pagination:hover { 262 | cursor: pointer; 263 | } 264 | .pf-slider-pagination li { 265 | display: inline-block; 266 | margin: 0 5px !important; 267 | width: 10px !important; 268 | height: 10px !important; 269 | border-radius: 50%; 270 | background: #bbb; 271 | opacity: 1; 272 | transition: all .3s; 273 | border: 0; 274 | line-height: 0px; 275 | font-size: 0px; 276 | color: transparent; 277 | cursor: pointer; 278 | padding: 0; 279 | -webkit-appearance: none; 280 | } 281 | .pf-slider-pagination li:hover { 282 | background: #212121; 283 | } 284 | .pf-slider-pagination li.active { 285 | background: #212121; 286 | } 287 | 288 | /* Pagination style 1 */ 289 | .pf-slider-pagination.pagination-style-1 li { 290 | width: 20px !important; 291 | height: 5px !important; 292 | border-radius: 0px; 293 | } 294 | 295 | /* Pagination style 2 */ 296 | .pf-slider-pagination.pagination-style-2 li { 297 | position: relative; 298 | width: 8px !important; 299 | height: 8px !important; 300 | margin: 0 6px !important; 301 | background: transparent; 302 | } 303 | .pf-slider-pagination.pagination-style-2 li.active, 304 | .pf-slider-pagination.pagination-style-2 li:hover { 305 | background-color: #212121; 306 | } 307 | .pf-slider-pagination.pagination-style-2 li:after { 308 | transition: all .3s; 309 | width: 0; 310 | height: 0; 311 | } 312 | .pf-slider-pagination.pagination-style-2 li:after { 313 | position: absolute; 314 | content: ''; 315 | left: -2px; 316 | top: -2px; 317 | width: 12px; 318 | height: 12px; 319 | background: transparent; 320 | border: 1px solid #212121; 321 | border-radius: 50%; 322 | box-sizing: border-box; 323 | } 324 | 325 | /* Pagination style 3 */ 326 | .pf-slider-pagination.pagination-style-3 li { 327 | position: relative; 328 | width: 16px !important; 329 | height: 16px !important; 330 | margin: 0 5px !important; 331 | border: 2px solid #ffffff; 332 | background: transparent; 333 | } 334 | .pf-slider-pagination.pagination-style-3 li.active:after { 335 | position: absolute; 336 | content: ''; 337 | top: 3px; 338 | left: 3px; 339 | right: 3px; 340 | bottom: 3px; 341 | width: 6px; 342 | border: #fff solid 2px; 343 | border-radius: 100%; 344 | } 345 | -------------------------------------------------------------------------------- /dist/slider-x.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports["PageFly SliderX"]=e():t["PageFly SliderX"]=e()}(window,function(){return function(t){var e={};function i(s){if(e[s])return e[s].exports;var n=e[s]={i:s,l:!1,exports:{}};return t[s].call(n.exports,n,n.exports,i),n.l=!0,n.exports}return i.m=t,i.c=e,i.d=function(t,e,s){i.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:s})},i.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},i.t=function(t,e){if(1&e&&(t=i(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var s=Object.create(null);if(i.r(s),Object.defineProperty(s,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)i.d(s,n,function(e){return t[e]}.bind(null,n));return s},i.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return i.d(e,"a",e),e},i.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},i.p="",i(i.s=1)}([function(t,e){!function(){var t,e,i,s,n,o,a,r,l,d,c,u,h,v,p,f,y,g,S;function m(r){if(r.target.classList.contains("jsn-es-draggable"))p=r.target;else{var l=function(t){var e=t.parentNode,i=null;for(;e&&!i&&!e.isEqualNode(document.body);)e.classList&&e.classList.contains("jsn-es-draggable")?i=e:e=e.parentNode;return i}(r.target);if(!l)return;p=l}if(!f){switch(d=Date.now(),S={},r.type){case"mousedown":if(g)return;t=r.pageX,e=r.pageY;break;case"touchstart":if(1!=r.touches.length)return;t=r.touches[0].clientX,e=r.touches[0].clientY}i=0,s=0,n=0,o=0,v=null,0,a="none",h="none",f=!0}}function b(r){if(f){switch(r.type){case"mousemove":if(g)return;var l=r.pageX,u=r.pageY;r.preventDefault();break;case"touchmove":if(1!=r.touches.length)return;l=r.touches[0].clientX,u=r.touches[0].clientY}Date.now(),n+=i=l-t,o+=s=u-e,t=l,e=u,v||(h=Math.abs(n)>Math.abs(o)?"x":Math.abs(n)50&&(v=!0)),a=Math.abs(i)>=Math.abs(s)?i>0?"right":i<0?"left":"none":s>0?"down":s<0?"up":"none",y||(y=!0,C(p,P("es_dragstart",r),O()),document.body.classList.add("jsn-es-draggable-dragging"),p.classList.add("jsn-es-draggable-dragging")),y&&(c=Date.now(),w(),d=Date.now(),C(p,P("es_dragmove",r),O()))}}function x(t){if(f){switch(t.type){case"touchend":case"touchcancel":if(t.touches.length)return}f=!1,y&&(c=Date.now(),w(),y=!1,document.body.classList.remove("jsn-es-draggable-dragging"),p.classList.remove("jsn-es-draggable-dragging"),C(p,P("es_dragstop",t),O()))}}function w(){u=c-d,r=Math.abs(i/u),l=Math.abs(s/u)}function O(){var t=S;return t.direction=a,t.axis=h,t.deltaX=i,t.deltaY=s,t.moveX=n,t.moveY=o,t.velocityX=r,t.velocityY=l,S=t,t}function k(t,e,i,s){return t.addEventListener?t.addEventListener(e,i,s):t.attachEvent?t.attachEvent(e,i,s):void 0}function P(t,e){var i;return"function"==typeof Event?i=new Event(t):(i=document.createEvent("Event")).initEvent(t,!0,!0),i.originalEvent=e,i.preventDefault=e.preventDefault.bind(e),i.stopPropagation=e.stopPropagation.bind(e),i}function C(t,e,i){return e.data=i,(t||window).dispatchEvent(e)}window.addEventListener("touchstart",function t(e){g=!0,window.removeEventListener("touchstart",t)}),window.addEventListener("load",function(){k(document.body,"touchstart",m),k(document.body,"touchmove",b),k(document.body,"touchend",x),k(document.body,"touchcancel",x),k(document.body,"mousedown",m),k(document.body,"mousemove",b),k(document.body,"mouseup",x),k(document.body,"blur",x),window.addEventListener("mouseout",function(t){t.toElement||x(t)})})}()},function(t,e,i){"use strict";function s(t,e,i){return e in t?Object.defineProperty(t,e,{value:i,enumerable:!0,configurable:!0,writable:!0}):t[e]=i,t}i.r(e);var n=function(t,e){return t.map(function(t){return function(t){for(var e=1;e=i&&c0))&&!(l+d>=this.totalSlide&&!this.opts.loop&&n<0)){for(var m=0;m1||Math.abs(o)>.3){if(!d){if(o<0&&c+2*u>r)return a="prev",s=r-u,this.missingSlidesOnDrag=!0,void this.moveSlide(a,s,v);if(o>0&&c-u<0)return a="next",s=0,this.missingSlidesOnDrag=!0,void this.moveSlide(a,s,v)}v=h-v,o<0?a="next":o>0&&(a="prev")}else o<0?(this.opts.curr+=u,a="prev"):o>0&&(this.opts.curr=3*r+(c-u),a="next"),this.opts.curr%=3*r;s="next"===a?this.opts.curr+u:3*r+(this.opts.curr-u),s%=3*r,this.moveSlide(a,s,v)}}},{key:"verifyOptions",value:function(){var t=this.constructor.defaultOptions;for(var e in t){var i=t[e];c(this.opts[e])!==c(i)&&(this.opts[e]=i)}this.opts.height=this.sliderHeight||this.opts.height,this.opts.loop||(this.opts.autoPlay=!1),1===this.opts.slidesToShow&&(this.opts.gutter=0);var s=this.constructor.styleOptions.paginations,n=this.constructor.styleOptions.navs;s.indexOf(this.opts.paginationStyle)<0&&(this.opts.paginationStyle=s[0]),n.indexOf(this.opts.navStyle)<0&&(this.opts.navStyle=n[0])}},{key:"updateOptions",value:function(t){var e=this.constructor.defaultOptions,i=this.constructor.styleOptions.paginations,s=this.opts.paginationStyle,n=this.constructor.styleOptions.navs,o=this.opts.navStyle;for(var a in t)if(c(t[a])===c(e[a])&&"curr"!==a){var r=t[a];"paginationStyle"===a&&i.indexOf(t[a])<0&&(r=s),"navStyle"===a&&n.indexOf(t[a])<0&&(r=o),this.opts[a]=r}this.opts.loop||(this.opts.autoPlay=!1),this.destroy(),this.init()}},{key:"setAutoPlay",value:function(){var t=this;this.opts.autoPlay&&!this.paused&&(this.autoPlayTimeoutId=setTimeout(function(){t.moveSlide("next")},this.opts.autoPlayDelay))}},{key:"clearAutoPlay",value:function(){clearTimeout(this.autoPlayTimeoutId)}},{key:"destroy",value:function(){var t=this.el;this.clearAutoPlay();for(var e=[],i=0;i=this.totalSlide-1||"next"===t&&e+r-1>this.totalSlide-1)){this.el.classList.add(b);var l=a(this,t,e),d=l.nextIndex,c=l.nextSlidesReadyPos,u=l.currSlidesNewPos,h=l.nextSlidesNewPos;if(!this.moveByDrag)for(var v=0;v=o?this.goto(o-n):this.moveSlide("next")}},{key:"prev",value:function(){var t=this.opts,e=t.curr,i=t.loop,s=t.slidesToScroll;i?this.moveSlide("prev"):e-s<=0?this.goto(0):this.moveSlide("prev")}},{key:"goto",value:function(t){var e=this.opts,i=e.curr,s=e.loop,n=e.slidesToShow,o=this.totalSlide;if(t>i){if(!s&&t+n>o)return void this.moveSlide("next",o-n);this.moveSlide("next",t)}else t= curr && i < curr + slidesToShow) ? (newSlideWidth + gutter) * (i - curr) : sliderWidth + gutter 130 | $slide.style.transform = `translate3d(${newLeft}px, 0, 0)` 131 | } 132 | } 133 | 134 | handleClick = e => { 135 | const action = e.target.dataset.sliderxAction 136 | switch (action) { 137 | case 'next': this.next(); break 138 | case 'prev': this.prev(); break 139 | case 'goto': 140 | // DOMStringMap convert dataset from hyphen style to upperCase (goto-slide => gotoSlide) 141 | const index = parseInt(e.target.dataset.gotoSlide) || 0 142 | this.goto(index) 143 | break 144 | default: console.log('Slider clicked') 145 | } 146 | } 147 | 148 | handleStyleChange() { 149 | this.sliderWidth = this.el.offsetWidth 150 | this.handleResize() 151 | } 152 | 153 | handleDragMove(e) { 154 | const data = e.data 155 | if (Math.abs(data.moveX) < SliderX.constructor.MIN_DRAG_DISTANCE) return 156 | this.clearAutoPlay() 157 | 158 | const translateRange = data.moveX // -30 159 | const { curr, slidesToShow, gutter, loop } = this.opts 160 | 161 | const nextIndex = (curr + slidesToShow) % (this.totalSlide * 3) 162 | let nextSlidesReadyPos = getSlidingData(this, 'next', nextIndex).nextSlidesReadyPos 163 | if (!loop) nextSlidesReadyPos = nextSlidesReadyPos.filter(obj => obj.index < this.totalSlide) 164 | 165 | const currSlidesPos = [] 166 | const slideWidth = getSlideWidth(this) 167 | for (let i = 0; i < slidesToShow; i++) { 168 | currSlidesPos.push({ index: (i + curr) % (this.totalSlide * 3), readyX: (slideWidth + gutter) * i }) 169 | } 170 | // TODO: fix this dummy code 171 | // This is stupid because the getMovementData return the variable name nextIndex 172 | const prevIndex = (this.totalSlide * 3 + (curr - slidesToShow)) % (this.totalSlide * 3) 173 | let prevSlidesReadyPos = getSlidingData(this, 'prev', prevIndex).nextSlidesReadyPos 174 | if (!loop) prevSlidesReadyPos = prevSlidesReadyPos.filter(obj => obj.index < this.totalSlide) 175 | 176 | // Drag is forbidden in these below cases: 177 | if (curr === 0 && !this.opts.loop && translateRange > 0) return 178 | if (curr + slidesToShow >= this.totalSlide && !this.opts.loop && translateRange < 0) return 179 | 180 | 181 | // The key is: move all 3 slide! Genius! 182 | for (let i = 0; i < prevSlidesReadyPos.length; i++) { 183 | const slide = prevSlidesReadyPos[i] 184 | const $slide = this.$slides[slide.index] 185 | this.translateSlide($slide, slide.readyX + translateRange) 186 | } 187 | for (let i = 0; i < currSlidesPos.length; i++) { 188 | const slide = currSlidesPos[i] 189 | const $slide = this.$slides[slide.index] 190 | this.translateSlide($slide, slide.readyX + translateRange) 191 | } 192 | for (let i = 0; i < nextSlidesReadyPos.length; i++) { 193 | const slide = nextSlidesReadyPos[i] 194 | const $slide = this.$slides[slide.index] 195 | this.translateSlide($slide, slide.readyX + translateRange) 196 | } 197 | } 198 | 199 | handleDragStop(e) { 200 | const data = e.data 201 | if (Math.abs(data.moveX) < SliderX.constructor.MIN_DRAG_DISTANCE) return 202 | // Turn off draggable until slides move completely 203 | this.moveByDrag = true 204 | 205 | const MIN_MOUSE_SPEED_TO_MOVE_SLIDE = 1 206 | const MIN_DISTANCE_RATIO_TO_MOVE_SLIDE = 0.3 // 30% 207 | 208 | const mouseSpeed = data.velocityX 209 | const distRatio = data.moveX / this.sliderWidth 210 | 211 | let direction = '', toIndex 212 | const { totalSlide } = this 213 | const { loop, curr, slidesToShow, duration } = this.opts 214 | let customDuration = Math.abs(distRatio) * duration 215 | 216 | // TODO: Make this block code shorter (Seem unneccessary ?) 217 | // debugger 218 | if (mouseSpeed > MIN_MOUSE_SPEED_TO_MOVE_SLIDE || Math.abs(distRatio) > MIN_DISTANCE_RATIO_TO_MOVE_SLIDE) { // Move slides to new position 219 | /** 220 | * The 2 following cases a special so we have to manually handle them 221 | * Drag slides with (loop = false) and the last slides are't enough for the window 222 | * So we left a blank at the last/first 223 | */ 224 | if (!loop) { 225 | if (distRatio < 0 && curr + 2 * slidesToShow > totalSlide) { 226 | direction = 'prev' 227 | toIndex = totalSlide - slidesToShow 228 | this.missingSlidesOnDrag = true 229 | this.moveSlide(direction, toIndex, customDuration) 230 | return 231 | 232 | } 233 | else if (distRatio > 0 && curr - slidesToShow < 0) { 234 | direction = 'next' 235 | toIndex = 0 236 | this.missingSlidesOnDrag = true 237 | this.moveSlide(direction, toIndex, customDuration) 238 | return 239 | } 240 | } 241 | 242 | customDuration = duration - customDuration 243 | 244 | if (distRatio < 0) direction = 'next' 245 | else if (distRatio > 0) direction = 'prev' 246 | } 247 | else { // Revert slides to original position 248 | /** 249 | * This is a bit tricky 250 | * In case we have to move the slides to the original position, I change the curr slide index to reuse the 251 | * next() n prev() functions 252 | */ 253 | if (distRatio < 0) { 254 | this.opts.curr += slidesToShow 255 | direction = 'prev' 256 | } 257 | else if (distRatio > 0) { 258 | this.opts.curr = (totalSlide * 3 + (curr - slidesToShow)) 259 | direction = 'next' 260 | } 261 | this.opts.curr %= (totalSlide * 3) 262 | } 263 | 264 | // Dummy: the moveSlide() need toIndex as the 2nd argument 265 | if (direction === 'next') toIndex = this.opts.curr + slidesToShow 266 | else toIndex = (totalSlide * 3 + (this.opts.curr - slidesToShow)) 267 | toIndex %= (totalSlide * 3) 268 | // debugger 269 | 270 | this.moveSlide(direction, toIndex, customDuration) 271 | } 272 | 273 | /* LOGIC FUNCTION */ 274 | verifyOptions() { 275 | const defOpts = this.constructor.defaultOptions 276 | 277 | for (let key in defOpts) { 278 | const value = defOpts[key] 279 | if (typeof this.opts[key] !== typeof value) { 280 | this.opts[key] = value 281 | } 282 | } 283 | 284 | // Set the slider height 285 | this.opts.height = this.sliderHeight || this.opts.height 286 | 287 | if (!this.opts.loop) this.opts.autoPlay = false 288 | if (this.opts.slidesToShow === 1) this.opts.gutter = 0 289 | 290 | // Set the style of slider nav n pagination to valid values 291 | const defPags = this.constructor.styleOptions.paginations 292 | const defNavs = this.constructor.styleOptions.navs 293 | 294 | if (defPags.indexOf(this.opts.paginationStyle) < 0) this.opts.paginationStyle = defPags[0] 295 | if (defNavs.indexOf(this.opts.navStyle) < 0) this.opts.navStyle = defNavs[0] 296 | } 297 | 298 | updateOptions(newOtps) { 299 | const { defaultOptions } = this.constructor 300 | 301 | const defPags = this.constructor.styleOptions.paginations 302 | const currPag = this.opts.paginationStyle 303 | 304 | const defNavs = this.constructor.styleOptions.navs 305 | const currNav = this.opts.navStyle 306 | 307 | for (let key in newOtps) { 308 | // The type of newOpts values must be legal 309 | // And update 'curr' key is forbidden 310 | if (typeof newOtps[key] === typeof defaultOptions[key] && key !== 'curr') { 311 | let newValue = newOtps[key] 312 | 313 | // Keep the current pagination n nav style if the new options is not valid 314 | if (key === 'paginationStyle' && defPags.indexOf(newOtps[key]) < 0) newValue = currPag 315 | if (key === 'navStyle' && defNavs.indexOf(newOtps[key]) < 0) newValue = currNav 316 | 317 | this.opts[key] = newValue 318 | } 319 | } 320 | 321 | // Turn off autoPlay if loop is false 322 | if (!this.opts.loop) this.opts.autoPlay = false 323 | 324 | // Set the style of slider nav n pagination to valid values 325 | // this.updateSliderStyle() 326 | // this.updateSliderCtrlStyle(this.opts.curr) 327 | // this.setAutoPlay() 328 | this.destroy() 329 | this.init() 330 | } 331 | 332 | setAutoPlay() { 333 | if (this.opts.autoPlay && !this.paused) { 334 | this.autoPlayTimeoutId = setTimeout(() => { 335 | this.moveSlide('next') 336 | }, (this.opts.autoPlayDelay)) 337 | } 338 | } 339 | 340 | clearAutoPlay() { 341 | clearTimeout(this.autoPlayTimeoutId) 342 | } 343 | 344 | pause = () => { 345 | this.paused = true 346 | this.clearAutoPlay() 347 | } 348 | 349 | play = () => { 350 | this.paused = false 351 | this.setAutoPlay() 352 | } 353 | 354 | /* CONTROLLER FUNCTIONS */ 355 | destroy() { 356 | const { el } = this 357 | this.clearAutoPlay() 358 | 359 | // Save all items, remove all classes, inline-style n reverse the original style 360 | const items = [] 361 | // this.$slides = this.$slider.children 362 | for (let i = 0; i < this.totalSlide; i++) { 363 | const $slide = this.$slides[i] 364 | $slide.classList.remove(slide) 365 | $slide.classList.remove('active') 366 | $slide.style = this.originalStyles.inner[i] 367 | items.push($slide) 368 | } 369 | 370 | // Remove class, event handler, dataset and all children 371 | el.classList.remove(wrapper) 372 | el.style = this.originalStyles.wrapper 373 | el.removeEventListener('click', this.handleClick) 374 | delete el.dataset['pfSliderXInited'] 375 | 376 | delete this.$slider 377 | delete this.$slides 378 | this.observer.unobserve(el) 379 | window.removeEventListener('blur', this.pause) 380 | window.removeEventListener('focus', this.play) 381 | 382 | while (el.firstChild) { el.removeChild(el.firstChild) } 383 | 384 | // Append the original item 385 | for (let i = 0; i < items.length; i++) { el.appendChild(items[i]) } 386 | } 387 | 388 | moveSlide(direction, toIndex, customDuration) { 389 | this.clearAutoPlay() 390 | const { curr, slidesToShow, loop } = this.opts 391 | 392 | // Move slide is forbidden in the 1st n last slide if Slider movement is not loop 393 | // debugger 394 | if (!loop && !this.moveByDrag) { 395 | if ( 396 | (direction === 'prev' && curr === 0) 397 | || 398 | (direction === 'next' && curr + slidesToShow - 1 >= (this.totalSlide - 1)) 399 | || 400 | (direction === 'next' && toIndex + slidesToShow - 1 > (this.totalSlide - 1)) 401 | ) { 402 | this.moveByDrag = false 403 | return 404 | } 405 | } 406 | 407 | // Turn off mouse event on moving 408 | this.el.classList.add(mouseEventOff) 409 | 410 | const { nextIndex, nextSlidesReadyPos, currSlidesNewPos, nextSlidesNewPos } = getSlidingData(this, direction, toIndex) 411 | // console.log(nextSlidesReadyPos, currSlidesNewPos, nextSlidesNewPos) 412 | 413 | // if (this.opts.adaptiveHeight) { 414 | // const nextHeight = $next.height() 415 | // this.$slider.css({ height: `${nextHeight}px` }) 416 | // } 417 | 418 | if (!this.moveByDrag) { 419 | // Only move the $next slide to the ready-position in case user does not drag 420 | for (let i = 0; i < nextSlidesReadyPos.length; i++) { 421 | const slide = nextSlidesReadyPos[i] 422 | const $slide = this.$slides[slide.index] 423 | this.translateSlide($slide, slide.readyX) 424 | } 425 | } 426 | 427 | this.updateSliderCtrlStyle(nextIndex) 428 | 429 | // Move 2 slides contemporary 430 | let duration = customDuration ? customDuration : this.opts.duration 431 | 432 | setTimeout(() => { 433 | /** 434 | * Tricky: when move slides in a special case - drag but missing slides (loop = false) 435 | * The curr slides is out of window so just don't move them 436 | */ 437 | if (!this.missingSlidesOnDrag) { 438 | for (let i = 0; i < currSlidesNewPos.length; i++) { 439 | const slide = currSlidesNewPos[i] 440 | const $slide = this.$slides[slide.index] 441 | this.translateSlide($slide, slide.newX, duration) 442 | } 443 | } 444 | 445 | for (let i = 0; i < nextSlidesNewPos.length; i++) { 446 | const slide = nextSlidesNewPos[i] 447 | const $slide = this.$slides[slide.index] 448 | this.translateSlide($slide, slide.newX, duration) 449 | } 450 | 451 | // Do stuffs after slides moving complete 452 | setTimeout(() => { 453 | this.setAutoPlay() 454 | this.el.classList.remove(mouseEventOff) 455 | this.moveByDrag = false 456 | this.missingSlidesOnDrag = false 457 | }, duration) 458 | }, 50) 459 | 460 | // Update curr index n reset the .active 461 | this.opts.curr = nextIndex 462 | this.udpateActiveSlideStyle() 463 | } 464 | 465 | translateSlide($slide, toX, duration) { 466 | const transition = duration ? `transform ${duration}ms ease` : '' 467 | $slide.style.transition = transition 468 | $slide.style.transform = `translate3d(${toX}px, 0, 0)` 469 | } 470 | 471 | next() { 472 | const { curr, loop, slidesToScroll, slidesToShow } = this.opts 473 | const { totalSlide } = this 474 | 475 | if (!loop) { 476 | if (curr + slidesToScroll + slidesToShow >= totalSlide) { 477 | this.goto(totalSlide - slidesToShow) 478 | } else this.moveSlide("next") 479 | } 480 | else this.moveSlide("next") 481 | } 482 | 483 | prev() { 484 | const { curr, loop, slidesToScroll } = this.opts 485 | 486 | if (!loop) { 487 | if (curr - slidesToScroll <= 0) { 488 | this.goto(0) 489 | } else this.moveSlide("prev") 490 | } 491 | else this.moveSlide("prev") 492 | } 493 | 494 | goto(index) { 495 | const { curr, loop, slidesToShow } = this.opts 496 | const { totalSlide } = this 497 | 498 | if (index > curr) { 499 | if (!loop) { 500 | if (index + slidesToShow > totalSlide) { 501 | this.moveSlide("next", totalSlide - slidesToShow) 502 | return 503 | } 504 | } 505 | this.moveSlide("next", index) 506 | } else if (index < curr) { 507 | this.moveSlide("prev", index) 508 | } 509 | } 510 | 511 | /* STYLE FUNCTIONS */ 512 | updateSliderStyle() { 513 | const { $slider, $slides, el, opts } = this 514 | const $curr = $slides[this.opts.curr] 515 | const slideWidth = getSlideWidth(this) 516 | 517 | const { adaptiveHeight, height } = opts 518 | if (!adaptiveHeight) { 519 | // Slider height = the highest child in case adaptiveHeight is off 520 | // for (let i = 0; i < $slides.length; i++) { 521 | // if ($slides.eq(i).height() > sliderHeight) sliderHeight = $slides.eq(i).height() 522 | // } 523 | 524 | // Stretch all slide height to equal to the heightest slide 525 | for (let i = 0; i < $slides.length; i++) { $slides[i].style.cssText += `height: 100%; width: ${slideWidth}px;` } 526 | $slider.style.cssText = `height: 100%; transition: '';` 527 | el.style.transition = '' 528 | } else { 529 | for (let i = 0; i < $slides.length; i++) { $slides[i].style.height = '' } 530 | el.style.transition = `height ${opts.duration}ms ease-in-out` 531 | $slider.style.transition = `height ${opts.duration}ms ease-in-out` 532 | 533 | el.style.height = '' 534 | $slider.style.height = `${$curr.height()}px` 535 | } 536 | 537 | for (let i = 0; i < $slides.length; i++) { $slides[i].classList.add(slide) } 538 | const { gutter } = opts 539 | 540 | for (let i = 0; i < $slides.length; i++) { 541 | const slideX = i * (slideWidth + gutter) 542 | $slides[i].style.transform = `translate3d(${slideX}px, 0, 0)` 543 | } 544 | 545 | $slider.classList.add(inner) 546 | opts.draggable ? $slider.classList.add('jsn-es-draggable') : $slider.classList.remove('jsn-es-draggable') 547 | 548 | // Styling for navigators and indicators 549 | const { navStyle, paginationStyle } = opts 550 | 551 | el.querySelector('[data-sliderx-action="next"]').setAttribute('class', `${nextCtrl} ${controller} ${navStyle}-right`) 552 | el.querySelector('[data-sliderx-action="prev"]').setAttribute('class', `${prevCtrl} ${controller} ${navStyle}-left`) 553 | el.querySelector('ol').setAttribute('class', `${indicators} ${paginationStyle}`) 554 | 555 | // Toggle show/hide nav/pagination 556 | const navDisplay = navStyle === 'none' ? 'none' : 'flex' 557 | const paginationDisplay = paginationStyle === 'none' ? 'none' : 'block' 558 | 559 | el.querySelector('[data-sliderx-action="next"]').style.display = navDisplay 560 | el.querySelector('[data-sliderx-action="prev"]').style.display = navDisplay 561 | el.querySelector('ol.pf-slider-pagination').style.display = paginationDisplay 562 | } 563 | 564 | updateSliderCtrlStyle(index) { 565 | const { totalSlide, opts, el } = this 566 | const { loop, slidesToShow } = opts 567 | 568 | const $next = el.querySelector('.pf-next-nav') 569 | const $prev = el.querySelector('.pf-prev-nav') 570 | 571 | $next.classList.remove(disabledCtrl) 572 | $prev.classList.remove(disabledCtrl) 573 | 574 | if (index === 0 && !loop) $prev.classList.add(disabledCtrl) 575 | else if (index === totalSlide - slidesToShow && !loop) $next.classList.add(disabledCtrl) 576 | } 577 | 578 | udpateActiveSlideStyle() { 579 | // Add class .active for current active slide n indicator 580 | const { curr, slidesToShow, loop } = this.opts 581 | 582 | const $currSlide = this.$slider.querySelector(`.${slide}.active`) 583 | $currSlide ? $currSlide.classList.remove('active') : null 584 | 585 | this.$slides[curr].classList.add('active') 586 | 587 | let activeSlide = Math.floor((curr % (this.totalSlide)) / slidesToShow) * slidesToShow 588 | 589 | if (!loop) { 590 | if (curr + slidesToShow === this.totalSlide) { 591 | activeSlide = Math.floor((this.totalSlide - 1) / slidesToShow) * slidesToShow 592 | } 593 | } 594 | 595 | const $currLi = this.el.querySelector('ol.pf-slider-pagination li.active') 596 | $currLi ? $currLi.classList.remove('active') : null 597 | 598 | this.el.querySelector(`li[data-goto-slide="${activeSlide}"]`).classList.add('active') 599 | 600 | this.updateSliderCtrlStyle(curr) 601 | } 602 | } 603 | 604 | // Class properties 605 | SliderX.constructor.MIN_DRAG_DISTANCE = 5 606 | 607 | SliderX.defaultOptions = { 608 | curr: 0, 609 | slidesToShow: 1, 610 | slidesToScroll: 1, 611 | gutter: 15, 612 | autoPlay: true, 613 | autoPlayDelay: 3000, 614 | duration: 400, 615 | loop: true, 616 | draggable: true, 617 | paginationStyle: 'pagination-style-1', 618 | navStyle: 'nav-style-1 fa-caret', 619 | adaptiveHeight: false, 620 | height: 400, 621 | } 622 | 623 | SliderX.styleOptions = { 624 | /** 625 | * The default style is the first array element 626 | * Add more style (class name) for pagination or nav to these below arrays 627 | * Remember to keep the 'none' in order to toggle show/hide pagination/nav option 628 | */ 629 | paginations: ['pagination-style-1', 'pagination-style-2', 'pagination-style-3', 'none'], 630 | navs: ['nav-style-1 fa-caret', 'nav-style-2 fa-angle', 'nav-style-3 fa-angle', 'nav-style-4 fa-long-arrow', 'nav-style-5 fa-long-arrow', 'none'] 631 | } 632 | 633 | // Comment this line before bundling for production version 634 | window.SliderX = SliderX 635 | --------------------------------------------------------------------------------