锁定老帖子 主题:HTML5+JS 画图板
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2013-06-05
最后修改:2013-06-25
演示地址: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 这个很简单就不做多的解释了 代码如下。 //初始化 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 层 。 那么这里就包含三个事件 。 点击,移动,松开 , 对应了三个不同的方法 。代码如下: //鼠标按下获取 开始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中就可以了。代码如下: //保存 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】 如: <a href="javascript:void(0);" id="history_download_1" download="picture.png">下载</a></td> 浏览器默认会把他当做一个下载链接去处理,下载的文件名就是 download 中的 picture.png 下载的内容对应的是src 中的值。所以我们只需要把 图片的dataUrl 动态赋值上去 即可。 今天就先讲到这里哈,有问题可以给我留言。 --------------//2013-06-258 --------------- 昨天新加了 拖拽图片的功能, 从文件夹中 拖到画图板里面可以直接覆盖。 代码页很简单 如下: // 处理文件拖入事件,防止浏览器默认事件带来的重定向 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 对象,把图片绘制进去就可以了。 有兴趣探讨的 或者想要源码的读者可以给我留言 ,我会定时回复 谢谢! 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2013-06-05
写的很好啊,我练手的时候怎么就没写出来呢,当时只写了个验证码……
|
|
返回顶楼 | |
发表时间:2013-06-06
楼主最好能把你的代码详细的解释一下吧
|
|
返回顶楼 | |
发表时间:2013-06-06
LZ 好样的。。。
学习了! |
|
返回顶楼 | |
发表时间:2013-06-15
最近正好在用这方面的知识,学习了。感谢分享
|
|
返回顶楼 | |
发表时间:2013-06-25
airhunter 写道 楼主最好能把你的代码详细的解释一下吧
我抽空尽量把代码解释了一下,可以再看一下 ,有不懂的可以给我留言 |
|
返回顶楼 | |
发表时间:2013-08-01
很好很强大...
|
|
返回顶楼 | |
发表时间:2013-10-03
楼主的源代码,是只有draw.js和stroage.js两个文件是吧?
能不能增加一个windows画板中text的功能呢? |
|
返回顶楼 | |
发表时间:2013-10-09
有时间细读
|
|
返回顶楼 | |
发表时间:2013-10-31
赞一个,支持一下
|
|
返回顶楼 | |