html5 的 canvas 想寫個小專案 畫板
阿新 • • 發佈:2019-02-17
最近在研究下 html5 的 canvas 想寫個小專案,練練手,結果寫了一個畫圖板,功能點有 繪製、直線、圓、方形、塗鴉、線條粗細、顏色切換、撤銷、回退、儲存、下載、外部圖片拖入等 , 用的技術是包含 html5 中的本地儲存、下載、canvas 等技術,上圖。
演示地址:http://chengxinwei.github.io/html5/2013/06/20/HTML5_CANVAS_%E7%94%BB%E5%9B%BE%E6%9D%BF/
這個專案中用到了 canvas 的很多基礎功能 。在這裡解釋一下核心程式碼的思路。
在這個專案中 我用到了 2 層 canvas , 原因是當用戶在畫部分圖形的時候希望看到繪畫的過程,比如在畫圓的時候,而canvas 目前支援的就是清空 和 繪製操作, 所以在這裡我用了 bak 層做了一個 假象。使用者一開始的所有繪製都是在 bak 層做的繪製 , 之後當滑鼠鬆開的時候 才會到 真正的 canvas 層儲存。這個是核心思路。
接下來我們來看一下程式碼的構造。
1.首先第一部。是做物件初始化, 包括有 初始化canvas , context , height, width 這個很簡單就不做多的解釋了 程式碼如下。
Javascript程式碼
2. 第二部 就是繪製了 , 思路是當滑鼠點下得時候 確定一個初始點, 當滑鼠移動的時候開始繪製。之前說過 滑鼠移動只是在bak 層繪製 , 當鬆開滑鼠時把bak層的 新增到 canvas 層 。 那麼這裡就包含三個事件 。 點選,移動,鬆開 , 對應了三個不同的方法 。程式碼如下:
Javascript程式碼
順便提一下撤銷和回退的做法。之前有提過在滑鼠鬆開的時候,我們會把 bak 層的內容繪製到 canvas 層中, 那麼在這個時候,同步的會把一份 圖片資訊 存到一個 陣列中去,用於回滾 , 當點選撤銷的時候 只需要把上一個的 圖片資訊取出來,在繪製一遍canvas即可。撤銷回退同理
4.接下來講一下儲存功能實現。儲存圖片使用得 是html5 的 storage 的功能實現的。storage 是瀏覽器開闢了一個5M 的控制元件提供方開發者使用 存放key value 的鍵值對, 有點類似於 cookie ,那麼women儲存的實現就很簡單了,當點選儲存按鈕的時候 , 獲取圖片的 dataUrl 儲存與 storage 中即可,下次開啟瀏覽器 獲取再放入canvas中就可以了。程式碼如下:
Javascript程式碼
5.最後說一下 下載,可能很多人因為這個頭疼,因為沒有後臺的處理,怎麼能做到下載圖片呢。其實在html5中 對於 a 標籤提供了一個新的屬性 【download】 如:
Java程式碼
瀏覽器預設會把他當做一個下載連結去處理,下載的檔名就是 download 中的 picture.png 下載的內容對應的是src 中的值。所以我們只需要把 圖片的dataUrl 動態賦值上去 即可。
今天就先講到這裡哈,有問題可以給我留言。
--------------//2013-06-258 ---------------
昨天新加了 拖拽圖片的功能, 從資料夾中 拖到畫圖板裡面可以直接覆蓋。
內碼表很簡單 如下:
Javascript程式碼
簡單解釋一下 , 在html5支援的瀏覽器中, 有drop的回撥函式 , 在其中獲得event之後 裡面有一個物件 dataTransfer.files , 獲取的是 file 檔案資訊 , 最後通過 FileReader.readAsDataURL 的函式讀入,可以獲取到 html5 支援的圖片資訊 , 最後通過建立 image 物件,把圖片繪製進去就可以了。
演示地址:http://chengxinwei.github.io/html5/2013/06/20/HTML5_CANVAS_%E7%94%BB%E5%9B%BE%E6%9D%BF/
這個專案中用到了 canvas 的很多基礎功能 。在這裡解釋一下核心程式碼的思路。
在這個專案中 我用到了 2 層 canvas , 原因是當用戶在畫部分圖形的時候希望看到繪畫的過程,比如在畫圓的時候,而canvas 目前支援的就是清空 和 繪製操作, 所以在這裡我用了 bak 層做了一個 假象。使用者一開始的所有繪製都是在 bak 層做的繪製 , 之後當滑鼠鬆開的時候 才會到 真正的 canvas 層儲存。這個是核心思路。
接下來我們來看一下程式碼的構造。
1.首先第一部。是做物件初始化, 包括有 初始化canvas , context , height, width 這個很簡單就不做多的解釋了 程式碼如下。
Javascript程式碼
- //初始化
- var initCanvas = function(){
- canvas = document.getElementById("canvas");
- canvas.width = canvasWidth;
- canvas.height = canvasHeight;
- context = canvas.getContext('2d');
- canvasTop = $(canvas).offset().top
- canvasLeft = $(canvas).offset().left;
- canvas_bak = document.getElementById("canvas_bak");
- canvas_bak.width = canvasWidth;
- canvas_bak.height = canvasHeight;
- context_bak = canvas_bak.getContext('2d');
- }
//初始化 var initCanvas = function(){ canvas = document.getElementById("canvas"); canvas.width = canvasWidth; canvas.height = canvasHeight; context = canvas.getContext('2d'); canvasTop = $(canvas).offset().top canvasLeft = $(canvas).offset().left; canvas_bak = document.getElementById("canvas_bak"); canvas_bak.width = canvasWidth; canvas_bak.height = canvasHeight; context_bak = canvas_bak.getContext('2d'); }
2. 第二部 就是繪製了 , 思路是當滑鼠點下得時候 確定一個初始點, 當滑鼠移動的時候開始繪製。之前說過 滑鼠移動只是在bak 層繪製 , 當鬆開滑鼠時把bak層的 新增到 canvas 層 。 那麼這裡就包含三個事件 。 點選,移動,鬆開 , 對應了三個不同的方法 。程式碼如下:
Javascript程式碼
- //滑鼠按下獲取 開始xy開始畫圖
- var mousedown = function(e){
- context.strokeStyle= color;
- context_bak.strokeStyle= color;
- context_bak.lineWidth = size;
- e=e||window.event;
- startX = e.clientX - canvasLeft;
- startY = e.clientY - canvasTop;
- context_bak.moveTo(startX ,startY );
- canDraw = true;
- if(graphType == 'pencil'){
- context_bak.beginPath();
- }else if(graphType == 'circle'){
- context.beginPath();
- context.moveTo(startX ,startY );
- context.lineTo(startX +2 ,startY+2);
- context.stroke();
- }else if(graphType == 'rubber'){
- context.clearRect(startX - size * 10 , startY - size * 10 , size * 20 , size * 20);
- }
- };
- //滑鼠離開 把蒙版canvas的圖片生成到canvas中
- var mouseup = function(e){
- e=e||window.event;
- canDraw = false;
- var image = new Image();
- if(graphType!='rubber'){
- image.src = canvas_bak.toDataURL();
- image.onload = function(){
- context.drawImage(image , 0 ,0 , image.width , image.height , 0 ,0 , canvasWidth , canvasHeight);
- clearContext();
- saveImageToAry();
- }
- var x = e.clientX - canvasLeft;
- var y = e.clientY - canvasTop;
- context.beginPath();
- context.moveTo(x ,y );
- context.lineTo(x +2 ,y+2);
- context.stroke();
- }
- };
- // 滑鼠移動
- var mousemove = function(e){
- e=e||window.event;
- var x = e.clientX - canvasLeft;
- var y = e.clientY - canvasTop;
- //方塊 4條直線搞定
- if(graphType == 'square'){
- if(canDraw){
- context_bak.beginPath();
- clearContext();
- context_bak.moveTo(startX , startY);
- context_bak.lineTo(x ,startY );
- context_bak.lineTo(x ,y );
- context_bak.lineTo(startX ,y );
- context_bak.lineTo(startX ,startY );
- context_bak.stroke();
- }
- //直線
- }else if(graphType =='line'){
- if(canDraw){
- context_bak.beginPath();
- clearContext();
- context_bak.moveTo(startX , startY);
- context_bak.lineTo(x ,y );
- context_bak.stroke();
- }
- //畫筆
- }else if(graphType == 'pencil'){
- if(canDraw){
- context_bak.lineTo(e.clientX - canvasLeft ,e.clientY - canvasTop);
- context_bak.stroke();
- }
- //圓 未畫得時候 出現一個小圓
- }else if(graphType == 'circle'){
- clearContext();
- if(canDraw){
- context_bak.beginPath();
- var radii = Math.sqrt((startX - x) * (startX - x) + (startY - y) * (startY - y));
- context_bak.arc(startX,startY,radii,0,Math.PI * 2,false);
- context_bak.stroke();
- }else{
- context_bak.beginPath();
- context_bak.arc(x,y,20,0,Math.PI * 2,false);
- context_bak.stroke();
- }
- //塗鴉 未畫得時候 出現一個小圓
- }else if(graphType == 'handwriting'){
- if(canDraw){
- context_bak.beginPath();
- context_bak.strokeStyle = color;
- context_bak.fillStyle = color;
- context_bak.arc(x,y,size*10,0,Math.PI * 2,false);
- context_bak.fill();
- context_bak.stroke();
- context_bak.restore();
- }else{
- clearContext();
- context_bak.beginPath();
- context_bak.fillStyle = color;
- context_bak.arc(x,y,size*10,0,Math.PI * 2,false);
- context_bak.fill();
- context_bak.stroke();
- }
- //橡皮擦 不管有沒有在畫都出現小方塊 按下滑鼠 開始清空區域
- }else if(graphType == 'rubber'){
- context_bak.lineWidth = 1;
- clearContext();
- context_bak.beginPath();
- context_bak.strokeStyle = '#000000';
- context_bak.moveTo(x - size * 10 , y - size * 10 );
- context_bak.lineTo(x + size * 10 , y - size * 10 );
- context_bak.lineTo(x + size * 10 , y + size * 10 );
- context_bak.lineTo(x - size * 10 , y + size * 10 );
- context_bak.lineTo(x - size * 10 , y - size * 10 );
- context_bak.stroke();
- if(canDraw){
- context.clearRect(x - size * 10 , y - size * 10 , size * 20 , size * 20);
- }
- }
- };
//滑鼠按下獲取 開始xy開始畫圖
var mousedown = function(e){
context.strokeStyle= color;
context_bak.strokeStyle= color;
context_bak.lineWidth = size;
e=e||window.event;
startX = e.clientX - canvasLeft;
startY = e.clientY - canvasTop;
context_bak.moveTo(startX ,startY );
canDraw = true;
if(graphType == 'pencil'){
context_bak.beginPath();
}else if(graphType == 'circle'){
context.beginPath();
context.moveTo(startX ,startY );
context.lineTo(startX +2 ,startY+2);
context.stroke();
}else if(graphType == 'rubber'){
context.clearRect(startX - size * 10 , startY - size * 10 , size * 20 , size * 20);
}
};
//滑鼠離開 把蒙版canvas的圖片生成到canvas中
var mouseup = function(e){
e=e||window.event;
canDraw = false;
var image = new Image();
if(graphType!='rubber'){
image.src = canvas_bak.toDataURL();
image.onload = function(){
context.drawImage(image , 0 ,0 , image.width , image.height , 0 ,0 , canvasWidth , canvasHeight);
clearContext();
saveImageToAry();
}
var x = e.clientX - canvasLeft;
var y = e.clientY - canvasTop;
context.beginPath();
context.moveTo(x ,y );
context.lineTo(x +2 ,y+2);
context.stroke();
}
};
// 滑鼠移動
var mousemove = function(e){
e=e||window.event;
var x = e.clientX - canvasLeft;
var y = e.clientY - canvasTop;
//方塊 4條直線搞定
if(graphType == 'square'){
if(canDraw){
context_bak.beginPath();
clearContext();
context_bak.moveTo(startX , startY);
context_bak.lineTo(x ,startY );
context_bak.lineTo(x ,y );
context_bak.lineTo(startX ,y );
context_bak.lineTo(startX ,startY );
context_bak.stroke();
}
//直線
}else if(graphType =='line'){
if(canDraw){
context_bak.beginPath();
clearContext();
context_bak.moveTo(startX , startY);
context_bak.lineTo(x ,y );
context_bak.stroke();
}
//畫筆
}else if(graphType == 'pencil'){
if(canDraw){
context_bak.lineTo(e.clientX - canvasLeft ,e.clientY - canvasTop);
context_bak.stroke();
}
//圓 未畫得時候 出現一個小圓
}else if(graphType == 'circle'){
clearContext();
if(canDraw){
context_bak.beginPath();
var radii = Math.sqrt((startX - x) * (startX - x) + (startY - y) * (startY - y));
context_bak.arc(startX,startY,radii,0,Math.PI * 2,false);
context_bak.stroke();
}else{
context_bak.beginPath();
context_bak.arc(x,y,20,0,Math.PI * 2,false);
context_bak.stroke();
}
//塗鴉 未畫得時候 出現一個小圓
}else if(graphType == 'handwriting'){
if(canDraw){
context_bak.beginPath();
context_bak.strokeStyle = color;
context_bak.fillStyle = color;
context_bak.arc(x,y,size*10,0,Math.PI * 2,false);
context_bak.fill();
context_bak.stroke();
context_bak.restore();
}else{
clearContext();
context_bak.beginPath();
context_bak.fillStyle = color;
context_bak.arc(x,y,size*10,0,Math.PI * 2,false);
context_bak.fill();
context_bak.stroke();
}
//橡皮擦 不管有沒有在畫都出現小方塊 按下滑鼠 開始清空區域
}else if(graphType == 'rubber'){
context_bak.lineWidth = 1;
clearContext();
context_bak.beginPath();
context_bak.strokeStyle = '#000000';
context_bak.moveTo(x - size * 10 , y - size * 10 );
context_bak.lineTo(x + size * 10 , y - size * 10 );
context_bak.lineTo(x + size * 10 , y + size * 10 );
context_bak.lineTo(x - size * 10 , y + size * 10 );
context_bak.lineTo(x - size * 10 , y - size * 10 );
context_bak.stroke();
if(canDraw){
context.clearRect(x - size * 10 , y - size * 10 , size * 20 , size * 20);
}
}
};
順便提一下撤銷和回退的做法。之前有提過在滑鼠鬆開的時候,我們會把 bak 層的內容繪製到 canvas 層中, 那麼在這個時候,同步的會把一份 圖片資訊 存到一個 陣列中去,用於回滾 , 當點選撤銷的時候 只需要把上一個的 圖片資訊取出來,在繪製一遍canvas即可。撤銷回退同理
4.接下來講一下儲存功能實現。儲存圖片使用得 是html5 的 storage 的功能實現的。storage 是瀏覽器開闢了一個5M 的控制元件提供方開發者使用 存放key value 的鍵值對, 有點類似於 cookie ,那麼women儲存的實現就很簡單了,當點選儲存按鈕的時候 , 獲取圖片的 dataUrl 儲存與 storage 中即可,下次開啟瀏覽器 獲取再放入canvas中就可以了。程式碼如下:
Javascript程式碼
- //儲存
- var save = function(){
- for(var i = 1;i<=8;i++){
- var dataUrl = getStorage(i);
- if(dataUrl == null || dataUrl == ''){
- putStorage(i,canvas.toDataURL());
- $("#history_"+i).attr("src",canvas.toDataURL());
- initHistorty();
- return ;
- }
- }
- }
//儲存
var save = function(){
for(var i = 1;i<=8;i++){
var dataUrl = getStorage(i);
if(dataUrl == null || dataUrl == ''){
putStorage(i,canvas.toDataURL());
$("#history_"+i).attr("src",canvas.toDataURL());
initHistorty();
return ;
}
}
}
5.最後說一下 下載,可能很多人因為這個頭疼,因為沒有後臺的處理,怎麼能做到下載圖片呢。其實在html5中 對於 a 標籤提供了一個新的屬性 【download】 如:
Java程式碼
- <a href="javascript:void(0);" id="history_download_1" download="picture.png">下載</a></td>
<a href="javascript:void(0);" id="history_download_1" download="picture.png">下載</a></td>
瀏覽器預設會把他當做一個下載連結去處理,下載的檔名就是 download 中的 picture.png 下載的內容對應的是src 中的值。所以我們只需要把 圖片的dataUrl 動態賦值上去 即可。
今天就先講到這裡哈,有問題可以給我留言。
--------------//2013-06-258 ---------------
昨天新加了 拖拽圖片的功能, 從資料夾中 拖到畫圖板裡面可以直接覆蓋。
內碼表很簡單 如下:
Javascript程式碼
- // 處理檔案拖入事件,防止瀏覽器預設事件帶來的重定向
- function handleDragOver(evt) {
- evt.stopPropagation();
- evt.preventDefault();
- }
- // 判斷是否圖片
- function isImage(type) {
- switch (type) {
- case 'image/jpeg':
- case 'image/png':
- case 'image/gif':
- case 'image/bmp':
- case 'image/jpg':
- return true;
- default:
- return false;
- }
- }
- // 處理拖放檔案列表
- function handleFileSelect(evt) {
- evt.stopPropagation();
- evt.preventDefault();
- var files = evt.dataTransfer.files;
- for (var i = 0, f; f = files[i]; i++) {
- var t = f.type ? f.type : 'n/a';
- reader = new FileReader();
- isImg = isImage(t);
- // 處理得到的圖片
- if (isImg) {
- reader.onload = (function (theFile) {
- return function (e) {
- var image = new Image();
- image.src = e.target.result ;
- image.onload = function(){
- context.drawImage(image , 0 ,0 , image.width , image.height , 0 ,0 , canvasWidth , canvasHeight);
- }
- };
- })(f)
- reader.readAsDataURL(f);
- }
- }
- }
- //初始化拖入效果
- var initDrag= function(){
- var dragDiv = document.getElementById("canvas_bak");
- dragDiv.addEventListener('dragover', handleDragOver, false);
- dragDiv.addEventListener('drop', handleFileSelect, false);
- }
// 處理檔案拖入事件,防止瀏覽器預設事件帶來的重定向
function handleDragOver(evt) {
evt.stopPropagation();
evt.preventDefault();
}
// 判斷是否圖片
function isImage(type) {
switch (type) {
case 'image/jpeg':
case 'image/png':
case 'image/gif':
case 'image/bmp':
case 'image/jpg':
return true;
default:
return false;
}
}
// 處理拖放檔案列表
function handleFileSelect(evt) {
evt.stopPropagation();
evt.preventDefault();
var files = evt.dataTransfer.files;
for (var i = 0, f; f = files[i]; i++) {
var t = f.type ? f.type : 'n/a';
reader = new FileReader();
isImg = isImage(t);
// 處理得到的圖片
if (isImg) {
reader.onload = (function (theFile) {
return function (e) {
var image = new Image();
image.src = e.target.result ;
image.onload = function(){
context.drawImage(image , 0 ,0 , image.width , image.height , 0 ,0 , canvasWidth , canvasHeight);
}
};
})(f)
reader.readAsDataURL(f);
}
}
}
//初始化拖入效果
var initDrag= function(){
var dragDiv = document.getElementById("canvas_bak");
dragDiv.addEventListener('dragover', handleDragOver, false);
dragDiv.addEventListener('drop', handleFileSelect, false);
}
簡單解釋一下 , 在html5支援的瀏覽器中, 有drop的回撥函式 , 在其中獲得event之後 裡面有一個物件 dataTransfer.files , 獲取的是 file 檔案資訊 , 最後通過 FileReader.readAsDataURL 的函式讀入,可以獲取到 html5 支援的圖片資訊 , 最後通過建立 image 物件,把圖片繪製進去就可以了。