uikit
Version:
UIkit is a lightweight and modular front-end framework for developing fast and powerful web interfaces.
193 lines (157 loc) • 5.56 kB
JavaScript
import {
children,
clamp,
createEvent,
css,
dimensions,
findIndex,
includes,
isRtl,
noop,
position,
sumBy,
Transition,
trigger,
} from 'uikit-util';
import { translate } from '../../mixin/internal/slideshow-animations';
export default function (prev, next, dir, { center, easing, list }) {
const from = prev
? getLeft(prev, list, center)
: getLeft(next, list, center) + dimensions(next).width * dir;
const to = next
? getLeft(next, list, center)
: from + dimensions(prev).width * dir * (isRtl ? -1 : 1);
let resolve;
return {
dir,
show(duration, percent = 0, linear) {
const timing = linear ? 'linear' : easing;
duration -= Math.round(duration * clamp(percent, -1, 1));
// Force previous transition to be canceled in Safari
css(list, 'transitionProperty', 'none');
this.translate(percent);
// Reset the forced transition property
css(list, 'transitionProperty', '');
percent = prev ? percent : clamp(percent, 0, 1);
triggerUpdate(this.getItemIn(), 'itemin', { percent, duration, timing, dir });
prev &&
triggerUpdate(this.getItemIn(true), 'itemout', {
percent: 1 - percent,
duration,
timing,
dir,
});
return new Promise((res) => {
resolve ||= res;
Transition.start(
list,
{ transform: translate(-to * (isRtl ? -1 : 1), 'px') },
duration,
timing,
).then(resolve, noop);
});
},
cancel() {
return Transition.cancel(list);
},
reset() {
css(list, 'transform', '');
},
async forward(duration, percent = this.percent()) {
await this.cancel();
return this.show(duration, percent, true);
},
translate(percent) {
if (percent === this.percent()) {
return;
}
const distance = this.getDistance() * dir * (isRtl ? -1 : 1);
css(
list,
'transform',
translate(
clamp(
-to + (distance - distance * percent),
-getWidth(list),
dimensions(list).width,
) * (isRtl ? -1 : 1),
'px',
),
);
const actives = this.getActives();
const itemIn = this.getItemIn();
const itemOut = this.getItemIn(true);
percent = prev ? clamp(percent, -1, 1) : 0;
for (const slide of children(list)) {
const isActive = includes(actives, slide);
const isIn = slide === itemIn;
const isOut = slide === itemOut;
const translateIn =
isIn ||
(!isOut &&
(isActive ||
(dir * (isRtl ? -1 : 1) === -1) ^
(getElLeft(slide, list) > getElLeft(prev || next))));
triggerUpdate(slide, `itemtranslate${translateIn ? 'in' : 'out'}`, {
dir,
percent: isOut ? 1 - percent : isIn ? percent : isActive ? 1 : 0,
});
}
},
percent() {
return Math.abs(
(css(list, 'transform').split(',')[4] * (isRtl ? -1 : 1) + from) / (to - from),
);
},
getDistance() {
return Math.abs(to - from);
},
getItemIn(out = false) {
let actives = this.getActives();
let nextActives = inView(list, getLeft(next || prev, list, center));
if (out) {
const temp = actives;
actives = nextActives;
nextActives = temp;
}
return nextActives[findIndex(nextActives, (el) => !includes(actives, el))];
},
getActives() {
return inView(list, getLeft(prev || next, list, center));
},
};
}
function getLeft(el, list, center) {
const left = getElLeft(el, list);
return center ? left - centerEl(el, list) : Math.min(left, getMax(list));
}
export function getMax(list) {
return Math.max(0, getWidth(list) - dimensions(list).width);
}
export function getWidth(list, index) {
return sumBy(children(list).slice(0, index), (el) => dimensions(el).width);
}
function centerEl(el, list) {
return dimensions(list).width / 2 - dimensions(el).width / 2;
}
function getElLeft(el, list) {
return (
(el &&
(position(el).left + (isRtl ? dimensions(el).width - dimensions(list).width : 0)) *
(isRtl ? -1 : 1)) ||
0
);
}
function inView(list, listLeft) {
listLeft -= 1;
const listWidth = dimensions(list).width;
const listRight = listLeft + listWidth + 2;
return children(list).filter((slide) => {
const slideLeft = getElLeft(slide, list);
const slideRight = slideLeft + Math.min(dimensions(slide).width, listWidth);
return slideLeft >= listLeft && slideRight <= listRight;
});
}
function triggerUpdate(el, type, data) {
trigger(el, createEvent(type, false, false, data));
}