react-easy-crop을 사용한 이미지 조작

55136 단어
의해 Clarence Bakosi

이미지 조작에는 원하는 결과를 얻기 위해 다양한 방법과 기술을 사용하여 이미지를 변경하는 작업이 포함됩니다. 이를 통해 다른 부분은 무시하면서 이미지 측면에 집중할 수 있습니다. 결과적으로 이미지 종횡비, 형식 또는 크기가 줄어듭니다. 이미지 조작은 프런트 엔드 앱에서 중요한 역할을 합니다.

react-easy-crop 패키지는 무료 오픈 소스 JavaScript 라이브러리입니다. URL로 이미지 형식(JPEG, PNG 및 GIF) 또는 HTML5에서 지원되는 문자열 및 비디오 형식base64을 지원합니다. 프런트 엔드 앱에서 이미지/비디오를 조작하기 위한 효율적인 도구입니다.

이 기사에서는 react-easy-crop으로 이미지를 조작하는 데 중점을 둘 것입니다. 우리는 간단한 파일 업로드 애플리케이션을 만들 것입니다. 잘린 영역의 결과는 react-easy-crop에서 지원하는 다음 기능을 사용하여 표시됩니다.
  • 끌기 기능
  • 종횡비
  • 자르기
  • 확대/축소
  • 크롭 모양
  • 회전 상호 작용

  • React 앱 만들기



    시작하려면 아래 명령을 사용하여 React 애플리케이션을 시작하십시오.

     npx create-react-app easy-crop
     cd easy-crop
     npm i react-easy-crop
     npm i @material-ui/core
     npm start
    


    시작하기 전에 수정app.css하여 일부 스타일을 응용 프로그램에 추가합니다. 아래 코드 블록을 복사하여 app.css에 붙여넣습니다.

    .App {
      background-color: #c4c4c4;
      font-family: 'Helvetica Neue', sans-serif;
    }
    
    .App-header {
      padding-top: 20px;
      height: 100vh;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: flex-start;
      color: #fff;
    }
    
    label {
      font-weight: bold;
      padding-left: 10px;
    }
    
    button {
      z-index: 2;
      cursor: pointer;
      width: 100%;
      background-color: #000;
      color: #fff;
      padding: 10px 0;
    }
    
    .cropped-image {
      height: 600px;
      width: auto;
    }
    
    .cropped-image-container {
      margin-top: 20px;
      display: flex;
      flex-direction: column;
    }
    
    ._coverImage-holder  {
      padding: 25px 40px;
      background-color:black;
      border-radius: 5px;
      cursor: pointer;
      margin-bottom: 20px;
    }
    
    
    .container {
      display: flex;
      flex-direction: column;
      position: relative;
    }
    
    .crop-container {
      height: 600px;
      width: 600px;
    }
    
    .controls {
      display: flex;
      flex-direction: column;
      width: 600px;
      position: absolute;
      bottom: -15px;
    }
    


    react-easy-crop 설정


    EasyCrop.js라는 구성 요소를 만들고 아래 코드 블록을 붙여넣습니다.

    import React, { useState } from "react";
    import Cropper from 'react-easy-crop';
    
    const EasyCrop = () => {
     const [crop, setCrop] = useState({ x: 0, y: 0 });
    
      return (
        <Cropper
          image="https://cdn.pixabay.com/photo/2016/07/07/16/46/dice-1502706__340.jpg"
          crop={crop}
          onCropChange={setCrop}
        />
      )
    }
    
    export default EasyCrop;
    


    react-easy-crop이 지원하는 기능을 살펴보기 전에 아래 코드 블록으로 App.js 구성 요소를 업데이트하겠습니다.

    import EasyCrop from "./EasyCrop";
    
    function App() {
      return (
        <div className="App">
          <header className="App-header">
            <EasyCrop  />
          </header>
        </div>
      );
    }
    
    export default App;
    
    

    drag 기능은 기본적으로 활성화되어 있습니다. 앞으로 우리는 react-easy-crop이 제공하는 다른 기능을 구현할 것입니다.



    종횡비



    종횡비는 이미지의 너비와 높이를 나타냅니다. aspect 구성 요소에 Cropper 소품을 추가하여 조작할 수 있습니다.

    소품의 값 구문은 width/height 이고 기본값은 4/3 입니다.

    'Cropper' 구성 요소의 값을 5/5로 수정합니다.

    <Cropper
      image="https://cdn.pixabay.com/photo/2016/07/07/16/46/dice-1502706__340.jpg"
      crop={crop}
      aspect={5 / 5}
      onCropChange={setCrop}
    />
    




    자르기 모양


    cropShape 소품을 추가하여 이미지 모양을 변경할 수 있습니다. 이 소품은 rect 또는 round 문자열을 허용합니다. 기본값은 rect입니다. 이미지를 둥글게 만들기 위해 cropShape 값을 round 로 변경합니다.

    <Cropper
      image="https://cdn.pixabay.com/photo/2016/07/07/16/46/dice-1502706__340.jpg"
      crop={crop}
      aspect={4 / 4}
      cropShape="round"
      onCropChange={setCrop}
    />
    






    확대/축소 기능을 활성화하기 위해 react-easy-crop은 5개의 소품을 제공합니다. 이러한 소품에는 zoom , zoomWithScroll , zoomSpeed , minZoommaxZoom 가 포함됩니다.

    import React, { useState } from "react";
    import Slider from "@material-ui/core/Slider";
    import Cropper from "react-easy-crop";
    
    const EasyCrop = () => {
      const [crop, setCrop] = useState({ x: 0, y: 0 });
      const [zoom, setZoom] = useState(1);
    
      return (
        <div>
          <div className="crop-container">
            <Cropper
              image="https://cdn.pixabay.com/photo/2016/07/07/16/46/dice-1502706__340.jpg"
              crop={crop}
              zoom={zoom}
              zoomSpeed={4}
              maxZoom={3}
              zoomWithScroll={true}
              showGrid={true}
              aspect={4 / 3}
              onCropChange={setCrop}
              onZoomChange={setZoom}
            />
          </div>
          <div className="controls">
            <label>
              Zoom
              <Slider
                value={zoom}
                min={1}
                max={3}
                step={0.1}
                aria-labelledby="zoom"
                onChange={(e, zoom) => setZoom(zoom)}
                className="range"
              />
            </label>
          </div>
        </div>
      );
    };
    
    export default EasyCrop;
    




    회전



    회전 기능은 rotationonRotationChange 두 가지 소품을 사용하여 추가할 수 있습니다.

    import React, { useState } from "react";
    import Slider from "@material-ui/core/Slider";
    import Cropper from "react-easy-crop";
    
      const EasyCrop = () => {
      const [crop, setCrop] = useState({ x: 0, y: 0 });
      const [zoom, setZoom] = useState(1);
      const [rotation, setRotation] = useState(0);
    
      return (
         <div>
            <div className="crop-container">
              <Cropper
                image='https://cdn.pixabay.com/photo/2016/07/07/16/46/dice-1502706__340.jpg'
                crop={crop}
                rotation={rotation}
                zoom={zoom}
                zoomSpeed={4}
                maxZoom={3}
                zoomWithScroll={true}
                showGrid={true}
                aspect={4 / 3}
                onCropChange={setCrop}
                onZoomChange={setZoom}
                onRotationChange={setRotation}
              />
            </div>
            <div className="controls">
              <label>
                Rotate
                <Slider
                  value={rotation}
                  min={0}
                  max={360}
                  step={1}
                  aria-labelledby="rotate"
                  onChange={(e, rotation) => setRotation(rotation)}
                  className="range"
                />
              </label>
            </div>
          </div>
      );
    };
    
    export default EasyCrop;
    




    오픈 소스 세션 재생



    OpenReplay은 사용자가 웹 앱에서 수행하는 작업을 볼 수 있는 오픈 소스 세션 재생 제품군으로, 문제를 더 빨리 해결할 수 있도록 도와줍니다. OpenReplay는 데이터를 완벽하게 제어할 수 있도록 자체 호스팅됩니다.



    디버깅 경험을 즐기십시오 - start using OpenReplay for free .

    자른 이미지 표시



    다음으로, react-easy-crop이 제공하는 지원 기능을 사용하여 애플리케이션에 파일 업로드 기능을 구현하여 동적으로 만들 것입니다. 이를 위해 먼저 Crop.js라는 구성 요소를 생성하고 아래 코드 블록을 붙여넣습니다.

    export const createImage = (url) =>
      new Promise((resolve, reject) => {
        const image = new Image();
        image.addEventListener("load", () => resolve(image));
        image.addEventListener("error", (error) => reject(error));
        image.setAttribute("crossOrigin", "anonymous"); 
        image.src = url;
      });
    
    export function getRadianAngle(degreeValue) {
      return (degreeValue * Math.PI) / 180;
    }
    
    export function rotateSize(width, height, rotation) {
      const rotRad = getRadianAngle(rotation);
    
      return {
        width:
          Math.abs(Math.cos(rotRad) * width) + Math.abs(Math.sin(rotRad) * height),
        height:
          Math.abs(Math.sin(rotRad) * width) + Math.abs(Math.cos(rotRad) * height),
      };
    }
    
    export default async function getCroppedImg(
      imageSrc,
      pixelCrop,
      rotation = 0,
      flip = { horizontal: false, vertical: false }
    ) {
      const image = await createImage(imageSrc);
      const canvas = document.createElement("canvas");
      const ctx = canvas.getContext("2d");
    
      if (!ctx) {
        return null;
      }
    
      const rotRad = getRadianAngle(rotation);
    
      const { width: bBoxWidth, height: bBoxHeight } = rotateSize(
        image.width,
        image.height,
        rotation
      );
    
      // set canvas size to match the bounding box
      canvas.width = bBoxWidth;
      canvas.height = bBoxHeight;
    
      ctx.translate(bBoxWidth / 2, bBoxHeight / 2);
      ctx.rotate(rotRad);
      ctx.scale(flip.horizontal ? -1 : 1, flip.vertical ? -1 : 1);
      ctx.translate(-image.width / 2, -image.height / 2);
    
      ctx.drawImage(image, 0, 0);
    
      const data = ctx.getImageData(
        pixelCrop.x,
        pixelCrop.y,
        pixelCrop.width,
        pixelCrop.height
      );
    
    
      canvas.width = pixelCrop.width;
      canvas.height = pixelCrop.height;
    
      ctx.putImageData(data, 0, 0);
    
      return new Promise((resolve, reject) => {
        canvas.toBlob((file) => {
          resolve(URL.createObjectURL(file));
        }, "image/jpeg");
      });
    }
    


    후속 단계에서는 EasyCrop.js 구성 요소를 수정하여 이미지 소품을 동적으로 만듭니다.

    import { useCallback, useState } from "react";
    import Slider from "@material-ui/core/Slider";
    import Cropper from "react-easy-crop";
    import getCroppedImg from "./Crop";
    
    const EasyCrop = ({ image }) => {
      const [crop, setCrop] = useState({ x: 0, y: 0 });
      const [zoom, setZoom] = useState(1);
      const [rotation, setRotation] = useState(0);
      const [croppedAreaPixels, setCroppedAreaPixels] = useState(null);
      const [croppedImage, setCroppedImage] = useState(null);
    
      const onCropComplete = useCallback((croppedArea, croppedAreaPixels) => {
        setCroppedAreaPixels(croppedAreaPixels);
      }, []);
    
      const showCroppedImage = useCallback(async () => {
        try {
          const croppedImage = await getCroppedImg(
            image,
            croppedAreaPixels,
            rotation
          );
          console.log("donee", { croppedImage });
          setCroppedImage(croppedImage);
        } catch (e) {
          console.error(e);
        }
      }, [croppedAreaPixels, rotation, image]);
    
      const onClose = useCallback(() => {
        setCroppedImage(null);
      }, []);
    
      return (
        <div>
          <button
            style={{
              display: image === null || croppedImage !== null ? "none" : "block",
            }}
            onClick={showCroppedImage}
          >
            Crop
          </button>
          <div
            className="container"
            style={{
              display: image === null || croppedImage !== null ? "none" : "block",
            }}
          >
            <div className="crop-container">
              <Cropper
                image={image}
                crop={crop}
                rotation={rotation}
                zoom={zoom}
                zoomSpeed={4}
                maxZoom={3}
                zoomWithScroll={true}
                showGrid={true}
                aspect={4 / 3}
                onCropChange={setCrop}
                onCropComplete={onCropComplete}
                onZoomChange={setZoom}
                onRotationChange={setRotation}
              />
            </div>
            <div className="controls">
              <label>
                Rotate
                <Slider
                  value={rotation}
                  min={0}
                  max={360}
                  step={1}
                  aria-labelledby="rotate"
                  onChange={(e, rotation) => setRotation(rotation)}
                  className="range"
                />
              </label>
              <label>
                Zoom
                <Slider
                  value={zoom}
                  min={1}
                  max={3}
                  step={0.1}
                  aria-labelledby="zoom"
                  onChange={(e, zoom) => setZoom(zoom)}
                  className="range"
                />
              </label>
            </div>
          </div>
          <div className="cropped-image-container">
            {croppedImage && (
              <img className="cropped-image" src={croppedImage} alt="cropped" />
            )}
            {croppedImage && <button onClick={onClose}>close</button>}
          </div>
        </div>
      );
    };
    
    export default EasyCrop;
    


    또한 파일 업로드 기능을 포함하여 App.js 구성 요소를 최종적으로 변경합니다.

    import React, { useState } from "react";
    import EasyCrop from "./EasyCrop";
    
    function App() {
      const [image, setImage] = useState(null);
    
      const handleImageUpload = async (e) => {
        setImage(URL.createObjectURL(e.target.files[0]));
      };
    
      return (
        <div className="App">
          <header className="App-header">
            <label className="_coverImage-holder">
              Upload Image
              <input
                type="file"
                name="cover"
                onChange={handleImageUpload}
                accept="img/*"
                style={{ display: "none" }}
              />
            </label>
            <EasyCrop image={image}  />
          </header>
        </div>
      );
    }
    
    export default App;
    
    




    결론



    React-easy-crop은 React 애플리케이션에서 이미지를 조작하는 데 효율적입니다. 웹용 이미지를 변경할 때 원하는 결과를 얻을 수 있는 유연성을 제공합니다. 다음은 source codelive app에 대한 링크입니다.
  • react-easy-crop
  • Github

  • A TIP FROM THE EDITOR: On the topic of working with images, our React 18 - What's New and How it Will Benefit Developers article highlights some advantages of the latest version of React.



    좋은 웹페이지 즐겨찾기