UNPKG

element-plus

Version:

A Component Library for Vue 3

1 lines 16.2 kB
{"version":3,"file":"image-viewer2.mjs","sources":["../../../../../../packages/components/image-viewer/src/image-viewer.vue"],"sourcesContent":["<template>\n <teleport to=\"body\" :disabled=\"!teleported\">\n <transition name=\"viewer-fade\" appear>\n <div\n ref=\"wrapper\"\n :tabindex=\"-1\"\n :class=\"ns.e('wrapper')\"\n :style=\"{ zIndex: computedZIndex }\"\n >\n <div :class=\"ns.e('mask')\" @click.self=\"hideOnClickModal && hide()\" />\n\n <!-- CLOSE -->\n <span :class=\"[ns.e('btn'), ns.e('close')]\" @click=\"hide\">\n <el-icon><close /></el-icon>\n </span>\n\n <!-- ARROW -->\n <template v-if=\"!isSingle\">\n <span\n :class=\"[\n ns.e('btn'),\n ns.e('prev'),\n ns.is('disabled', !infinite && isFirst),\n ]\"\n @click=\"prev\"\n >\n <el-icon><arrow-left /></el-icon>\n </span>\n <span\n :class=\"[\n ns.e('btn'),\n ns.e('next'),\n ns.is('disabled', !infinite && isLast),\n ]\"\n @click=\"next\"\n >\n <el-icon><arrow-right /></el-icon>\n </span>\n </template>\n <!-- ACTIONS -->\n <div :class=\"[ns.e('btn'), ns.e('actions')]\">\n <div :class=\"ns.e('actions__inner')\">\n <el-icon @click=\"handleActions('zoomOut')\">\n <zoom-out />\n </el-icon>\n <el-icon @click=\"handleActions('zoomIn')\">\n <zoom-in />\n </el-icon>\n <i :class=\"ns.e('actions__divider')\"></i>\n <el-icon @click=\"toggleMode\">\n <component :is=\"mode.icon\" />\n </el-icon>\n <i :class=\"ns.e('actions__divider')\"></i>\n <el-icon @click=\"handleActions('anticlockwise')\">\n <refresh-left />\n </el-icon>\n <el-icon @click=\"handleActions('clockwise')\">\n <refresh-right />\n </el-icon>\n </div>\n </div>\n <!-- CANVAS -->\n <div :class=\"ns.e('canvas')\">\n <img\n v-for=\"(url, i) in urlList\"\n v-show=\"i === index\"\n :ref=\"(el) => (imgRefs[i] = el)\"\n :key=\"url\"\n :src=\"url\"\n :style=\"imgStyle\"\n :class=\"ns.e('img')\"\n @load=\"handleImgLoad\"\n @error=\"handleImgError\"\n @mousedown=\"handleMouseDown\"\n />\n </div>\n <slot />\n </div>\n </transition>\n </teleport>\n</template>\n\n<script lang=\"ts\">\nimport {\n defineComponent,\n computed,\n ref,\n onMounted,\n watch,\n nextTick,\n effectScope,\n markRaw,\n} from 'vue'\nimport { useEventListener, isNumber } from '@vueuse/core'\nimport { throttle } from 'lodash-unified'\nimport ElIcon from '@element-plus/components/icon'\nimport { useLocale, useNamespace, useZIndex } from '@element-plus/hooks'\nimport { EVENT_CODE } from '@element-plus/constants'\nimport { isFirefox } from '@element-plus/utils'\nimport {\n Close,\n ArrowLeft,\n ArrowRight,\n ZoomOut,\n ZoomIn,\n RefreshLeft,\n RefreshRight,\n FullScreen,\n ScaleToOriginal,\n} from '@element-plus/icons-vue'\nimport { imageViewerProps, imageViewerEmits } from './image-viewer'\n\nimport type { CSSProperties } from 'vue'\n\nconst Mode = {\n CONTAIN: {\n name: 'contain',\n icon: markRaw(FullScreen),\n },\n ORIGINAL: {\n name: 'original',\n icon: markRaw(ScaleToOriginal),\n },\n}\n\nconst mousewheelEventName = isFirefox() ? 'DOMMouseScroll' : 'mousewheel'\nexport type ImageViewerAction =\n | 'zoomIn'\n | 'zoomOut'\n | 'clockwise'\n | 'anticlockwise'\n\nexport default defineComponent({\n name: 'ElImageViewer',\n components: {\n ElIcon,\n Close,\n ArrowLeft,\n ArrowRight,\n ZoomOut,\n ZoomIn,\n RefreshLeft,\n RefreshRight,\n },\n props: imageViewerProps,\n emits: imageViewerEmits,\n\n setup(props, { emit }) {\n const { t } = useLocale()\n const ns = useNamespace('image-viewer')\n const { nextZIndex } = useZIndex()\n const wrapper = ref<HTMLDivElement>()\n const imgRefs = ref<any[]>([])\n\n const scopeEventListener = effectScope()\n\n const loading = ref(true)\n const index = ref(props.initialIndex)\n const mode = ref(Mode.CONTAIN)\n const transform = ref({\n scale: 1,\n deg: 0,\n offsetX: 0,\n offsetY: 0,\n enableTransition: false,\n })\n\n const isSingle = computed(() => {\n const { urlList } = props\n return urlList.length <= 1\n })\n\n const isFirst = computed(() => {\n return index.value === 0\n })\n\n const isLast = computed(() => {\n return index.value === props.urlList.length - 1\n })\n\n const currentImg = computed(() => {\n return props.urlList[index.value]\n })\n\n const imgStyle = computed(() => {\n const { scale, deg, offsetX, offsetY, enableTransition } = transform.value\n let translateX = offsetX / scale\n let translateY = offsetY / scale\n\n switch (deg % 360) {\n case 90:\n case -270:\n ;[translateX, translateY] = [translateY, -translateX]\n break\n case 180:\n case -180:\n ;[translateX, translateY] = [-translateX, -translateY]\n break\n case 270:\n case -90:\n ;[translateX, translateY] = [-translateY, translateX]\n break\n }\n\n const style: CSSProperties = {\n transform: `scale(${scale}) rotate(${deg}deg) translate(${translateX}px, ${translateY}px)`,\n transition: enableTransition ? 'transform .3s' : '',\n }\n if (mode.value.name === Mode.CONTAIN.name) {\n style.maxWidth = style.maxHeight = '100%'\n }\n return style\n })\n\n const computedZIndex = computed(() => {\n return isNumber(props.zIndex) ? props.zIndex : nextZIndex()\n })\n\n function hide() {\n unregisterEventListener()\n emit('close')\n }\n\n function registerEventListener() {\n const keydownHandler = throttle((e: KeyboardEvent) => {\n switch (e.code) {\n // ESC\n case EVENT_CODE.esc:\n hide()\n break\n // SPACE\n case EVENT_CODE.space:\n toggleMode()\n break\n // LEFT_ARROW\n case EVENT_CODE.left:\n prev()\n break\n // UP_ARROW\n case EVENT_CODE.up:\n handleActions('zoomIn')\n break\n // RIGHT_ARROW\n case EVENT_CODE.right:\n next()\n break\n // DOWN_ARROW\n case EVENT_CODE.down:\n handleActions('zoomOut')\n break\n }\n })\n const mousewheelHandler = throttle(\n (e: WheelEvent | any /* TODO: wheelDelta is deprecated */) => {\n const delta = e.wheelDelta ? e.wheelDelta : -e.detail\n if (delta > 0) {\n handleActions('zoomIn', {\n zoomRate: 1.2,\n enableTransition: false,\n })\n } else {\n handleActions('zoomOut', {\n zoomRate: 1.2,\n enableTransition: false,\n })\n }\n }\n )\n\n scopeEventListener.run(() => {\n useEventListener(document, 'keydown', keydownHandler)\n useEventListener(document, mousewheelEventName, mousewheelHandler)\n })\n }\n\n function unregisterEventListener() {\n scopeEventListener.stop()\n }\n\n function handleImgLoad() {\n loading.value = false\n }\n\n function handleImgError(e: Event) {\n loading.value = false\n ;(e.target as HTMLImageElement).alt = t('el.image.error')\n }\n\n function handleMouseDown(e: MouseEvent) {\n if (loading.value || e.button !== 0 || !wrapper.value) return\n transform.value.enableTransition = false\n\n const { offsetX, offsetY } = transform.value\n const startX = e.pageX\n const startY = e.pageY\n\n const dragHandler = throttle((ev: MouseEvent) => {\n transform.value = {\n ...transform.value,\n offsetX: offsetX + ev.pageX - startX,\n offsetY: offsetY + ev.pageY - startY,\n }\n })\n const removeMousemove = useEventListener(\n document,\n 'mousemove',\n dragHandler\n )\n useEventListener(document, 'mouseup', () => {\n removeMousemove()\n })\n\n e.preventDefault()\n }\n\n function reset() {\n transform.value = {\n scale: 1,\n deg: 0,\n offsetX: 0,\n offsetY: 0,\n enableTransition: false,\n }\n }\n\n function toggleMode() {\n if (loading.value) return\n\n const modeNames = Object.keys(Mode)\n const modeValues = Object.values(Mode)\n const currentMode = mode.value.name\n const index = modeValues.findIndex((i) => i.name === currentMode)\n const nextIndex = (index + 1) % modeNames.length\n mode.value = Mode[modeNames[nextIndex]]\n reset()\n }\n\n function prev() {\n if (isFirst.value && !props.infinite) return\n const len = props.urlList.length\n index.value = (index.value - 1 + len) % len\n }\n\n function next() {\n if (isLast.value && !props.infinite) return\n const len = props.urlList.length\n index.value = (index.value + 1) % len\n }\n\n function handleActions(action: ImageViewerAction, options = {}) {\n if (loading.value) return\n const { zoomRate, rotateDeg, enableTransition } = {\n zoomRate: 1.4,\n rotateDeg: 90,\n enableTransition: true,\n ...options,\n }\n switch (action) {\n case 'zoomOut':\n if (transform.value.scale > 0.2) {\n transform.value.scale = parseFloat(\n (transform.value.scale / zoomRate).toFixed(3)\n )\n }\n break\n case 'zoomIn':\n if (transform.value.scale < 7) {\n transform.value.scale = parseFloat(\n (transform.value.scale * zoomRate).toFixed(3)\n )\n }\n break\n case 'clockwise':\n transform.value.deg += rotateDeg\n break\n case 'anticlockwise':\n transform.value.deg -= rotateDeg\n break\n }\n transform.value.enableTransition = enableTransition\n }\n\n watch(currentImg, () => {\n nextTick(() => {\n const $img = imgRefs.value[0]\n if (!$img?.complete) {\n loading.value = true\n }\n })\n })\n\n watch(index, (val) => {\n reset()\n emit('switch', val)\n })\n\n onMounted(() => {\n registerEventListener()\n // add tabindex then wrapper can be focusable via Javascript\n // focus wrapper so arrow key can't cause inner scroll behavior underneath\n wrapper.value?.focus?.()\n })\n\n return {\n index,\n wrapper,\n imgRefs,\n isSingle,\n isFirst,\n isLast,\n currentImg,\n imgStyle,\n mode,\n computedZIndex,\n handleActions,\n prev,\n next,\n hide,\n toggleMode,\n handleImgLoad,\n handleImgError,\n handleMouseDown,\n ns,\n }\n },\n})\n</script>\n"],"names":["_normalizeStyle"],"mappings":"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"}