'react native circular crop
using react-image-crop package trying to create circular bagde, cropping as square works fine but need to create badge as circular png file
here is the code:
import React, { useState, useRef, forwardRef, useImperativeHandle } from 'react'
import ReactCrop, {
centerCrop,
makeAspectCrop,
Crop,
PixelCrop,
convertToPixelCrop,
} from 'react-image-crop'
export interface ImageCropperProps {
imgSrc: string
file: File
}
export const ImageCropper = forwardRef(({ imgSrc, file }: ImageCropperProps, ref) => {
const imgRef = useRef<HTMLImageElement>(null)
const [crop, setCrop] = useState<Crop>({
unit: '%', // Can be 'px' or '%'
x: 25,
y: 25,
width: 50,
height: 50,
})
const [completedCrop, setCompletedCrop] = useState<PixelCrop>()
const [scale, setScale] = useState(1.0)
const [circularCrop, setCircularCrop] = useState(true)
const [rotate, setRotate] = useState(0)
const previewCanvasRef = useRef<HTMLCanvasElement>(null)
function onImageLoad(e: React.SyntheticEvent<HTMLImageElement>) {
// @ts-ignore
imgRef.current = e.currentTarget
const { width, height } = e.currentTarget
// This is to demonstate how to make and center a % aspect crop
// which is a bit trickier so we use some helper functions.
const newCrop = centerCrop(
makeAspectCrop(
{
unit: '%',
width: 50,
},
1,
width,
height,
),
width,
height,
)
const newPXCrop = centerCrop(
makeAspectCrop(
{
unit: 'px',
width: width / 2,
},
1,
width,
height,
),
width,
height,
)
setCrop(newCrop)
setCompletedCrop(convertToPixelCrop(newPXCrop, newPXCrop.width, newPXCrop.height))
}
useImperativeHandle(ref, () => ({
async onCrop() {
debugger // eslint-disable-line no-debugger
if (crop?.height || crop?.width) {
if (imgRef.current) {
const { blob: croppedBlob, blobUrl, revokeUrl } = await cropImage(
imgRef.current,
file,
completedCrop,
true,
scale,
)
// this is always square eventhough cropping as circle
console.log(`blobUrl`, blobUrl)
return { croppedBlob, blobUrl, revokeUrl }
}
}
},
}))
return (
<div
style={{
display: 'flex',
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
}}
>
{Boolean(imgSrc) && (
<ReactCrop
crop={crop}
onChange={(_, percentCrop) => {
console.log(`_`, _)
console.log(`percentCrop`, percentCrop)
setCrop(percentCrop)
}}
onComplete={c => setCompletedCrop(c)}
aspect={1}
circularCrop={circularCrop}
keepSelection
locked
>
<img
alt="Crop me"
src={imgSrc}
style={{ transform: `scale(${scale}) rotate(${rotate}deg)` }}
onLoad={onImageLoad}
/>
</ReactCrop>
)}
<div style={{ display: 'flex', justifyContent: 'center' }}>
<IconButton disabled={scale <= 0.8} onClick={() => setScale(scale - 0.1)}>
<Remove />
</IconButton>
<Resizer step={0.1} min={0.8} max={1.8} value={scale} setValue={value => setScale(value)} />
<IconButton
disabled={scale >= 1.8}
onClick={() => {
setScale(scale + 0.1)
}}
>
<Add />
</IconButton>
</div>
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<Checkbox
color="primary"
onChange={e => setCircularCrop(e.target.checked)}
checked={circularCrop}
/>
<Text text="Circular Crop" variant="body1Medium" />
</div>
</div>
)
})
cropImage function:
const cropImage = async (
imageElm: HTMLImageElement,
file: File,
crop: PixelCrop | undefined,
withUrl = false,
scale = 1,
) => {
const canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d')
const scaleX = imageElm.naturalWidth / imageElm.width
const scaleY = imageElm.naturalHeight / imageElm.height
const pixelRatio = window.devicePixelRatio || 1
if (crop) {
canvas.width = Math.floor(crop.width * scaleX * pixelRatio)
canvas.height = Math.floor(crop.height * scaleY * pixelRatio)
}
ctx?.scale(pixelRatio, pixelRatio)
if (ctx?.imageSmoothingQuality) {
ctx.imageSmoothingQuality = 'high'
}
const cropX = crop ? crop.x * scaleX : scaleX
const cropY = crop ? crop?.y * scaleY : scaleY
const rotateRads = 0
const centerX = imageElm.naturalWidth / 2
const centerY = imageElm.naturalHeight / 2
ctx?.save()
// 5) Move the crop origin to the canvas origin (0,0)
ctx?.translate(-cropX, -cropY)
// 4) Move the origin to the center of the original position
ctx?.translate(centerX, centerY)
// 3) Rotate around the origin
ctx?.rotate(rotateRads)
// 2) Scale the image
ctx?.scale(scale, scale)
// 1) Move the center of the image to the origin (0,0)
ctx?.translate(-centerX, -centerY)
ctx?.drawImage(
imageElm,
0,
0,
imageElm.naturalWidth,
imageElm.naturalHeight,
0,
0,
imageElm.naturalWidth,
imageElm.naturalHeight,
)
ctx?.restore()
return getBlobFromCanvas(canvas, file, withUrl)
}
export default cropImage
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|