import './index.less'; import { defineComponent, ref, unref, computed, reactive, watchEffect } from 'vue'; // @ts-ignore import { basicProps } from './props'; // @ts-ignore import { Props } from './types'; import { CloseOutlined, LeftOutlined, RightOutlined } from '@ant-design/icons-vue'; // import { Spin } from 'ant-design-vue'; import resumeSvg from '/@/assets/svg/preview/resume.svg'; import rotateSvg from '/@/assets/svg/preview/p-rotate.svg'; import scaleSvg from '/@/assets/svg/preview/scale.svg'; import unScaleSvg from '/@/assets/svg/preview/unscale.svg'; import unRotateSvg from '/@/assets/svg/preview/unrotate.svg'; enum StatueEnum { LOADING, DONE, FAIL, } interface ImgState { currentUrl: string; imgScale: number; imgRotate: number; imgTop: number; imgLeft: number; currentIndex: number; status: StatueEnum; moveX: number; moveY: number; show: boolean; } const prefixCls = 'img-preview'; export default defineComponent({ name: 'ImagePreview', props: basicProps, setup(props: Props) { const imgState = reactive({ currentUrl: '', imgScale: 1, imgRotate: 0, imgTop: 0, imgLeft: 0, status: StatueEnum.LOADING, currentIndex: 0, moveX: 0, moveY: 0, show: props.show, }); const wrapElRef = ref(null); const imgElRef = ref(null); // 初始化 function init() { initMouseWheel(); const { index, imageList } = props; if (!imageList || !imageList.length) { throw new Error('imageList is undefined'); } imgState.currentIndex = index; handleIChangeImage(imageList[index]); } // 重置 function initState() { imgState.imgScale = 1; imgState.imgRotate = 0; imgState.imgTop = 0; imgState.imgLeft = 0; } // 初始化鼠标滚轮事件 function initMouseWheel() { const wrapEl = unref(wrapElRef); if (!wrapEl) { return; } (wrapEl as any).onmousewheel = scrollFunc; // 火狐浏览器没有onmousewheel事件,用DOMMouseScroll代替 document.body.addEventListener('DOMMouseScroll', scrollFunc); // 禁止火狐浏览器下拖拽图片的默认事件 document.ondragstart = function () { return false; }; } // 监听鼠标滚轮 function scrollFunc(e: any) { e = e || window.event; e.delta = e.wheelDelta || -e.detail; e.preventDefault(); if (e.delta > 0) { // 滑轮向上滚动 scaleFunc(0.015); } if (e.delta < 0) { // 滑轮向下滚动 scaleFunc(-0.015); } } // 缩放函数 function scaleFunc(num: number) { if (imgState.imgScale <= 0.2 && num < 0) return; imgState.imgScale += num; } // 旋转图片 function rotateFunc(deg: number) { imgState.imgRotate += deg; } // 鼠标事件 function handleMouseUp() { const imgEl = unref(imgElRef); if (!imgEl) return; imgEl.onmousemove = null; } // 更换图片 function handleIChangeImage(url: string) { imgState.status = StatueEnum.LOADING; const img = new Image(); img.src = url; img.onload = () => { imgState.currentUrl = url; imgState.status = StatueEnum.DONE; }; img.onerror = () => { imgState.status = StatueEnum.FAIL; }; } // 关闭 function handleClose(e: MouseEvent) { e && e.stopPropagation(); imgState.show = false; // 移除火狐浏览器下的鼠标滚动事件 document.body.removeEventListener('DOMMouseScroll', scrollFunc); // 恢复火狐及Safari浏览器下的图片拖拽 document.ondragstart = null; } // 图片复原 function resume() { initState(); } // 上一页下一页 function handleChange(direction: 'left' | 'right') { const { currentIndex } = imgState; const { imageList } = props; if (direction === 'left') { imgState.currentIndex--; if (currentIndex <= 0) { imgState.currentIndex = imageList.length - 1; } } if (direction === 'right') { imgState.currentIndex++; if (currentIndex >= imageList.length - 1) { imgState.currentIndex = 0; } } handleIChangeImage(imageList[imgState.currentIndex]); } function handleAddMoveListener(e: MouseEvent) { e = e || window.event; imgState.moveX = e.clientX; imgState.moveY = e.clientY; const imgEl = unref(imgElRef); if (imgEl) { imgEl.onmousemove = moveFunc; } } function moveFunc(e: MouseEvent) { e = e || window.event; e.preventDefault(); const movementX = e.clientX - imgState.moveX; const movementY = e.clientY - imgState.moveY; imgState.imgLeft += movementX; imgState.imgTop += movementY; imgState.moveX = e.clientX; imgState.moveY = e.clientY; } // 获取图片样式 const getImageStyle = computed(() => { const { imgScale, imgRotate, imgTop, imgLeft } = imgState; return { transform: `scale(${imgScale}) rotate(${imgRotate}deg)`, marginTop: `${imgTop}px`, marginLeft: `${imgLeft}px`, }; }); const getIsMultipleImage = computed(() => { const { imageList } = props; return imageList.length > 1; }); watchEffect(() => { if (props.show) { init(); } if (props.imageList) { initState(); } }); const renderClose = () => { return (
); }; const renderIndex = () => { if (!unref(getIsMultipleImage)) { return null; } const { currentIndex } = imgState; const { imageList } = props; return (
{currentIndex + 1} / {imageList.length}
); }; const renderController = () => { return (
scaleFunc(-0.15)}>
scaleFunc(0.15)}>
rotateFunc(-90)}>
rotateFunc(90)}>
); }; const renderArrow = (direction: 'left' | 'right') => { if (!unref(getIsMultipleImage)) { return null; } return (
handleChange(direction)}> {direction === 'left' ? : }
); }; return () => { return ( imgState.show && (
{/*}*/} {/* spinning={true}*/} {/* class={[*/} {/* `${prefixCls}-image`,*/} {/* {*/} {/* hidden: imgState.status !== StatueEnum.LOADING,*/} {/* },*/} {/* ]}*/} {/*/>*/} {renderClose()} {renderIndex()} {renderController()} {renderArrow('left')} {renderArrow('right')}
) ); }; }, });