html5 的 canvas 想写个小项目 画板

 最近在研究下 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代码 复制代码  收藏代码
  1. //初始化   
  2.         var initCanvas = function(){   
  3.             canvas =  document.getElementById("canvas");   
  4.             canvas.width = canvasWidth;   
  5.             canvas.height = canvasHeight;   
  6.             context = canvas.getContext('2d');   
  7.             canvasTop = $(canvas).offset().top   
  8.             canvasLeft = $(canvas).offset().left;   
  9.        
  10.   
  11.             canvas_bak =  document.getElementById("canvas_bak");   
  12.             canvas_bak.width = canvasWidth;   
  13.             canvas_bak.height = canvasHeight;   
  14.             context_bak = canvas_bak.getContext('2d');         
  15.         }     
//初始化
		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代码 复制代码  收藏代码
  1.   
  2. //鼠标按下获取 开始xy开始画图   
  3.     var mousedown = function(e){   
  4.         context.strokeStyle= color;   
  5.         context_bak.strokeStyle= color;   
  6.         context_bak.lineWidth = size;   
  7.         e=e||window.event;   
  8.         startX = e.clientX - canvasLeft;   
  9.         startY = e.clientY - canvasTop;   
  10.         context_bak.moveTo(startX ,startY );   
  11.         canDraw = true;            
  12.            
  13.         if(graphType == 'pencil'){   
  14.             context_bak.beginPath();   
  15.         }else if(graphType == 'circle'){   
  16.             context.beginPath();   
  17.             context.moveTo(startX ,startY );   
  18.             context.lineTo(startX +2 ,startY+2);   
  19.             context.stroke();      
  20.                
  21.         }else if(graphType == 'rubber'){                               
  22.             context.clearRect(startX - size * 10 ,  startY - size * 10 , size * 20 , size * 20);                   
  23.         }      
  24.     };     
  25.   
  26.     //鼠标离开 把蒙版canvas的图片生成到canvas中   
  27.     var mouseup = function(e){   
  28.         e=e||window.event;   
  29.         canDraw = false;   
  30.         var image = new Image();   
  31.         if(graphType!='rubber'){       
  32.                
  33.             image.src = canvas_bak.toDataURL();   
  34.             image.onload = function(){   
  35.                 context.drawImage(image , 0 ,0 , image.width , image.height , 0 ,0 , canvasWidth , canvasHeight);   
  36.                 clearContext();   
  37.                 saveImageToAry();   
  38.             }   
  39.             var x = e.clientX   - canvasLeft;   
  40.             var y = e.clientY  - canvasTop;    
  41.             context.beginPath();   
  42.             context.moveTo(x ,y );   
  43.             context.lineTo(x +2 ,y+2);   
  44.             context.stroke();      
  45.         }   
  46.     };   
  47.   
  48.     // 鼠标移动   
  49.     var  mousemove = function(e){   
  50.         e=e||window.event;   
  51.         var x = e.clientX   - canvasLeft;   
  52.         var y = e.clientY  - canvasTop;    
  53.         //方块  4条直线搞定   
  54.         if(graphType == 'square'){   
  55.             if(canDraw){   
  56.                 context_bak.beginPath();   
  57.                 clearContext();   
  58.                 context_bak.moveTo(startX , startY);                           
  59.                 context_bak.lineTo(x  ,startY );   
  60.                 context_bak.lineTo(x  ,y );   
  61.                 context_bak.lineTo(startX  ,y );   
  62.                 context_bak.lineTo(startX  ,startY );   
  63.                 context_bak.stroke();   
  64.             }   
  65.         //直线   
  66.         }else if(graphType =='line'){                          
  67.             if(canDraw){   
  68.                 context_bak.beginPath();   
  69.                 clearContext();   
  70.                 context_bak.moveTo(startX , startY);   
  71.                 context_bak.lineTo(x  ,y );   
  72.                 context_bak.stroke();   
  73.             }   
  74.         //画笔   
  75.         }else if(graphType == 'pencil'){   
  76.             if(canDraw){   
  77.                 context_bak.lineTo(e.clientX   - canvasLeft ,e.clientY  - canvasTop);   
  78.                 context_bak.stroke();                          
  79.             }   
  80.         //圆 未画得时候 出现一个小圆   
  81.         }else if(graphType == 'circle'){                           
  82.             clearContext();   
  83.             if(canDraw){   
  84.                 context_bak.beginPath();               
  85.                 var radii = Math.sqrt((startX - x) *  (startX - x)  + (startY - y) * (startY - y));   
  86.                 context_bak.arc(startX,startY,radii,0,Math.PI * 2,false);                                      
  87.                 context_bak.stroke();   
  88.             }else{     
  89.                 context_bak.beginPath();                       
  90.                 context_bak.arc(x,y,20,0,Math.PI * 2,false);   
  91.                 context_bak.stroke();   
  92.             }   
  93.         //涂鸦 未画得时候 出现一个小圆   
  94.         }else if(graphType == 'handwriting'){                                              
  95.             if(canDraw){   
  96.                 context_bak.beginPath();       
  97.                 context_bak.strokeStyle = color;   
  98.                 context_bak.fillStyle  = color;   
  99.                 context_bak.arc(x,y,size*10,0,Math.PI * 2,false);          
  100.                 context_bak.fill();   
  101.                 context_bak.stroke();   
  102.                 context_bak.restore();   
  103.             }else{     
  104.                 clearContext();   
  105.                 context_bak.beginPath();                       
  106.                 context_bak.fillStyle  = color;   
  107.                 context_bak.arc(x,y,size*10,0,Math.PI * 2,false);   
  108.                 context_bak.fill();   
  109.                 context_bak.stroke();   
  110.             }   
  111.         //橡皮擦 不管有没有在画都出现小方块 按下鼠标 开始清空区域   
  112.         }else if(graphType == 'rubber'){       
  113.             context_bak.lineWidth = 1;   
  114.             clearContext();   
  115.             context_bak.beginPath();               
  116.             context_bak.strokeStyle =  '#000000';                          
  117.             context_bak.moveTo(x - size * 10 ,  y - size * 10 );                           
  118.             context_bak.lineTo(x + size * 10  , y - size * 10 );   
  119.             context_bak.lineTo(x + size * 10  , y + size * 10 );   
  120.             context_bak.lineTo(x - size * 10  , y + size * 10 );   
  121.             context_bak.lineTo(x - size * 10  , y - size * 10 );       
  122.             context_bak.stroke();          
  123.             if(canDraw){                               
  124.                 context.clearRect(x - size * 10 ,  y - size * 10 , size * 20 , size * 20);   
  125.                                            
  126.             }              
  127.         }   
  128.     };  
