Browse Source

fix: [Image] prevent sliding pages when previewing pictures (#1289)

* fix: [Image] prevent sliding pages when previewing pictures

* chore: fix spell error

Co-authored-by: zhangyumei.0319 <[email protected]>
edc-hui 2 years ago
parent
commit
babf75cd16

+ 9 - 5
packages/semi-foundation/image/previewInnerFoundation.ts

@@ -18,7 +18,9 @@ export interface PreviewInnerAdapter<P = Record<string, any>, S = Record<string,
     setStopTiming: (value: boolean) => void;
     getStartMouseDown: () => {x: number; y: number};
     setStartMouseDown: (x: number, y: number) => void;
-    setMouseActiveTime: (time: number) => void
+    setMouseActiveTime: (time: number) => void;
+    disabledBodyScroll: () => void;
+    enabledBodyScroll: () => void
 }
 
 const NOT_CLOSE_TARGETS = ["icon", "footer"];
@@ -31,10 +33,12 @@ export default class PreviewInnerFoundation<P = Record<string, any>, S = Record<
 
     beforeShow() {
         this._adapter.registerKeyDownListener();
+        this._adapter.disabledBodyScroll();
     }
 
     afterHide() {
         this._adapter.unregisterKeyDownListener();
+        this._adapter.enabledBodyScroll();
     }
 
     handleRatio(type: string) {
@@ -125,7 +129,7 @@ export default class PreviewInnerFoundation<P = Record<string, any>, S = Record<
             rotation: 0,
         } as any);
         this._adapter.notifyRotateChange(0);
-    }  
+    }
 
     handleDownload = () => {
         const { currentIndex, imgSrc } = this.getStates();
@@ -194,7 +198,7 @@ export default class PreviewInnerFoundation<P = Record<string, any>, S = Record<
             callback(e);
         };
         Img.onerror = callback;
-        Img.src = preloadImages[0];  
+        Img.src = preloadImages[0];
     }
 
     // 在切换左右图片时,当被切换图片完成加载后,根据方向决定下一个预加载的图片
@@ -260,5 +264,5 @@ export default class PreviewInnerFoundation<P = Record<string, any>, S = Record<
         } else {
             this.preloadSingleImage();
         }
-    }   
-}
+    }
+}

+ 43 - 0
packages/semi-ui/image/__test__/imagePreview.test.js

@@ -0,0 +1,43 @@
+import { Image, ImagePreview } from '../../index';
+import { BASE_CLASS_PREFIX } from '../../../semi-foundation/base/constants';
+
+function getImagePreview(imageProps) {
+    const props = imageProps ? imageProps : {};
+    const srcList = [
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/abstract.jpg",
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/sky.jpg",
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/greenleaf.jpg",
+        "https://lf3-static.bytednsdoc.com/obj/eden-cn/ptlz_zlp/ljhwZthlaukjlkulzlp/root-web-sites/colorful.jpg",
+    ];
+    return (
+        <ImagePreview {...props}>
+            {srcList.map((src, index) => {
+                return (
+                    <Image
+                        key={index}
+                        src={src}
+                        width={200}
+                        alt={`lamp${index + 1}`}
+                        style={{ marginRight: 5 }}
+                    />
+                );
+            })}
+        </ImagePreview>
+    )
+}
+
+describe('ImagePreview', () => {
+    it('visible', function () {
+        const imageComponent = getImagePreview();
+        const image = mount(imageComponent, { attachTo: document.getElementById('container') });
+        expect(image.exists(`div.${BASE_CLASS_PREFIX}-image-preview`)).toEqual(false);
+        image.setProps({ visible: true })
+        image.update();
+        expect(image.exists(`div.${BASE_CLASS_PREFIX}-image-preview`)).toEqual(true);
+        expect(document.body.style.overflow).toEqual('hidden');
+        image.setProps({ visible: false })
+        image.update();
+        expect(document.body.style.overflow).not.toEqual('hidden');
+        expect(document.querySelector(`div.${BASE_CLASS_PREFIX}-image-preview`)).toEqual(null);
+    });
+})

+ 60 - 22
packages/semi-ui/image/previewInner.tsx

