import BaseFoundation, { DefaultAdapter } from "../base/foundation"; import { handlePrevent } from "../utils/a11y"; import { throttle, isUndefined } from "lodash"; export interface PreviewImageAdapter

, S = Record> extends DefaultAdapter { getOriginImageSize: () => { originImageWidth: number; originImageHeight: number }; setOriginImageSize: (size: { originImageWidth: number; originImageHeight: number }) => void; getContainer: () => HTMLDivElement; getImage: () => HTMLImageElement; getMouseMove: () => boolean; setStartMouseMove: (move: boolean) => void; getMouseOffset: () => { x: number; y: number }; setStartMouseOffset: (offset: { x: number; y: number }) => void; setLoading: (loading: boolean) => void; setImageCursor: (canDrag: boolean) => void } export interface DragDirection { canDragVertical: boolean; canDragHorizontal: boolean } export interface ExtremeBounds { left: number; top: number } export interface ImageOffset { x: number; y: number } const DefaultDOMRect = { bottom: 0, height: 0, left: 0, right: 0, top: 0, width: 0, x: 0, y: 0, toJSON: () => ({}) }; export default class PreviewImageFoundation

, S = Record> extends BaseFoundation, P, S> { constructor(adapter: PreviewImageAdapter) { super({ ...adapter }); } _isImageVertical = (): boolean => this.getProp("rotation") % 180 !== 0; _getImageBounds = (): DOMRect => { const imageDOM = this._adapter.getImage(); if (imageDOM) { return imageDOM.getBoundingClientRect(); } return DefaultDOMRect; }; _getContainerBounds = (): DOMRect => { const containerDOM = this._adapter.getContainer(); if (containerDOM) { return containerDOM.getBoundingClientRect(); } return DefaultDOMRect; } _getOffset = (e: any): ImageOffset => { const { left, top } = this._getImageBounds(); return { x: e.clientX - left, y: e.clientY - top, }; } setLoading = (loading: boolean) => { this._adapter.setLoading(loading); } handleWindowResize = (): void => { const { ratio, setRatio } = this.getProps(); const { originImageWidth, originImageHeight } = this._adapter.getOriginImageSize(); if (originImageWidth && originImageHeight) { if (ratio !== "adaptation") { setRatio("adaptation"); } else { this.handleResizeImage(); } } }; handleLoad = (e: any): void => { if (e.target) { const { width: w, height: h } = e.target as any; this._adapter.setOriginImageSize({ originImageWidth: w, originImageHeight: h }); this.setState({ loading: false, } as any); this.handleResizeImage(); } const { src, onLoad } = this.getProps(); onLoad && onLoad(src); } handleError = (e: any): void => { const { onError, src } = this.getProps(); this.setState({ loading: false, } as any); onError && onError(src); } handleResizeImage = () => { const horizontal = !this._isImageVertical(); const { originImageWidth, originImageHeight } = this._adapter.getOriginImageSize(); const imgWidth = horizontal ? originImageWidth : originImageHeight; const imgHeight = horizontal ? originImageHeight : originImageWidth; const { onZoom } = this.getProps(); const containerDOM = this._adapter.getContainer(); if (containerDOM) { const { width: containerWidth, height: containerHeight } = this._getContainerBounds(); const reservedWidth = containerWidth - 80; const reservedHeight = containerHeight - 80; const _zoom = Number( Math.min(reservedWidth / imgWidth, reservedHeight / imgHeight).toFixed(2) ); onZoom(_zoom); } } handleRightClickImage = (e: any) => { const { disableDownload } = this.getProps(); if (disableDownload) { e.preventDefault(); e.stopPropagation(); return false; } else { return true; } }; // e: WheelEvent handleWheel = (e: any) => { this.onWheel(e); handlePrevent(e); } // e: WheelEvent onWheel = throttle((e: any): void => { const { onZoom, zoomStep, maxZoom, minZoom } = this.getProps(); const { currZoom } = this.getStates(); let _zoom:number; if (e.deltaY < 0) { /* zoom in */ if (currZoom + zoomStep <= maxZoom) { _zoom = Number((currZoom + zoomStep).toFixed(2)); } } else if (e.deltaY > 0) { /* zoom out */ if (currZoom - zoomStep >= minZoom) { _zoom = Number((currZoom - zoomStep).toFixed(2)); } } if (!isUndefined(_zoom)) { onZoom(_zoom); } }, 50); calcCanDragDirection = (): DragDirection => { const { width, height } = this.getStates(); const { rotation } = this.getProps(); const { width: containerWidth, height: containerHeight } =this._getContainerBounds(); let canDragHorizontal = width > containerWidth; let canDragVertical = height > containerHeight; if (this._isImageVertical()) { canDragHorizontal = height > containerWidth; canDragVertical = width > containerHeight; } return { canDragVertical, canDragHorizontal, }; }; handleZoomChange = (newZoom: number, e: any): void => { const imageDOM = this._adapter.getImage(); const { originImageWidth, originImageHeight } = this._adapter.getOriginImageSize(); const { canDragVertical, canDragHorizontal } = this.calcCanDragDirection(); const canDrag = canDragVertical || canDragHorizontal; const { width: containerWidth, height: containerHeight } = this._getContainerBounds(); const newWidth = Math.floor(originImageWidth * newZoom); const newHeight = Math.floor(originImageHeight * newZoom); // debugger; let _offset; const horizontal = !this._isImageVertical(); let newTop = 0; let newLeft = 0; if (horizontal) { _offset = { x: 0.5 * (containerWidth - newWidth), y: 0.5 * (containerHeight - newHeight), }; newLeft = _offset.x; newTop= _offset.y; } else { _offset = { x: 0.5 * (containerWidth - newHeight), y: 0.5 * (containerHeight - newWidth), }; newLeft = _offset.x - (newWidth - newHeight) / 2; newTop = _offset.y + (newWidth - newHeight) / 2; } this.setState({ width: newWidth, height: newHeight, offset: _offset, left: newLeft, top: newTop, currZoom: newZoom, } as any); if (imageDOM) { this._adapter.setImageCursor(canDrag); } }; calcExtremeBounds = (): ExtremeBounds => { const { width, height } = this.getStates(); const { width: containerWidth, height: containerHeight } = this._getContainerBounds(); let extremeLeft = containerWidth - width; let extremeTop = containerHeight - height; if (this._isImageVertical()) { extremeLeft = containerWidth - height; extremeTop = containerHeight - width; } return { left: extremeLeft, top: extremeTop, }; }; handleMoveImage = (e: any): void => { const { offset, width, height } = this.getStates(); const startMouseMove = this._adapter.getMouseMove(); const startMouseOffset = this._adapter.getMouseOffset(); const { canDragVertical, canDragHorizontal } = this.calcCanDragDirection(); if (startMouseMove && (canDragVertical || canDragHorizontal)) { const { clientX, clientY } = e; const { left: containerLeft, top: containerTop } = this._getContainerBounds(); const { left: extremeLeft, top: extremeTop } = this.calcExtremeBounds(); let newX = canDragHorizontal ? clientX - containerLeft - startMouseOffset.x : offset.x; let newY = canDragVertical ? clientY - containerTop - startMouseOffset.y : offset.y; if (canDragHorizontal) { newX = newX > 0 ? 0 : newX < extremeLeft ? extremeLeft : newX; } if (canDragVertical) { newY = newY > 0 ? 0 : newY < extremeTop ? extremeTop : newY; } const _offset = { x: newX, y: newY, }; this.setState({ offset: _offset, left: this._isImageVertical() ? _offset.x - (width - height) / 2 : _offset.x, top: this._isImageVertical() ? _offset.y + (width - height) / 2 : _offset.y, } as any); } }; handleImageMouseDown = (e: any): void => { this._adapter.setStartMouseOffset(this._getOffset(e)); this._adapter.setStartMouseMove(true); }; handleImageMouseUp = (): void => { this._adapter.setStartMouseMove(false); }; }