preface

It was interesting to see the drawing tool in the system before, so I used React and Canvas to implement a simple drawing tool without React. I mainly used React because I wrote it in the original project.

Here is the online address: Paint

The source code is in the project Library: source code

implementation

Brush function

<canvas
    id="canvas"
    onMouseDown={mouseEvent}
    onMouseMove={mouseEvent}
    onMouseUp={mouseEvent}
>
</canvas>
Copy the code

First, add mousedown, Mousemove and Mouseup to canvas.

Turn the isDraw switch on when the mouse is down and turn it off when the mouse is up to control the Mousemove, otherwise the Mousemove event will be triggered as soon as you enter the Canvas area.

The mouseEvent event is listened for for brevity, and different logic is fired within the event based on event.type.

Here’s a brush idea:

  1. Click the mouse, isDraw switch open;

  2. Drag the mouse to trigger the Mousemove event

  3. I’m going to record the position of the mouse click, and I’m going to use a two-dimensional array here, and I’m going to record the X axis and the Y axis

  4. Initialize the color and thickness of the brush and its shape

  5. Start drawing a line, get the position of the mouse in the array, stroke between each point, so that the dots connect to form a line

  6. Mouse up, turn isDraw off, disable mousemove trigger

That’s the general idea. Here’s the code:

    const mouseEvent = (e) = > {
        let ctx = canvas2D.getContext('2d')

        e.persist()
        if (e.type === 'mousedown') {
            switch (active) {
                case 'spray':
                    return canvas2D.style.backgroundColor = color
                default:
                    isDraw = true
                    arr = []
                    return}}if (e.type === 'mousemove' && isDraw) {
            arr.push([e.pageX - canvas2D.offsetLeft, e.pageY - document.querySelector('.admin-box').offsetTop - 40])

            switch (active) {
                case 'pen':
                    ctx.strokeStyle = color
                    ctx.lineJoin = "round";
                    ctx.lineWidth = 5;
                    ctx.beginPath();
                    arr.length > 1 && ctx.moveTo(arr[arr.length - 2] [0], arr[arr.length - 2] [1]);
                    ctx.lineTo(arr[arr.length - 1] [0], arr[arr.length - 1] [1]);
                    ctx.closePath();
                    ctx.stroke();  / / stroke
                    return
                
                default:
                    return}}if (e.type === 'mouseup') {
            setCanvasUrl(url= > {
                url.push(canvas2D.toDataURL())
                return url
            })
            isDraw = false}}Copy the code

The eraser

The eraser is a bit tricky here, overlaying the brush color with the background color so that it looks like erasure. The idea is the same as the brush, but change the color to the background color

    case 'eraser':
        ctx.strokeStyle = canvas2D.style.backgroundColor || '#ccc'
        ctx.lineJoin = "round";
        ctx.lineWidth = 50;
        ctx.beginPath();
        arr.length > 1 && ctx.moveTo(arr[arr.length - 2] [0], arr[arr.length - 2] [1]);
        ctx.lineTo(arr[arr.length - 1] [0], arr[arr.length - 1] [1]);
        ctx.closePath();
        ctx.stroke();  / / stroke
        return
Copy the code

The paint bucket

This is the easiest one, get the color in the color palette and replace the background color

Draw a rectangular

To draw a rectangle, two methods, clearRect and strokeRect, take four parameters: the starting coordinates X and Y, and the length and width of the rectangle.

Ideas:

Each time mousemove is triggered it will actually draw a rectangle, but we can clear the area before drawing a rectangle.

ClearRect is to clear the traces in the rectangle

So it looks like we’ve drawn a rectangle

case 'rectangle':
    let left = arr[0] [0]
    let top = arr[0] [1]
    let prewidth = arr.length > 1 && arr[arr.length - 2] [0] - left
    let preheight = arr.length > 1 && arr[arr.length - 2] [1] - top
    let width = arr[arr.length - 1] [0] - left
    let height = arr[arr.length - 1] [1] - top
    ctx.beginPath();
    ctx.lineWidth = "6";
    ctx.strokeStyle = "red";
    ctx.clearRect(left, top, prewidth, preheight)
    ctx.strokeRect(left, top, width, height);
    return
Copy the code

Undo the previous function

This will use the canvas drawImage and toDataURL methods.

Ideas:

  1. After each operation, convert the Canvas to IMG via the toDataURL method and store it in the array

  2. When you click Recall, the canvas image from the previous operation is loaded using the drawImage method.

  3. Remove the last data in the array to facilitate the next recall operation.

  4. Check if the array is empty. If it is empty, no more operations are allowed

The code is as follows:

const recallClick = (e) = > {
    let ctx = canvas2D.getContext('2d')
    let step = canvasUrl.length - 1
    if (step >= 0) {
        step--;
        ctx.clearRect(0.0.1000.1000);
        let canvasPic = new Image();
        canvasPic.src = canvasUrl[step];
        canvasPic.addEventListener('load', () => {
            ctx.drawImage(canvasPic, 0.0);
        });

        setCanvasUrl(canvasUrl= > {
            canvasUrl.pop()
            return canvasUrl
        })
    } else {
        console.log('I can't undo it anymore.'); }}Copy the code

Download function

Get the graph of the current canvas via the toDataURL. Create an A tag, assign the address to it, and perform a click event download. This implementation is a bit crude, but this is the general idea. You can also pop up the window according to the scene and modify the relevant information downloaded.

The code is as follows:

const downloadImg = (a)= > {
    var url = canvas2D.toDataURL('image/png');
    var a = document.createElement('a');
    document.body.appendChild(a);
    a.href = url;
    a.download = 'My Drawing';
    a.target = '_blank';
    a.click();
}
Copy the code

Color picker

The react-color plugin is used here.

The end of the

Here, the idea of simple sketchpad is clear, and you can develop more functions on this basis.

fighting~