Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 | 1x 2x 2x 2x 2x 2x 2x 1x 8x 8x 8x 16x 14x 2x 12x 8x 1x 8x 8x 8x 8x 8x 6x 5x 5x 6x 5x 8x 8x 7x 8x | import React, {useEffect, useRef} from 'react'; import Cropper from 'cropperjs'; interface ReactCropperElement extends HTMLImageElement { cropper: Cropper; } type ReactCropperRef = | ((instance: HTMLImageElement | ReactCropperElement | null) => void) | React.MutableRefObject<HTMLImageElement | ReactCropperElement | null> | null; interface ReactCropperDefaultOptions { scaleX?: number; scaleY?: number; enable?: boolean; zoomTo?: number; rotateTo?: number; } interface ReactCropperProps extends ReactCropperDefaultOptions, Cropper.Options<HTMLImageElement>, Omit<React.HTMLProps<HTMLImageElement>, 'data' | 'ref' | 'crossOrigin'> { crossOrigin?: '' | 'anonymous' | 'use-credentials' | undefined; on?: (eventName: string, callback: () => void | Promise<void>) => void | Promise<void>; onInitialized?: (instance: Cropper) => void | Promise<void>; } const applyDefaultOptions = (cropper: Cropper, options: ReactCropperDefaultOptions = {}): void => { const {enable = true, scaleX = 1, scaleY = 1, zoomTo = 0, rotateTo = 0} = options; enable ? cropper.enable() : cropper.disable(); cropper.scaleX(scaleX); cropper.scaleY(scaleY); cropper.rotateTo(rotateTo); zoomTo > 0 && cropper.zoomTo(zoomTo); }; /** * sourced from: https://itnext.io/reusing-the-ref-from-forwardref-with-react-hooks-4ce9df693dd */ const useCombinedRefs = (...refs: ReactCropperRef[]): React.RefObject<ReactCropperElement> => { const targetRef = useRef<ReactCropperElement>(null); React.useEffect(() => { refs.forEach((ref) => { if (!ref) return; if (typeof ref === 'function') { ref(targetRef.current); } else { ref.current = targetRef.current; } }); }, [refs]); return targetRef; }; const ReactCropper = React.forwardRef<ReactCropperElement | HTMLImageElement, ReactCropperProps>(({...props}, ref) => { const { dragMode = 'crop', src, style, className, crossOrigin, scaleX, scaleY, enable, zoomTo, rotateTo, alt = 'picture', ready, onInitialized, ...rest } = props; const defaultOptions: ReactCropperDefaultOptions = {scaleY, scaleX, enable, zoomTo, rotateTo}; const innerRef = useRef<HTMLImageElement>(null); const combinedRef = useCombinedRefs(ref, innerRef); useEffect(() => { if (combinedRef.current !== null) { const cropper = new Cropper(combinedRef.current, { dragMode, ...rest, ready: (e) => { if (e.currentTarget !== null) { applyDefaultOptions(e.currentTarget.cropper, defaultOptions); } ready && ready(e); }, }); onInitialized && onInitialized(cropper); } /** * destroy cropper on un-mount */ return () => { combinedRef.current?.cropper?.destroy(); }; }, [combinedRef]); /** * re-render when src changes */ useEffect(() => { if (combinedRef.current?.cropper && typeof src !== 'undefined') { combinedRef.current.cropper.reset().clear().replace(src); } }, [src]); return ( <div style={style} className={className}> <img crossOrigin={crossOrigin} src={src} alt={alt} style={{opacity: 0, maxWidth: '100%'}} ref={combinedRef} /> </div> ); }); export {ReactCropper, ReactCropperProps, ReactCropperElement, applyDefaultOptions}; |