Search code examples
javascriptreactjsdraggable

Move-able / Drag-able <div> in React


I'm currently trying to make an editable area to be shown on a picture inside a shopping website.

The aim is to have a picture with a box that can be moved and the size adjusted, using React.

I have been following a javascript tutorial on this from youtube however this isn't functioning as I have expected.

Below is the code I am using:

import React, { useState } from "react";
import axios from "axios";
import Delete from "../Buttons/Delete";
import Picture from "../Picture/Picture";

function EditableImageUploader(props){
    const [  loading, setLoading ] = useState(false);
    const editableArea = document.querySelector(".editableArea");
    const pictureEdit = document.querySelector(".pictureEdit");

    const uploadImage = async e => {
        const files = e.target.files
        const data = new FormData()
        data.append('file', files[0])
        data.append('upload_preset', 'charlotte-co')
        setLoading(true)
        axios.post('https://api.cloudinary.com/v1_1/charlotte-co/image/upload', data)
            .then(res => {
                setLoading(false);
                props.setImages([...props.images, res.data]);
            })
            .catch(err =>console.log(err));
    }

    function moveDiv(e){
        window.addEventListener("mousemove", mouseMove);
        window.addEventListener("mouseup", mouseRelease);
        let prevX = e.clientX;
        let prevY = e.clientY;

        function mouseMove(e){     
            let newX = prevX - e.clientX;
            let newY = prevY - e.clientY;
            console.log(prevY)
            console.log(`Y = ${e.clientY}`)
            console.log(prevX)
            console.log(`X = ${e.clientX}`)

            const rect = editableArea.getBoundingClientRect()
            editableArea.style.left = (rect.left - newX) + "px";
            editableArea.style.top = (rect.top - newY) + "px";
        }

        function mouseRelease(){
            window.removeEventListener("mousemove", mouseMove);
            window.removeEventListener("mouseup", mouseRelease);

            prevX = e.clientX;
            prevY = e.clientY; 
        }
    }

    return(<>
        <div className="image-uploader">
            {(props.images)?"":(<input
                type="file"
                name="file"
                placeholder="Upload a File"
                className="uploader"
                onChange={uploadImage}
            />)}
            <div>
                {loading ? (
                    <h4>loading...</h4>
                ):""}
                {(props.images)?(
                    props.images.map(image => {
                        return (<>
                            <Delete 
                                delete={image.public_id}
                                deleteFrom={props.images}
                                setDeleteFrom={props.setImages}
                            />
                         {

                         }   <Picture
                                className="pictureEdit"
                                publicId={image.public_id}
                                version={image.version}
                                width="300"
                                quality="100"
                            />
                            <div 
                                className="editableArea"
                                onMouseDown={(e)=> moveDiv(e)}
                            >
                                <div className="resizer north"></div>
                                <div className="resizer west"></div>
                                <div className="resizer south"></div>
                                <div className="resizer east"></div>
                                The transfer will spread across this box.
                            </div>
                        </>)
                    })
                ):(<p>No blank image available currently. Upload one to make this option available.</p>)}
            </div>
        </div>
    </>)
}

export default EditableImageUploader;

And here is the related CSS:


.editableArea{
    position: absolute;
    background-color: green;
    height: 150px;
    width: 150px;
    border-style: solid;
    border-color: black;
    border-width: 5px;
    z-index: 5;
    cursor: move;
}

It will allow you to move the div and change its position but it is moving it so far from where it was originally positioned.

I seem to think this is due to where the page is scrolled too, but I am very unsure about why it's reacting the way it is.

When the console reads:

653
Y = 653
325
X = 367

The actual position of the editable area is at:

left: 1005.55px; 
top: -12346.8px;

Any help and or explanation of how and why this is functioning the way it is and how to fix this problem would be greatly appreciated.


Solution

  • Consider including the offsetTop and offsetLeft in your calculations. For example if your div is not taking 100% of the width and height of the page, it is normal that you get different results, as clientX and clientY functions actually return the position of the mouse in the screen, and they are not relative to the DOM element. This article will help: How to get an element's top position relative to the browser's viewport?

    Also another thing you should take in consideration is the exact position that the element is clicked when started dragging. I can explain that with an example: Imagine that you have clicked the element in the middle (both horizontally and vertically) when started draggin. When you finish the calculation, the positions that you will get will be set to the top and left. Without this offset, the element won't be placed at the position where the mouse was released, but it will move a little (or a lot, depends on the size of the element).

    I would like to help you with some code, but it would be difficult as you have a lot of logic, including resources (images) and axios calls. If you cut this kind of logic and hardcode some images and add the code in online editor and compiler and set a link, I will try to help you with the code as well.

    If you have any other questions, I will be happy to help.