1. 程式人生 > >canvas畫圖--流暢沒有齒痕的線,影象畫線

canvas畫圖--流暢沒有齒痕的線,影象畫線

畫圖,首先要獲取滑鼠位置,當滑鼠在畫圖板上移動時,隨之畫線。

1.畫圖板canvas,監聽滑鼠事件

2.獲取滑鼠事件,得到滑鼠位置。

var mouse = {x: 0, y: 0}; //起始滑鼠位置,也就是mousedown
var last_mouse = {x: 0, y: 0};
 
/* Mouse Capturing Work */
canvas.addEventListener('mousemove', function(e) { 
    last_mouse.x = mouse.x;
    last_mouse.y = mouse.y;
 
    mouse.x = e.pageX - this
.offsetLeft; mouse.y = e.pageY - this.offsetTop; }, false);

以上,通過canvas.addEventListener使畫圖板canvas監聽到滑鼠事件。

3. 畫線

var onPaint = function() {
    ctx.beginPath();
    ctx.moveTo(last_mouse.x, last_mouse.y); //moveTo 從一個點移動到另一個點。
    ctx.lineTo(mouse.x, mouse.y); //lineTo方法畫直線; 方法接受終點的座標(x,y)作為引數
ctx.closePath(); ctx.stroke(); };

第一步是用 beginPath 建立一個路徑。在記憶體裡,路徑是以一組子路徑(直線,弧線等)的形式儲存的,它們共同構成一個圖形。每次呼叫 beginPath,子路徑組都會被重置,然後可以繪製新的圖形。

第二步就是實際繪製路徑的部分,moveTo和lineTo。

第三步是呼叫 closePath 方法,它會嘗試用直線連線當前端點與起始端點來關閉路徑,但如果圖形已經關閉或者只有一個點,它會什麼都不做。這一步不是必須的。

最後一步是呼叫 stroke

 或 fill 方法,這時,圖形才是實際的繪製到 canvas 上去。stroke 是繪製圖形的邊框,fill 會用填充出一個實心圖形。

基礎知識,請參考 https://developer.mozilla.org/zh-CN/docs/Canvas_tutorial/Drawing_shapes

以下是完整的程式碼。

 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 2 <html xmlns="http://www.w3.org/1999/xhtml">
 3 <head>
 4     <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
 5     <meta http-equiv="Content-Script-Type" content="text/javascript" />
 6     <meta http-equiv="Content-Style-Type" content="text/css" />
 7     <meta name="viewport" content=" initial-scale=0.80,user-scalable=no" />
 8     <link rel="stylesheet" href="http://code.jquery.com/ui/1.9.1/themes/base/jquery-ui.css" />   
 9     <script src="http://code.jquery.com/jquery-1.8.2.js"></script>
10     <script src="http://code.jquery.com/ui/1.9.1/jquery-ui.js"></script>       
11     <title>Pencil</title>
12 
13     <style type="text/css">
14         html, body {
15             width: 100%;
16             height: 100%;
17         }
18         #sketch {
19             border: 10px solid gray;
20             height: 100%;
21             position: relative;
22         }
23         #tmp_canvas {
24             position: absolute;
25             left: 0px; right: 0;
26             bottom: 0; top: 0;
27             cursor: crosshair;
28         }
29     </style>
30      
31     <script type="text/javascript">
32     $(document).ready(function(){
33         var canvas = document.querySelector('#paint');
34         var ctx = canvas.getContext('2d');
35         
36         var sketch = document.querySelector('#sketch');
37         var sketch_style = getComputedStyle(sketch);
38         canvas.width = parseInt(sketch_style.getPropertyValue('width'));
39         canvas.height = parseInt(sketch_style.getPropertyValue('height'));
40 
41         var mouse = {x: 0, y: 0};
42         var last_mouse = {x: 0, y: 0};
43         
44         /* Mouse Capturing Work */
45         canvas.addEventListener('mousemove', function(e) {
46             last_mouse.x = mouse.x;
47             last_mouse.y = mouse.y;
48             
49             mouse.x = e.pageX - this.offsetLeft;
50             mouse.y = e.pageY - this.offsetTop;
51         }, false);
52         
53         
54         /* Drawing on Paint App */
55         ctx.lineWidth = 5;
56         ctx.lineJoin = 'round';
57         ctx.lineCap = 'round';
58         ctx.strokeStyle = 'blue';
59         
60         canvas.addEventListener('mousedown', function(e) {
61             canvas.addEventListener('mousemove', onPaint, false);
62         }, false);
63         
64         canvas.addEventListener('mouseup', function() {
65             canvas.removeEventListener('mousemove', onPaint, false);
66         }, false);
67         
68         var onPaint = function() {
69             ctx.beginPath();
70             ctx.moveTo(last_mouse.x, last_mouse.y);
71             ctx.lineTo(mouse.x, mouse.y);
72             ctx.closePath();
73             ctx.stroke();
74         };
75     });
76     </script>
77 </head>
78         
79 
80 <body>
81     <div id="sketch">
82         <canvas id="paint"></canvas>
83     </div>
84 
85 </body>
86 </html>

 

