skip to Main Content

I have try implement react-image-cropper in my react.js project. But Image not load to show for crop issue.
Here I select the image using onSelectFile() function it take file but ReactCrop not load to image for cropper

Here the way I added the react-image-cropper

import React, { useState, useRef } from "react";
import ReactCrop from "react-image-crop";
import "react-image-crop/dist/ReactCrop.css";

import "./App.css";

function App() {
  const [src, setSrc] = useState(null);
  const [crop, setCrop] = useState({
    unit: "%",
    x: 0,
    y: 0,
    width: 50,
    height: 50,
  });
  const [croppedImageUrl, setCroppedImageUrl] = useState(null);
  const imageRef = useRef(null);

  const onSelectFile = (e) => {
    if (e.target.files && e.target.files.length > 0) {
      const reader = new FileReader();
      reader.addEventListener("load", () => {
        setSrc(reader.result);
        // Reset the crop to default values when a new image is selected
        setCrop({
          unit: "%",
          x: 0,
          y: 0,
          width: 50,
          height: 50,
        });
      });
      reader.readAsDataURL(e.target.files[0]);
    }
  };

  const onImageLoaded = (image) => {
    imageRef.current = image;
  };

  const onCropComplete = (crop) => {
    makeClientCrop(crop);
  };

  const onCropChange = (crop) => {
    setCrop(crop);
  };

  const makeClientCrop = async (crop) => {
    if (imageRef.current && crop.width && crop.height) {
      const croppedImageUrl = await getCroppedImg(
        imageRef.current,
        crop,
        "newFile.jpeg"
      );
      setCroppedImageUrl(croppedImageUrl);
    }
  };

  const getCroppedImg = (image, crop, fileName) => {
    const canvas = document.createElement("canvas");
    const scaleX = image.naturalWidth / image.width;
    const scaleY = image.naturalHeight / image.height;
    canvas.width = crop.width;
    canvas.height = crop.height;
    const ctx = canvas.getContext("2d");

    ctx.drawImage(
      image,
      crop.x * scaleX,
      crop.y * scaleY,
      crop.width * scaleX,
      crop.height * scaleY,
      0,
      0,
      crop.width,
      crop.height
    );

    return new Promise((resolve) => {
      canvas.toBlob((blob) => {
        if (!blob) {
          console.error("Canvas is empty");
          return;
        }
        blob.name = fileName;
        if (croppedImageUrl) {
          window.URL.revokeObjectURL(croppedImageUrl);
        }
        const imageUrl = window.URL.createObjectURL(blob);
        setCroppedImageUrl(imageUrl);
        resolve(imageUrl);
      }, "image/jpeg");
    });
  };

  return (
    <div className="App">
      <div>
        <input type="file" accept="image/*" onChange={onSelectFile} />
      </div>
      {src && (
        <ReactCrop
          src={src}
          crop={crop}
          ruleOfThirds
          onImageLoaded={onImageLoaded}
          onComplete={onCropComplete}
          onChange={onCropChange}
        />
      )}
      {croppedImageUrl && (
        <img alt="Crop" style={{ maxWidth: "100%" }} src={croppedImageUrl} />
      )}
    </div>
  );
}

export default App;

Please provide the relevant solutions to me, thanks in advance

3

