mirror of
https://github.com/trafficlunar/tomodachi-share.git
synced 2026-05-13 13:17:45 +00:00
feat: rotate images
This commit is contained in:
parent
6167da3703
commit
5ef104904a
2 changed files with 28 additions and 7 deletions
|
|
@ -11,7 +11,7 @@ interface Props {
|
||||||
setImage: React.Dispatch<React.SetStateAction<string | undefined>>;
|
setImage: React.Dispatch<React.SetStateAction<string | undefined>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function CropPortrait({ isOpen, setIsOpen, image, setImage }: Props) {
|
export default function ImageEditorPortrait({ isOpen, setIsOpen, image, setImage }: Props) {
|
||||||
const [isVisible, setIsVisible] = useState(false);
|
const [isVisible, setIsVisible] = useState(false);
|
||||||
const [crop, setCrop] = useState<Crop>();
|
const [crop, setCrop] = useState<Crop>();
|
||||||
|
|
||||||
|
|
@ -38,9 +38,27 @@ export default function CropPortrait({ isOpen, setIsOpen, image, setImage }: Pro
|
||||||
ctx.drawImage(image, crop.x * scaleX, crop.y * scaleY, crop.width * scaleX, crop.height * scaleY, 0, 0, crop.width * scaleX, crop.height * scaleY);
|
ctx.drawImage(image, crop.x * scaleX, crop.y * scaleY, crop.width * scaleX, crop.height * scaleY, 0, 0, crop.width * scaleX, crop.height * scaleY);
|
||||||
|
|
||||||
setImage(canvas.toDataURL());
|
setImage(canvas.toDataURL());
|
||||||
close();
|
setCrop(undefined);
|
||||||
}, [crop, setImage]);
|
}, [crop, setImage]);
|
||||||
|
|
||||||
|
const rotate = () => {
|
||||||
|
if (!imageRef.current || !canvasRef.current) return;
|
||||||
|
|
||||||
|
const image = imageRef.current;
|
||||||
|
const canvas = canvasRef.current;
|
||||||
|
const ctx = canvas.getContext("2d");
|
||||||
|
if (!ctx) return;
|
||||||
|
|
||||||
|
canvas.width = image.naturalHeight;
|
||||||
|
canvas.height = image.naturalWidth;
|
||||||
|
|
||||||
|
ctx.translate(canvas.width / 2, canvas.height / 2);
|
||||||
|
ctx.rotate(Math.PI / 2);
|
||||||
|
ctx.drawImage(image, -image.naturalWidth / 2, -image.naturalHeight / 2);
|
||||||
|
|
||||||
|
setImage(canvas.toDataURL());
|
||||||
|
};
|
||||||
|
|
||||||
const close = () => {
|
const close = () => {
|
||||||
setIsVisible(false);
|
setIsVisible(false);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|
@ -68,7 +86,7 @@ export default function CropPortrait({ isOpen, setIsOpen, image, setImage }: Pro
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="flex justify-between items-center mb-2">
|
<div className="flex justify-between items-center mb-2">
|
||||||
<h2 className="text-xl font-bold">Crop Portrait</h2>
|
<h2 className="text-xl font-bold">Edit Image</h2>
|
||||||
<button type="button" aria-label="Close" onClick={close} className="text-red-400 hover:text-red-500 text-2xl cursor-pointer">
|
<button type="button" aria-label="Close" onClick={close} className="text-red-400 hover:text-red-500 text-2xl cursor-pointer">
|
||||||
<Icon icon="material-symbols:close-rounded" />
|
<Icon icon="material-symbols:close-rounded" />
|
||||||
</button>
|
</button>
|
||||||
|
|
@ -88,6 +106,9 @@ export default function CropPortrait({ isOpen, setIsOpen, image, setImage }: Pro
|
||||||
<button type="button" onClick={applyCrop} className="pill button">
|
<button type="button" onClick={applyCrop} className="pill button">
|
||||||
Crop
|
Crop
|
||||||
</button>
|
</button>
|
||||||
|
<button type="button" onClick={rotate} className="pill button">
|
||||||
|
Rotate
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -5,7 +5,7 @@ import { FileWithPath } from "react-dropzone";
|
||||||
import { Icon } from "@iconify/react";
|
import { Icon } from "@iconify/react";
|
||||||
import Dropzone from "../dropzone";
|
import Dropzone from "../dropzone";
|
||||||
import Camera from "./camera";
|
import Camera from "./camera";
|
||||||
import CropPortrait from "./crop-portrait";
|
import ImageEditorPortrait from "./image-editor";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
text: string;
|
text: string;
|
||||||
|
|
@ -55,8 +55,8 @@ export default function SwitchFileUpload({ text, forceCrop, image, setImage }: P
|
||||||
Use your camera
|
Use your camera
|
||||||
</button>
|
</button>
|
||||||
<button type="button" aria-label="Crop image" onClick={() => setIsCropOpen(true)} className="pill button gap-2">
|
<button type="button" aria-label="Crop image" onClick={() => setIsCropOpen(true)} className="pill button gap-2">
|
||||||
<Icon icon="material-symbols:crop" fontSize={20} />
|
<Icon icon="mdi:image-edit" fontSize={20} />
|
||||||
Crop Image
|
Edit Image
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<Camera
|
<Camera
|
||||||
|
|
@ -67,7 +67,7 @@ export default function SwitchFileUpload({ text, forceCrop, image, setImage }: P
|
||||||
if (forceCrop) setIsCropOpen(true);
|
if (forceCrop) setIsCropOpen(true);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<CropPortrait isOpen={isCropOpen} setIsOpen={setIsCropOpen} image={image} setImage={setImage} />
|
<ImageEditorPortrait isOpen={isCropOpen} setIsOpen={setIsCropOpen} image={image} setImage={setImage} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue