Add the ability to zoom and position uploaded images, add checkbox to hide all model selections

This commit is contained in:
NilsUeter
2025-03-08 15:58:37 +01:00
parent 04558102db
commit cae8eca247
3 changed files with 136 additions and 12 deletions
+86
View File
@@ -0,0 +1,86 @@
import { useState, useRef, useEffect } from "react";
import { useLocalStorage } from "../helpers/useLocalStorage";
export const ImgEditor = ({ image, name }) => {
const [scale, setScale] = useLocalStorage(`scale_${name}`, 1);
const [positionX, setPositionX] = useLocalStorage(`positionX_${name}`, 0);
const [positionY, setPositionY] = useLocalStorage(`positionY_${name}`, 0);
const [dragging, setDragging] = useState(false);
const [startPos, setStartPos] = useState({ x: 0, y: 0 });
const imgRef = useRef(null);
const handleWheel = (e) => {
e.preventDefault();
setScale((prevScale) =>
Math.min(Math.max(parseFloat(prevScale) + e.deltaY * -0.001, 0.8), 5),
);
};
const handleMouseDown = (e) => {
setDragging(true);
setStartPos({
x: e.clientX - parseFloat(positionX),
y: e.clientY - parseFloat(positionY),
});
};
const handleMouseMove = (e) => {
if (dragging) {
setPositionX(e.clientX - startPos.x);
setPositionY(e.clientY - startPos.y);
}
};
const handleMouseUp = () => {
setDragging(false);
};
const handleDragStart = (e) => {
e.preventDefault();
};
useEffect(() => {
const imgElement = imgRef.current;
if (imgElement) {
imgElement.addEventListener("wheel", handleWheel);
imgElement.addEventListener("mousedown", handleMouseDown);
imgElement.addEventListener("dragstart", handleDragStart);
window.addEventListener("mousemove", handleMouseMove);
window.addEventListener("mouseup", handleMouseUp);
}
return () => {
if (imgElement) {
imgElement.removeEventListener("wheel", handleWheel);
imgElement.removeEventListener("mousedown", handleMouseDown);
imgElement.removeEventListener("dragstart", handleDragStart);
window.removeEventListener("mousemove", handleMouseMove);
window.removeEventListener("mouseup", handleMouseUp);
}
};
}, [dragging]);
return (
<div
ref={imgRef}
className="h-full w-full"
style={{
cursor: dragging ? "grabbing" : "grab",
overflow: "hidden",
}}
>
<img
src={image}
alt=""
style={{
width: "100%",
height: "100%",
transform: `translate(${positionX}px, ${positionY}px) scale(${scale})`,
transformOrigin: "center",
userSelect: "none",
objectFit: "contain",
}}
/>
</div>
);
};
+9 -10
View File
@@ -4,6 +4,7 @@ import adeptusAstartesIcon from "../assets/adeptusAstartesIcon.png";
import { Arrow, wavyLine } from "../assets/icons";
import { Weapons, hasDifferentProfiles } from "./Weapons";
import { useLocalStorage } from "../helpers/useLocalStorage";
import { ImgEditor } from "./ImgEditor";
export const Roster = ({ roster, onePerPage, colorUserChoice }) => {
if (!roster) {
@@ -220,6 +221,7 @@ const Unit = ({
}}
>
<input
className="hide-model-selection"
type="checkbox"
onChange={(e) => setHideModelCount(e.target.checked)}
/>
@@ -319,7 +321,7 @@ const Unit = ({
{cost.points}pts
</span>
</div>
<div className="relative z-10 flex gap-4">
<div className="relative flex gap-4">
<div
style={{
display: "flex",
@@ -341,11 +343,12 @@ const Unit = ({
</div>
{!hideModelCount && modelStats?.[0] && (
<div
className={`mt-[17px] ${
className={`pointer-events-none mt-[17px] ${
modelStats.length > 1 ? "" : "self-center"
}`}
style={{
fontSize: "0.7em",
zIndex: 101,
}}
>
{modelList.map((model, index) => (
@@ -362,16 +365,12 @@ const Unit = ({
top: 0,
height: "100%",
bottom: 0,
width: "45%",
width: "50%",
zIndex: 100,
overflow: "hidden",
}}
>
{hasImage && (
<img
src={image}
alt=""
style={{ width: "100%", height: "100%", objectFit: "contain" }}
/>
)}
{hasImage && <ImgEditor image={image} name={name} />}
<div className="absolute right-[1px] top-[2px] flex gap-1">
{hasImage && !bgRemoved && (
<button