Merge branch 'cropper-patch' into 'develop'
fix: 🐛 modification of the image generation from ultimate cropper
See merge request ldc-web/projets/ldc!61
This commit is contained in:
commit
057f9980b5
2 changed files with 57 additions and 111 deletions
|
|
@ -9,7 +9,7 @@ import { Label } from "@/components/ui/label";
|
|||
import { Button } from "@/components/ui/button";
|
||||
// * Other
|
||||
import InputError from "@/back/Components/InputError";
|
||||
import ImageCropperLogic from "@/back/Components/Inputs/ImageCropper/ImageCropperLogic";
|
||||
import ImageCropper from "@/back/Components/Inputs/ImageCropper";
|
||||
import useTrans from "@/hooks/use-translations";
|
||||
import str from "@/hooks/use-string";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
|
|
@ -53,7 +53,7 @@ export default function ImageCropperWrapper(props: ImageCropperWrapperProps) {
|
|||
const { url } = usePage().props.appEnv as AppEnv;
|
||||
const [isReset, setIsReset] = useState<boolean>(false);
|
||||
const finalFileName = `${fileName}.png`;
|
||||
console.log(value)
|
||||
|
||||
const [selectedFile, setSelectedFile] = useState<FileWithPreview | null>(
|
||||
() => {
|
||||
// When there is an image selected but not stored yet.
|
||||
|
|
@ -234,7 +234,7 @@ export default function ImageCropperWrapper(props: ImageCropperWrapperProps) {
|
|||
{selectedFile ? (
|
||||
<div className="relative">
|
||||
{inputType === enumDynamicFieldTypes.image.value && (
|
||||
<ImageCropperLogic
|
||||
<ImageCropper
|
||||
dialogOpen={isDialogOpen}
|
||||
value={value}
|
||||
reset={reset}
|
||||
|
|
|
|||
|
|
@ -64,7 +64,7 @@ import { cn } from "@/lib/utils";
|
|||
};
|
||||
|
||||
/* eslint-disable no-unused-vars, @typescript-eslint/no-unused-vars */
|
||||
interface ImageCropperLogicProps {
|
||||
interface ImageCropperProps {
|
||||
dialogOpen: boolean;
|
||||
name: string;
|
||||
value?: string | File | null;
|
||||
|
|
@ -80,7 +80,7 @@ import { cn } from "@/lib/utils";
|
|||
}
|
||||
/* eslint-enable no-unused-vars, @typescript-eslint/no-unused-vars */
|
||||
|
||||
export default function ImageCropperLogic({
|
||||
export default function ImageCropper({
|
||||
dialogOpen,
|
||||
setDialogOpen,
|
||||
selectedFile,
|
||||
|
|
@ -93,7 +93,7 @@ import { cn } from "@/lib/utils";
|
|||
isReset,
|
||||
url,
|
||||
finalFileName,
|
||||
}: ImageCropperLogicProps) {
|
||||
}: ImageCropperProps) {
|
||||
const { errors } = usePage().props;
|
||||
|
||||
const aspect = 1;
|
||||
|
|
@ -135,104 +135,59 @@ import { cn } from "@/lib/utils";
|
|||
* @param cropLocalization Crop localization in the image.
|
||||
* @return string
|
||||
*/
|
||||
function getCroppedImg(
|
||||
image: HTMLImageElement,
|
||||
cropLocalization: PixelCrop,
|
||||
): string {
|
||||
function getCroppedImg(image: HTMLImageElement, crop: PixelCrop): string {
|
||||
const canvas = document.createElement("canvas");
|
||||
|
||||
// NOUVEAU CALCUL: l'image a une taille de base (scaleFactor) + zoom appliqué via transform
|
||||
const baseDisplayedWidth = originalDimensions.width * scaleFactor;
|
||||
const baseDisplayedHeight = originalDimensions.height * scaleFactor;
|
||||
const effectiveZoom = 1 + zoom;
|
||||
|
||||
// Calculer l'échelle vers l'image naturelle
|
||||
const scaleToNatural = image.naturalWidth / baseDisplayedWidth;
|
||||
|
||||
// Les coordonnées de crop doivent être ajustées pour le zoom transform
|
||||
// Le zoom étant centré, on doit calculer le décalage
|
||||
const zoomOffsetX =
|
||||
(baseDisplayedWidth * effectiveZoom - baseDisplayedWidth) / 2;
|
||||
const zoomOffsetY =
|
||||
(baseDisplayedHeight * effectiveZoom - baseDisplayedHeight) / 2;
|
||||
|
||||
// Ajuster les coordonnées par rapport à la position ET au zoom
|
||||
const adjustedCropX =
|
||||
((cropLocalization.x - imagePosition.x + zoomOffsetX) / effectiveZoom) *
|
||||
scaleToNatural;
|
||||
const adjustedCropY =
|
||||
((cropLocalization.y - imagePosition.y + zoomOffsetY) / effectiveZoom) *
|
||||
scaleToNatural;
|
||||
const cropWidth = (cropLocalization.width / effectiveZoom) * scaleToNatural;
|
||||
const cropHeight =
|
||||
(cropLocalization.height / effectiveZoom) * scaleToNatural;
|
||||
|
||||
// Calculer l'angle en radians
|
||||
const rad = (rotation * Math.PI) / 180;
|
||||
|
||||
// Créer un canvas temporaire assez grand pour contenir toute la rotation
|
||||
const tmpCanvas = document.createElement("canvas");
|
||||
const tmpCtx = tmpCanvas.getContext("2d")!;
|
||||
tmpCanvas.width = image.naturalWidth;
|
||||
tmpCanvas.height = image.naturalHeight;
|
||||
|
||||
tmpCtx.save();
|
||||
|
||||
// Appliquer les filtres CSS au contexte du canvas (sauf blur qui ne marche pas bien)
|
||||
tmpCtx.filter = `brightness(${brightness}%) contrast(${contrast}%) saturate(${saturation}%) sepia(${sepia}%) grayscale(${grayscale}%)`;
|
||||
|
||||
// Placer le centre de rotation
|
||||
tmpCtx.translate(tmpCanvas.width / 2, tmpCanvas.height / 2);
|
||||
|
||||
// Appliquer seulement le flip (le scale est maintenant dans les dimensions)
|
||||
tmpCtx.scale(flipX ? -1 : 1, flipY ? -1 : 1);
|
||||
|
||||
// Appliquer rotation
|
||||
const getRotation = () => {
|
||||
if (flipX && flipY) return rad;
|
||||
if (flipX || flipY) return -rad;
|
||||
return rad;
|
||||
};
|
||||
tmpCtx.rotate(getRotation());
|
||||
|
||||
// Dessiner l'image au centre avec les filtres appliqués
|
||||
tmpCtx.drawImage(image, -image.naturalWidth / 2, -image.naturalHeight / 2);
|
||||
|
||||
tmpCtx.restore();
|
||||
|
||||
// Maintenant, extraire la zone cropée depuis le canvas temporaire
|
||||
const imageData = tmpCtx.getImageData(
|
||||
adjustedCropX,
|
||||
adjustedCropY,
|
||||
cropWidth,
|
||||
cropHeight,
|
||||
);
|
||||
|
||||
// IMPORTANT: Redimensionner à la taille finale souhaitée (validationRules)
|
||||
canvas.width = validationRules.width;
|
||||
canvas.height = validationRules.height;
|
||||
|
||||
const ctx = canvas.getContext("2d")!;
|
||||
|
||||
// Créer un canvas temporaire avec l'image cropée
|
||||
const tempCanvas = document.createElement("canvas");
|
||||
const tempCtx = tempCanvas.getContext("2d")!;
|
||||
tempCanvas.width = cropWidth;
|
||||
tempCanvas.height = cropHeight;
|
||||
tempCtx.putImageData(imageData, 0, 0);
|
||||
|
||||
// Redimensionner l'image cropée aux dimensions finales souhaitées
|
||||
ctx.drawImage(
|
||||
tempCanvas,
|
||||
0,
|
||||
0,
|
||||
cropWidth,
|
||||
cropHeight, // Source
|
||||
0,
|
||||
0,
|
||||
validationRules.width,
|
||||
validationRules.height, // Destination
|
||||
|
||||
const tmpCanvas = document.createElement("canvas");
|
||||
tmpCanvas.width = maxContainerWidth;
|
||||
tmpCanvas.height = maxContainerHeight;
|
||||
const tmpCtx = tmpCanvas.getContext("2d")!;
|
||||
|
||||
tmpCtx.save();
|
||||
|
||||
// Taille de l'image affichée avec zoom
|
||||
const displayedWidth = originalDimensions.width * scaleFactor * (1 + zoom);
|
||||
const displayedHeight = originalDimensions.height * scaleFactor * (1 + zoom);
|
||||
|
||||
// Coin supérieur gauche réel après zoom
|
||||
const offsetX = imagePosition.x - (displayedWidth - originalDimensions.width * scaleFactor) / 2;
|
||||
const offsetY = imagePosition.y - (displayedHeight - originalDimensions.height * scaleFactor) / 2;
|
||||
|
||||
// Déplacer au centre pour rotation + flip
|
||||
tmpCtx.translate(offsetX + displayedWidth / 2, offsetY + displayedHeight / 2);
|
||||
tmpCtx.rotate((rotation * Math.PI) / 180);
|
||||
tmpCtx.scale(flipX ? -1 : 1, flipY ? -1 : 1);
|
||||
|
||||
// Appliquer les filtres
|
||||
tmpCtx.filter = `brightness(${brightness}%) contrast(${contrast}%) saturate(${saturation}%) sepia(${sepia}%) grayscale(${grayscale}%)`;
|
||||
|
||||
// Dessiner l'image centrée
|
||||
tmpCtx.drawImage(
|
||||
image,
|
||||
-displayedWidth / 2,
|
||||
-displayedHeight / 2,
|
||||
displayedWidth,
|
||||
displayedHeight
|
||||
);
|
||||
|
||||
tmpCtx.restore();
|
||||
|
||||
// Extraire la zone cropée visible
|
||||
ctx.drawImage(
|
||||
tmpCanvas,
|
||||
crop.x,
|
||||
crop.y,
|
||||
crop.width,
|
||||
crop.height,
|
||||
0,
|
||||
0,
|
||||
canvas.width,
|
||||
canvas.height
|
||||
);
|
||||
|
||||
return canvas.toDataURL("image/png");
|
||||
}
|
||||
|
||||
|
|
@ -726,7 +681,6 @@ import { cn } from "@/lib/utils";
|
|||
<ReactCrop
|
||||
crop={cropZone}
|
||||
onChange={(_, percentCrop) => setCropZone(percentCrop)}
|
||||
onComplete={() => {}}
|
||||
aspect={aspect}
|
||||
className="w-full"
|
||||
locked
|
||||
|
|
@ -752,20 +706,12 @@ import { cn } from "@/lib/utils";
|
|||
position: "absolute",
|
||||
left: `${imagePosition.x}px`,
|
||||
top: `${imagePosition.y}px`,
|
||||
width:
|
||||
originalDimensions.width > 0
|
||||
? `${originalDimensions.width * scaleFactor}px`
|
||||
: "auto",
|
||||
height:
|
||||
originalDimensions.height > 0
|
||||
? `${originalDimensions.height * scaleFactor}px`
|
||||
: "auto",
|
||||
width: originalDimensions.width > 0 ? `${originalDimensions.width * scaleFactor}px` : "auto",
|
||||
height: originalDimensions.height > 0 ? `${originalDimensions.height * scaleFactor}px` : "auto",
|
||||
transform: `scale(${1 + zoom}) rotate(${rotation}deg) scaleX(${flipX ? -1 : 1}) scaleY(${flipY ? -1 : 1})`,
|
||||
filter: `brightness(${brightness}%) contrast(${contrast}%) saturate(${saturation}%) sepia(${sepia}%) grayscale(${grayscale}%)`,
|
||||
transformOrigin: "center center",
|
||||
transition: isDragging
|
||||
? "none"
|
||||
: "transform 0.2s ease, filter 0.2s ease",
|
||||
transition: !isDragging ? "transform 0.2s ease, filter 0.2s ease" : "none",
|
||||
cursor: isDragging ? "grabbing" : "grab",
|
||||
userSelect: "none",
|
||||
}}
|
||||
Loading…
Add table
Reference in a new issue