1. 程式人生 > >JS底層實現canvas線上畫板

JS底層實現canvas線上畫板

  本篇文章作為初學者角度編寫  

連線:碼雲開源地址:http://exeup.gitee.io/watercolor-online/

相信畫板這類應用大家都不陌生,

感覺接觸計算機不多久就接觸到了畫板,帶給我們很多方便,也用於在教學,辦公中提升效率.

前段時間自己做了這樣的一個畫板,主體部分由兩層絕對定位100%寬高的canvas組成;然後由一個絕對定位z軸更加靠上的DIV作為工具欄;

首先介紹一下兩層canvas,之所以使用兩層,就是因為有一層是在滑鼠移動但並不是拖動的時候,將落筆點呈現在使用者面前,這一層是在不斷的擦除的;而底層則是在出現繪畫動作的時候,將 繪畫內容記錄在底層畫板上的.

首先,canvas本身就是一個畫板,所以這些實現起來難點只在於拖動;(於初學者而言);

瀏覽器準備了一個拖動事件(ondragover)但並不好用(我是這麼覺得);那麼我們嘗試將多個事件組合在一起,拼湊成拖動事件;

(初學者容易產生困惑的地方)一張圖片在瀏覽器中被拖動應該是會出現拖拽複製的情況,(大多數瀏覽器確實如此),導致這種情況出現的原因是IMG物件預設可以觸發的拖動事件;所以只需要重寫這些事件就可以,並且拖動事件的觸發是按下滑鼠左鍵,那麼我們重寫滑鼠左鍵onclick事件就會導致這張圖片不會被拖動.

那麼我們需要一個拖動事件,事實上我們處理的是左鍵按下,滑鼠滑動,左鍵擡起,三個事件,說起來也簡單,原本在較上層canvas中就有一個滑鼠拖動時擦除並繪製一個筆觸的事件,只需要在按下左鍵時繫結新的事件即可,

同樣的我們需要調色盤可以被拖動以及調色盤上的調色條也是可以使用同樣的道理實現的;此處以拖動調色盤的原始碼作為示例(原因是短小精悍通俗易懂)

function pango(e){//移動調色盤
	var x = e.clientX;
	var y = e.clientY;
	panel.style.top = (y-5)+"px";
	panel.style.left = (x-50)+"px";
}
function panto(){//按下滑鼠左鍵時,觸發這個函式為事件繫結一個方法
	window.onmousemove=function(){
		pango(event);
	}
}
function panup(){
//擡起滑鼠解綁
	window.onmousemove=function(){
	}
}
在這裡繫結事件的時候並不是繫結到調色盤這個DIV物件上的,特別是調色盤內的調色滑動條,更是不要繫結在自己身上,因為滑動條過小,很容易就出了範圍了,而window這個物件在這裡就扮演了一個很重要的可以全屏捕獲滑鼠位置的好方法,(注:在canvas上時應繫結在上層畫板上,儘管繪製的是底層畫板,)

再新增一段關於滑動條的程式碼,也就是如何處理在一個可變的DIV中對一個子元素應該處於那個位置的定位;(我的語文好差)

function blueto(){
	window.onmousemove=function(){
		bluego();
	}
}
function bluego(){//藍色塊位移
	var x = event.clientX;
	var ox = x-sa.offsetLeft-panel.offsetLeft;
	if(ox>=0&&ox<=127.5){
		bluebut.style.marginLeft = ox+"px";
		intblue = ox*2;
		ToUpdate();
	}
}
同樣的,這裡的事件依然會被繫結到window上 ,這裡的難點在於ox  即滑塊左邊大小的控制,這裡使用的(sa)物件是滑塊的背景(即那個灰色的滑槽),使用這個物件的offsetLeft值減去面板的offsetLeft之後在被當前滑鼠位置減去就是滑塊本身對應於滑槽的座標了.
在此時改變定義於全域性引數的藍色值(我使用的是RGBA演算法所以滑槽是127.5PX,*2以後直接作為藍色值使用,其他顏色值/size/透明度等原理都相同);
然後展現一下核心程式碼(哈哈 其實真正繪製的只有幾句話而已)
function myonclick(){//這個是滑鼠按下時觸發的函式
ifclick = true ;
canvalu.strokeStyle = "rgba("+intred+","+intgreen+","+intblue+","+transparent+")";//設定線的顏色
canvalu.lineWidth=osize;//設定線的粗細
var x = event.clientX/$(window).width()*cantotest.width;
var y = event.clientY/$(window).height()*cantotest.height;
canvalu.moveTo(x,y);//這句話是滑鼠按下時的點作為起點.
cantotest.onmousemove=function(){//為滑鼠在上層canvas上拖動建立的繫結事件
otesto(event);//這個當然就是繪製了,這個函式將在後面列出,功能是計算x,y的值並傳給canvasto
otest(event);//這個函式是在上層canvas中預覽操作;
}
}
unction canvasto(x,y){//繪製
	if(isover==false){//滑鼠移動的時候觸發的事件算出滑鼠的位置x,y,
		canvalu.lineTo(x,y);//其實在移動的時候  只需要建立線段的點就可以了
	}else{//這一句是我的橡皮擦.
		canvalu.clearRect((x-(oversize/2)),(y-(oversize/2)),oversize,oversize);
	}
}
function up(){//這個自然就是繪製過程結束/擡筆的動作了;
ifclick = false;
if(ifGrap){
canvalu.closePath();
canvalu.fill();
}
canvalu.stroke();
canvalu.beginPath();//至此就把這條線畫出來了;
canvalu.lineWidth=0;
cantotest.onmousemove=function(){//繫結事件更新/此時就不再繪製了而是隻觸發預覽的方法
otest(event);
}
}
function otesto(e){//這裡是繪製事直接呼叫的方法,用來計算x.y
var x = e.clientX/$(window).width()*cantotest.width;//由於canvas的畫素值改變時內容會被清空;因此為了保持繪畫過程足夠流暢,採用這種演算法計算出落筆點;
var y = e.clientY/$(window).height()*cantotest.height;
canvasto(x,y);
}
這樣畫的線條不是很規整,咱們讓canvas使用圓滑處理:canvalu.lineCap="round"當然也可以使用方形,這隻需要將值設定為"square"就可以了;這樣大體上一個canvas畫板就出來了;忍不住要畫兩筆了 哈哈,儘管手殘,還是將"大作"展示給大家!

哈哈 忍不住畫兩筆!