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.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>