這個程式碼很容易實現,但是它有個問題,就是畫線不流暢,出現折斷痕跡。

4.畫流暢的線

可以根據二次貝塞爾曲線,連線2點畫弧得到沒有摺痕的流暢曲線。

可以參照:

http://www.html5canvastutorials.com/labs/html5-canvas-modify-curves-with-anchor-points-using-kineticjs/

http://www.cartogrammar.com/blog/actionscript-curves-update/

 1 tmp_ctx.beginPath();
 2 tmp_ctx.moveTo(ppts[0].x, ppts[0].y); //使用 beginPath() 和 moveTo() 方法來定義開始點
 3 
 4 for (var i = 1; i < ppts.length - 2; i++) {
 5     var c = (ppts[i].x + ppts[i + 1].x) / 2;
 6     var d = (ppts[i].y + ppts[i + 1].y) / 2;
 7  
 8     tmp_ctx.quadraticCurveTo(ppts[i].x, ppts[i].y, c, d); //二次貝塞曲線函式   
 9 }
10  
11 // For the last 2 points
12 tmp_ctx.quadraticCurveTo(
13     ppts[i].x,
14     ppts[i].y,
15     ppts[i + 1].x,
16     ppts[i + 1].y
17 );
18 tmp_ctx.stroke(); //這時,圖形才是實際的繪製到 canvas 上去,stroke 是繪製圖形的邊框
quadraticCurveTo(cpx, cpy, x, y);方法通過使用表示二次貝塞爾曲線的指定控制點,向當前路徑新增一個點。第一個點是用於二次貝塞爾計算中的控制點,第二個點是曲線的結束點。曲線的開始點是當前路徑中最後一個點。

雖然這樣子可以畫出貝塞爾曲線,但是你可以發現,它還是有鋸齒邊緣的。但是在畫圖之前加一個clearRect()函式,則鋸齒消失!我不曉得這是為什麼。。。

 

5.畫沒有齒痕的流暢曲線 

tmp_ctx.beginPath();
tmp_ctx.moveTo(ppts[0].x, ppts[0].y); 

tmp_ctx.clearRect(0, 0, 2000, 2000);//它可以消除齒痕!

for (var i = 1; i < ppts.length - 2; i++) {
    var c = (ppts[i].x + ppts[i + 1].x) / 2;
    var d = (ppts[i].y + ppts[i + 1].y) / 2;
 
    tmp_ctx.quadraticCurveTo(ppts[i].x, ppts[i].y, c, d); 
}
 
// For the last 2 points
tmp_ctx.quadraticCurveTo(
    ppts[i].x,
    ppts[i].y,
    ppts[i + 1].x,
    ppts[i + 1].y
);
tmp_ctx.stroke();

6.接下來是橡皮的問題,雖然可以用相同背景色來代替,但是如果背景不單一,那就不管用了。所以,我們需要一個完完全全的橡皮,而不是相同背景,或者消除一塊區域的痕跡。

那麼,我們可以簡單的引用globalCompositeOperation()函式,這個函式是用來在畫布上組合顏色,我們可以利用這個原理,疊加(數學上的"或"原理)來製作橡皮。

同畫筆一樣,獲得滑鼠位置,疊加畫板上已有的顏色,則為取消。

globalCompositeOperation()函式,請參考 http://www.html5canvastutorials.com/advanced/html5-canvas-global-composite-operations-tutorial/

 畫筆畫圖時: context.globalCompositeOperation = 'source-over'; //新的顏色覆蓋之前的

 橡皮擦除時: context.globalCompositeOperation = 'destination-out'; //新的顏色與之前顏色,重疊的部分消失

 

7. 畫具有透明度的線

 http://css.dzone.com/articles/sketching-html5-canvas-and

