UNPKG

8.18 kBJavaScriptView Raw
1import { now } from '../../shared/utils.js';
2export default function freeMode(_ref) {
3 let {
4 swiper,
5 extendParams,
6 emit,
7 once
8 } = _ref;
9 extendParams({
10 freeMode: {
11 enabled: false,
12 momentum: true,
13 momentumRatio: 1,
14 momentumBounce: true,
15 momentumBounceRatio: 1,
16 momentumVelocityRatio: 1,
17 sticky: false,
18 minimumVelocity: 0.02
19 }
20 });
21
22 function onTouchStart() {
23 const translate = swiper.getTranslate();
24 swiper.setTranslate(translate);
25 swiper.setTransition(0);
26 swiper.touchEventsData.velocities.length = 0;
27 swiper.freeMode.onTouchEnd({
28 currentPos: swiper.rtl ? swiper.translate : -swiper.translate
29 });
30 }
31
32 function onTouchMove() {
33 const {
34 touchEventsData: data,
35 touches
36 } = swiper; // Velocity
37
38 if (data.velocities.length === 0) {
39 data.velocities.push({
40 position: touches[swiper.isHorizontal() ? 'startX' : 'startY'],
41 time: data.touchStartTime
42 });
43 }
44
45 data.velocities.push({
46 position: touches[swiper.isHorizontal() ? 'currentX' : 'currentY'],
47 time: now()
48 });
49 }
50
51 function onTouchEnd(_ref2) {
52 let {
53 currentPos
54 } = _ref2;
55 const {
56 params,
57 $wrapperEl,
58 rtlTranslate: rtl,
59 snapGrid,
60 touchEventsData: data
61 } = swiper; // Time diff
62
63 const touchEndTime = now();
64 const timeDiff = touchEndTime - data.touchStartTime;
65
66 if (currentPos < -swiper.minTranslate()) {
67 swiper.slideTo(swiper.activeIndex);
68 return;
69 }
70
71 if (currentPos > -swiper.maxTranslate()) {
72 if (swiper.slides.length < snapGrid.length) {
73 swiper.slideTo(snapGrid.length - 1);
74 } else {
75 swiper.slideTo(swiper.slides.length - 1);
76 }
77
78 return;
79 }
80
81 if (params.freeMode.momentum) {
82 if (data.velocities.length > 1) {
83 const lastMoveEvent = data.velocities.pop();
84 const velocityEvent = data.velocities.pop();
85 const distance = lastMoveEvent.position - velocityEvent.position;
86 const time = lastMoveEvent.time - velocityEvent.time;
87 swiper.velocity = distance / time;
88 swiper.velocity /= 2;
89
90 if (Math.abs(swiper.velocity) < params.freeMode.minimumVelocity) {
91 swiper.velocity = 0;
92 } // this implies that the user stopped moving a finger then released.
93 // There would be no events with distance zero, so the last event is stale.
94
95
96 if (time > 150 || now() - lastMoveEvent.time > 300) {
97 swiper.velocity = 0;
98 }
99 } else {
100 swiper.velocity = 0;
101 }
102
103 swiper.velocity *= params.freeMode.momentumVelocityRatio;
104 data.velocities.length = 0;
105 let momentumDuration = 1000 * params.freeMode.momentumRatio;
106 const momentumDistance = swiper.velocity * momentumDuration;
107 let newPosition = swiper.translate + momentumDistance;
108 if (rtl) newPosition = -newPosition;
109 let doBounce = false;
110 let afterBouncePosition;
111 const bounceAmount = Math.abs(swiper.velocity) * 20 * params.freeMode.momentumBounceRatio;
112 let needsLoopFix;
113
114 if (newPosition < swiper.maxTranslate()) {
115 if (params.freeMode.momentumBounce) {
116 if (newPosition + swiper.maxTranslate() < -bounceAmount) {
117 newPosition = swiper.maxTranslate() - bounceAmount;
118 }
119
120 afterBouncePosition = swiper.maxTranslate();
121 doBounce = true;
122 data.allowMomentumBounce = true;
123 } else {
124 newPosition = swiper.maxTranslate();
125 }
126
127 if (params.loop && params.centeredSlides) needsLoopFix = true;
128 } else if (newPosition > swiper.minTranslate()) {
129 if (params.freeMode.momentumBounce) {
130 if (newPosition - swiper.minTranslate() > bounceAmount) {
131 newPosition = swiper.minTranslate() + bounceAmount;
132 }
133
134 afterBouncePosition = swiper.minTranslate();
135 doBounce = true;
136 data.allowMomentumBounce = true;
137 } else {
138 newPosition = swiper.minTranslate();
139 }
140
141 if (params.loop && params.centeredSlides) needsLoopFix = true;
142 } else if (params.freeMode.sticky) {
143 let nextSlide;
144
145 for (let j = 0; j < snapGrid.length; j += 1) {
146 if (snapGrid[j] > -newPosition) {
147 nextSlide = j;
148 break;
149 }
150 }
151
152 if (Math.abs(snapGrid[nextSlide] - newPosition) < Math.abs(snapGrid[nextSlide - 1] - newPosition) || swiper.swipeDirection === 'next') {
153 newPosition = snapGrid[nextSlide];
154 } else {
155 newPosition = snapGrid[nextSlide - 1];
156 }
157
158 newPosition = -newPosition;
159 }
160
161 if (needsLoopFix) {
162 once('transitionEnd', () => {
163 swiper.loopFix();
164 });
165 } // Fix duration
166
167
168 if (swiper.velocity !== 0) {
169 if (rtl) {
170 momentumDuration = Math.abs((-newPosition - swiper.translate) / swiper.velocity);
171 } else {
172 momentumDuration = Math.abs((newPosition - swiper.translate) / swiper.velocity);
173 }
174
175 if (params.freeMode.sticky) {
176 // If freeMode.sticky is active and the user ends a swipe with a slow-velocity
177 // event, then durations can be 20+ seconds to slide one (or zero!) slides.
178 // It's easy to see this when simulating touch with mouse events. To fix this,
179 // limit single-slide swipes to the default slide duration. This also has the
180 // nice side effect of matching slide speed if the user stopped moving before
181 // lifting finger or mouse vs. moving slowly before lifting the finger/mouse.
182 // For faster swipes, also apply limits (albeit higher ones).
183 const moveDistance = Math.abs((rtl ? -newPosition : newPosition) - swiper.translate);
184 const currentSlideSize = swiper.slidesSizesGrid[swiper.activeIndex];
185
186 if (moveDistance < currentSlideSize) {
187 momentumDuration = params.speed;
188 } else if (moveDistance < 2 * currentSlideSize) {
189 momentumDuration = params.speed * 1.5;
190 } else {
191 momentumDuration = params.speed * 2.5;
192 }
193 }
194 } else if (params.freeMode.sticky) {
195 swiper.slideToClosest();
196 return;
197 }
198
199 if (params.freeMode.momentumBounce && doBounce) {
200 swiper.updateProgress(afterBouncePosition);
201 swiper.setTransition(momentumDuration);
202 swiper.setTranslate(newPosition);
203 swiper.transitionStart(true, swiper.swipeDirection);
204 swiper.animating = true;
205 $wrapperEl.transitionEnd(() => {
206 if (!swiper || swiper.destroyed || !data.allowMomentumBounce) return;
207 emit('momentumBounce');
208 swiper.setTransition(params.speed);
209 setTimeout(() => {
210 swiper.setTranslate(afterBouncePosition);
211 $wrapperEl.transitionEnd(() => {
212 if (!swiper || swiper.destroyed) return;
213 swiper.transitionEnd();
214 });
215 }, 0);
216 });
217 } else if (swiper.velocity) {
218 emit('_freeModeNoMomentumRelease');
219 swiper.updateProgress(newPosition);
220 swiper.setTransition(momentumDuration);
221 swiper.setTranslate(newPosition);
222 swiper.transitionStart(true, swiper.swipeDirection);
223
224 if (!swiper.animating) {
225 swiper.animating = true;
226 $wrapperEl.transitionEnd(() => {
227 if (!swiper || swiper.destroyed) return;
228 swiper.transitionEnd();
229 });
230 }
231 } else {
232 swiper.updateProgress(newPosition);
233 }
234
235 swiper.updateActiveIndex();
236 swiper.updateSlidesClasses();
237 } else if (params.freeMode.sticky) {
238 swiper.slideToClosest();
239 return;
240 } else if (params.freeMode) {
241 emit('_freeModeNoMomentumRelease');
242 }
243
244 if (!params.freeMode.momentum || timeDiff >= params.longSwipesMs) {
245 swiper.updateProgress();
246 swiper.updateActiveIndex();
247 swiper.updateSlidesClasses();
248 }
249 }
250
251 Object.assign(swiper, {
252 freeMode: {
253 onTouchStart,
254 onTouchMove,
255 onTouchEnd
256 }
257 });
258}
\No newline at end of file