From 837b4a2d643957e7db985106c29591535453e0b4 Mon Sep 17 00:00:00 2001 From: wangcch Date: Wed, 30 Dec 2020 19:58:19 +0800 Subject: [PATCH] feat: display preview-src after loading --- src/Image.tsx | 9 +++--- src/Preview.tsx | 73 ++++++++++++++++++++++++++++++++++++++++-- src/PreviewGroup.tsx | 18 +++++++++-- tests/preview.test.tsx | 8 +++++ 4 files changed, 99 insertions(+), 9 deletions(-) diff --git a/src/Image.tsx b/src/Image.tsx index 04268165..95becc9f 100644 --- a/src/Image.tsx +++ b/src/Image.tsx @@ -39,7 +39,7 @@ interface CompoundedComponent

extends React.FC

{ PreviewGroup: typeof PreviewGroup; } -type ImageStatus = 'normal' | 'error' | 'loading'; +export type ImageStatus = 'normal' | 'error' | 'loading'; const ImageInternal: CompoundedComponent = ({ src: imgSrc, @@ -77,6 +77,7 @@ const ImageInternal: CompoundedComponent = ({ mask: previewMask, }: ImagePreviewType = typeof preview === 'object' ? preview : {}; const src = previewSrc ?? imgSrc; + const thumbnail = previewSrc ? imgSrc : undefined; const isControlled = previewVisible !== undefined; const [isShowPreview, setShowPreview] = useMergedState(!!previewVisible, { value: previewVisible, @@ -153,14 +154,13 @@ const ImageInternal: CompoundedComponent = ({ return () => {}; } - const unRegister = registerImage(currentId, src); - + const unRegister = registerImage(currentId, src, thumbnail); if (!canPreview) { unRegister(); } return unRegister; - }, [src, canPreview]); + }, [thumbnail, src, canPreview]); const wrapperClass = cn(prefixCls, wrapperClassName, { [`${prefixCls}-error`]: isError, @@ -228,6 +228,7 @@ const ImageInternal: CompoundedComponent = ({ onClose={onPreviewClose} mousePosition={mousePosition} src={mergedSrc} + thumbnail={thumbnail} alt={alt} getContainer={getPreviewContainer} /> diff --git a/src/Preview.tsx b/src/Preview.tsx index 92ef3cd7..afa6e731 100644 --- a/src/Preview.tsx +++ b/src/Preview.tsx @@ -14,6 +14,7 @@ import { warning } from 'rc-util/lib/warning'; import useFrameSetState from './hooks/useFrameSetState'; import getFixScaleEleTransPosition from './getFixScaleEleTransPosition'; import { context } from './PreviewGroup'; +import { ImageStatus } from './Image'; const { useState, useEffect } = React; @@ -21,6 +22,8 @@ interface PreviewProps extends Omit { onClose?: (e: React.SyntheticEvent) => void; src?: string; alt?: string; + /** thumbnail src value */ + thumbnail?: string; } const initialPosition = { @@ -29,7 +32,16 @@ const initialPosition = { }; const Preview: React.FC = props => { - const { prefixCls, src, alt, onClose, afterClose, visible, ...restProps } = props; + const { + prefixCls, + src, + alt, + thumbnail: originalThumbnail, + onClose, + afterClose, + visible, + ...restProps + } = props; const [scale, setScale] = useState(1); const [rotate, setRotate] = useState(0); const [position, setPosition] = useFrameSetState<{ @@ -48,12 +60,38 @@ const Preview: React.FC = props => { deltaX: 0, deltaY: 0, }); + const { + previewUrls, + thumbnailUrls, + current, + isPreviewGroup, + setCurrent, + setThumbnailUrls, + } = React.useContext(context); + + const thumbnail = isPreviewGroup ? thumbnailUrls.get(current)?.url : originalThumbnail; + const containThumbnail = !!thumbnail; + const initCurThumbnailLoadState = + containThumbnail && (isPreviewGroup ? thumbnailUrls?.get(current)?.done : false); + const [fakeImgStatus, setFakeImgStatus] = React.useState( + containThumbnail && !initCurThumbnailLoadState ? 'loading' : 'normal', + ); + + const combinationSrc = (() => { + if (fakeImgStatus === 'loading') { + return thumbnail; + } + if (isPreviewGroup) { + return previewUrls.get(current); + } + + return src; + })(); + const [isMoving, setMoving] = React.useState(false); - const { previewUrls, current, isPreviewGroup, setCurrent } = React.useContext(context); const previewGroupCount = previewUrls.size; const previewUrlsKeys = Array.from(previewUrls.keys()); const currentPreviewIndex = previewUrlsKeys.indexOf(current); - const combinationSrc = isPreviewGroup ? previewUrls.get(current) : src; const showLeftOrRightSwitches = isPreviewGroup && previewGroupCount > 1; const [lastWheelZoomDirection, setLastWheelZoomDirection] = React.useState({ wheelDirection: 0 }); @@ -184,6 +222,20 @@ const Preview: React.FC = props => { setLastWheelZoomDirection({ wheelDirection }); }; + const onFakeLoad: React.ReactEventHandler = () => { + setFakeImgStatus('normal'); + + if (isPreviewGroup && containThumbnail) { + setThumbnailUrls(oldThumbnailUrls => { + const url = oldThumbnailUrls.get(current)?.url; + return new Map(oldThumbnailUrls).set(current, { url, done: true }); + }); + } + }; + const onFakeError: React.ReactEventHandler = () => { + setFakeImgStatus('error'); + }; + useEffect(() => { const { wheelDirection } = lastWheelZoomDirection; if (wheelDirection > 0) { @@ -227,6 +279,12 @@ const Preview: React.FC = props => { }; }, [visible, isMoving]); + useEffect(() => { + if (isPreviewGroup && thumbnail && !initCurThumbnailLoadState) { + setFakeImgStatus('loading'); + } + }, [initCurThumbnailLoadState, thumbnail, isPreviewGroup]); + return (

= props => { transform: `scale3d(${scale}, ${scale}, 1) rotate(${rotate}deg)`, }} /> + {containThumbnail && ( + + )} {showLeftOrRightSwitches && (
; setPreviewUrls: React.Dispatch>>; + thumbnailUrls?: Map; + setThumbnailUrls?: React.Dispatch>>; current: number; setCurrent: React.Dispatch>; setShowPreview: React.Dispatch>; setMousePosition: React.Dispatch>; - registerImage: (id: number, url: string) => () => void; + registerImage: (id: number, url: string, thumbnail?: string) => () => void; } /* istanbul ignore next */ @@ -35,15 +37,24 @@ const Group: React.FC = ({ children, }) => { const [previewUrls, setPreviewUrls] = useState>(new Map()); + const [thumbnailUrls, setThumbnailUrls] = useState>( + new Map(), + ); const [current, setCurrent] = useState(); const [isShowPreview, setShowPreview] = useState(false); const [mousePosition, setMousePosition] = useState(null); - const registerImage = (id: number, url: string) => { + const registerImage = (id: number, url: string, thumbnail?: string) => { setPreviewUrls(oldPreviewUrls => { return new Map(oldPreviewUrls).set(id, url); }); + if (thumbnail) { + setThumbnailUrls(oldThumbnailUrls => { + return new Map(oldThumbnailUrls).set(id, { url: thumbnail, done: false }); + }); + } + return () => { setPreviewUrls(oldPreviewUrls => { const clonePreviewUrls = new Map(oldPreviewUrls); @@ -65,6 +76,8 @@ const Group: React.FC = ({ isPreviewGroup: true, previewUrls, setPreviewUrls, + thumbnailUrls, + setThumbnailUrls, current, setCurrent, setShowPreview, @@ -80,6 +93,7 @@ const Group: React.FC = ({ onClose={onPreviewClose} mousePosition={mousePosition} src={previewUrls.get(current)} + thumbnail={thumbnailUrls.get(current)?.url} /> ); diff --git a/tests/preview.test.tsx b/tests/preview.test.tsx index 58c05ca5..9c86a344 100644 --- a/tests/preview.test.tsx +++ b/tests/preview.test.tsx @@ -444,6 +444,14 @@ describe('Preview', () => { }); expect(wrapper.find('.rc-image-preview').get(0)).toBeTruthy(); + expect(wrapper.find('.rc-image-preview-img').prop('src')).toBe(src); + expect(wrapper.find('.rc-image-preview-faker-img').prop('src')).toBe(previewSrc); + + act(() => { + wrapper.find('.rc-image-preview-faker-img').simulate('load'); + jest.runAllTimers(); + wrapper.update(); + }); expect(wrapper.find('.rc-image-preview-img').prop('src')).toBe(previewSrc); }); });