Answers


  1. I think it’s because you have to create an image, as in this example:

     let img = new Image();
     img.onload = ()=>{
        //Your code
     }
     img.src = "file://" + src;
                     
    
    Login or Signup to reply.
  2. The <ReactCrop> component doesn’t have a src property. You need to wrap it around an <img />.

    Additionally, if you want to display a preview of the cropped image, you can draw the image directly to a canvas element ref.

    Below is a simplified, annotated example.

    import { useState, useRef, useEffect } from 'react';
    
    import ReactCrop from 'react-image-crop';
    import 'react-image-crop/dist/ReactCrop.css';
    
    
    const initialCropValues = {
      unit: '%',
      x: 0,
      y: 0,
      width: 50,
      height: 50,
    };
    
    function App() {
      // original image src
      const [src, setSrc] = useState(null);
      // state to store onCropChange crop values (as percentages)
      const [crop, setCrop] = useState(initialCropValues);
      // state to store onCropComplete crop values (as pixels)
      const [completedCrop, setCompletedCrop] = useState(null);
    
      // ref for img element
      const imgRef = useRef(null);
      // ref for canvas element
      const canvasRef = useRef(null);
    
      const onSelectFile = (e) => {
        // if a file is selected
        if (e.target.files && e.target.files.length > 0) {
          // instantiate a FileReader
          const reader = new FileReader();
          // listen for the load event only once
          reader.addEventListener(
            'load',
            () => {
              // if the result is a string set it as the src state
              if (typeof reader.result === 'string') setSrc(reader.result);
              // reset the crop values
              setCrop(initialCropValues);
              // set the initial completed crop to clear the canvas
              // of a previous image crop, if any
              setCompletedCrop({
                unit: 'px',
                x: 0,
                y: 0,
                width: 0,
                height: 0,
              });
            },
            { once: true }
          );
          // read the file as a data url
          reader.readAsDataURL(e.target.files[0]);
        }
      };
    
      // set the crop values onCropChange
      const onCropChange = (crop) => setCrop(crop);
    
      // set the completed crop values onCropComplete
      const onCropComplete = (crop) => {
        setCompletedCrop(crop);
      };
      
      useEffect(() => {
        // return if there's no canvas element, image or crop values
        if (!canvasRef.current || !imgRef.current || !completedCrop) return;
    
        // reset the completed crop state
        setCompletedCrop(null);
    
        // destructure the crop values for convenience
        const { x, y, width, height } = completedCrop;
    
        // get the 2d context for the canvas element
        const ctx = canvasRef.current.getContext('2d');
    
        // make sure there is a context
        if (!ctx) return;
    
        // clear the canvas
        ctx.clearRect(0, 0, canvasRef.current.width, canvasRef.current.height);
    
        // set the canvas height and width to the crop size
        canvasRef.current.width = width;
        canvasRef.current.height = height;
    
        // calculate the original image scale
        const scaleX = imgRef.current.naturalWidth / imgRef.current.width;
        const scaleY = imgRef.current.naturalHeight / imgRef.current.height;
    
        // draw the cropped image to the canvas
        ctx.drawImage(
          imgRef.current,
          x * scaleX,
          y * scaleY,
          width * scaleX,
          height * scaleY,
          0,
          0,
          width,
          height
        );
        return () => {};
      }, [completedCrop, src]);
      
      return (
        <>
          <h1>React Image Crop</h1>
          <input type="file" accept="image/*" onChange={onSelectFile} />
          {src && (
            <ReactCrop
              crop={crop}
              ruleOfThirds
              onComplete={onCropComplete}
              onChange={onCropChange}
            >
              <img
                src={src}
                alt="Image to be cropped"
                style={{ maxHeight: '80vh' }}
                ref={imgRef}
              />
            </ReactCrop>
          )}
          <canvas ref={canvasRef} width={0} height={0} />
        </>
      )
    }
    
    export default App;
    
    Login or Signup to reply.
  3. evt.target.files is always going to be an Array, even if it’s empty. so checking for evt.target.files
    is unnecessary.

    using the async FileReader API is also legacy, unnecessary and should be avoided.
    converting things into base64 introduce a lot of overhead by converting the binary data into a string
    and then back into binary data again.
    a ObjectURL is just a pointer to where the file is located on the disc and reads faster and use less memory.

    const onSelectFile = evt => {
      const [ file ] = evt.target.files
      if (file) {
        setSrc(URL.createObjectURL(file))
        // reset the crop values
        setCrop(initialCropValues)
        // set the initial completed crop to clear the canvas
        // of a previous image crop, if any
        setCompletedCrop({
          unit: 'px',
          x: 0,
          y: 0,
          width: 0,
          height: 0,
        })
      }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search