Canvas 涂鸦

第一步,我们先实现简单的绘制,并且在绘制之后将图片保存到本地

    var canvas = document.getElementById('canvas'),
    ctx = canvas.getContext('2d'),
    iptColor = document.getElementById('iptColor'),//画笔颜色
    iptSize = document.getElementById('iptSize'),//画笔大小
    btnClear = document.getElementById('btnClear'), //清除按钮
    btnSave = document.getElementById('btnSave'),//保存按钮
    canvasWidth = 800,
    canvasHeight = 600;
    canvas.setAttribute('width',canvasWidth);
    canvas.setAttribute('height',canvasHeight);    

    iptSize.oninput = function() {
        document.querySelector('.txt-size').innerHTML = iptSize.value
    }

    canvas.addEventListener('mousedown',function(e){
        var e = e || window.event;
        ctx.lineWidth = iptSize.value; 
        ctx.strokeStyle = iptColor.value;
        ctx.lineCap = "round";
        ctx.lineJoin = "round";
        ctx.beginPath();
        ctx.moveTo(e.clientX - canvas.offsetLeft,e.clientY - canvas.offsetTop); //线条起始位置
        document.onmousemove = function(e) {
            var e = e || window.event;        
            ctx.lineTo(e.clientX - canvas.offsetLeft,e.clientY - canvas.offsetTop);
            ctx.stroke();//绘制线条
        };
        canvas.onmouseup = function() {
            document.onmousemove = null;
            document.onmouseup = null;
        };
    })
    //清除画布
    btnClear.addEventListener('click',function(){
        ctx.clearRect(0, 0, canvasWidth, canvasHeight);
    })
    
    //保存图片到本地
    btnSave.addEventListener('click',function(){
        var imgData = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream");
        var filename = '图片.png';
        saveFile(imgData,filename)
    })
    
    var saveFile = function(data, filename) {  
        var save_link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');  
        save_link.href = data;  
        save_link.download = filename;  
        var event = document.createEvent('MouseEvents');  
        event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);  
        save_link.dispatchEvent(event);  
    };

这样看起来虽然说好像没问题,但是这一句有一些问题:

e.clientX - canvas.offsetLeft,e.clientY - canvas.offsetTop

如果页面比较高,产生了滚动条,当滚动条向上滑的时候,绘图的位置就不准确了,示例如:https://codepen.io/jianxiujiu...

所以此处应该使用

e.clientX - canvas.getBoundingClientRect().left,e.clientY - canvas.getBoundingClientRect().top

来替换。

第二步,消除涂鸦锯齿的办法

简单的绘制和图片保存完成了,但是在这种情况下,线条会有很明显的锯齿(灵魂画手来了)。
图片描述

有一个简单粗暴的方法,将线条的边缘进行模糊:

ctx.shadowBlur = 1;
ctx.shadowColor = iptColor.value;

示例:https://codepen.io/jianxiujiu...
加上边缘模糊之后,线条明显柔和了许多,但是还是有一个问题,就是在画笔收笔的地方,线条会变小(灵魂画手又来了)。
图片描述

在经过搜索查阅之后,发现有一个绘制办法可以降低锯齿的问题。
原理如图:
图片描述
绘制一个圆点,在下一个圆点之间,绘制一个矩形进行填充。这样绘制出来的线条的衔接会是圆滑的。

    var canvas = document.getElementById('canvas'),
        ctx = canvas.getContext('2d'),
        btnClear = document.getElementById('btnClear'),
        btnSave = document.getElementById('btnSave'),
        iptColor = document.getElementById('iptColor'),
        iptSize = document.getElementById('iptSize'),
        canvasWidth = 800,
        canvasHeight = 600;
    canvas.setAttribute('width',canvasWidth);
    canvas.setAttribute('height',canvasHeight);    
    
    iptSize.oninput = function() {
        document.querySelector('.txt-size').innerHTML = iptSize.value
    }
    canvas.addEventListener('mousedown',function(e){
        var e = e || window.event;
        var x1 = e.clientX - canvas.getBoundingClientRect().left,
            y1 = e.clientY - canvas.getBoundingClientRect().top;
        
        lineSize = iptSize.value;
        lineColor = iptColor.value;
        ctx.beginPath();
        ctx.fillStyle = lineColor;
        ctx.lineCap = "round";
        ctx.lineJoin = "round";
        ctx.arc(x1, y1, lineSize, 0, 2 * Math.PI);
        ctx.fill();
        ctx.closePath();
        
        document.onmousemove = function(e) {
            var e = e || window.event;    
            var x2 = e.clientX - canvas.getBoundingClientRect().left,
                y2 = e.clientY - canvas.getBoundingClientRect().top;
            var asin = lineSize * Math.sin(Math.atan((y2 - y1) / (x2 - x1)));
            var acos = lineSize * Math.cos(Math.atan((y2 - y1) / (x2 - x1)));
            //分别获取矩形的四个点的xy轴位置
            var x3 = x1 + asin;
            var y3 = y1 - acos;
            var x4 = x1 - asin;
            var y4 = y1 + acos;
            var x5 = x2 + asin;
            var y5 = y2 - acos;
            var x6 = x2 - asin;
            var y6 = y2 + acos;
                                
            ctx.beginPath();
            ctx.fillStyle = lineColor;
            ctx.arc(x2, y2, lineSize, 0, 2 * Math.PI);
            ctx.fill();
            ctx.closePath();
            ctx.beginPath();
            ctx.fillStyle = lineColor;
            ctx.moveTo(x3, y3);
            ctx.lineTo(x5, y5);
            ctx.lineTo(x6, y6);
            ctx.lineTo(x4, y4);
            ctx.fill();
            ctx.closePath();
            x1 = x2;
            y1 = y2;
        };
        canvas.onmouseup = function() {
            document.onmousemove = null;
            document.onmouseup = null;
        };
    })

    btnClear.addEventListener('click',function(){
        ctx.clearRect(0, 0, canvasWidth, canvasHeight);
    })

    btnSave.addEventListener('click',function(){
        var imgData = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream");
        var filename = '图片.png';
        saveFile(imgData,filename)
        
    })
    
    var saveFile = function(data, filename) {  
        var save_link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');  
        save_link.href = data;  
        save_link.download = filename;  
        var event = document.createEvent('MouseEvents');  
        event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);  
        save_link.dispatchEvent(event);  
    };
         

