ValentinH / react-easy-crop

A React component to crop images/videos with easy interactions
https://valentinh.github.io/react-easy-crop/
MIT License
2.34k stars 167 forks source link

Black background when cropping image #515

Closed lolottetheclash closed 8 months ago

lolottetheclash commented 8 months ago

Hello,

I read the same issue many times but the solution didn't work for me (saving as png instead of jpeg) I show you my code:

import { Area } from 'react-easy-crop'
import { useState, useEffect } from 'react'

type Size = {
  width: number
  height: number
}

export const useCroppedImage = (
  imageSrc: string,
  croppedArea: Area | null,
  requestedSize?: Size,
) => {
  const [image, setImage] = useState<File | null>(null)
  const [imageError, setImageError] = useState<Error | null>(null)

  useEffect(() => {
    if (!imageSrc || !croppedArea) {
      setImage(null)
      setImageError(null)
      return
    }

    const createImage = async () => {
      try {
        const imageResult: File | null = await new Promise((resolve, reject) => {
          const image = new Image()
          image.src = imageSrc
          image.onload = () => {
            const canvas = document.createElement('canvas')
            const canvasWidth = requestedSize?.width || croppedArea?.width
            const canvasHeight = requestedSize?.height || croppedArea?.height
            const scaleX = image.naturalWidth / image.width
            const scaleY = image.naturalHeight / image.height
            canvas.width = canvasWidth
            canvas.height = canvasHeight
            const ctx = canvas.getContext('2d')

            if (ctx) {
              croppedArea
                ? ctx?.drawImage(
                    image,
                    croppedArea.x * scaleX,
                    croppedArea.y * scaleY,
                    croppedArea.width * scaleX,
                    croppedArea.height * scaleY,
                    0,
                    0,
                    canvasWidth,
                    canvasHeight,
                  )
                : ctx?.drawImage(image, 0, 0, canvasWidth, canvasHeight)
            }

            canvas.toBlob((blob) => {
              if (!blob) {
                reject(new Error('Created canvas is empty'))
                return
              }
              const file = new File([blob], 'croppedImage.png', { type: 'image/png' })
              resolve(file)
            })
          }
        })

        setImage(imageResult)
        setImageError(null)
      } catch (e: unknown) {
        console.error(e instanceof Error ? e.message : 'An unexpected error happened')
        setImageError(e instanceof Error ? e : new Error('An unexpected error happened'))
      }
    }
    createImage()
  }, [croppedArea, imageSrc, requestedSize])

  return { image, imageError }
}

the cropper:

import { useCallback, useState } from 'react'
import Cropper, { Area } from 'react-easy-crop'
import { ImageCropProps, Point } from './ImageCropper.model'
import { SliderInput } from '../inputs/SliderInput/SliderInput.component'
import styles from './ImageCropper.module.scss'
import classNames from 'classnames'

export const ImageCropper = ({
  src,
  onCropComplete,
  ratio = 1,
  initialCrop = { x: 0, y: 0 },
  withSlider = true,
  showGrid = false,
  sliderMin = 0.1,
  sliderMax = 3,
  sliderStep = 0.1,
  restrictPosition = false,
}: ImageCropProps) => {
  const [crop, setCrop] = useState<Point>(initialCrop)
  const [zoom, setZoom] = useState(1)

  const handleCropComplete = useCallback(
    (_, croppedAreaPixels: Area) => onCropComplete(croppedAreaPixels),
    [onCropComplete],
  )

  const handleZoom = useCallback((zoom: number) => setZoom(zoom), [])

  return (
    <div className={styles.imageCropperContainer}>
      <div className={classNames(styles.imageCropper, { [styles.withoutSlider]: !withSlider })}>
        <Cropper
          image={src}
          crop={crop}
          minZoom={0.1}
          zoom={zoom}
          aspect={ratio}
          onCropChange={setCrop}
          onCropComplete={handleCropComplete}
          onZoomChange={handleZoom}
          showGrid={showGrid}
          restrictPosition={restrictPosition}
        />
      </div>
      {withSlider && (
        <SliderInput
          appearance="compact"
          value={zoom}
          min={sliderMin}
          max={sliderMax}
          step={sliderStep}
          onChange={handleZoom}
        />
      )}
    </div>
  )
}

If the crop has a minZoom of 0.1 and I crop the image or crop a png, in all cases the transparent background appears black... if anyone has a clue... thanks!

Capture d’écran 2024-03-06 à 11 14 28 Capture d’écran 2024-03-06 à 11 14 36