react-avatar-editor
Version:
Avatar / profile picture component. Resize and crop your uploaded image using a intuitive user interface.
1 lines • 43.5 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","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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"}