react-easy-crop

· Padrão Frontend
Juan Kalleo
Juan Kalleo
Senior Fullstack
This page hasn't been translated to English yet — showing the original Portuguese content.

O que é

Biblioteca headless de recorte/posicionamento de imagem — um <Cropper> controlado que recebe crop/zoom/aspect e devolve, via callback, a área (em pixels da imagem original) que o usuário arrastou/deu zoom pra enquadrar. Não desenha canvas nem gera arquivo nenhum sozinha — isso fica por conta de quem consome.

Por que essa

Os campos de imagem da Identidade institucional (ícone do sistema, fundo do login) já têm proporção e tamanho máximo fixados em código (ICONE_SISTEMA_RECORTE, IMAGEM_FUNDO_LOGIN_RECORTE — ver (../UPLOAD-DE-IMAGEM.md), então o admin precisa poder posicionar a imagem antes de enviar, não só escolher um arquivo e torcer. Alternativas de mercado como react-avatar-editor e cropperjs vêm com estilo/DOM próprio difícil de encaixar sem conflitar com o tema DaisyUI; react-image-crop é parecida mas exige montar o canvas de recorte na mão de um jeito mais verboso. react-easy-crop é puramente headless (só transform/overflow via CSS, sem folha de estilo própria), o corpo do crop em si (canvas, redimensionamento pro tamanho máximo) já é responsabilidade nossa em lib/image-crop.ts de qualquer forma — então não duplica trabalho nem traz um segundo design system pra dentro do modal DaisyUI. Leitura de apoio: repositório oficial do react-easy-crop — por que um cropper puramente headless não conflita com o tema DaisyUI.

Versão

^6.2.3 (package.json).

Como importar

pnpm add react-easy-crop
import Cropper, { type Area } from "react-easy-crop";

Exemplo real

shared/ui/sistema/image-cropper-modal.tsx, dentro de um modal DaisyUI (<dialog className="modal">):

const [crop, setCrop] = useState({ x: 0, y: 0 });
const [zoom, setZoom] = useState(1);
const [areaRecortePixels, setAreaRecortePixels] = useState<Area | null>(null);

<div className="relative h-72 w-full overflow-hidden rounded-lg bg-base-300">
  <Cropper
    image={imageSrc}
    crop={crop}
    zoom={zoom}
    aspect={aspect}
    onCropChange={setCrop}
    onZoomChange={setZoom}
    onCropComplete={(_area, areaPixels) => setAreaRecortePixels(areaPixels)}
  />
</div>

Convenção do projeto

O <Cropper> só entrega a área recortada (em pixels); quem desenha o canvas final, redimensiona pro dimensoesMaximas do campo e devolve um File pronto pra enviar é lib/image-crop.ts (gerarImagemRecortada) — biblioteca fica restrita à interação visual, nunca ao processamento do arquivo. Detalhe completo do padrão em (../UPLOAD-DE-IMAGEM.md.