<!-- 
Internet Explorer 9、Firefox、Opera、Chrome 以及 Safari 支援。
From: http://www.cnblogs.com/muzijia/admin/EditPosts.aspx?postid=2841967
 -->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
    <meta http-equiv="Content-Script-Type" content="text/javascript" />
    <meta http-equiv="Content-Style-Type" content="text/css" />

    <meta name="viewport" content=" initial-scale=0.80,user-scalable=no" />
    <link rel="stylesheet" href="http://code.jquery.com/ui/1.9.1/themes/base/jquery-ui.css" />   
    <script src="http://code.jquery.com/jquery-1.8.2.js"></script>
    <script src="http://code.jquery.com/ui/1.9.1/jquery-ui.js"></script>
    
    
    <title>Pencil</title>
    <style type="text/css">
        ul, li{
            clear:both;
            list-style:none;
            margin:0 auto;
            display: inline; 
        }
    
        html, body {
            width: 100%;
            height: 100%;
             
        }    
        
        #sketch {
            border: 10px solid gray;
            height: 100%;
            position: relative;
             
        }
        #tmp_canvas {
            position: absolute;
            left: 0px; right: 0;
            bottom: 0; top: 0;
            cursor: crosshair;
        }
    </style>
     
    <script type="text/javascript">


    $(document).ready(function(){
        var canvas = document.querySelector('#paint');
        var ctx = canvas.getContext('2d');
        
        var sketch = document.querySelector('#sketch');
        var sketch_style = getComputedStyle(sketch);
        canvas.width = parseInt(sketch_style.getPropertyValue('width'));
        canvas.height = parseInt(sketch_style.getPropertyValue('height'));
        
        
        // Creating a tmp canvas
        var tmp_canvas = document.createElement('canvas');
        var tmp_ctx = tmp_canvas.getContext('2d');
        tmp_canvas.id = 'tmp_canvas';
        tmp_canvas.width = canvas.width;
        tmp_canvas.height = canvas.height;
        
        sketch.appendChild(tmp_canvas);

        var mouse = {x: 0, y: 0};
        var last_mouse = {x: 0, y: 0};
        
        var paint = {
            init:function(canvasID_, canvas, context, brushImage){
                this.canvasID_ = canvasID_;
                this.canvasID = $("#"+canvasID_);
                this.canvas = canvas;
                this.context = context;
                    
                /** Drawing on Line Paint App */
                this.context.lineWidth = 5;
                this.context.lineJoin = 'round';
                this.context.lineCap = 'round';
                this.context.strokeStyle = 'red';
                this.context.fillStyle = 'red';        

                this.color=["#000000","#9E9E9E","#FFFFFF","#8B5742","#FF0000","#FFC125","#00688B","#CDB38B","#CD8C95"];                    
                                
                this.lock = false;    
                this.line = false;                
                
                ppts = [];        // Pencil Points                     
                
                this.brush = brushImage;
                this.context.globalAlpha = 1;
                
                /** mouse event */                
                if (this.touchSupported) {
                    this.mouseDownEvent = "touchstart";
                    this.mouseMoveEvent = "touchmove";
                    this.mouseUpEvent = "touchend";
                }
                else {
                    this.mouseDownEvent = "mousedown";
                    this.mouseMoveEvent = "mousemove";
                    this.mouseUpEvent = "mouseup";
                }
                
                this.bind();
            },    
            bind:function(){
                var t = this; //paint Instance     
                this.canvasID.live({        
                    mousedown: function(e){        
                    
                        mouse.x = typeof e.offsetX !== 'undefined' ? e.offsetX : e.layerX;
                        mouse.y = typeof e.offsetY !== 'undefined' ? e.offsetY : e.layerY;
                        
                        ppts.push({x: mouse.x , y: mouse.y}); 
                        
                        t.lock=true;
                    },
                    mousemove: function(e){                    
                        if(t.lock){                    
                            mouse.x = typeof e.offsetX !== 'undefined' ? e.offsetX : e.layerX;
                            mouse.y = typeof e.offsetY !== 'undefined' ? e.offsetY : e.layerY;                            
                            
                            ppts.push({x: mouse.x , y: mouse.y });
                            
                            //tmp_ctx.clearRect(0, 0, 2000, 2000);
                            
                            //if(t.line == true){ //draw line
                                t.onPaint();                                                                
                            //}    
                            //if(t.line ==false){
                                //t.drawPoint();    //draw image                                
                            //}
                            
                            
                        }            
                    },
                    mouseleave:function(e){
                        t.lock = false;    
                        //ctx.drawImage(tmp_canvas, 0, 0);
                        //tmp_ctx.clearRect(0, 0, 2000, 2000);        
                        ppts = [];
                    },
                    mouseup: function(e){            
                        t.lock = false;    
                        //ctx.drawImage(tmp_canvas, 0, 0);
                        //tmp_ctx.clearRect(0, 0, 2000, 2000);    
                        //tmp_ctx.drawImage(canvas, 0, 0);
                        ppts = [];                
                    }
                });    

            },
            onPaint:function() 
            {
                var tmp_ctx = this.context;                    
                // Tmp canvas is always cleared up before drawing.
                tmp_ctx.clearRect(0, 0, 2000, 2000);      //取消齒痕; 如果想用橡皮,則註釋這句,因為繪圖痕跡繪在兩個圖層中。          
                
                if (ppts.length < 3) {
                    var b = ppts[0];
                    tmp_ctx.beginPath();

                    tmp_ctx.arc(b.x, b.y, tmp_ctx.lineWidth / 2, 0, Math.PI * 2, !0);
                    tmp_ctx.fill();
                    tmp_ctx.closePath();
                    
                    return;
                }                
                                
                tmp_ctx.beginPath();
                tmp_ctx.moveTo(ppts[0].x, ppts[0].y);
                
                for (var i = 1; i < ppts.length - 2; i++) {
                    var c = (ppts[i].x + ppts[i + 1].x) / 2;
                    var d = (ppts[i].y + ppts[i + 1].y) / 2;                        
                    tmp_ctx.quadraticCurveTo(ppts[i].x, ppts[i].y, c, d);    
                }
                            
                // For the last 2 points
                tmp_ctx.quadraticCurveTo(
                    ppts[i].x,
                    ppts[i].y,
                    ppts[i + 1].x,
                    ppts[i + 1].y
                );                
                tmp_ctx.stroke();                
            },
            changeColor:function(style,color)
            {          
                var t=this;
                var styleNum = style;
                var colorNum = color;                

                console.debug("pen ="+style+", color="+color);
                
                t.context.strokeStyle = t.color[colorNum];
                t.context.fillStyle = t.color[colorNum];    
                
                if(styleNum == 0){  //mark pen    
                    t.line = true;    
                    t.context.lineWidth = 30;
                    t.context.globalAlpha = 0.5;
                    t.context.globalCompositeOperation = 'source-over';         
                    
                    console.debug("mark");    
                    
                }
                if(styleNum == 1){  //peicnl   
                    t.line = true;    
                    t.context.lineWidth = 5;
                    t.context.globalAlpha = 1;    
                    t.context.globalCompositeOperation = 'source-over';    
                    
                    console.debug("pencil");    
                    
                }
                if(styleNum == 2){  //resetEraser 
                    t.line = true;    
                    //t.context = ctx;
                    t.resetEraser();
                }             
                    
                
            },
            resetEraser:function()
            {         
                var t=this; 
                //t.context = ctx;
                t.context.lineWidth = 30;
                t.context.globalAlpha = 1;
                t.context.globalCompositeOperation = 'destination-out';
                console.debug("resetEraser");
            },
            clear:function()
            {
                ppts = [];
                this.context.globalAlpha = 0;
                this.context.clearRect(0, 0, this.w, this.h);
            }
        };
            
        <!-- drawing -->
        var brush = new Image();    
        brush.src = "images/color_01.png";        //defalut red
        brush.onload = function(){     
            paint.init('tmp_canvas',tmp_canvas,tmp_ctx, brush); 
        };
        
    
        var style = 1;
        var color = 1;     
        $('.tool> li > a').click(function() {            
            var idx = $('.tool> li > a').index(this);            
            style = idx;
            paint.changeColor(style, color);    
            
            if(idx == 2){
                //paint.init('paint',paint,ctx, brush);
            }
        });

        $('.brush > li > a').click(function() {            
            var idx = $('.brush > li > a').index(this);                
            var i = idx + 1;
            
            //brush.src = "images/color_0"+i+".png";                
            color = idx;
            paint.changeColor(style, color);    
        });
    
    });
    </script>
