├── .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 |
--------------------------------------------------------------------------------