Search code examples
htmlreactjscanvas

Image is not rotating properly


import rotate_i from "../assets/rotate.png";
import resize_1 from "../assets/resize_1.png";
import resize_2 from "../assets/resize_2.png";
import cancel from "../assets/cancel.png";

var cancel_x = null;
var cancel_y = null;

var resize_v_x = null;
var resize_v_y = null;

var resize_x = null;
var resize_y = null;

var rotate_x = null;
var rotate_y = null;

function Design() {

    const [isHovered_t, setIsHovered_t] = useState(false);
    const [isHovered_i, setIsHovered_i] = useState(false);
    const [isHovered_c, setIsHovered_c] = useState(false);

    const [isHovered_tg, setIsHovered_tg] = useState(false);
    const [isHovered_ig, setIsHovered_ig] = useState(false);
    const [isHovered_cg, setIsHovered_cg] = useState(false);

    const [fontSize, setFontSize] = useState(10);
    const [outlineWidth, setOutlineWidth] = useState(0);
    const [rotate, setRotate] = useState(0);

    const [soleColor, setSoleColor] = useState(right_w);
    const [shoeSide, setShoeSide] = useState("right");

    const [imageSize, setImageSize] = useState({ x: 100, y: 100 });
    const [imageSizes, setImageSizes] = useState([]);

    const [images, setImages] = useState([]);
    const [imagePositions, setImagePositions] = useState([]);

    var targetImage;
    const [strapColor, setStrapColor] = useState("black");

    const [rotationAngle, setRotationAngle] = useState(0);
    const [isRotating, setIsRotating] = useState(false);
    const [initialAngle, setInitialAngle] = useState(0);

    var resize_v_s = false;

    const canvasRef = useRef(null);
    var animationFrame;

    const quick_draw = () => {
        const canvas = canvasRef.current;
        const ctx = canvas.getContext('2d');
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        ctx.fillStyle = strapColor; // Assuming you have a strapColor state to hold the selected strap color
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        images.forEach((imageUrl, i) => {
            const canvas = canvasRef.current;
            const ctx = canvas.getContext('2d');


            const image = new Image();
            image.src = imageUrl;
            image.onload = () => {
                ctx.drawImage(image, imagePositions[i].x, imagePositions[i].y, imageSizes[i].x, imageSizes[i].y);

            };
        });
    }

    const draw_image = () => {
        console.log(images);
        const canvas = canvasRef.current;
        const ctx = canvas.getContext('2d');
        const image_loading = false;
        console.log("Rotating State : ", isRotating);

        if (isRotating === false) {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            ctx.fillStyle = strapColor; // Assuming you have a strapColor state to hold the selected strap color

            ctx.fillRect(0, 0, canvas.width, canvas.height);
        }

        // Check if all images are loaded
        const allImagesLoaded = images.every((imageUrl) => {
            return imageUrl in imageCache;
        });

        if (!allImagesLoaded) {
            // If not all images are loaded, wait for the next animation frame to redraw
            animationFrame = requestAnimationFrame(draw_image);
            return;
        }

        cancel_x = imagePositions[targetImage].x - 14;
        cancel_y = imagePositions[targetImage].y - 14;
        resize_v_x = imagePositions[targetImage].x - 14;
        resize_v_y = imagePositions[targetImage].y + imageSizes[targetImage].y;
        resize_x = imagePositions[targetImage].x - 5 + imageSizes[targetImage].x;
        resize_y = imagePositions[targetImage].y - 5 + imageSizes[targetImage].y;
        rotate_x = imagePositions[targetImage].x - 5 + imageSizes[targetImage].x;
        rotate_y = imagePositions[targetImage].y - 12;

        ctx.strokeRect(imagePositions[targetImage].x - 5, imagePositions[targetImage].y - 5, imageSizes[targetImage].x + 10, imageSizes[targetImage].y + 10);
        const cancel_ = new Image();
        cancel_.src = cancel;

        const rotate_ = new Image();
        rotate_.src = rotate_i;

        const resize_v = new Image();
        resize_v.src = resize_2;

        const resize_ = new Image();
        resize_.src = resize_1;

        console.log("Target Image : ", targetImage);

        images.forEach((imageUrl, i) => {
            if (!(isRotating === true && i === targetImage)) {
                const image = imageCache[imageUrl];
                ctx.drawImage(image, imagePositions[i].x, imagePositions[i].y, imageSizes[i].x, imageSizes[i].y);
            }
        });

        animationFrame = requestAnimationFrame(draw_image); // Request the next animation frame
        ctx.drawImage(cancel_, cancel_x, cancel_y, 25, 25);
        ctx.drawImage(resize_v, resize_v_x, resize_v_y, 25, 25);
        ctx.drawImage(resize_, resize_x, resize_y, 25, 25);
        ctx.drawImage(rotate_, rotate_x, rotate_y, 25, 25);
    }

    const stopDrawingImages = () => {
        cancelAnimationFrame(animationFrame); // Stop the animation loop when dragging is done
        animationFrame = null;
    };

    const imageCache = {};
    const preloadImages = () => {
        images.forEach((imageUrl) => {
            const image = new Image();
            image.src = imageUrl;
            imageCache[imageUrl] = image;
        });
    };

    useEffect(() => {
        const canvas = canvasRef.current;
        const ctx = canvas.getContext('2d');

        ctx.fillStyle = strapColor; // Assuming you have a strapColor state to hold the selected strap color
        ctx.fillRect(0, 0, canvas.width, canvas.height);
        preloadImages();

        images.forEach((imageUrl, i) => {
            const canvas = canvasRef.current;
            const ctx = canvas.getContext('2d');


            const image = new Image();
            image.src = imageUrl;
            image.onload = () => {
                ctx.drawImage(image, imagePositions[i].x, imagePositions[i].y, imageSizes[i].x, imageSizes[i].y);

            };
        });
    }, [images, imagePositions, strapColor, imageSizes])

    const handleOnMouse = (e) => {

        const canvas = canvasRef.current;
        const rect = canvas.getBoundingClientRect();
        var scaleX = canvas.width / rect.width;    // relationship bitmap vs. element for x
        var scaleY = canvas.height / rect.height;

        const mouseX = (e.clientX - rect.left) * scaleX;
        const mouseY = (e.clientY - rect.top) * scaleY;

        if ((mouseX >= cancel_x &&
            mouseX <= cancel_x + 25 && // Width of the image (you can adjust this value based on the image size)
            mouseY >= cancel_y &&
            mouseY <= cancel_y + 25) || (mouseX >= resize_v_x &&
                mouseX <= resize_v_x + 25 && // Width of the image (you can adjust this value based on the image size)
                mouseY >= resize_v_y &&
                mouseY <= resize_v_y + 25) || (mouseX >= resize_x &&
                    mouseX <= resize_x + 25 && // Width of the image (you can adjust this value based on the image size)
                    mouseY >= resize_y &&
                    mouseY <= resize_y + 25) || (mouseX >= rotate_x &&
                        mouseX <= rotate_x + 25 && // Width of the image (you can adjust this value based on the image size)
                        mouseY >= rotate_y &&
                        mouseY <= rotate_y + 25)) {
            const canvas_ = document.getElementById("canvas");
            canvas_.style.cursor = "pointer";
            console.log("enter");
        }
        else {
            const canvas_ = document.getElementById("canvas");
            canvas_.style.cursor = "default";
        }
    }

    const handleMouseDown = (e) => {
        const canvas = canvasRef.current;
        const rect = canvas.getBoundingClientRect();
        var scaleX = canvas.width / rect.width;    // relationship bitmap vs. element for x
        var scaleY = canvas.height / rect.height;

        const mouseX = (e.clientX - rect.left) * scaleX;
        const mouseY = (e.clientY - rect.top) * scaleY;

        if (targetImage != undefined) {

            if (!(mouseX >= imagePositions[targetImage].x &&
                mouseX <= imagePositions[targetImage].x + imageSizes[targetImage].x && // Width of the image (you can adjust this value based on the image size)
                mouseY >= imagePositions[targetImage].y &&
                mouseY <= imagePositions[targetImage].y + imageSizes[targetImage].y) && !(mouseX >= cancel_x &&
                    mouseX <= cancel_x + 25 && // Width of the image (you can adjust this value based on the image size)
                    mouseY >= cancel_y &&
                    mouseY <= cancel_y + 25) && !(mouseX >= resize_v_x &&
                        mouseX <= resize_v_x + 25 && // Width of the image (you can adjust this value based on the image size)
                        mouseY >= resize_v_y &&
                        mouseY <= resize_v_y + 25) && !(mouseX >= resize_x &&
                            mouseX <= resize_x + 25 && // Width of the image (you can adjust this value based on the image size)
                            mouseY >= resize_y &&
                            mouseY <= resize_y + 25) && !(mouseX >= rotate_x &&
                                mouseX <= rotate_x + 25 && // Width of the image (you can adjust this value based on the image size)
                                mouseY >= rotate_y &&
                                mouseY <= rotate_y + 25)) {

                console.log(mouseX, mouseY);
                console.log(cancel_x, cancel_y);
                quick_draw();
            }
        }

        if ((mouseX >= cancel_x &&
            mouseX <= cancel_x + 25 && // Width of the image (you can adjust this value based on the image size)
            mouseY >= cancel_y &&
            mouseY <= cancel_y + 25)) {
            setImages(prevState => prevState.filter((_, index) => index !== targetImage));
            setImagePositions(prevState => prevState.filter((_, index) => index !== targetImage));
            setImageSizes(prevState => prevState.filter((_, index) => index !== targetImage));

            preloadImages();
        }

        if ((mouseX >= resize_v_x &&
            mouseX <= resize_v_x + 25 && // Width of the image (you can adjust this value based on the image size)
            mouseY >= resize_v_y &&
            mouseY <= resize_v_y + 25)) {
            canvas.addEventListener('mousemove', handleResizeZ);
            canvas.addEventListener('mouseup', handleMouseUp);
        }

        if ((mouseX >= resize_x &&
            mouseX <= resize_x + 25 && // Width of the image (you can adjust this value based on the image size)
            mouseY >= resize_y &&
            mouseY <= resize_y + 25)) {
            canvas.addEventListener('mousemove', handleResize);
            canvas.addEventListener('mouseup', handleMouseUp);
        }

        if ((mouseX >= rotate_x &&
            mouseX <= rotate_x + 25 && // Width of the image (you can adjust this value based on the image size)
            mouseY >= rotate_y &&
            mouseY <= rotate_y + 25)) {

            setIsRotating(true);
            canvas.addEventListener('mousemove', handleRotate);
            canvas.addEventListener('mouseup', handleMouseUp);
        }

        // Check if the mouse is within the image boundaries
        imagePositions.forEach((pos, i) => {
            if (
                mouseX >= imagePositions[i].x &&
                mouseX <= imagePositions[i].x + imageSizes[i].x && // Width of the image (you can adjust this value based on the image size)
                mouseY >= imagePositions[i].y &&
                mouseY <= imagePositions[i].y + imageSizes[i].y // Height of the image (you can adjust this value based on the image size)
            ) {

                //setTargetImage(i);
                const ctx = canvas.getContext('2d');
                targetImage = i;

                ctx.strokeStyle = "blue";
                ctx.strokeRect(imagePositions[i].x - 5, imagePositions[i].y - 5, imageSizes[i].x + 10, imageSizes[i].y + 10);

                cancel_x = imagePositions[i].x - 14;
                cancel_y = imagePositions[i].y - 14;
                resize_v_x = imagePositions[i].x - 14;
                resize_v_y = imagePositions[i].y + imageSizes[i].y;

                resize_x = imagePositions[i].x - 5 + imageSizes[i].x;
                resize_y = imagePositions[i].y - 5 + imageSizes[i].y;

                rotate_x = imagePositions[i].x - 5 + imageSizes[i].x;
                rotate_y = imagePositions[i].y - 12;

                const cancel_ = new Image();
                cancel_.src = cancel;

                const resize_v = new Image();
                resize_v.src = resize_2;

                const resize_ = new Image();
                resize_.src = resize_1;

                const rotate_ = new Image();
                rotate_.src = rotate_i;

                rotate_.onload = () => {
                    ctx.drawImage(rotate_, rotate_x, rotate_y, 25, 25);
                }

                resize_.onload = () => {
                    ctx.drawImage(resize_, resize_x, resize_y, 25, 25);
                }

                resize_v.onload = () => {
                    ctx.drawImage(resize_v, resize_v_x, resize_v_y, 25, 25);
                }
                cancel_.onload = () => {
                    ctx.drawImage(cancel_, cancel_x, cancel_y, 25, 25);
                }

                console.log(targetImage);
                console.log("match", targetImage);
                // If the mouse is inside the image, initiate dragging
                canvas.addEventListener('mousemove', handleMouseMove);
                canvas.addEventListener('mouseup', handleMouseUp);
                const canvas_ = document.getElementById("canvas");
                canvas_.style.cursor = "move";
            }
        })
    };

    const handleRotate = (e) => {
        const canvas = canvasRef.current;
        const rect = canvas.getBoundingClientRect();
        var scaleX = canvas.width / rect.width; // relationship bitmap vs. element for x
        var scaleY = canvas.height / rect.height;
        const ctx = canvas.getContext("2d");

        const mouseX = (e.clientX - rect.left) * scaleX;
        const mouseY = (e.clientY - rect.top) * scaleY;
        const centerX = imagePositions[targetImage].x + imageSizes[targetImage].x / 2; // Assuming the image's center is at (150, 100)
        const centerY = imagePositions[targetImage].y - imageSizes[targetImage].y / 2;
        const deltaX = mouseX - centerX;
        const deltaY = mouseY - centerY;
        const angle = Math.atan2(deltaY, deltaX);
        setRotationAngle(angle);
        ctx.fillStyle = strapColor;
        ctx.fillRect(0, 0, canvas.width, canvas.height);

        const image = new Image();
        image.src = images[targetImage];

        image.onload = () => {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            ctx.save();
            ctx.translate(centerX, centerY);
            ctx.rotate(angle);
            const pos = imagePositions;
            pos[targetImage] = { x: -imageSizes[targetImage].x / 2, y: -imageSizes[targetImage].y / 2 };
            setImagePositions(imagePositions);
            ctx.drawImage(
                image,
                -imageSizes[targetImage].x / 2,
                -imageSizes[targetImage].y / 2,
                imageSizes[targetImage].x,
                imageSizes[targetImage].y
            );
            ctx.restore();
        };
    }

    const handleResize = (e) => {
        const canvas = canvasRef.current;
        const rect = canvas.getBoundingClientRect();
        var scaleX = canvas.width / rect.width;    // relationship bitmap vs. element for x
        var scaleY = canvas.height / rect.height;

        const mouseX = (e.clientX - rect.left) * scaleX;
        const mouseY = (e.clientY - rect.top) * scaleY;

        var imgSize = imageSizes;
        imgSize[targetImage] = { x: mouseX, y: mouseY };
        setImageSizes(imgSize);

        if (!animationFrame) {
            // Request animation frame only if it's not already requested
            animationFrame = requestAnimationFrame(draw_image);
        }
    }

    const handleResizeZ = (e) => {
        const canvas = canvasRef.current;
        const rect = canvas.getBoundingClientRect();
        var scaleX = canvas.width / rect.width;    // relationship bitmap vs. element for x
        var scaleY = canvas.height / rect.height;

        const mouseX = (e.clientX - rect.left) * scaleX;
        const mouseY = (e.clientY - rect.top) * scaleY;

        var imgSize = imageSizes;
        imgSize[targetImage] = { x: imgSize[targetImage].x, y: mouseY };
        setImageSizes(imgSize);

        if (!animationFrame) {
            // Request animation frame only if it's not already requested
            animationFrame = requestAnimationFrame(draw_image);
        }
    }

    const handleMouseMove = (e) => {
        const canvas = canvasRef.current;
        const rect = canvas.getBoundingClientRect();
        var scaleX = canvas.width / rect.width;    // relationship bitmap vs. element for x
        var scaleY = canvas.height / rect.height;

        const mouseX = (e.clientX - rect.left) * scaleX;
        const mouseY = (e.clientY - rect.top) * scaleY;


        var pos = imagePositions;
        pos[targetImage] = {
            x: mouseX - 50, // Half of the width of the image
            y: mouseY - 50
        }
        setImagePositions(pos);

        if (!animationFrame) {
            // Request animation frame only if it's not already requested
            animationFrame = requestAnimationFrame(draw_image);
        }
    };

    const handleMouseUp = () => {
        const canvas = canvasRef.current;
        // Remove the event listeners when dragging is done
        canvas.removeEventListener('mousemove', handleMouseMove);
        canvas.removeEventListener('mouseup', handleMouseUp);
        canvas.removeEventListener('mousemove', handleResizeZ);
        canvas.removeEventListener('mousemove', handleResize);
        canvas.removeEventListener('mousemove', handleRotate);
        setIsRotating(false);


        const canvas_ = document.getElementById("canvas");
        canvas_.style.cursor = "default";
        stopDrawingImages();
        resize_v_s = false;
    };
    const handleImageUpload = (event) => {
        const file = event.target.files[0];

        if (file) {
            const reader = new FileReader();
            reader.onload = function (e) {
                const imageDataUrl = e.target.result;
                // Use the imageDataUrl for further processing (e.g., display image or upload to server)
                setImages((prev) => [...prev, imageDataUrl]);
                setImagePositions((prev) => [...prev, { x: 50, y: 50 }]);
                setImageSizes((prev) => [...prev, { x: 100, y: 100 }]);
            };
            reader.readAsDataURL(file);

        }
    };
   return (
        <>
            <Navbar />
            <div id="designContainer">

                <div id="canvasContainer">
                    <img src={soleColor} alt="black slippers" />
                    <canvas onMouseMove={handleOnMouse} onMouseDown={handleMouseDown} ref={canvasRef} id="canvas">
                        <p>Hello, I am canvas</p>
                    </canvas>
                </div>
        </>
    );
}

export default Design;

I am using React JS along with HTML5 Canvas, I am have a canvas in which images are uploaded, on clicking and image, a box appears around the picture selected with 4 icons for rotate, resize Vericaly, close and resize overall, all the icons are working perfectly but the rotation is not perfect, when I click on the rotate icon and move the mouse, the image first moves upwards and then rotate around its center and it cannot be clicked again as its position in the canvas goes wrong. The handleRotate function handles the image rotation when the mouse button is pressed and the mouse is moved.


Solution

  • So, I was not properly centering the canvas, the canvas was not being centered at the centre of the target image as I was subtracting from the position.