@varlet/ui
Version:
A Vue3 component library based on Material Design 2 and 3, supporting mobile and desktop.
337 lines (336 loc) • 12.1 kB
JavaScript
import { computed, defineComponent, ref } from "vue";
import { call, clamp, preventDefault, toNumber } from "@varlet/shared";
import { useEventListener, useTouch, useVModel } from "@varlet/use";
import VarIcon from "../icon/index.mjs";
import VarPopup from "../popup/index.mjs";
import VarSwipe from "../swipe/index.mjs";
import VarSwipeItem from "../swipe-item/index.mjs";
import { createNamespace } from "../utils/components.mjs";
import { props } from "./props.mjs";
const { name, n, classes } = createNamespace("image-preview");
const DISTANCE_OFFSET = 12;
const EVENT_DELAY = 200;
const TAP_DELAY = 350;
const ANIMATION_DURATION = 200;
const LONG_PRESS_DELAY = 500;
const BASE_RATIO = 1;
import { renderList as _renderList, Fragment as _Fragment, openBlock as _openBlock, createElementBlock as _createElementBlock, normalizeClass as _normalizeClass, createElementVNode as _createElementVNode, normalizeStyle as _normalizeStyle, resolveComponent as _resolveComponent, withCtx as _withCtx, createBlock as _createBlock, renderSlot as _renderSlot, toDisplayString as _toDisplayString, createCommentVNode as _createCommentVNode, mergeProps as _mergeProps, createVNode as _createVNode } from "vue";
const _hoisted_1 = ["onTouchstart"];
const _hoisted_2 = ["src", "alt"];
function __render__(_ctx, _cache) {
const _component_var_swipe_item = _resolveComponent("var-swipe-item");
const _component_var_swipe = _resolveComponent("var-swipe");
const _component_var_icon = _resolveComponent("var-icon");
const _component_var_popup = _resolveComponent("var-popup");
return _openBlock(), _createBlock(_component_var_popup, {
show: _ctx.show,
"onUpdate:show": _cache[3] || (_cache[3] = ($event) => _ctx.show = $event),
"var-image-preview-cover": "",
class: _normalizeClass(_ctx.n("popup")),
transition: _ctx.n("$-fade"),
overlay: false,
"close-on-click-overlay": false,
"close-on-key-escape": _ctx.closeOnKeyEscape,
"lock-scroll": _ctx.lockScroll,
teleport: _ctx.teleport,
onOpen: _ctx.onOpen,
onClose: _ctx.onClose,
onClosed: _ctx.onClosed,
onOpened: _ctx.onOpened,
onKeyEscape: _ctx.onKeyEscape,
onRouteChange: _ctx.onRouteChange
}, {
default: _withCtx(() => [
_createVNode(_component_var_swipe, _mergeProps({
ref: "swipeRef",
class: _ctx.n("swipe"),
"var-image-preview-cover": "",
touchable: _ctx.canSwipe,
indicator: _ctx.indicator && _ctx.images.length > 1,
"initial-index": _ctx.toNumber(_ctx.initialIndex),
loop: _ctx.loop
}, _ctx.$attrs, { onChange: _ctx.onChange }), {
default: _withCtx(() => [
(_openBlock(true), _createElementBlock(
_Fragment,
null,
_renderList(_ctx.images, (image, idx) => {
return _openBlock(), _createBlock(_component_var_swipe_item, {
key: image,
class: _normalizeClass(_ctx.n("swipe-item")),
"var-image-preview-cover": ""
}, {
default: _withCtx(() => [
_createElementVNode("div", {
class: _normalizeClass(_ctx.n("zoom-container")),
style: _normalizeStyle({
transform: `scale(${_ctx.scale}) translate(${_ctx.translateX}px, ${_ctx.translateY}px)`,
transitionTimingFunction: _ctx.transitionTimingFunction,
transitionDuration: _ctx.transitionDuration
}),
onTouchstart: ($event) => _ctx.handleTouchstart($event, idx),
onTouchmove: _cache[0] || (_cache[0] = (...args) => _ctx.handleTouchmove && _ctx.handleTouchmove(...args)),
onTouchend: _cache[1] || (_cache[1] = (...args) => _ctx.handleTouchend && _ctx.handleTouchend(...args)),
onTouchcancel: _cache[2] || (_cache[2] = (...args) => _ctx.handleTouchcancel && _ctx.handleTouchcancel(...args))
}, [
_createElementVNode("img", {
role: "img",
class: _normalizeClass(_ctx.classes(_ctx.n("image"), [_ctx.isPreventDefault, _ctx.n("--prevent")])),
src: image,
alt: image
}, null, 10, _hoisted_2)
], 46, _hoisted_1)
]),
_: 2
/* DYNAMIC */
}, 1032, ["class"]);
}),
128
/* KEYED_FRAGMENT */
))
]),
indicator: _withCtx(({ index, length }) => [
_renderSlot(_ctx.$slots, "indicator", {
index,
length
}, () => [
_ctx.indicator && _ctx.images.length > 1 ? (_openBlock(), _createElementBlock(
"div",
{
key: 0,
class: _normalizeClass(_ctx.n("indicators"))
},
_toDisplayString(index + 1) + " / " + _toDisplayString(length),
3
/* TEXT, CLASS */
)) : _createCommentVNode("v-if", true)
])
]),
_: 3
/* FORWARDED */
}, 16, ["class", "touchable", "indicator", "initial-index", "loop", "onChange"]),
_renderSlot(_ctx.$slots, "close-icon", {}, () => [
_ctx.closeable ? (_openBlock(), _createBlock(_component_var_icon, {
key: 0,
class: _normalizeClass(_ctx.n("close-icon")),
name: "close-circle",
"var-image-preview-cover": "",
onClick: _ctx.close
}, null, 8, ["class", "onClick"])) : _createCommentVNode("v-if", true)
]),
_ctx.$slots.extra ? (_openBlock(), _createElementBlock(
"div",
{
key: 0,
class: _normalizeClass(_ctx.n("extra"))
},
[
_renderSlot(_ctx.$slots, "extra")
],
2
/* CLASS */
)) : _createCommentVNode("v-if", true)
]),
_: 3
/* FORWARDED */
}, 8, ["show", "class", "transition", "close-on-key-escape", "lock-scroll", "teleport", "onOpen", "onClose", "onClosed", "onOpened", "onKeyEscape", "onRouteChange"]);
}
const __sfc__ = defineComponent({
name,
components: {
VarSwipe,
VarSwipeItem,
VarPopup,
VarIcon
},
inheritAttrs: false,
props,
setup(props2) {
const show = useVModel(props2, "show");
const scale = ref(1);
const translateX = ref(0);
const translateY = ref(0);
const transitionTimingFunction = ref();
const transitionDuration = ref();
const canSwipe = ref(true);
const swipeRef = ref(null);
const { moveX, moveY, distance, startTime, startTouch, moveTouch, endTouch } = useTouch();
const isPreventDefault = computed(() => {
const { imagePreventDefault, show: show2 } = props2;
return show2 && imagePreventDefault;
});
let closeRunner = null;
let longPressRunner = null;
let isLongPress = false;
const targets = {
start: null,
prev: null
};
useEventListener(() => document, "contextmenu", preventImageDefault);
function zoomIn(ratio) {
scale.value = toNumber(ratio);
canSwipe.value = false;
targets.prev = null;
window.setTimeout(() => {
transitionTimingFunction.value = "linear";
transitionDuration.value = "0s";
}, ANIMATION_DURATION);
}
function zoomOut() {
scale.value = 1;
translateX.value = 0;
translateY.value = 0;
canSwipe.value = true;
targets.prev = null;
transitionTimingFunction.value = void 0;
transitionDuration.value = void 0;
}
function isDoubleTouch(target) {
if (!targets.prev) {
return false;
}
return distance.value <= DISTANCE_OFFSET && performance.now() - startTime.value <= EVENT_DELAY && targets.prev === target;
}
function isTapTouch(target) {
if (!target || !targets.start || !targets.prev) {
return false;
}
return distance.value <= DISTANCE_OFFSET && performance.now() - startTime.value < TAP_DELAY && (target === targets.start || target.parentNode === targets.start);
}
function handleTouchcancel() {
endTouch();
window.clearTimeout(longPressRunner);
isLongPress = false;
targets.start = null;
}
function handleTouchend(event) {
endTouch();
window.clearTimeout(longPressRunner);
if (isLongPress) {
isLongPress = false;
return;
}
const isTap = isTapTouch(event.target);
closeRunner = window.setTimeout(() => {
isTap && close();
targets.start = null;
}, EVENT_DELAY);
}
function handleTouchstart(event, idx) {
window.clearTimeout(closeRunner);
window.clearTimeout(longPressRunner);
const target = event.currentTarget;
targets.start = target;
longPressRunner = window.setTimeout(() => {
isLongPress = true;
call(props2.onLongPress, idx);
}, LONG_PRESS_DELAY);
if (isDoubleTouch(target)) {
scale.value > BASE_RATIO ? zoomOut() : zoomIn(props2.zoom);
return;
}
startTouch(event);
targets.prev = target;
}
function getZoom(target) {
const { offsetWidth, offsetHeight } = target;
const { naturalWidth, naturalHeight } = target.querySelector(`.${n("image")}`);
return {
width: offsetWidth,
height: offsetHeight,
imageRadio: naturalHeight / naturalWidth,
rootRadio: offsetHeight / offsetWidth,
zoom: toNumber(props2.zoom)
};
}
function getLimitX(target) {
const { zoom: zoom2, imageRadio, rootRadio, width, height } = getZoom(target);
if (!imageRadio) {
return 0;
}
const displayWidth = imageRadio > rootRadio ? height / imageRadio : width;
return Math.max(0, (zoom2 * displayWidth - width) / 2) / zoom2;
}
function getLimitY(target) {
const { zoom: zoom2, imageRadio, rootRadio, width, height } = getZoom(target);
if (!imageRadio) {
return 0;
}
const displayHeight = imageRadio > rootRadio ? height : width * imageRadio;
return Math.max(0, (zoom2 * displayHeight - height) / 2) / zoom2;
}
function handleTouchmove(event) {
if (!targets.prev) {
return;
}
moveTouch(event);
const target = event.currentTarget;
if (distance.value > DISTANCE_OFFSET) {
window.clearTimeout(longPressRunner);
}
if (scale.value > BASE_RATIO) {
const limitX = getLimitX(target);
const limitY = getLimitY(target);
translateX.value = clamp(translateX.value + moveX.value, -limitX, limitX);
translateY.value = clamp(translateY.value + moveY.value, -limitY, limitY);
}
targets.prev = target;
}
function close() {
if (scale.value > BASE_RATIO) {
zoomOut();
setTimeout(() => call(props2["onUpdate:show"], false), ANIMATION_DURATION);
return;
}
call(props2["onUpdate:show"], false);
}
function preventImageDefault(event) {
if (isPreventDefault.value) {
preventDefault(event);
}
}
function prev(options) {
var _a;
(_a = swipeRef.value) == null ? void 0 : _a.prev(options);
}
function next(options) {
var _a;
(_a = swipeRef.value) == null ? void 0 : _a.next(options);
}
function to(idx, options) {
var _a;
(_a = swipeRef.value) == null ? void 0 : _a.to(idx, options);
}
function zoom(ratio) {
ratio <= BASE_RATIO ? zoomOut() : zoomIn(ratio);
}
return {
swipeRef,
isPreventDefault,
show,
scale,
translateX,
translateY,
canSwipe,
transitionTimingFunction,
transitionDuration,
n,
classes,
toNumber,
handleTouchstart,
handleTouchmove,
handleTouchend,
handleTouchcancel,
close,
prev,
next,
to,
zoom
};
}
});
__sfc__.render = __render__;
var stdin_default = __sfc__;
export {
stdin_default as default
};