Demo address

adjlyadv.github.io/svg-editor/

Problems with dragging anchor points

Use mousedown, Mouseup, and Mousemove to implement anchor drag and drop. Note that the Mousemove event is bound to the parent element.

If you bind to an element that needs to be moved, moving the mouse too fast will move the element out of range, causing the element to stall.

Mobx is used as global state management in the project and there will be multiple anchors to drag, so a dragging object is set up in store to record which node on which path the current mouse click belongs to.

The sample code

<! DOCTYPEhtml>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="Width = device - width, initial - scale = 1.0">
    <title>test</title>
    <style>
        #container{
            margin-top:100px;
            margin-left:200px;
        }
        svg{
            width:400px;
            height:400px;
            background-color: black;
        }
        circle{
            cx:10;
            cy:10;
            fill: #0FF;
            stroke: #55f;
            r: 10;
        }
    </style>
</head>
<body>
    <div id="container">
        <svg id="bg" width="400" height="400">
            <circle id="ci"/>
        </svg>
    </div>

    <script>
        let container = document.getElementById('container')
        let svg = document.getElementById('bg');
        let circle = document.getElementById('ci');
        let dragged = false;

        circle.onmousedown = (event) = >{
            dragged = true;
            console.log('down');
        }

        circle.onmouseup = (event) = >{
            dragged = false;
            console.log('up');
        }
        svg.onmousemove = (event) = >{
            if(dragged){
                let x = event.clientX 
                lety = event.clientY circle.style.cx = x - container.offsetLeft; circle.style.cy = y - container.offsetTop; }}</script>
</body>
</html>
Copy the code

Render when creating a new path

By observing PS’s pen tool and other similar products, the process of creating a new path is summarized as shown in the figure below

Apply colours to a drawing

When selecting control points (mouse down movement process) the anchor points and mouse line need to be rendered.

<circle className="point-control" cx={newNode.posX} cy={newNode.posY} stroke="#55f" r="10" />
<line x1={newNode.posX} y1={newNode.posY} x2={newNode.ctrPosX} y2={newNode.ctrPosY} stroke="# 555" strokeWidth={width} />
<circle className="point-control" cx={newNode.ctrPosX} cy={newNode.ctrPosY} stroke="# 000" r="10" />
Copy the code

When selecting a new anchor point (the process of moving the mouse up), you need to render the path of the mouse and the previous anchor point.

let getD = `M ${lastNode.posX} ${lastNode.posY} C ${mockCtrX} ${mockCtrY} ${newNode.ctrPosX} ${newNode.ctrPosY}`;

if(lastNode.posX ! == newNode.posX && lastNode.posY ! == newNode.posY){ getD +=` ${newNode.posX} ${newNode.posY}`;
}else{
   getD += ` ${newNode.ctrPosX} ${newNode.ctrPosY}`;// When no new anchor is identified
}

<path d = {getD}  fill="none" stroke="# 000" strokeWidth="1"/>
Copy the code

The above code would look like this if we removed the else (newNode and oldNode overlap pos) :

The starting point to judge

There is only one control point in the path starting point, so you need to change the control point when starting at lastNode. A variable of StartNode is used to determine if it is the initial point on the path.

              if(startNode){// Render the first nodesetLastnode({ ... newNode,ctrPosX: mockCtrX,
                  ctrPosY: mockCtrY
                })
                setStartNode(false);
              }else{// It is another nodesetLastnode({ ... newNode }) }Copy the code

Distinguish between double click events and mouseDown mouseup

The order in mouse events is mousedown mouseup click mousedown mouseup click Doubleclick so if it is a doubleclick event, two mouseups will be triggered. In order to distinguish mouseup from doubleclick, a timer is added to the mouseup, and then the timer is cleared at doublecklick, so that the last anchor of the path can be added at the time of doubleclick.

const handleMouseUp = (event: any) = > {
    event.stopPropagation();
    clearTimeout(mouseUpTimeChange);
    mouseUpTimeChange = setTimeout(() = >{},250);
}
  const pathDoubleClick:any = () = > {
    clearTimeout(mouseUpTimeChange); . }Copy the code