@@ -25,7 +25,7 @@ let timer = null;
 
 export default class PreviewInner extends BaseComponent<PreviewInnerProps, PreviewInnerStates> {
     static contextType = PreviewContext;
-    
+
     static propTypes = {
         style: PropTypes.object,
         className: PropTypes.string,
@@ -74,17 +74,36 @@ export default class PreviewInner extends BaseComponent<PreviewInnerProps, Previ
         infinite: false,
         closeOnEsc: true,
         lazyLoad: false,
-        preLoad: true, 
+        preLoad: true,
         preLoadGap: 2,
         zIndex: 1000,
         maskClosable: true,
         viewerVisibleDelay: 10000,
     };
 
+    private bodyOverflow: string;
+    private scrollBarWidth: number;
+    private originBodyWidth: string;
+
     get adapter(): PreviewInnerAdapter<PreviewInnerProps, PreviewInnerStates> {
         return {
             ...super.adapter,
             getIsInGroup: () => this.isInGroup(),
+            disabledBodyScroll: () => {
+                const { getPopupContainer } = this.props;
+                this.bodyOverflow = document.body.style.overflow || '';
+                if (!getPopupContainer && this.bodyOverflow !== 'hidden') {
+                    document.body.style.overflow = 'hidden';
+                    document.body.style.width = `calc(${this.originBodyWidth || '100%'} - ${this.scrollBarWidth}px)`;
+                }
+            },
+            enabledBodyScroll: () => {
+                const { getPopupContainer } = this.props;
+                if (!getPopupContainer && this.bodyOverflow !== 'hidden') {
+                    document.body.style.overflow = this.bodyOverflow;
+                    document.body.style.width = this.originBodyWidth;
+                }
+            },
             notifyChange: (index: number, direction: string) => {
                 const { onChange, onPrev, onNext } = this.props;
                 isFunction(onChange) && onChange(index);
@@ -116,11 +135,11 @@ export default class PreviewInner extends BaseComponent<PreviewInnerProps, Previ
             },
             notifyRotateChange: (angle: number) => {
                 const { onRotateLeft } = this.props;
-                isFunction(onRotateLeft) && onRotateLeft(angle);   
+                isFunction(onRotateLeft) && onRotateLeft(angle);
             },
             notifyDownload: (src: string, index: number) => {
                 const { onDownload } = this.props;
-                isFunction(onDownload) && onDownload(src, index);  
+                isFunction(onDownload) && onDownload(src, index);
             },
             registerKeyDownListener: () => {
                 window && window.addEventListener("keydown", this.handleKeyDown);
@@ -147,7 +166,7 @@ export default class PreviewInner extends BaseComponent<PreviewInnerProps, Previ
                 mouseActiveTime = time;
             },
         };
-        
+
     }
 
     timer;
@@ -167,8 +186,11 @@ export default class PreviewInner extends BaseComponent<PreviewInnerProps, Previ
             visible: false,
             preloadAfterVisibleChange: true,
             direction: "",
-        }; 
+        };
         this.foundation = new PreviewInnerFoundation(this.adapter);
+        this.bodyOverflow = '';
+        this.originBodyWidth = '100%';
+        this.scrollBarWidth = 0;
     }
 
     static getDerivedStateFromProps(props: PreviewInnerProps, state: PreviewInnerStates) {
@@ -177,7 +199,7 @@ export default class PreviewInner extends BaseComponent<PreviewInnerProps, Previ
         if (props.visible) {
             // if src in props
             src = Array.isArray(props.src) ? props.src : [props.src];
-        } 
+        }
         if (!isEqual(src, state.imgSrc)) {
             willUpdateStates.imgSrc = src;
         }
@@ -193,6 +215,22 @@ export default class PreviewInner extends BaseComponent<PreviewInnerProps, Previ
         return willUpdateStates;
     }
 
