使用canvas实现一个简易画板

92 阅读1分钟

使用canvas实现一个简易画板

<!DOCTYPE html>
<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>Document</title>
    <style>
        * {
            margin: 0;
            padding: 0;
        }
    </style>
</head>

<body>
    <canvas id="canvas"></canvas>
    <div>
        <button onclick="cancel()">取消</button>
        <button onclick="save()">保存</button>
        <button onclick="prev()">上一步</button>
        <button onclick="next()">下一步</button>
        <button onclick="strong()">加粗</button>
        <button onclick="restoreWidth()">取消加粗</button>
        <input id="color" type="color" onchange="changeColor()" value="#ff0000">
        <button id="eraser" onclick="handleEraser()">橡皮檫</button>
    </div>
</body>
<script>
    let config = {
        width: 500, // 画布宽度
        height: 500, // 画笔高度
        lineHeight: 1, // 画笔宽度
        strongLineHeight: 5, // 画笔加粗
        lineStyle: 'red', // 画笔默认样式(颜色)
        eraserLineWidth: 20, // 橡皮檫宽度
        eraserLineStyle: 'white', // 橡皮檫样式
        eraserUrl: "https://www.suxieban.com/css/font/myfont/cursor-eraser-black.svg", // 橡皮檫图标url
        pencilUrl:"https://www.suxieban.com/css/font/myfont/cursor-pen-black.svg"  // 笔图标url
    }

    // 获取相应元素
    let canvas = document.getElementById('canvas')
    let colorEl = document.getElementById('color')
    let eraseBtn = document.getElementById('eraser')

    // 设置canvas大小
    canvas.width = config.width
    canvas.height = config.height
    
    // 设置canvas样式
    canvas.style.border = '1px solid red'
    canvas.style.cursor = `url(${config.pencilUrl}),auto`

    // 存储画的历史记录
    let stack = []
    // 当前画的指针
    let step = -1
    
    // 获取canvas 上下文
    let ctx = canvas.getContext('2d')

    // 设置画的线条宽度和样式
    ctx.lineWidth = config.lineHeight
    ctx.strokeStyle = config.lineStyle

    // 初始化canvas     
    function init (event) {
        // 获取画笔的相关位置数据
        const { offsetX, offsetY, pageX, pageY } = event
        // 开始画
        ctx.beginPath()
        // 把画笔移动到画画的位置
        ctx.moveTo(offsetX, offsetY)
        // 监听画笔的移动事件 进行画画
        canvas.addEventListener('mousemove', draw)
    }
    function draw (event) {
        // 获取画笔的相关位置数据
        const { offsetX, offsetY, pageX, pageY } = event
        // 连线
        ctx.lineTo(offsetX, offsetY)
        // 描线
        ctx.stroke()
    }
    function clearDraw (event) {
        // 结束画画
        ctx.closePath()
        // 把canvas转化为base64数据
        const base64 = canvas.toDataURL()
        // 把当前数据放到stack数组中
        stack.push(base64)
        // 移动当前画画指针
        step++
        // 移除画笔移动事件 结束
        canvas.removeEventListener('mousemove', draw)
    }
    // 取消
    function cancel () {
        // 清空画布
        ctx.clearRect(0, 0, config.width, config.height)
    }

    // 上一步
    function prev () {
        // 如果step 大于等于0 表示有历史记录 有上一步
        if (step >= 0) {
            // 首先让step减一
            step--
            // 清空画布
            ctx.clearRect(0, 0, config.width, config.height);
            // 如果历史记录没有数据了 则返回
            if(!stack[step]) return 
            // 创建image对象
            let img = new Image()
            // 让img对象的src属性指向上次操作的数据
            img.src = stack[step]
            // 等待图片加载完成
            img.onload = function () {
                // 加载完成后重新绘制canvas 这样就恢复了上次的操作
                ctx.drawImage(img, 0, 0, config.width, config.height)
            } 
        }else{
            // 如果step 小于0 则表示没有上步操作了
            console.log('没有更多数据了')
        }
    }
    // 下一步
    function next () {
        // 如果step < step.length -1 表示还有历史记录 有下一步
        if (step < stack.length - 1) {
            // 让step累加
            step++
            // 清除画布
            ctx.clearRect(0, 0, config.width, config.height);
            // 创建img对象
            let img = new Image()
            // 让img对象的src属性指向下次操作的数据
            img.src = stack[step]
            // 等待图片加载完成
            img.onload = function () {
                // 加载完成后重新绘制canvas 这样就恢复了下次的操作
                ctx.drawImage(img, 0, 0, config.width, config.height)
            } 
        }else{
            console.log('没有更多数据了')
        }
    }
    // 保存为图片
    function save () {
        // 以上两种方法均可
        // canvas.toBlob(function (data) {
        //     let obj = URL.createObjectURL(data)
        //     let a = document.createElement('a')
        //     a.href = obj
        //     a.download = Date.now()
        //     a.click()
        //     URL.revokeObjectURL(obj)
        // })
        const base64 = canvas.toDataURL()
        let a = document.createElement('a')
        a.href = base64
        a.download = Date.now()
        a.click()
        a.remove()
    }
    // 加粗
    function strong(){
        ctx.lineWidth = config.strongLineHeight
    }
    // 恢复正常大小
    function restoreWidth(){
        ctx.lineWidth = config.lineHeight
    }
    // 修改颜色值
    function changeColor(e){
        ctx.strokeStyle = colorEl.value
    }
    //橡皮檫
    function handleEraser(){
        if(!ctx.isEraser){
            ctx.strokeStyle = config.eraserLineStyle
            ctx.lineWidth = config.eraserLineWidth
            canvas.style.cursor = `url(${config.eraserUrl}),auto`
            eraseBtn.innerHTML = '取消橡皮檫'
        }else{
            ctx.strokeStyle = config.lineStyle
            ctx.lineWidth = config.lineHeight
            canvas.style.cursor = `url(${config.pencilUrl}),auto`
            eraseBtn.innerHTML = '橡皮檫'
        }
        ctx.isEraser = !ctx.isEraser
        
    }
    // 监听鼠标按下事件 初始化画布
    canvas.addEventListener('mousedown', init)
    // 监听鼠标抬起事件 清除画布
    canvas.addEventListener('mouseup', clearDraw)
</script>

</html>

最终效果

image.png