一个React版的画图工具

7,706 阅读3分钟

前言

之前看到系统自带的画图工具, 感觉挺有意思, 于是用react和canvas实现了个简易画图工具, 不用react也行, 我主要是因为在原来的项目里写的, 所以用了react。

这里是线上地址: Paint

源码在项目下的Library中: 源码

实现

画笔功能

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

首先给canvas添加mousedown, mousemove, mouseup三个监听事件。

当鼠标按下的时候,将isDraw开关打开,鼠标抬起时将其关闭, 这样就能控制mousemove了, 不然一进入canvas的区域就会触发mousemove事件。

这里为了简洁都监听的是mouseEvent事件, 在事件内部根据event.type来触发不同的逻辑。

来捋一捋画笔的思路:

  1. 鼠标按下, 将isDraw开关打开;

  2. 拖住鼠标滑动触发mousemove事件

  3. 记录下鼠标点击的位置信息, 我这里用的是一个二维数组, 分别记录X轴和Y轴

  4. 初始化画笔的颜色和粗度以及形状

  5. 开始画线, 获取数组里鼠标画过的位置信息, 每两点之间进行一次描边, 这样点点连接就成了线

  6. 鼠标抬起, 将isDraw关闭, 禁止触发mousemove

大体思路就是这样, 下面是代码实现:

    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();  //描边
                    return
                
                default:
                    return
            }

        }
        if (e.type === 'mouseup') {
            setCanvasUrl(url => {
                url.push(canvas2D.toDataURL())
                return url
            })
            isDraw = false
        }
    }

橡皮擦

橡皮擦这里比较取巧, 是用背景色覆盖掉了画笔的颜色,这样看起来就像擦除了一样。 思路和画笔的思路是一样的, 只是将颜色改成了背景色

    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();  //描边
        return

油漆桶

这个最简单, 获取到颜色板中的颜色, 将背景色替换掉就ok了

画矩形

画矩形要用到clearRectstrokeRect两个方法, 它们接受4个参数, 分别是起点坐标X, Y和矩形的长, 宽。

思路:

每次触发mousemove其实都会画矩形, 但我们可以在每次画矩形前清空之前的区域。

clearRect就是清空矩形内的痕迹

这样看起来就像是我们画了一个矩形

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

撤回上一步功能

这个要用到canvas的drawImagetoDataURL两个方法。

思路:

  1. 在每次操作之后, 将canvas通过toDataURL方法转成img存起来,push到数组中

  2. 点击recall时, 通过drawImage方法加载上次操作时的canvas图形。

  3. 移除数组最后的数据, 方便下次执行recall操作。

  4. 判断数组是否已清空, 清空了就不再让操作了

代码如下:

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('不能再继续撤销了');
    }

}

下载功能

通过toDataURL获取当前canvas的图形。 创建一个a标签, 将地址赋给它, 执行点击事件下载。这个实现比较简陋, 但这是一般的思路, 也可以根据场景进行弹窗, 修改下载的相关信息。

代码如下:

const downloadImg = () => {
    var url = canvas2D.toDataURL('image/png');
    var a = document.createElement('a');
    document.body.appendChild(a);
    a.href = url;
    a.download = '我的绘画';
    a.target = '_blank';
    a.click();
}

颜色选择器

这里使用的是react-color插件。

结束

到这里,简易画板的思路就交代清楚了, 后续各位还可以在这个基础上开发更多的功能。

fighting~