DEMO:
https://codepen.io/jianxiujiu...

第三步,添加橡皮擦工具

用这个方法,我们还可以加上橡皮擦。
橡皮擦的原理是,将橡皮绘制的路径覆盖到原来的画笔上。
这里我们将用到canvas的一个属性globalCompositeOperation,详解参照:
http://www.w3school.com.cn/ta...

var canvas = document.getElementById('canvas'),
        ctx = canvas.getContext('2d'),
        btnClear = document.getElementById('btnClear'),
        btnSave = document.getElementById('btnSave'),
        btnRestore = document.getElementById('btnRestore'),
        iptColor = document.getElementById('iptColor'),
        iptSize = document.getElementById('iptSize'),
        eraser = document.getElementById('eraser')
        pen = document.getElementById('pen'),
        canvasWidth = 800,
        canvasHeight = 600,
        isClear = 0;
    canvas.setAttribute('width',canvasWidth);
    canvas.setAttribute('height',canvasHeight);    
    
    iptSize.oninput = function() {
        document.querySelector('.txt-size').innerHTML = iptSize.value
    }
    eraser.addEventListener('click',function(){
        isClear = 1;
        this.classList.add('cho');
        pen.classList.remove('cho')
    })
    pen.addEventListener('click',function(){ 
        isClear = 0;
        this.classList.add('cho');
        eraser.classList.remove('cho')
    })
    canvas.addEventListener('mousedown',function(e){
        var e = e || window.event;
        var x1 = e.clientX - canvas.getBoundingClientRect().left,
            y1 = e.clientY - canvas.getBoundingClientRect().top;
            lineSize = iptSize.value;
        if(isClear == 0){      
            lineColor = iptColor.value;
            ctx.beginPath();
            ctx.fillStyle = lineColor;
            ctx.arc(x1, y1, lineSize, 0, 2 * Math.PI);
            ctx.fill();
            ctx.closePath();
            document.onmousemove = function(e) {
                draw();
            };
            btnClear.classList.remove('dis');
            btnSave.classList.remove('dis');
        }else{
            ctx.strokeStyle = "rgba(250,250,250,0)";
            document.onmousemove = function() {
                ctx.globalCompositeOperation = "destination-out";    
                draw();
                ctx.globalCompositeOperation = "source-over"
            }
        }

        canvas.onmouseup = function() {
            document.onmousemove = null;
            document.onmouseup = null;
        };

        draw = function(e){
            var e = e || window.event,
                x2 = e.clientX  - canvas.getBoundingClientRect().left,
                y2 = e.clientY  - canvas.getBoundingClientRect().top,
                asin = lineSize * Math.sin(Math.atan((y2 - y1) / (x2 - x1))),
                acos = lineSize * Math.cos(Math.atan((y2 - y1) / (x2 - x1))),
                x3 = x1 + asin,
                y3 = y1 - acos,
                x4 = x1 - asin,
                y4 = y1 + acos,
                x5 = x2 + asin,
                y5 = y2 - acos,
                x6 = x2 - asin,
                y6 = y2 + acos;                          
            ctx.beginPath();
            ctx.arc(x2, y2, lineSize, 0, 2 * Math.PI);
            ctx.fill();
            ctx.closePath();
            ctx.beginPath();
            ctx.fillStyle = lineColor;
            ctx.moveTo(x3, y3);
            ctx.lineTo(x5, y5);
            ctx.lineTo(x6, y6);
            ctx.lineTo(x4, y4);
            ctx.fill();
            ctx.closePath();
            x1 = x2;
            y1 = y2;
        }
    })

    // 清除
    btnClear.addEventListener('click',function(){
        ctx.clearRect(0, 0, canvasWidth, canvasHeight);
        this.classList.add('dis');
        btnSave.classList.add('dis');
    })

    //保存图片
    btnSave.addEventListener('click',function(){
        var imgData = canvas.toDataURL("image/png").replace("image/png", "image/octet-stream");
        var filename = '图片.png';
        saveFile(imgData,filename);
        this.classList.add('dis');
        
    })
    
    var saveFile = function(data, filename) {  
        var save_link = document.createElementNS('http://www.w3.org/1999/xhtml', 'a');  
        save_link.href = data;  
        save_link.download = filename;  
        var event = document.createEvent('MouseEvents');  
        event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);  
        save_link.dispatchEvent(event);  
    };

地址:https://codepen.io/jianxiujiu...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值