UNPKG

react-avatar-editor

Version:

Avatar / profile picture component. Resize and crop your uploaded image using a intuitive user interface.

1 lines 43.4 kB
{"version":3,"file":"index.mjs","names":[],"sources":["../../core/src/utils/drawRoundedRect.ts","../../core/src/utils/drawGrid.ts","../../core/src/utils/loadImageURL.ts","../../core/src/utils/loadImageFile.ts","../../core/src/utils/isFileAPISupported.ts","../../core/src/AvatarEditor.ts","../../core/src/utils/isPassiveSupported.ts","../src/index.ts"],"sourcesContent":["/**\n * Draws a rounded rectangle on a 2D context.\n */\nexport const drawRoundedRect = (\n context: CanvasRenderingContext2D,\n x: number,\n y: number,\n width: number,\n height: number,\n borderRadius: number,\n) => {\n if (borderRadius === 0) {\n context.rect(x, y, width, height)\n } else {\n const widthMinusRad = width - borderRadius\n const heightMinusRad = height - borderRadius\n context.translate(x, y)\n context.arc(\n borderRadius,\n borderRadius,\n borderRadius,\n Math.PI,\n Math.PI * 1.5,\n )\n context.lineTo(widthMinusRad, 0)\n context.arc(\n widthMinusRad,\n borderRadius,\n borderRadius,\n Math.PI * 1.5,\n Math.PI * 2,\n )\n context.lineTo(width, heightMinusRad)\n context.arc(\n widthMinusRad,\n heightMinusRad,\n borderRadius,\n Math.PI * 2,\n Math.PI * 0.5,\n )\n context.lineTo(borderRadius, height)\n context.arc(\n borderRadius,\n heightMinusRad,\n borderRadius,\n Math.PI * 0.5,\n Math.PI,\n )\n context.closePath()\n context.translate(-x, -y)\n }\n}\n","/**\n * Draws a \"Rule of Three\" grid on the canvas.\n */\nexport const drawGrid = (\n context: CanvasRenderingContext2D,\n x: number,\n y: number,\n width: number,\n height: number,\n gridColor: string,\n) => {\n context.fillStyle = gridColor\n const thirdsX = width / 3\n const thirdsY = height / 3\n\n // vertical bars\n context.fillRect(x, y, 1, height)\n context.fillRect(thirdsX + x, y, 1, height)\n context.fillRect(thirdsX * 2 + x, y, 1, height)\n context.fillRect(thirdsX * 3 + x, y, 1, height)\n context.fillRect(thirdsX * 4 + x, y, 1, height)\n\n // horizontal bars\n context.fillRect(x, y, width, 1)\n context.fillRect(x, thirdsY + y, width, 1)\n context.fillRect(x, thirdsY * 2 + y, width, 1)\n context.fillRect(x, thirdsY * 3 + y, width, 1)\n context.fillRect(x, thirdsY * 4 + y, width, 1)\n}\n","const isDataURL = (str: string) => {\n const regex =\n /^\\s*data:([a-z]+\\/[a-z]+(;[a-z-]+=[a-z-]+)?)?(;base64)?,[a-z0-9!$&',()*+;=\\-._~:@/?%\\s]*\\s*$/i\n return !!str.match(regex)\n}\n\nexport const loadImageURL = (imageURL: string, crossOrigin?: string) =>\n new Promise<HTMLImageElement>((resolve, reject) => {\n const image = new Image()\n image.addEventListener('load', () => resolve(image))\n image.addEventListener('error', reject)\n if (!isDataURL(imageURL) && crossOrigin) {\n image.crossOrigin = crossOrigin\n }\n image.src = imageURL\n })\n","import { loadImageURL } from './loadImageURL'\n\nexport const loadImageFile = (file: File) =>\n new Promise<HTMLImageElement>((resolve, reject) => {\n const reader = new FileReader()\n reader.addEventListener('load', (e) => {\n try {\n if (!e?.target?.result) {\n throw new Error('No image data')\n }\n const image = loadImageURL(e.target.result as string)\n resolve(image)\n } catch (err) {\n reject(err)\n }\n })\n reader.readAsDataURL(file)\n })\n","export const isFileAPISupported = typeof File !== 'undefined'\n","import type {\n ImageState,\n Position,\n AvatarEditorConfig,\n Dimensions,\n CroppingRect,\n} from './types'\nimport { drawRoundedRect } from './utils/drawRoundedRect'\nimport { drawGrid } from './utils/drawGrid'\nimport { loadImageURL } from './utils/loadImageURL'\nimport { loadImageFile } from './utils/loadImageFile'\nimport { isFileAPISupported } from './utils/isFileAPISupported'\n\nconst toRadians = (degree: number) => degree * (Math.PI / 180)\n\nconst defaultEmptyImage: ImageState = {\n x: 0.5,\n y: 0.5,\n}\n\nexport class AvatarEditorCore {\n private config: Required<AvatarEditorConfig>\n private imageState: ImageState = defaultEmptyImage\n private pixelRatio: number\n\n constructor(config: AvatarEditorConfig) {\n this.config = {\n border: 25,\n borderRadius: 0,\n scale: 1,\n rotate: 0,\n color: [0, 0, 0, 0.5],\n backgroundColor: '',\n borderColor: undefined,\n showGrid: false,\n gridColor: '#666',\n disableBoundaryChecks: false,\n disableHiDPIScaling: false,\n disableCanvasRotation: true,\n crossOrigin: undefined,\n ...config,\n } as Required<AvatarEditorConfig>\n\n this.pixelRatio =\n typeof window !== 'undefined' &&\n window.devicePixelRatio &&\n !this.config.disableHiDPIScaling\n ? window.devicePixelRatio\n : 1\n }\n\n getPixelRatio(): number {\n return this.pixelRatio\n }\n\n getImageState(): ImageState {\n return this.imageState\n }\n\n setImageState(state: ImageState): void {\n this.imageState = state\n }\n\n updateConfig(config: Partial<AvatarEditorConfig>): void {\n this.config = { ...this.config, ...config } as Required<AvatarEditorConfig>\n }\n\n isVertical(): boolean {\n return !this.config.disableCanvasRotation && this.config.rotate % 180 !== 0\n }\n\n getBorders(border?: number | [number, number]): [number, number] {\n const b = border ?? this.config.border\n return Array.isArray(b) ? b : [b, b]\n }\n\n getDimensions(): Dimensions {\n const { width, height, rotate, border } = this.config\n const canvas = { width: 0, height: 0 }\n const [borderX, borderY] = this.getBorders(border)\n\n if (this.isVertical()) {\n canvas.width = height\n canvas.height = width\n } else {\n canvas.width = width\n canvas.height = height\n }\n\n canvas.width += borderX * 2\n canvas.height += borderY * 2\n\n return {\n canvas,\n rotate,\n width,\n height,\n border,\n }\n }\n\n getXScale(): number {\n if (!this.imageState.width || !this.imageState.height) {\n throw new Error('Image dimension is unknown.')\n }\n\n const canvasAspect = this.config.width / this.config.height\n const imageAspect = this.imageState.width / this.imageState.height\n\n return Math.min(1, canvasAspect / imageAspect)\n }\n\n getYScale(): number {\n if (!this.imageState.width || !this.imageState.height) {\n throw new Error('Image dimension is unknown.')\n }\n\n const canvasAspect = this.config.height / this.config.width\n const imageAspect = this.imageState.height / this.imageState.width\n\n return Math.min(1, canvasAspect / imageAspect)\n }\n\n getCroppingRect(position?: Position): CroppingRect {\n if (!this.imageState.width || !this.imageState.height) {\n return { x: 0, y: 0, width: 1, height: 1 }\n }\n\n const pos = position || {\n x: this.imageState.x,\n y: this.imageState.y,\n }\n const rectWidth = (1 / this.config.scale) * this.getXScale()\n const rectHeight = (1 / this.config.scale) * this.getYScale()\n\n const croppingRect = {\n x: pos.x - rectWidth / 2,\n y: pos.y - rectHeight / 2,\n width: rectWidth,\n height: rectHeight,\n }\n\n let xMin = 0\n let xMax = 1 - croppingRect.width\n let yMin = 0\n let yMax = 1 - croppingRect.height\n\n const isLargerThanImage =\n this.config.disableBoundaryChecks || rectWidth > 1 || rectHeight > 1\n\n if (isLargerThanImage) {\n xMin = -croppingRect.width\n xMax = 1\n yMin = -croppingRect.height\n yMax = 1\n }\n\n return {\n ...croppingRect,\n x: Math.max(xMin, Math.min(croppingRect.x, xMax)),\n y: Math.max(yMin, Math.min(croppingRect.y, yMax)),\n }\n }\n\n getInitialSize(\n imgWidth: number,\n imgHeight: number,\n ): { width: number; height: number } {\n let newHeight: number\n let newWidth: number\n\n const dimensions = this.getDimensions()\n const canvasRatio = dimensions.height / dimensions.width\n const imageRatio = imgHeight / imgWidth\n\n if (canvasRatio > imageRatio) {\n newHeight = dimensions.height\n newWidth = imgWidth * (newHeight / imgHeight)\n } else {\n newWidth = dimensions.width\n newHeight = imgHeight * (newWidth / imgWidth)\n }\n\n return {\n height: newHeight,\n width: newWidth,\n }\n }\n\n async loadImage(file: File | string): Promise<ImageState> {\n let image: HTMLImageElement\n\n if (isFileAPISupported && file instanceof File) {\n image = await loadImageFile(file)\n } else if (typeof file === 'string') {\n image = await loadImageURL(file, this.config.crossOrigin)\n } else {\n throw new Error('Invalid image source')\n }\n\n const imageState: ImageState = {\n ...this.getInitialSize(image.width, image.height),\n resource: image,\n x: 0.5,\n y: 0.5,\n }\n\n this.imageState = imageState\n return imageState\n }\n\n clearImage(): void {\n this.imageState = defaultEmptyImage\n }\n\n calculatePosition(\n image = this.imageState,\n border?: number | [number, number],\n ): {\n x: number\n y: number\n width: number\n height: number\n } {\n const [borderX, borderY] = this.getBorders(border)\n\n if (!image.width || !image.height) {\n throw new Error('Image dimension is unknown.')\n }\n\n const croppingRect = this.getCroppingRect()\n\n const width = image.width * this.config.scale\n const height = image.height * this.config.scale\n\n let x = -croppingRect.x * width\n let y = -croppingRect.y * height\n\n if (this.isVertical()) {\n x += borderY\n y += borderX\n } else {\n x += borderX\n y += borderY\n }\n\n return { x, y, height, width }\n }\n\n paint(context: CanvasRenderingContext2D): void {\n context.save()\n context.scale(this.pixelRatio, this.pixelRatio)\n context.translate(0, 0)\n context.fillStyle = 'rgba(' + this.config.color.slice(0, 4).join(',') + ')'\n\n let borderRad = this.config.borderRadius\n const dimensions = this.getDimensions()\n const [borderSizeX, borderSizeY] = this.getBorders(dimensions.border)\n const h = dimensions.canvas.height\n const w = dimensions.canvas.width\n\n // clamp border radius between zero (perfect rectangle) and half the size without borders (perfect circle or \"pill\")\n borderRad = Math.max(borderRad, 0)\n borderRad = Math.min(borderRad, w / 2 - borderSizeX, h / 2 - borderSizeY)\n\n context.beginPath()\n // inner rect, possibly rounded\n drawRoundedRect(\n context,\n borderSizeX,\n borderSizeY,\n w - borderSizeX * 2,\n h - borderSizeY * 2,\n borderRad,\n )\n context.rect(w, 0, -w, h) // outer rect, drawn \"counterclockwise\"\n context.fill('evenodd')\n\n // Draw 1px border around the mask only if borderColor is provided\n if (this.config.borderColor) {\n context.strokeStyle =\n 'rgba(' + this.config.borderColor.slice(0, 4).join(',') + ')'\n context.lineWidth = 1\n context.beginPath()\n drawRoundedRect(\n context,\n borderSizeX + 0.5,\n borderSizeY + 0.5,\n w - borderSizeX * 2 - 1,\n h - borderSizeY * 2 - 1,\n borderRad,\n )\n context.stroke()\n }\n\n if (this.config.showGrid) {\n drawGrid(\n context,\n borderSizeX,\n borderSizeY,\n w - borderSizeX * 2,\n h - borderSizeY * 2,\n this.config.gridColor,\n )\n }\n context.restore()\n }\n\n paintImage(\n context: CanvasRenderingContext2D,\n image: ImageState,\n borderValue: number | [number, number],\n scaleFactor = this.pixelRatio,\n ): void {\n if (!image.resource) return\n\n const pos = this.calculatePosition(image, borderValue)\n\n context.save()\n\n context.translate(context.canvas.width / 2, context.canvas.height / 2)\n context.rotate((this.config.rotate * Math.PI) / 180)\n context.translate(-(context.canvas.width / 2), -(context.canvas.height / 2))\n\n if (this.isVertical()) {\n context.translate(\n (context.canvas.width - context.canvas.height) / 2,\n (context.canvas.height - context.canvas.width) / 2,\n )\n }\n\n context.scale(scaleFactor, scaleFactor)\n\n context.globalCompositeOperation = 'destination-over'\n context.drawImage(image.resource, pos.x, pos.y, pos.width, pos.height)\n\n if (this.config.backgroundColor) {\n context.fillStyle = this.config.backgroundColor\n context.fillRect(0, 0, context.canvas.width, context.canvas.height)\n }\n\n context.restore()\n }\n\n getImage(): HTMLCanvasElement {\n const cropRect = this.getCroppingRect()\n const image = this.imageState\n\n if (!image.resource) {\n throw new Error(\n 'No image resource available, please report this to: https://github.com/mosch/react-avatar-editor/issues',\n )\n }\n\n // get actual pixel coordinates\n cropRect.x *= image.resource.width\n cropRect.y *= image.resource.height\n cropRect.width *= image.resource.width\n cropRect.height *= image.resource.height\n\n // create a canvas with the correct dimensions\n const canvasEl = document.createElement('canvas')\n\n if (this.isVertical()) {\n canvasEl.width = Math.round(cropRect.height)\n canvasEl.height = Math.round(cropRect.width)\n } else {\n canvasEl.width = Math.round(cropRect.width)\n canvasEl.height = Math.round(cropRect.height)\n }\n\n const context = canvasEl.getContext('2d')\n\n if (!context) {\n throw new Error(\n 'No context found, please report this to: https://github.com/mosch/react-avatar-editor/issues',\n )\n }\n\n context.translate(canvasEl.width / 2, canvasEl.height / 2)\n context.rotate((this.config.rotate * Math.PI) / 180)\n context.translate(-(canvasEl.width / 2), -(canvasEl.height / 2))\n\n if (this.isVertical()) {\n context.translate(\n (canvasEl.width - canvasEl.height) / 2,\n (canvasEl.height - canvasEl.width) / 2,\n )\n }\n\n if (this.config.backgroundColor) {\n context.fillStyle = this.config.backgroundColor\n context.fillRect(0, 0, canvasEl.width, canvasEl.height)\n }\n\n context.drawImage(image.resource, -cropRect.x, -cropRect.y)\n\n return canvasEl\n }\n\n getImageScaledToCanvas(): HTMLCanvasElement {\n const dimensions = this.getDimensions()\n const image = this.imageState\n const canvasEl = document.createElement('canvas')\n\n if (this.isVertical()) {\n canvasEl.width = dimensions.height\n canvasEl.height = dimensions.width\n } else {\n canvasEl.width = dimensions.width\n canvasEl.height = dimensions.height\n }\n\n if (!image.resource) return canvasEl\n\n const context = canvasEl.getContext('2d')\n if (!context) return canvasEl\n\n const pos = this.calculatePosition(image, 0)\n\n context.save()\n context.translate(canvasEl.width / 2, canvasEl.height / 2)\n context.rotate((this.config.rotate * Math.PI) / 180)\n context.translate(-(canvasEl.width / 2), -(canvasEl.height / 2))\n\n if (this.isVertical()) {\n context.translate(\n (canvasEl.width - canvasEl.height) / 2,\n (canvasEl.height - canvasEl.width) / 2,\n )\n }\n\n if (this.config.backgroundColor) {\n context.fillStyle = this.config.backgroundColor\n context.fillRect(0, 0, canvasEl.width, canvasEl.height)\n }\n\n context.drawImage(image.resource, pos.x, pos.y, pos.width, pos.height)\n context.restore()\n\n return canvasEl\n }\n\n calculateDragPosition(\n mousePositionX: number,\n mousePositionY: number,\n lastMx: number,\n lastMy: number,\n ): Position {\n const deltaX = lastMx - mousePositionX\n const deltaY = lastMy - mousePositionY\n\n if (!this.imageState.width || !this.imageState.height) {\n throw new Error('Image dimension is unknown.')\n }\n\n const w = this.imageState.width * this.config.scale\n const h = this.imageState.height * this.config.scale\n\n let { x: lastX, y: lastY } = this.getCroppingRect()\n\n lastX *= w\n lastY *= h\n\n // helpers to calculate vectors\n let rot = this.config.rotate\n rot %= 360\n rot = rot < 0 ? rot + 360 : rot\n\n const cos = Math.cos(toRadians(rot))\n const sin = Math.sin(toRadians(rot))\n\n const x = lastX + deltaX * cos + deltaY * sin\n const y = lastY + -deltaX * sin + deltaY * cos\n\n const relativeWidth = (1 / this.config.scale) * this.getXScale()\n const relativeHeight = (1 / this.config.scale) * this.getYScale()\n\n return {\n x: x / w + relativeWidth / 2,\n y: y / h + relativeHeight / 2,\n }\n }\n}\n","// https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener\nconst noop = () => {}\n\nexport const isPassiveSupported = () => {\n let passiveSupported = false\n try {\n const options = Object.defineProperty({}, 'passive', {\n get: function () {\n passiveSupported = true\n },\n })\n\n window.addEventListener('test', noop, options)\n window.removeEventListener('test', noop, options)\n } catch {\n passiveSupported = false\n }\n return passiveSupported\n}\n","import React, {\n type TouchEventHandler,\n type CSSProperties,\n type MouseEventHandler,\n useState,\n useRef,\n useEffect,\n useCallback,\n useImperativeHandle,\n forwardRef,\n} from 'react'\n\nimport {\n AvatarEditorCore,\n type ImageState,\n type Position,\n type AvatarEditorConfig,\n isPassiveSupported,\n} from '@react-avatar-editor/core'\n\nexport interface Props extends AvatarEditorConfig {\n style?: CSSProperties\n image?: string | File\n position?: Position\n onLoadStart?: () => void\n onLoadFailure?: () => void\n onLoadSuccess?: (image: ImageState) => void\n onImageReady?: () => void\n onImageChange?: () => void\n onMouseUp?: () => void\n onMouseMove?: (e: TouchEvent | MouseEvent) => void\n onPositionChange?: (position: Position) => void\n}\n\nexport type { Position, ImageState }\n\nexport interface AvatarEditorRef {\n getImage: () => HTMLCanvasElement\n getImageScaledToCanvas: () => HTMLCanvasElement\n getCroppingRect: () => { x: number; y: number; width: number; height: number }\n}\n\nconst AvatarEditor = forwardRef<AvatarEditorRef, Props>((props, ref) => {\n const {\n scale = 1,\n rotate = 0,\n border = 25,\n borderRadius = 0,\n width = 200,\n height = 200,\n color = [0, 0, 0, 0.5],\n showGrid = false,\n gridColor = '#666',\n disableBoundaryChecks = false,\n disableHiDPIScaling = false,\n disableCanvasRotation = true,\n image,\n position,\n backgroundColor,\n crossOrigin,\n onLoadStart,\n onLoadFailure,\n onLoadSuccess,\n onImageReady,\n onImageChange,\n onMouseUp,\n onMouseMove,\n onPositionChange,\n borderColor,\n style,\n } = props\n\n const canvas = useRef<HTMLCanvasElement>(null)\n const coreRef = useRef<AvatarEditorCore>(\n new AvatarEditorCore({\n width,\n height,\n border,\n borderRadius,\n scale,\n rotate,\n color,\n backgroundColor,\n borderColor,\n showGrid,\n gridColor,\n disableBoundaryChecks,\n disableHiDPIScaling,\n disableCanvasRotation,\n crossOrigin,\n }),\n )\n\n // Use refs for drag state to avoid stale closures in document-level listeners.\n // These values are read/written in handlers registered once on mount.\n const dragRef = useRef(false)\n const mxRef = useRef<number | undefined>(undefined)\n const myRef = useRef<number | undefined>(undefined)\n\n // Keep state for `drag` and `loading` to trigger re-renders\n const [drag, setDrag] = useState(false)\n const [loading, setLoading] = useState(false)\n const [imageState, setImageState] = useState<ImageState>(\n coreRef.current.getImageState(),\n )\n\n // Store latest callback props in refs so document handlers always call current versions\n const onMouseUpRef = useRef(onMouseUp)\n onMouseUpRef.current = onMouseUp\n const onMouseMoveRef = useRef(onMouseMove)\n onMouseMoveRef.current = onMouseMove\n const onPositionChangeRef = useRef(onPositionChange)\n onPositionChangeRef.current = onPositionChange\n\n // Update core config when props change\n useEffect(() => {\n coreRef.current.updateConfig({\n width,\n height,\n border,\n borderRadius,\n scale,\n rotate,\n color,\n backgroundColor,\n borderColor,\n showGrid,\n gridColor,\n disableBoundaryChecks,\n disableHiDPIScaling,\n disableCanvasRotation,\n crossOrigin,\n })\n }, [\n width,\n height,\n border,\n borderRadius,\n scale,\n rotate,\n color,\n backgroundColor,\n borderColor,\n showGrid,\n gridColor,\n disableBoundaryChecks,\n disableHiDPIScaling,\n disableCanvasRotation,\n crossOrigin,\n ])\n\n const getCanvas = useCallback((): HTMLCanvasElement => {\n if (!canvas.current) {\n throw new Error(\n 'No canvas found, please report this to: https://github.com/mosch/react-avatar-editor/issues',\n )\n }\n return canvas.current\n }, [])\n\n const getContext = useCallback(() => {\n const context = getCanvas().getContext('2d')\n if (!context) {\n throw new Error(\n 'No context found, please report this to: https://github.com/mosch/react-avatar-editor/issues',\n )\n }\n return context\n }, [getCanvas])\n\n const loadImage = useCallback(\n async (file: File | string) => {\n setLoading(true)\n onLoadStart?.()\n try {\n const newImageState = await coreRef.current.loadImage(file)\n dragRef.current = false\n setDrag(false)\n setImageState(newImageState)\n onImageReady?.()\n onLoadSuccess?.(newImageState)\n } catch {\n onLoadFailure?.()\n } finally {\n setLoading(false)\n }\n },\n [onLoadStart, onImageReady, onLoadSuccess, onLoadFailure],\n )\n\n const clearImage = useCallback(() => {\n const canvasEl = getCanvas()\n const context = getContext()\n context.clearRect(0, 0, canvasEl.width, canvasEl.height)\n coreRef.current.clearImage()\n setImageState(coreRef.current.getImageState())\n }, [getCanvas, getContext])\n\n const repaint = useCallback(() => {\n const context = getContext()\n const canvasEl = getCanvas()\n context.clearRect(0, 0, canvasEl.width, canvasEl.height)\n coreRef.current.paint(context)\n coreRef.current.paintImage(context, imageState, border)\n // eslint-disable-next-line -- all visual props must trigger repaint\n }, [\n getContext,\n getCanvas,\n imageState,\n border,\n width,\n height,\n borderRadius,\n scale,\n rotate,\n color,\n backgroundColor,\n borderColor,\n showGrid,\n gridColor,\n disableBoundaryChecks,\n disableHiDPIScaling,\n disableCanvasRotation,\n crossOrigin,\n ])\n\n const handleMouseDown: MouseEventHandler<HTMLCanvasElement> = useCallback(\n (e) => {\n e.preventDefault()\n dragRef.current = true\n mxRef.current = undefined\n myRef.current = undefined\n setDrag(true)\n },\n [],\n )\n\n const handleTouchStart: TouchEventHandler<HTMLCanvasElement> =\n useCallback(() => {\n dragRef.current = true\n mxRef.current = undefined\n myRef.current = undefined\n setDrag(true)\n }, [])\n\n // Expose imperative methods via ref\n useImperativeHandle(\n ref,\n () => ({\n getImage: () => coreRef.current.getImage(),\n getImageScaledToCanvas: () => coreRef.current.getImageScaledToCanvas(),\n getCroppingRect: () => coreRef.current.getCroppingRect(),\n }),\n [],\n )\n\n // Mount effect - setup document-level event listeners.\n // Handlers read from refs (not closures) to always have current values.\n useEffect(() => {\n const context = getContext()\n\n if (image) {\n loadImage(image)\n }\n coreRef.current.paint(context)\n\n const handleDocumentMouseMove = (e: MouseEvent | TouchEvent) => {\n if (!dragRef.current) {\n return\n }\n\n if (e.cancelable) e.preventDefault()\n\n const mousePositionX =\n 'targetTouches' in e ? e.targetTouches[0].pageX : e.clientX\n const mousePositionY =\n 'targetTouches' in e ? e.targetTouches[0].pageY : e.clientY\n\n const prevMx = mxRef.current\n const prevMy = myRef.current\n\n mxRef.current = mousePositionX\n myRef.current = mousePositionY\n\n if (prevMx !== undefined && prevMy !== undefined) {\n const currentImageState = coreRef.current.getImageState()\n if (currentImageState.width && currentImageState.height) {\n const newPosition = coreRef.current.calculateDragPosition(\n mousePositionX,\n mousePositionY,\n prevMx,\n prevMy,\n )\n\n onPositionChangeRef.current?.(newPosition)\n\n const updatedImageState = { ...currentImageState, ...newPosition }\n coreRef.current.setImageState(updatedImageState)\n setImageState(updatedImageState)\n }\n }\n\n onMouseMoveRef.current?.(e)\n }\n\n const handleDocumentMouseUp = () => {\n if (dragRef.current) {\n dragRef.current = false\n setDrag(false)\n onMouseUpRef.current?.()\n }\n }\n\n const options = isPassiveSupported() ? { passive: false } : false\n document.addEventListener('mousemove', handleDocumentMouseMove, options)\n document.addEventListener('mouseup', handleDocumentMouseUp, options)\n document.addEventListener('touchmove', handleDocumentMouseMove, options)\n document.addEventListener('touchend', handleDocumentMouseUp, options)\n\n return () => {\n document.removeEventListener('mousemove', handleDocumentMouseMove, false)\n document.removeEventListener('mouseup', handleDocumentMouseUp, false)\n document.removeEventListener('touchmove', handleDocumentMouseMove, false)\n document.removeEventListener('touchend', handleDocumentMouseUp, false)\n }\n }, [])\n\n // Effect to handle image changes\n useEffect(() => {\n if (image) {\n loadImage(image)\n } else if (!image && imageState.x !== 0.5 && imageState.y !== 0.5) {\n clearImage()\n }\n }, [image, width, height, backgroundColor])\n\n // Effect to repaint canvas whenever relevant props/state change\n useEffect(() => {\n repaint()\n }, [repaint])\n\n // Pulsate the full canvas while loading\n useEffect(() => {\n if (!loading) return\n const canvasEl = canvas.current\n if (!canvasEl) return\n const ctx = canvasEl.getContext('2d')\n if (!ctx) return\n\n let frameId: number\n const start = performance.now()\n\n const draw = (now: number) => {\n const t = (now - start) / 1000\n const alpha = 0.03 + Math.sin(t * 2.5) * 0.02 + 0.02\n\n ctx.save()\n ctx.clearRect(0, 0, canvasEl.width, canvasEl.height)\n ctx.fillStyle = `rgba(255,255,255,${alpha})`\n ctx.fillRect(0, 0, canvasEl.width, canvasEl.height)\n ctx.restore()\n\n frameId = requestAnimationFrame(draw)\n }\n\n frameId = requestAnimationFrame(draw)\n return () => cancelAnimationFrame(frameId)\n }, [loading])\n\n // Effect to trigger onImageChange callback\n const prevPropsRef = useRef({\n image,\n width,\n height,\n position,\n scale,\n rotate,\n imageX: imageState.x,\n imageY: imageState.y,\n })\n useEffect(() => {\n const prev = prevPropsRef.current\n if (\n prev.image !== image ||\n prev.width !== width ||\n prev.height !== height ||\n prev.position !== position ||\n prev.scale !== scale ||\n prev.rotate !== rotate ||\n prev.imageX !== imageState.x ||\n prev.imageY !== imageState.y\n ) {\n onImageChange?.()\n prevPropsRef.current = {\n image,\n width,\n height,\n position,\n scale,\n rotate,\n imageX: imageState.x,\n imageY: imageState.y,\n }\n }\n }, [\n image,\n width,\n height,\n position,\n scale,\n rotate,\n imageState.x,\n imageState.y,\n onImageChange,\n ])\n\n const dimensions = coreRef.current.getDimensions()\n const pixelRatio = coreRef.current.getPixelRatio()\n\n const defaultStyle: CSSProperties = {\n width: dimensions.canvas.width,\n height: dimensions.canvas.height,\n cursor: drag ? 'grabbing' : 'grab',\n touchAction: 'none',\n maxWidth: 'none',\n maxHeight: 'none',\n }\n\n return React.createElement('canvas', {\n width: dimensions.canvas.width * pixelRatio,\n height: dimensions.canvas.height * pixelRatio,\n onMouseDown: handleMouseDown,\n onTouchStart: handleTouchStart,\n style: { ...defaultStyle, ...style },\n ref: canvas,\n })\n})\n\nAvatarEditor.displayName = 'AvatarEditor'\n\nexport function useAvatarEditor() {\n const ref = useRef<AvatarEditorRef>(null)\n\n return {\n ref,\n getImage: (): HTMLCanvasElement | null => {\n try {\n return ref.current?.getImage() ?? null\n } catch {\n return null\n }\n },\n getImageScaledToCanvas: (): HTMLCanvasElement | null => {\n try {\n return ref.current?.getImageScaledToCanvas() ?? null\n } catch {\n return null\n }\n },\n getCroppingRect: (): {\n x: number\n y: number\n width: number\n height: number\n } | null => {\n return ref.current?.getCroppingRect() ?? null\n },\n }\n}\n\nexport default AvatarEditor\n"],"mappings":"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"}