UNPKG

@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
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 };