1 | import {translate} from '../../mixin/internal/slideshow-animations';
|
2 | import {clamp, createEvent, css, Deferred, includes, index, isRtl, noop, position, sortBy, toNodes, Transition, trigger} from 'uikit-util';
|
3 |
|
4 | export default function (prev, next, dir, {center, easing, list}) {
|
5 |
|
6 | const deferred = new Deferred();
|
7 |
|
8 | const from = prev
|
9 | ? getLeft(prev, list, center)
|
10 | : getLeft(next, list, center) + bounds(next).width * dir;
|
11 | const to = next
|
12 | ? getLeft(next, list, center)
|
13 | : from + bounds(prev).width * dir * (isRtl ? -1 : 1);
|
14 |
|
15 | return {
|
16 |
|
17 | dir,
|
18 |
|
19 | show(duration, percent = 0, linear) {
|
20 |
|
21 | const timing = linear ? 'linear' : easing;
|
22 | duration -= Math.round(duration * clamp(percent, -1, 1));
|
23 |
|
24 | this.translate(percent);
|
25 |
|
26 | prev && this.updateTranslates();
|
27 | percent = prev ? percent : clamp(percent, 0, 1);
|
28 | triggerUpdate(this.getItemIn(), 'itemin', {percent, duration, timing, dir});
|
29 | prev && triggerUpdate(this.getItemIn(true), 'itemout', {percent: 1 - percent, duration, timing, dir});
|
30 |
|
31 | Transition
|
32 | .start(list, {transform: translate(-to * (isRtl ? -1 : 1), 'px')}, duration, timing)
|
33 | .then(deferred.resolve, noop);
|
34 |
|
35 | return deferred.promise;
|
36 |
|
37 | },
|
38 |
|
39 | stop() {
|
40 | return Transition.stop(list);
|
41 | },
|
42 |
|
43 | cancel() {
|
44 | Transition.cancel(list);
|
45 | },
|
46 |
|
47 | reset() {
|
48 | css(list, 'transform', '');
|
49 | },
|
50 |
|
51 | forward(duration, percent = this.percent()) {
|
52 | Transition.cancel(list);
|
53 | return this.show(duration, percent, true);
|
54 | },
|
55 |
|
56 | translate(percent) {
|
57 |
|
58 | const distance = this.getDistance() * dir * (isRtl ? -1 : 1);
|
59 |
|
60 | css(list, 'transform', translate(clamp(
|
61 | -to + (distance - distance * percent),
|
62 | -getWidth(list),
|
63 | bounds(list).width
|
64 | ) * (isRtl ? -1 : 1), 'px'));
|
65 |
|
66 | this.updateTranslates();
|
67 |
|
68 | if (prev) {
|
69 | percent = clamp(percent, -1, 1);
|
70 | triggerUpdate(this.getItemIn(), 'itemtranslatein', {percent, dir});
|
71 | triggerUpdate(this.getItemIn(true), 'itemtranslateout', {percent: 1 - percent, dir});
|
72 | }
|
73 |
|
74 | },
|
75 |
|
76 | percent() {
|
77 | return Math.abs((css(list, 'transform').split(',')[4] * (isRtl ? -1 : 1) + from) / (to - from));
|
78 | },
|
79 |
|
80 | getDistance() {
|
81 | return Math.abs(to - from);
|
82 | },
|
83 |
|
84 | getItemIn(out = false) {
|
85 |
|
86 | const actives = this.getActives();
|
87 | const all = sortBy(slides(list), 'offsetLeft');
|
88 | const i = index(all, actives[dir * (out ? -1 : 1) > 0 ? actives.length - 1 : 0]);
|
89 |
|
90 | return ~i && all[i + (prev && !out ? dir : 0)];
|
91 |
|
92 | },
|
93 |
|
94 | getActives() {
|
95 |
|
96 | const left = getLeft(prev || next, list, center);
|
97 |
|
98 | return sortBy(slides(list).filter(slide => {
|
99 | const slideLeft = getElLeft(slide, list);
|
100 | return slideLeft >= left && slideLeft + bounds(slide).width <= bounds(list).width + left;
|
101 | }), 'offsetLeft');
|
102 |
|
103 | },
|
104 |
|
105 | updateTranslates() {
|
106 |
|
107 | const actives = this.getActives();
|
108 |
|
109 | slides(list).forEach(slide => {
|
110 | const isActive = includes(actives, slide);
|
111 |
|
112 | triggerUpdate(slide, `itemtranslate${isActive ? 'in' : 'out'}`, {
|
113 | percent: isActive ? 1 : 0,
|
114 | dir: slide.offsetLeft <= next.offsetLeft ? 1 : -1
|
115 | });
|
116 | });
|
117 | }
|
118 |
|
119 | };
|
120 |
|
121 | }
|
122 |
|
123 | function getLeft(el, list, center) {
|
124 |
|
125 | const left = getElLeft(el, list);
|
126 |
|
127 | return center
|
128 | ? left - centerEl(el, list)
|
129 | : Math.min(left, getMax(list));
|
130 |
|
131 | }
|
132 |
|
133 | export function getMax(list) {
|
134 | return Math.max(0, getWidth(list) - bounds(list).width);
|
135 | }
|
136 |
|
137 | export function getWidth(list) {
|
138 | return slides(list).reduce((right, el) => bounds(el).width + right, 0);
|
139 | }
|
140 |
|
141 | export function getMaxWidth(list) {
|
142 | return slides(list).reduce((right, el) => Math.max(right, bounds(el).width), 0);
|
143 | }
|
144 |
|
145 | function centerEl(el, list) {
|
146 | return bounds(list).width / 2 - bounds(el).width / 2;
|
147 | }
|
148 |
|
149 | export function getElLeft(el, list) {
|
150 | return (position(el).left + (isRtl ? bounds(el).width - bounds(list).width : 0)) * (isRtl ? -1 : 1);
|
151 | }
|
152 |
|
153 | export function bounds(el) {
|
154 | return el.getBoundingClientRect();
|
155 | }
|
156 |
|
157 | function triggerUpdate(el, type, data) {
|
158 | trigger(el, createEvent(type, false, false, data));
|
159 | }
|
160 |
|
161 | function slides(list) {
|
162 | return toNodes(list.children);
|
163 | }
|