+    static getScrollbarWidth() {
+        if (globalThis && Object.prototype.toString.call(globalThis) === '[object Window]') {
+            return window.innerWidth - document.documentElement.clientWidth;
+        }
+        return 0;
+    }
+
+    componentDidMount() {
+
+        this.scrollBarWidth = PreviewInner.getScrollbarWidth();
+        this.originBodyWidth = document.body.style.width;
+        if (this.props.visible) {
+            this.foundation.beforeShow();
+        }
+    }
+
     componentDidUpdate(prevProps: PreviewInnerProps, prevState: PreviewInnerStates) {
         if (prevState.visible !== this.props.visible && this.props.visible) {
             mouseActiveTime = new Date().getTime();
@@ -267,8 +305,8 @@ export default class PreviewInner extends BaseComponent<PreviewInnerProps, Previ
 
     onImageLoad = (src) => {
         this.foundation.onImageLoad(src);
-    }   
-    
+    }
+
     handleMouseDown = (e): void => {
         this.foundation.handleMouseDown(e);
     }
@@ -278,13 +316,13 @@ export default class PreviewInner extends BaseComponent<PreviewInnerProps, Previ
     }
 
     render() {
-        const { 
-            getPopupContainer, 
-            zIndex, 
-            visible, 
-            className, 
-            style, 
-            infinite, 
+        const {
+            getPopupContainer,
+            zIndex,
+            visible,
+            className,
+            style,
+            infinite,
             zoomStep,
             crossOrigin,
             prevTip,
@@ -315,7 +353,7 @@ export default class PreviewInner extends BaseComponent<PreviewInnerProps, Previ
             };
         }
         const previewPrefixCls = `${prefixCls}-preview`;
-        const previewWrapperCls = cls(previewPrefixCls, 
+        const previewWrapperCls = cls(previewPrefixCls,
             {
                 [`${prefixCls}-hide`]: !visible,
                 [`${previewPrefixCls}-popup`]: getPopupContainer,
@@ -324,16 +362,16 @@ export default class PreviewInner extends BaseComponent<PreviewInnerProps, Previ
         );
         const hideViewerCls = !viewerVisible ? `${previewPrefixCls}-hide` : "";
         const total = imgSrc.length;
-        const showPrev = total !== 1 && (infinite || currentIndex !== 0); 
+        const showPrev = total !== 1 && (infinite || currentIndex !== 0);
         const showNext = total !== 1 && (infinite || currentIndex !== total - 1);
         return (
-            <Portal 
+            <Portal
                 getPopupContainer={getPopupContainer}
                 style={wrapperStyle}
             >
-                {visible && 
+                {visible &&
                 // eslint-disable-next-line jsx-a11y/mouse-events-have-key-events,jsx-a11y/no-static-element-interactions
-                <div 
+                <div
                     className={previewWrapperCls}
                     style={style}
                     onMouseDown={this.handleMouseDown}
@@ -406,4 +444,4 @@ export default class PreviewInner extends BaseComponent<PreviewInnerProps, Previ
             </Portal>
         );
     }
-}
+}

+ 5 - 5
packages/semi-ui/modal/Modal.tsx

@@ -113,7 +113,7 @@ class Modal extends BaseComponent<ModalReactProps, ModalState> {
     private readonly modalRef: LegacyRef<ModalContent>;
     private bodyOverflow: string;
     private scrollBarWidth: number;
-    private originBodyWith: string;
+    private originBodyWidth: string;
     private _haveRendered: boolean;
 
     constructor(props: ModalReactProps) {
@@ -126,7 +126,7 @@ class Modal extends BaseComponent<ModalReactProps, ModalState> {
         this.modalRef = React.createRef();
         this.bodyOverflow = '';
         this.scrollBarWidth = 0;
-        this.originBodyWith = '100%';
+        this.originBodyWidth = '100%';
 
     }
 
@@ -139,14 +139,14 @@ class Modal extends BaseComponent<ModalReactProps, ModalState> {
                 this.bodyOverflow = document.body.style.overflow || '';
                 if (!getPopupContainer && this.bodyOverflow !== 'hidden') {
                     document.body.style.overflow = 'hidden';
-                    document.body.style.width = `calc(${this.originBodyWith || '100%'} - ${this.scrollBarWidth}px)`;
+                    document.body.style.width = `calc(${this.originBodyWidth || '100%'} - ${this.scrollBarWidth}px)`;
                 }
             },
             enabledBodyScroll: () => {
                 const { getPopupContainer } = this.props;
                 if (!getPopupContainer && this.bodyOverflow !== 'hidden') {
                     document.body.style.overflow = this.bodyOverflow;
-                    document.body.style.width = this.originBodyWith;
+                    document.body.style.width = this.originBodyWidth;
                 }
             },
             notifyCancel: (e: React.MouseEvent) => {
@@ -232,7 +232,7 @@ class Modal extends BaseComponent<ModalReactProps, ModalState> {
     componentDidMount() {
 
         this.scrollBarWidth = Modal.getScrollbarWidth();
-        this.originBodyWith = document.body.style.width;
+        this.originBodyWidth = document.body.style.width;
         if (this.props.visible) {
             this.foundation.beforeShow();
         }