import { Show, onCleanup, onMount, createEffect } from "solid-js"; import { Spin } from "../Spin"; import usePortal from "../utils/usePortal"; import { Portal } from "solid-js/web"; import { createStore } from "solid-js/store"; import { Space } from "../Layout"; import { Icon } from "../Icon"; import createModel from "../utils/createModel"; import usezIndex from "../utils/usezIndex"; type ImagePreviewProps = { classList?: any, class?: string, style?: any, failInfo?: string, previewList: string[], infinite?: boolean, onClose?: Function, visible?: Function[], onSwitch?: Function, maskClosable?: boolean, initIndex?: number }; export async function downloadFile(url: string, name = 'unnamed') { try { const res = await fetch(url); const blob = await res.blob(); if (!blob) return Promise.reject(); const localUrl = URL.createObjectURL(blob); const a = document.createElement('a'); a.setAttribute('href', localUrl); a.setAttribute('download', name); a.click(); URL.revokeObjectURL(localUrl); return Promise.resolve(); } catch(e) { return Promise.reject(e); } } export function ImagePreview (props: ImagePreviewProps) { const [visible, setVisible] = createModel(props, 'visible', false); const zindex = usezIndex(); const [store, setStore] = createStore({ transition: true, original: false, translate: {x: 0, y: 0}, currentIndex: props.initIndex || 0, scale: 1, degree: 0, startX: 0, startY: 0, prevOverflow: '', // prevent body scrolling status: 'loading', // image status downloading: false }); const maskClosable = props.maskClosable ?? true; const infinite = props.infinite ?? true; const failInfo = props.failInfo ?? '失败'; const handleClickMask = (e: any) => { e.preventDefault && e.preventDefault(); e.stopPropagation && e.stopPropagation(); if (maskClosable) { handleClose(e); } } // 显示的时候如果有默认index则重新设置currentIndex createEffect(() => { let v = visible(); if (v) { setStore('currentIndex', props.initIndex || 0); resetStyle(); setStore('original', false); } }); createEffect(() => { store.currentIndex; setStore('status', 'loading'); }); const handleMousedown = (e: any) => { e.preventDefault && e.preventDefault(); e.stopPropagation && e.stopPropagation(); const { pageX, pageY, which } = e; if (which !== 1) return; setStore('startX', pageX); setStore('startY', pageY); setStore('transition', false); document.addEventListener('mousemove', handleMousemove); document.addEventListener('mouseup', handleMouseup); } const handleMousemove = (event: any) => { event.stopPropagation(); const { pageX, pageY } = event; const x = store.translate.x + (pageX - store.startX); const y = store.translate.y + (pageY - store.startY); setStore('translate', 'x', x); setStore('translate', 'y', y); setStore('startX', pageX); setStore('startY', pageY); } const handleMouseup = () => { setStore('transition', true); document.removeEventListener('mousemove', handleMousemove); document.removeEventListener('mouseup', handleMouseup); } const handleKeydown = (e: any) => { if (!visible()) return; const { keyCode } = e; if (keyCode === 37) handleSwitch(false); if (keyCode === 39) handleSwitch(true); if (keyCode === 38) handleOperation(e, 'zoomIn'); if (keyCode === 40) handleOperation(e, 'zoomOut'); if (keyCode === 32) { e.preventDefault && e.preventDefault(); setStore('original', !store.original); } } const handleKeyup = (e: any) => { if (!visible()) return; const { keyCode } = e; if (keyCode === 27) handleClose(e); } const handleWheel = (e: any) => { if (!visible()) return; e.preventDefault && e.preventDefault(); e.stopPropagation && e.stopPropagation(); e.stopImmediatePropagation && e.stopImmediatePropagation(); handleOperation(e, e.deltaY < 0 ? 'zoomIn' : 'zoomOut'); return false; } onMount(() => { document.addEventListener('wheel', handleWheel, {passive: false}); document.addEventListener('keydown', handleKeydown); document.addEventListener('keyup', handleKeyup); }) onCleanup(() => { document.removeEventListener('mousemove', handleMousemove); document.removeEventListener('mouseup', handleMouseup); document.removeEventListener('wheel', handleWheel); document.removeEventListener('keydown', handleKeydown); document.removeEventListener('keyup', handleKeyup); }) const handleImageLoad = () => { setStore('status', 'loaded'); } const handleImageError = () => { setStore('status', 'failed'); } const handleOperation = (e: any, op: string) => { e.stopPropagation && e.stopPropagation(); if (op === 'zoomIn' && store.scale < 6) { setStore('scale', store.scale + 0.25); } if (op === 'zoomOut' && store.scale > 0.25) { setStore('scale', store.scale - 0.25); } if (op === 'rotateLeft') { setStore('degree', store.degree - 90); } if (op === 'rotateRight') { setStore('degree', store.degree + 90); } if (op === 'original') { setStore('original', !store.original); setStore('transition', false); resetStyle(); setTimeout(() => { setStore('transition', true); }, 0); } if (op === 'download') { setStore('downloading', true); downloadFile(props.previewList[store.currentIndex]).then(() => { setStore('downloading', false); }).catch(() => { setStore('downloading', false); }); } } const resetStyle = () => { setStore('scale', 1); setStore('degree', 0); setStore('translate', 'x', 0); setStore('translate', 'y', 0); } // 左右切换 const handleSwitch = (next: boolean) => { if (next) { if (store.currentIndex + 1 === props.previewList.length) { if (infinite) { resetStyle(); setStore('currentIndex', 0) } } else { resetStyle(); setStore('currentIndex', store.currentIndex + 1); } } else { if (store.currentIndex === 0) { if (infinite) { resetStyle(); setStore('currentIndex', props.previewList.length - 1); } } else { resetStyle(); setStore('currentIndex', store.currentIndex - 1); } } props.onSwitch && props.onSwitch(store.currentIndex); } const handleClose = (e: any) => { setVisible(false); e.stopPropagation && e.stopPropagation(); props.onClose && props.onClose(); } const imgClasses = () => ({ 'cm-image-preview-image': true, 'cm-image-preview-grabbing': !store.transition, 'cm-image-preview-hidden': store.status === 'failed', 'cm-image-preview-transition': store.transition, 'cm-image-preview-limit': !store.original }) const imageStyle = () => { let translateX = store.translate.x / store.scale; let translateY = store.translate.y / store.scale; const mod = store.degree % 360; if ([90, -270].includes(mod)) { [translateX, translateY] = [translateY, -translateX]; } if ([180, -180].includes(mod)) { [translateX, translateY] = [-translateX, -translateY]; } if ([270, -90].includes(mod)) { [translateX, translateY] = [-translateY, translateX]; } return { transform: ` scale(${store.scale}) rotate(${store.degree}deg) translate(${translateX}px, ${translateY}px) ` }; } const hasLeftSwitchEnd = () => { return infinite ? false : store.currentIndex === 0; } const hasRightSwitchEnd = () => { const len = props.previewList.length; return infinite ? false : store.currentIndex >= len - 1; } const leftClasses = () => ({'cm-image-preview-arrow-left': true, 'cm-image-preview-arrow-disabled': hasLeftSwitchEnd()}) const rightClasses = () => ({'cm-image-preview-arrow-right': true, 'cm-image-preview-arrow-disabled': hasRightSwitchEnd()}) const currentSrc = () => props.previewList[store.currentIndex]; const stop = (e: any) => { e.stopPropagation && e.stopPropagation(); e.preventDefault && e.preventDefault(); } const id = 'cm-image-preview-portal'; return
{failInfo}
handleOperation(e, 'zoomIn')} viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7197" width="200" height="200"> handleOperation(e, 'zoomOut')} viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7412" width="200" height="200"> handleOperation(e, 'original')} viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="26672" width="200" height="200"> }> handleOperation(e, 'original')} viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1976" width="200" height="200"> handleOperation(e, 'rotateLeft')} viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="13308" width="200" height="200"> handleOperation(e, 'rotateRight')} viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="13521" width="200" height="200"> handleOperation(e, 'download')} viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8825" width="200" height="200"> }> 1}> {stop(e); handleSwitch(false)}} /> {stop(e); handleSwitch(true)}} />
}