ckgl/node_modules/vant/es/swipe/Swipe.mjs
2024-12-21 13:52:42 +08:00

376 lines
11 KiB
JavaScript

import { ref, watch, reactive, computed, onMounted, onActivated, onDeactivated, onBeforeUnmount, defineComponent, nextTick, createVNode as _createVNode } from "vue";
import { clamp, isHidden, truthProp, numericProp, windowWidth, windowHeight, preventDefault, createNamespace, makeNumericProp } from "../utils/index.mjs";
import { doubleRaf, useChildren, useEventListener, usePageVisibility } from "@vant/use";
import { useTouch } from "../composables/use-touch.mjs";
import { useExpose } from "../composables/use-expose.mjs";
import { onPopupReopen } from "../composables/on-popup-reopen.mjs";
const [name, bem] = createNamespace("swipe");
const swipeProps = {
loop: truthProp,
width: numericProp,
height: numericProp,
vertical: Boolean,
autoplay: makeNumericProp(0),
duration: makeNumericProp(500),
touchable: truthProp,
lazyRender: Boolean,
initialSwipe: makeNumericProp(0),
indicatorColor: String,
showIndicators: truthProp,
stopPropagation: truthProp
};
const SWIPE_KEY = Symbol(name);
var stdin_default = defineComponent({
name,
props: swipeProps,
emits: ["change", "dragStart", "dragEnd"],
setup(props, {
emit,
slots
}) {
const root = ref();
const track = ref();
const state = reactive({
rect: null,
width: 0,
height: 0,
offset: 0,
active: 0,
swiping: false
});
let dragging = false;
const touch = useTouch();
const {
children,
linkChildren
} = useChildren(SWIPE_KEY);
const count = computed(() => children.length);
const size = computed(() => state[props.vertical ? "height" : "width"]);
const delta = computed(() => props.vertical ? touch.deltaY.value : touch.deltaX.value);
const minOffset = computed(() => {
if (state.rect) {
const base = props.vertical ? state.rect.height : state.rect.width;
return base - size.value * count.value;
}
return 0;
});
const maxCount = computed(() => size.value ? Math.ceil(Math.abs(minOffset.value) / size.value) : count.value);
const trackSize = computed(() => count.value * size.value);
const activeIndicator = computed(() => (state.active + count.value) % count.value);
const isCorrectDirection = computed(() => {
const expect = props.vertical ? "vertical" : "horizontal";
return touch.direction.value === expect;
});
const trackStyle = computed(() => {
const style = {
transitionDuration: `${state.swiping ? 0 : props.duration}ms`,
transform: `translate${props.vertical ? "Y" : "X"}(${+state.offset.toFixed(2)}px)`
};
if (size.value) {
const mainAxis = props.vertical ? "height" : "width";
const crossAxis = props.vertical ? "width" : "height";
style[mainAxis] = `${trackSize.value}px`;
style[crossAxis] = props[crossAxis] ? `${props[crossAxis]}px` : "";
}
return style;
});
const getTargetActive = (pace) => {
const {
active
} = state;
if (pace) {
if (props.loop) {
return clamp(active + pace, -1, count.value);
}
return clamp(active + pace, 0, maxCount.value);
}
return active;
};
const getTargetOffset = (targetActive, offset = 0) => {
let currentPosition = targetActive * size.value;
if (!props.loop) {
currentPosition = Math.min(currentPosition, -minOffset.value);
}
let targetOffset = offset - currentPosition;
if (!props.loop) {
targetOffset = clamp(targetOffset, minOffset.value, 0);
}
return targetOffset;
};
const move = ({
pace = 0,
offset = 0,
emitChange
}) => {
if (count.value <= 1) {
return;
}
const {
active
} = state;
const targetActive = getTargetActive(pace);
const targetOffset = getTargetOffset(targetActive, offset);
if (props.loop) {
if (children[0] && targetOffset !== minOffset.value) {
const outRightBound = targetOffset < minOffset.value;
children[0].setOffset(outRightBound ? trackSize.value : 0);
}
if (children[count.value - 1] && targetOffset !== 0) {
const outLeftBound = targetOffset > 0;
children[count.value - 1].setOffset(outLeftBound ? -trackSize.value : 0);
}
}
state.active = targetActive;
state.offset = targetOffset;
if (emitChange && targetActive !== active) {
emit("change", activeIndicator.value);
}
};
const correctPosition = () => {
state.swiping = true;
if (state.active <= -1) {
move({
pace: count.value
});
} else if (state.active >= count.value) {
move({
pace: -count.value
});
}
};
const prev = () => {
correctPosition();
touch.reset();
doubleRaf(() => {
state.swiping = false;
move({
pace: -1,
emitChange: true
});
});
};
const next = () => {
correctPosition();
touch.reset();
doubleRaf(() => {
state.swiping = false;
move({
pace: 1,
emitChange: true
});
});
};
let autoplayTimer;
const stopAutoplay = () => clearTimeout(autoplayTimer);
const autoplay = () => {
stopAutoplay();
if (+props.autoplay > 0 && count.value > 1) {
autoplayTimer = setTimeout(() => {
next();
autoplay();
}, +props.autoplay);
}
};
const initialize = (active = +props.initialSwipe) => {
if (!root.value) {
return;
}
const cb = () => {
var _a, _b;
if (!isHidden(root)) {
const rect = {
width: root.value.offsetWidth,
height: root.value.offsetHeight
};
state.rect = rect;
state.width = +((_a = props.width) != null ? _a : rect.width);
state.height = +((_b = props.height) != null ? _b : rect.height);
}
if (count.value) {
active = Math.min(count.value - 1, active);
if (active === -1) {
active = count.value - 1;
}
}
state.active = active;
state.swiping = true;
state.offset = getTargetOffset(active);
children.forEach((swipe) => {
swipe.setOffset(0);
});
autoplay();
};
if (isHidden(root)) {
nextTick().then(cb);
} else {
cb();
}
};
const resize = () => initialize(state.active);
let touchStartTime;
const onTouchStart = (event) => {
if (!props.touchable || // avoid resetting position on multi-finger touch
event.touches.length > 1) return;
touch.start(event);
dragging = false;
touchStartTime = Date.now();
stopAutoplay();
correctPosition();
};
const onTouchMove = (event) => {
if (props.touchable && state.swiping) {
touch.move(event);
if (isCorrectDirection.value) {
const isEdgeTouch = !props.loop && (state.active === 0 && delta.value > 0 || state.active === count.value - 1 && delta.value < 0);
if (!isEdgeTouch) {
preventDefault(event, props.stopPropagation);
move({
offset: delta.value
});
if (!dragging) {
emit("dragStart", {
index: activeIndicator.value
});
dragging = true;
}
}
}
}
};
const onTouchEnd = () => {
if (!props.touchable || !state.swiping) {
return;
}
const duration = Date.now() - touchStartTime;
const speed = delta.value / duration;
const shouldSwipe = Math.abs(speed) > 0.25 || Math.abs(delta.value) > size.value / 2;
if (shouldSwipe && isCorrectDirection.value) {
const offset = props.vertical ? touch.offsetY.value : touch.offsetX.value;
let pace = 0;
if (props.loop) {
pace = offset > 0 ? delta.value > 0 ? -1 : 1 : 0;
} else {
pace = -Math[delta.value > 0 ? "ceil" : "floor"](delta.value / size.value);
}
move({
pace,
emitChange: true
});
} else if (delta.value) {
move({
pace: 0
});
}
dragging = false;
state.swiping = false;
emit("dragEnd", {
index: activeIndicator.value
});
autoplay();
};
const swipeTo = (index, options = {}) => {
correctPosition();
touch.reset();
doubleRaf(() => {
let targetIndex;
if (props.loop && index === count.value) {
targetIndex = state.active === 0 ? 0 : index;
} else {
targetIndex = index % count.value;
}
if (options.immediate) {
doubleRaf(() => {
state.swiping = false;
});
} else {
state.swiping = false;
}
move({
pace: targetIndex - state.active,
emitChange: true
});
});
};
const renderDot = (_, index) => {
const active = index === activeIndicator.value;
const style = active ? {
backgroundColor: props.indicatorColor
} : void 0;
return _createVNode("i", {
"style": style,
"class": bem("indicator", {
active
})
}, null);
};
const renderIndicator = () => {
if (slots.indicator) {
return slots.indicator({
active: activeIndicator.value,
total: count.value
});
}
if (props.showIndicators && count.value > 1) {
return _createVNode("div", {
"class": bem("indicators", {
vertical: props.vertical
})
}, [Array(count.value).fill("").map(renderDot)]);
}
};
useExpose({
prev,
next,
state,
resize,
swipeTo
});
linkChildren({
size,
props,
count,
activeIndicator
});
watch(() => props.initialSwipe, (value) => initialize(+value));
watch(count, () => initialize(state.active));
watch(() => props.autoplay, autoplay);
watch([windowWidth, windowHeight, () => props.width, () => props.height], resize);
watch(usePageVisibility(), (visible) => {
if (visible === "visible") {
autoplay();
} else {
stopAutoplay();
}
});
onMounted(initialize);
onActivated(() => initialize(state.active));
onPopupReopen(() => initialize(state.active));
onDeactivated(stopAutoplay);
onBeforeUnmount(stopAutoplay);
useEventListener("touchmove", onTouchMove, {
target: track
});
return () => {
var _a;
return _createVNode("div", {
"ref": root,
"class": bem()
}, [_createVNode("div", {
"ref": track,
"style": trackStyle.value,
"class": bem("track", {
vertical: props.vertical
}),
"onTouchstartPassive": onTouchStart,
"onTouchend": onTouchEnd,
"onTouchcancel": onTouchEnd
}, [(_a = slots.default) == null ? void 0 : _a.call(slots)]), renderIndicator()]);
};
}
});
export {
SWIPE_KEY,
stdin_default as default,
swipeProps
};