最近在研究下 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. varinitCanvas=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. canvas_bak=document.getElementById("canvas_bak");
  10. canvas_bak.width=canvasWidth;
  11. canvas_bak.height=canvasHeight;
  12. context_bak=canvas_bak.getContext('2d');
  13. }



2. 第二部 就是绘制了 , 思路是当鼠标点下得时候 确定一个初始点, 当鼠标移动的时候开始绘制。之前说过 鼠标移动只是在bak 层绘制 , 当松开鼠标时把bak层的 添加到 canvas 层 。 那么这里就包含三个事件 。 点击,移动,松开 , 对应了三个不同的方法 。代码如下:
Javascript代码
  1. //鼠标按下获取开始xy开始画图
  2. varmousedown=function(e){
  3. context.strokeStyle=color;
  4. context_bak.strokeStyle=color;
  5. context_bak.lineWidth=size;
  6. e=e||window.event;
  7. startX=e.clientX-canvasLeft;
  8. startY=e.clientY-canvasTop;
  9. context_bak.moveTo(startX,startY);
  10. canDraw=true;
  11. if(graphType=='pencil'){
  12. context_bak.beginPath();
  13. }elseif(graphType=='circle'){
  14. context.beginPath();
  15. context.moveTo(startX,startY);
  16. context.lineTo(startX+2,startY+2);
  17. context.stroke();
  18. }elseif(graphType=='rubber'){
  19. context.clearRect(startX-size*10,startY-size*10,size*20,size*20);
  20. }
  21. };
  22. //鼠标离开把蒙版canvas的图片生成到canvas中
  23. varmouseup=function(e){
  24. e=e||window.event;
  25. canDraw=false;
  26. varimage=newImage();
  27. if(graphType!='rubber'){
  28. image.src=canvas_bak.toDataURL();
  29. image.onload=function(){
  30. context.drawImage(image,0,0,image.width,image.height,0,0,canvasWidth,canvasHeight);
  31. clearContext();
  32. saveImageToAry();
  33. }
  34. varx=e.clientX-canvasLeft;
  35. vary=e.clientY-canvasTop;
  36. context.beginPath();
  37. context.moveTo(x,y);
  38. context.lineTo(x+2,y+2);
  39. context.stroke();
  40. }
  41. };
  42. //鼠标移动
  43. varmousemove=function(e){
  44. e=e||window.event;
  45. varx=e.clientX-canvasLeft;
  46. vary=e.clientY-canvasTop;
  47. //方块4条直线搞定
  48. if(graphType=='square'){
  49. if(canDraw){
  50. context_bak.beginPath();
  51. clearContext();
  52. context_bak.moveTo(startX,startY);
  53. context_bak.lineTo(x,startY);
  54. context_bak.lineTo(x,y);
  55. context_bak.lineTo(startX,y);
  56. context_bak.lineTo(startX,startY);
  57. context_bak.stroke();
  58. }
  59. //直线
  60. }elseif(graphType=='line'){
  61. if(canDraw){
  62. context_bak.beginPath();
  63. clearContext();
  64. context_bak.moveTo(startX,startY);
  65. context_bak.lineTo(x,y);
  66. context_bak.stroke();
  67. }
  68. //画笔
  69. }elseif(graphType=='pencil'){
  70. if(canDraw){
  71. context_bak.lineTo(e.clientX-canvasLeft,e.clientY-canvasTop);
  72. context_bak.stroke();
  73. }
  74. //圆未画得时候出现一个小圆
  75. }elseif(graphType=='circle'){
  76. clearContext();
  77. if(canDraw){
  78. context_bak.beginPath();
  79. varradii=Math.sqrt((startX-x)*(startX-x)+(startY-y)*(startY-y));
  80. context_bak.arc(startX,startY,radii,0,Math.PI*2,false);
  81. context_bak.stroke();
  82. }else{
  83. context_bak.beginPath();
  84. context_bak.arc(x,y,20,0,Math.PI*2,false);
  85. context_bak.stroke();
  86. }
  87. //涂鸦未画得时候出现一个小圆
  88. }elseif(graphType=='handwriting'){
  89. if(canDraw){
  90. context_bak.beginPath();
  91. context_bak.strokeStyle=color;
  92. context_bak.fillStyle=color;
  93. context_bak.arc(x,y,size*10,0,Math.PI*2,false);
  94. context_bak.fill();
  95. context_bak.stroke();
  96. context_bak.restore();
  97. }else{
  98. clearContext();
  99. context_bak.beginPath();
  100. context_bak.fillStyle=color;
  101. context_bak.arc(x,y,size*10,0,Math.PI*2,false);
  102. context_bak.fill();
  103. context_bak.stroke();
  104. }
  105. //橡皮擦不管有没有在画都出现小方块按下鼠标开始清空区域
  106. }elseif(graphType=='rubber'){
  107. context_bak.lineWidth=1;
  108. clearContext();
  109. context_bak.beginPath();
  110. context_bak.strokeStyle='#000000';
  111. context_bak.moveTo(x-size*10,y-size*10);
  112. context_bak.lineTo(x+size*10,y-size*10);
  113. context_bak.lineTo(x+size*10,y+size*10);
  114. context_bak.lineTo(x-size*10,y+size*10);
  115. context_bak.lineTo(x-size*10,y-size*10);
  116. context_bak.stroke();
  117. if(canDraw){
  118. context.clearRect(x-size*10,y-size*10,size*20,size*20);
  119. }
  120. }
  121. };