</head>
        

<body>
    
    <div class="pencil">    
        <ul class="tool">                                
            <li><a class="btn_pen" href="javascript:void(0)"><img src="images/crayon-outline.png" alt=""></a></li>            
            <li><a class="btn_mark" href="javascript:void(0)"><img src="images/marker-outline.png" alt=""></a></li>            
            <li><a class="btn_eraser" href="javascript:void(0)"><img src="images/eraser-outline.png" alt=""></a></li>    
            <li><a class="btn_mark" href="javascript:void(0)"><img src="images/image-outline.png" alt=""></a></li>            
        </ul>
        <ul class="brush">
            <li><a href="javascript: void(0);"><img src="images/color_01.png" /></a></li>
            <li><a href="javascript: void(0);"><img src="images/color_02.png" /></a></li>
            <li><a href="javascript: void(0);"><img src="images/color_03.png" /></a></li>
            <li><a href="javascript: void(0);"><img src="images/color_04.png" /></a></li>
            <li><a href="javascript: void(0);"><img src="images/color_05.png" /></a></li>
            <li><a href="javascript: void(0);"><img src="images/color_06.png" /></a></li>
            <li><a href="javascript: void(0);"><img src="images/color_07.png" /></a></li>
            <li><a href="javascript: void(0);"><img src="images/color_08.png" /></a></li>
            <li><a href="javascript: void(0);"><img src="images/color_09.png" /></a></li>
        </ul>            
    </div>
    <div id="sketch">
        <canvas id="paint"></canvas>
    </div>

</body>
</html>