//鼠标按下获取 开始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代码 复制代码  收藏代码
  1. //保存   
  2. var save = function(){   
  3.     for(var i = 1;i<=8;i++){   
  4.         var dataUrl = getStorage(i);   
  5.         if(dataUrl == null || dataUrl == ''){   
  6.             putStorage(i,canvas.toDataURL());   
  7.             $("#history_"+i).attr("src",canvas.toDataURL());   
  8.   
  9.             initHistorty();   
  10.             return ;   
  11.         }   
  12.     }              
  13. }  
//保存
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代码 复制代码  收藏代码
  1. <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代码 复制代码  收藏代码
  1. // 处理文件拖入事件,防止浏览器默认事件带来的重定向     
  2.        function handleDragOver(evt) {     
  3.         evt.stopPropagation();     
  4.         evt.preventDefault();     
  5.         }   
  6.         
  7.   
  8.     // 判断是否图片     
  9.     function isImage(type) {     
  10.         switch (type) {     
  11.         case 'image/jpeg':     
  12.         case 'image/png':     
  13.         case 'image/gif':     
  14.         case 'image/bmp':     
  15.         case 'image/jpg':     
  16.             return true;     
  17.         default:     
  18.             return false;     
  19.         }     
  20.     }     
  21.   
  22.   
  23.      // 处理拖放文件列表     
  24.     function handleFileSelect(evt) {     
  25.         evt.stopPropagation();     
  26.         evt.preventDefault();     
  27.     
  28.         var files = evt.dataTransfer.files;     
  29.     
  30.         for (var i = 0, f; f = files[i]; i++) {       
  31.             var t = f.type ? f.type : 'n/a';   
  32.             reader = new FileReader();   
  33.             isImg = isImage(t);   
  34.                  
  35.             // 处理得到的图片     
  36.             if (isImg) {     
  37.                 reader.onload = (function (theFile) {     
  38.                     return function (e) {     
  39.                         var  image = new Image();    
  40.                         image.src =  e.target.result ;   
  41.                         image.onload = function(){   
  42.                             context.drawImage(image , 0 ,0 , image.width , image.height , 0 ,0 , canvasWidth , canvasHeight);   
  43.                         }   
  44.   
  45.                     };     
  46.                 })(f)     
  47.                 reader.readAsDataURL(f);     
  48.             }      
  49.         }       
  50.     }     
  51.   
  52.     //初始化拖入效果   
  53.     var initDrag= function(){   
  54.         var dragDiv  = document.getElementById("canvas_bak");   
  55.         dragDiv.addEventListener('dragover', handleDragOver, false);     
  56.         dragDiv.addEventListener('drop', handleFileSelect, false);     
  57.     }  
 // 处理文件拖入事件,防止浏览器默认事件带来的重定向  
        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 对象,把图片绘制进去就可以了。 
  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值