顺便提一下撤销和回退的做法。之前有提过在鼠标松开的时候,我们会把 bak 层的内容绘制到 canvas 层中, 那么在这个时候,同步的会把一份 图片信息 存到一个 数组中去,用于回滚 , 当点击撤销的时候 只需要把上一个的 图片信息取出来,在绘制一遍canvas即可。撤销回退同理



4.接下来讲一下保存功能实现。保存图片使用得 是html5 的 storage 的功能实现的。storage 是浏览器开辟了一个5M 的控件提供方开发者使用 存放key value 的键值对, 有点类似于 cookie ,那么women保存的实现就很简单了,当点击保存按钮的时候 , 获取图片的 dataUrl 保存与 storage 中即可,下次打开浏览器 获取再放入canvas中就可以了。代码如下:

Javascript代码
  1. //保存
  2. varsave=function(){
  3. for(vari=1;i<=8;i++){
  4. vardataUrl=getStorage(i);
  5. if(dataUrl==null||dataUrl==''){
  6. putStorage(i,canvas.toDataURL());
  7. $("#history_"+i).attr("src",canvas.toDataURL());
  8. initHistorty();
  9. return;
  10. }
  11. }
  12. }



5.最后说一下 下载,可能很多人因为这个头疼,因为没有后台的处理,怎么能做到下载图片呢。其实在html5中 对于 a 标签提供了一个新的属性 【download】 如:
Java代码
  1. <ahref="javascript:void(0);"id="history_download_1"download="picture.png">下载</a></td>

浏览器默认会把他当做一个下载链接去处理,下载的文件名就是 download 中的 picture.png 下载的内容对应的是src 中的值。所以我们只需要把 图片的dataUrl 动态赋值上去 即可。

今天就先讲到这里哈,有问题可以给我留言。


--------------//2013-06-258 ---------------

昨天新加了 拖拽图片的功能, 从文件夹中 拖到画图板里面可以直接覆盖。
代码页很简单 如下:
Javascript代码
  1. //处理文件拖入事件,防止浏览器默认事件带来的重定向
  2. functionhandleDragOver(evt){
  3. evt.stopPropagation();
  4. evt.preventDefault();
  5. }
  6. //判断是否图片
  7. functionisImage(type){
  8. switch(type){
  9. case'image/jpeg':
  10. case'image/png':
  11. case'image/gif':
  12. case'image/bmp':
  13. case'image/jpg':
  14. returntrue;
  15. default:
  16. returnfalse;
  17. }
  18. }
  19. //处理拖放文件列表
  20. functionhandleFileSelect(evt){
  21. evt.stopPropagation();
  22. evt.preventDefault();
  23. varfiles=evt.dataTransfer.files;
  24. for(vari=0,f;f=files[i];i++){
  25. vart=f.type?f.type:'n/a';
  26. reader=newFileReader();
  27. isImg=isImage(t);
  28. //处理得到的图片
  29. if(isImg){
  30. reader.onload=(function(theFile){
  31. returnfunction(e){
  32. varimage=newImage();
  33. image.src=e.target.result;
  34. image.onload=function(){
  35. context.drawImage(image,0,0,image.width,image.height,0,0,canvasWidth,canvasHeight);
  36. }
  37. };
  38. })(f)
  39. reader.readAsDataURL(f);
  40. }
  41. }
  42. }
  43. //初始化拖入效果
  44. varinitDrag=function(){
  45. vardragDiv=document.getElementById("canvas_bak");
  46. dragDiv.addEventListener('dragover',handleDragOver,false);
  47. dragDiv.addEventListener('drop',handleFileSelect,false);
  48. }


简单解释一下 , 在html5支持的浏览器中, 有drop的回调函数 , 在其中获得event之后 里面有一个对象 dataTransfer.files , 获取的是 file 文件信息 , 最后通过 FileReader.readAsDataURL 的函数读入,可以获取到 html5 支持的图片信息 , 最后通过创建 image 对象,把图片绘制进去就可以了。

更多相关文章

  1. HTML5用户身份认证源代码:注册、登录、会话保持的解决方案
  2. Js 之将html转为图片html2canvas
  3. html页面中给img标签的src属性赋值为一张图片的存储路径,图片不显
  4. 将PHP代码添加到.html文件
  5. 如何让你的前端代码更像HTML5(用语义元素构造html5)
  6. HTML5批量拖拽图片到网页
  7. 如何在CSS中单独定位此HTML代码?
  8. QQ、MSN、淘包旺旺、Skype常设对话的html链接代码
  9. 10段实用的HTML5代码

随机推荐

  1. 聊聊 Python 的内置电池
  2. 一文读懂 Serverless,将配置化思想复用到
  3. 当 Python 中混进一只薛定谔的猫……
  4. 为什么 Python 3 把 print 改为函数?
  5. 如何用python制作动态二维码,来哄女朋友开
  6. MySQL 8 OCP(1Z0-908)认证考试题库原题(第
  7. 肝了一个月的编程导航诞生!轻松发现优质编
  8. mysql 常用命令
  9. 前端框架LayUI介绍及用法
  10. 编程语言之问:何时该借用,何时该创造?