實現思路:監聽鼠標按下、移動、松開事件,將鼠標按下的值賦值給moveTo的x和y值,作為起始位置。在移動事件中,將鼠標距離可視區x和y值賦給lineTo,再將路徑閉合。以下是具體的代碼
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1,user-scalable=no">
<style>
#canvas{
border:1px solid #000;
}
</style>
</head>
<body>
<canvas id="canvas" width="300" height="300"></canvas>
</body>
</html>
<script>
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
canvas.onmousedown = function(ev){
var x = ev.clientX - this.offsetLeft;
var y = ev.clientY - this.offsetTop;
ctx.beginPath();
ctx.moveTo(x,y);
canvas.onmousemove = function(ev){
var targetX = ev.clientX - this.offsetLeft;
var targetY = ev.clientY - this.offsetTop;
ctx.lineWidth = 1;
ctx.lineTo(targetX,targetY);
ctx.stroke();
};
window.onmouseup = function(ev){
canvas.onmousemove = null;
canvas.onmouseup = null;
};
};
</script>
以上的原理就是每次按下鼠標都重新開始一條路徑,並將畫筆移動到鼠標按下的那個位置,當鼠標移動的時候將線畫到鼠標所在的位置,然后閉合,一直重復這一過程,最終就會變成我們所要畫的形狀,當鼠標松開,清空移動事件和松開事件,直到鼠標再次按下開始新的一輪畫圖。
注意鼠標松開事件要給window或者document加,不然就會出現下面這個情況
給canvas加鼠標松開事件之所以會出現這種情況,是因為我並不是在canvas中松開鼠標的,因此自然就不會執行canvas中的onmouseup事件了,因而當我們再回到canvas中時它依然還會執行onmousemove事件,而如果給window加,就不會有這種問題,因為整個窗口都屬於window范圍的,只要你在窗口中松開鼠標就會響應onmouseup事件。
2017.02.24更新
完善canvas功能,新增后退,前進,清除功能
清除功能通過canvas上下文對象的clearRect方法實現。
后退和前進功能,我的想法是每畫一次就將整個畫布的數據push到一個數組中,按前進和后退時再將對應的數據取出來,這個可以通過getImageData和putImageData方法實現,這兩個方法的使用可以到http://www.w3school.com.cn/tags/html_ref_canvas.asp中查看,以下是功能的全部代碼
function Graffiti(dom,context){
this.canvas = document.querySelector(dom);
this.context = this.canvas.getContext("2d");
this.imgList = [];
this.prevIndex = 0;
this.nextIndex = 0;
this.currentIndex = 0;
this.init();
}
Graffiti.prototype = {
constructor:this,
init:function(){
var _this = this;
this.move(function(){
_this.pushImg();
_this.upIndex(_this.imgList.length - 1);
});
},
move:function(endCallback){
var _this = this;
var canvas = this.canvas;
var context = this.context;
canvas.onmousedown = function(ev){
var x = ev.clientX - this.offsetLeft;
var y = ev.clientY - this.offsetTop;
context.beginPath();
context.moveTo(x,y);
canvas.onmousemove = function(ev){
var targetX = ev.clientX - this.offsetLeft;
var targetY = ev.clientY - this.offsetTop;
context.lineWidth = 1;
context.lineTo(targetX,targetY);
context.stroke();
};
window.onmouseup = function(ev){
canvas.onmousemove = null;
canvas.onmouseup = null;
endCallback&&ev.target.matches("#canvas")&&endCallback();
};
};
},
back:function(){
this.clear();
this.context.putImageData(this.imgList[this.prevIndex],0,0);
this.upIndex(this.prevIndex);
},
go:function(){
this.clear();
this.context.putImageData(this.imgList[this.nextIndex],0,0);
this.upIndex(this.nextIndex);
},
pushImg:function(){
this.imgList.push(this.context.getImageData(0,0,this.canvas.width,this.canvas.height));
},
clear:function(){
this.context.clearRect(0,0,this.canvas.width,this.canvas.height);
},
upIndex:function(index){
if(index===0){
this.prevIndex = 0;
}else{
this.prevIndex = index - 1;
}
if(index===this.imgList.length-1){
this.nextIndex = index;
}else{
this.nextIndex = index + 1;
}
this.currentIndex = index;
}
};
var can = new Graffiti("#canvas");
var back = document.getElementById("back");
var go = document.getElementById("go");
var clear = document.getElementById("clear");
back.onclick = can.back.bind(can);
go.onclick = can.go.bind(can);
clear.onclick = can.clear.bind(can);
注意在window.onmouseup的時候有個坑,因為我是在這里面執行儲存信息的,因此會出現一個問題,就算我不是在canvas上提起也會相應這個事件,所以在里面我加了一個判斷,如果不是canvas就不儲存
ev.target.matches("#canvas")
總的來說,這幾個功能的難點就在於,你要知道有哪些API,以及如何取儲存數據和取數據。