返回顶部
首页 > 资讯 > 前端开发 > VUE >怎么用HTML5实现橡皮擦的涂抹效果
  • 619
分享到

怎么用HTML5实现橡皮擦的涂抹效果

2024-04-02 19:04:59 619人浏览 泡泡鱼
摘要

这篇文章主要讲解了“怎么用HTML5实现橡皮擦的涂抹效果”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么用html5实现橡皮擦的涂抹效果”吧!最近项目刚好

这篇文章主要讲解了“怎么用HTML5实现橡皮擦的涂抹效果”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么用html5实现橡皮擦的涂抹效果”吧!

最近项目刚好用到这种效果,也就是有点像刮刮卡一样,在移动设备上,把某张图片刮掉显示出另一张图片。效果图如下:
怎么用HTML5实现橡皮擦的涂抹效果

DEMO请戳右:DEMO 
这种在网上还是挺常见的,本来就想直接网上找个demo套用下他的方法就行了,套用了才发现,在Android上卡出翔了,因为客户要求,在android不要求特别流畅,至少要能玩,但是网上找的那个demo实在太卡,根本就是没法玩的情况。于是就想自己写一个算了,本文也就权当记录一下研究过程。

  这种刮图的效果,首先想到就是用HTML5的canvas来实现,而canvas的api中,可以清除像素的就是clearRect方法,但是clearRect方法的清除区域矩形,毕竟大部分人的习惯中的橡皮擦都是圆形的,所以就引入了剪辑区域这个强大的功能,也就是clip方法。用法很简单: 

XML/HTML Code复制内容到剪贴板

  1. ctx.save()   

  2. ctx.beginPath()   

  3. ctx.arc(x2,y2,a,0,2*Math.PI);   

  4. ctx.clip()   

  5. ctx.clearRect(0,0,canvas.width,canvas.height);   

  6. ctx.restore();   

  上面那段代码就实现了圆形区域的擦除,也就是先实现一个圆形路径,然后把这个路径作为剪辑区域,再清除像素就行了。有个注意点就是需要先保存绘图环境,清除完像素后要重置绘图环境,如果不重置的话以后的绘图都是会被限制在那个剪辑区域中。

  擦除效果有了,现在就是写鼠标移动擦除的效果了,下面我均用鼠标来描述,因为移动端也差不多,就是把mousedown换成touchstart,mousemove换成touchmove,mouseup换成touchend、以及获取坐标点由e.clientX换成e.targetTouches[0].pageX而已。

  实现鼠标移动擦除,刚开始就是想到鼠标移动时在触发的mousemove事件中对鼠标所在位置进行圆形区域擦除,写出来后发现,当鼠标移动速度很快的时候,擦除的区域就不连贯了,就会出现下面这种效果,这显然不是我们想要的橡皮擦擦除效果。
怎么用HTML5实现橡皮擦的涂抹效果

既然所有点不连贯,那接下来要做的事就是把这些点连贯起来,如果是实现画图功能的话,就可以直接通过lineTo把两点之间连接起来再绘制,但是擦除效果中的剪辑区域要求要是闭合路径,如果是单纯的把两个点连起来就无法形成剪辑区域了。然后我就想到用计算的方法,算出两个擦除区域中的矩形四个端点坐标来实现,也就是下图中的红色矩形:
怎么用HTML5实现橡皮擦的涂抹效果

计算方法也很简单,因为可以知道两个剪辑区域连线两个端点的坐标,又知道我们要多宽的线条,矩形的四个端点坐标就变得容易求了,所以就有了下面的代码:
XML/HTML Code复制内容到剪贴板

  1. var aasin = a*Math.sin(Math.atan((y2-y1)/(x2-x1)));   

  2. var aacos = a*Math.cos(Math.atan((y2-y1)/(x2-x1)))   

  3. var x3 = x1+asin;   

  4. var y3 = y1-acos;   

  5. var x4 = x1-asin;   

  6. var y4 = y1+acos;   

  7. var x5 = x2+asin;   

  8. var y5 = y2-acos;   

  9. var x6 = x2-asin;   

  10. var y6 = y2+acos;   

  x1、y1和x2、y2就是两个端点,从而求出了四个端点的坐标。这样一来,剪辑区域就是圈加矩形,代码组织起来就是:
XML/HTML Code复制内容到剪贴板

  1. var hastouch = "ontouchstart" in window?true:false,//判断是否为移动设备   

  2.     tapstart = hastouch?"touchstart":"mousedown",   

  3.     tapmove = hastouch?"touchmove":"mousemove",   

  4.     tapend = hastouch?"touchend":"mouseup";   

  5.   

  6. canvas.addEventListener(tapstart , function(e){   

  7.     e.preventDefault();   

  8.        

  9.     x1 = hastouch?e.targetTouches[0].pageX:e.clientX-canvas.offsetLeft;   

  10.     y1 = hastouch?e.targetTouches[0].pageY:e.clientY-canvas.offsetTop;   

  11.        

  12.   //鼠标第一次点下的时候擦除一个圆形区域,同时记录第一个坐标点   

  13.     ctx.save()   

  14.     ctx.beginPath()   

  15.     ctx.arc(x1,y1,a,0,2*Math.PI);   

  16.     ctx.clip()   

  17.     ctx.clearRect(0,0,canvas.width,canvas.height);   

  18.     ctx.restore();   

  19.        

  20.     canvas.addEventListener(tapmove , tapmoveHandler);   

  21.     canvas.addEventListener(tapend , function(){   

  22.         canvas.removeEventListener(tapmove , tapmoveHandler);   

  23.     });   

  24.   //鼠标移动时触发该事件   

  25.     function tapmoveHandler(e){   

  26.         e.preventDefault()   

  27.         x2 = hastouch?e.targetTouches[0].pageX:e.clientX-canvas.offsetLeft;   

  28.         y2 = hastouch?e.targetTouches[0].pageY:e.clientY-canvas.offsetTop;   

  29.            

  30.     //获取两个点之间的剪辑区域四个端点   

  31.         var aasin = a*Math.sin(Math.atan((y2-y1)/(x2-x1)));   

  32.         var aacos = a*Math.cos(Math.atan((y2-y1)/(x2-x1)))   

  33.         var x3 = x1+asin;   

  34.         var y3 = y1-acos;   

  35.         var x4 = x1-asin;   

  36.         var y4 = y1+acos;   

  37.         var x5 = x2+asin;   

  38.         var y5 = y2-acos;   

  39.         var x6 = x2-asin;   

  40.         var y6 = y2+acos;   

  41.            

  42.     //保证线条的连贯,所以在矩形一端画圆   

  43.         ctx.save()   

  44.         ctx.beginPath()   

  45.         ctx.arc(x2,y2,a,0,2*Math.PI);   

  46.         ctx.clip()   

  47.         ctx.clearRect(0,0,canvas.width,canvas.height);   

  48.         ctx.restore();   

  49.        

  50.     //清除矩形剪辑区域里的像素   

  51.         ctx.save()   

  52.         ctx.beginPath()   

  53.         ctx.moveTo(x3,y3);   

  54.         ctx.lineTo(x5,y5);   

  55.         ctx.lineTo(x6,y6);   

  56.         ctx.lineTo(x4,y4);   

  57.         ctx.closePath();   

  58.         ctx.clip()   

  59.         ctx.clearRect(0,0,canvas.width,canvas.height);   

  60.         ctx.restore();   

  61.            

  62.     //记录最后坐标   

  63.         x1 = x2;   

  64.         y1 = y2;   

  65.     }   

  66. })  

  如此一来,鼠标擦除的效果就实现了,不过还有一个要实现的点,就是大部分擦除的效果,当你擦了一定数量的像素后,就会自动把所有图片内容呈现出来,这个效果,我是用imgData来实现的。代码如下:

XML/HTML Code复制内容到剪贴板

  1. var imgData = ctx.getImageData(0,0,canvas.width,canvas.height);   

  2. var dd = 0;   

  3. for(var x=0;x<imgData.width;x+=1){   

  4.     for(var y=0;y<imgData.height;y+=1){   

  5.         var i = (y*imgData.width + x)*4;   

  6.         if(imgData.data[i+3] > 0){   

  7.             dd++   

  8.         }   

  9.     }   

  10. }   

  11. if(dd/(imgData.width*imgData.height)<0.4){   

  12.     canvas.className = "noOp";   

  13. }  

  获取到imgData,对imgData里的像素进行遍历,然后再对imgData的data数组里的rgba中的alpha进行分析,也就是分析透明度,如果像素被擦除了,那透明度就是0了,也就是把当前画布中透明度不为0的像素的数量跟画布总像素数进行比较,如果透明度不为0 的像素数比例低于40%,那说明当前画布上就以后有百分六十以上的区域被擦除了,就可以自动呈现图片了。

  此处注意,我是把检查像素这段代码方法mouseup事件里面的,因为这个计算量相对来说还是不小,如果用户狂点鼠标,就会狂触发mouseup事件,也就是会疯狂的触发那个循环计算像素,计算量大到阻塞进程,导致界面卡住的情况,缓解办法如下:加个timeout,延迟执行像素计算,而在每一次点击的时候再清除timeout,也就是如果用户点击很快,这个计算也就触发不了了,还有一个提升的办法就是抽样检查,我上面的写法是逐个像素检查,逐个像素检查的话像素量太大,肯定会卡的,所以可以采用抽样检查,比如每隔30个像素检查一次,修改后的代码如下:

XML/HTML Code复制内容到剪贴板

  1. timeout = setTimeout(function(){   

  2.     var imgData = ctx.getImageData(0,0,canvas.width,canvas.height);   

  3.     var dd = 0;   

  4.     for(var x=0;x<imgData.width;x+=30){   

  5.         for(var y=0;y<imgData.height;y+=30){   

  6.             var i = (y*imgData.width + x)*4;   

  7.             if(imgData.data[i+3] >0){   

  8.                 dd++   

  9.             }   

  10.         }   

  11.     }   

  12.     if(dd/(imgData.width*imgData.height/900)<0.4){   

  13.         canvas.className = "noOp";   

  14.     }   

  15. },100)   

  这样就可以较大限度的防止用户狂点击了,如果有其他更好的检查方法欢迎给出意见,谢谢。

  到了这一步就都写完了,然后就是测试的时候了,结果并不乐观,在android上还是卡啊卡啊,所以又得另想办法,最终发现了绘图环境中的globalCompositeOperation这个属性,这个属性的默认值是source-over,也就是,当你在已有像素上进行绘图时会叠加,但是还有一个属性是destination-out,官方解释就是:在源图像外显示目标图像。只有源图像外的目标图像部分才会被显示,源图像是透明的。好像不太好理解,但是其实自己测试一下就会发现很简单,也就是在已有像素的基础上进行绘图时,你绘制的区域里的已有像素都会被置为透明,直接看张图更容易理解:
怎么用HTML5实现橡皮擦的涂抹效果

globalCompositeOperation属性效果图解。
有了这个属性后,就意味着不需要用到clip,也就不需要用sin、cos什么的计算剪辑区域,直接用条粗线就行了,这样一来就能够很大限度的降低了计算量,同时减少了绘图环境API的调用,性能提升了,在android上运行应该也会流畅很多,下面是修改后的代码:

XML/HTML Code复制内容到剪贴板

  1. //通过修改globalCompositeOperation来达到擦除的效果   

  2. function tapClip(){   

  3.     var hastouch = "ontouchstart" in window?true:false,   

  4.         tapstart = hastouch?"touchstart":"mousedown",   

  5.         tapmove = hastouch?"touchmove":"mousemove",   

  6.         tapend = hastouch?"touchend":"mouseup";   

  7.        

  8.     canvas.addEventListener(tapstart , function(e){   

  9.      clearTimeout(timeout)   

  10.         e.preventDefault();   

  11.            

  12.         x1 = hastouch?e.targetTouches[0].pageX:e.clientX-canvas.offsetLeft;   

  13.         y1 = hastouch?e.targetTouches[0].pageY:e.clientY-canvas.offsetTop;   

  14.            

  15.         ctx.lineCap = "round";  //设置线条两端为圆弧   

  16.         ctx.lineJoin = "round";  //设置线条转折为圆弧   

  17.         ctx.lineWidth = a*2;     

  18.         ctx.globalCompositeOperation = "destination-out";   

  19.            

  20.         ctx.save();   

  21.         ctx.beginPath()   

  22.         ctx.arc(x1,y1,1,0,2*Math.PI);   

  23.         ctx.fill();   

  24.         ctx.restore();   

  25.            

  26.         canvas.addEventListener(tapmove , tapmoveHandler);   

  27.         canvas.addEventListener(tapend , function(){   

  28.             canvas.removeEventListener(tapmove , tapmoveHandler);   

  29.                

  30.        timeout = setTimeout(function(){   

  31.             var imgData = ctx.getImageData(0,0,canvas.width,canvas.height);   

  32.             var dd = 0;   

  33.             for(var x=0;x<imgData.width;x+=30){   

  34.                 for(var y=0;y<imgData.height;y+=30){   

  35.                     var i = (y*imgData.width + x)*4;   

  36.                     if(imgData.data[i+3] > 0){   

  37.                         dd++   

  38.                     }   

  39.                 }   

  40.             }   

  41.             if(dd/(imgData.width*imgData.height/900)<0.4){   

  42.                 canvas.className = "noOp";   

  43.             }   

  44.        },100)   

  45.         });   

  46.         function tapmoveHandler(e){   

  47.             e.preventDefault()   

  48.             x2 = hastouch?e.targetTouches[0].pageX:e.clientX-canvas.offsetLeft;   

  49.             y2 = hastouch?e.targetTouches[0].pageY:e.clientY-canvas.offsetTop;   

  50.                

  51.             ctx.save();   

  52.             ctx.moveTo(x1,y1);   

  53.             ctx.lineTo(x2,y2);   

  54.             ctx.stroke();   

  55.             ctx.restore()   

  56.                

  57.             x1 = x2;   

  58.             y1 = y2;   

  59.         }   

  60.     })   

  61. }   

  擦除那部分代码就这么一点,也就相当于画图功能,直接设置line属性后通过lineTo进行绘制线条,只要事前把globalCompositeOperation设成destination-out,你所进行的一切绘制,都变成了擦除效果。鼠标滑动触发的事件里面代码也少了很多,绘图对象的调用次数减少了,计算也减少了,性能提升大大滴。

  改好代码后就立即用自己的android机子测试了一下,果然如此,跟上一个相比,流畅了很多,至少达到了客户要求的能玩的地步了。

  源码地址:https://GitHub.com/whxaxes/canvas-test/blob/gh-pages/src/Funny-demo/clip/clip.html

感谢各位的阅读,以上就是“怎么用HTML5实现橡皮擦的涂抹效果”的内容了,经过本文的学习后,相信大家对怎么用HTML5实现橡皮擦的涂抹效果这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

--结束END--

本文标题: 怎么用HTML5实现橡皮擦的涂抹效果

本文链接: https://lsjlt.com/news/76512.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • 怎么用HTML5实现橡皮擦的涂抹效果
    这篇文章主要讲解了“怎么用HTML5实现橡皮擦的涂抹效果”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么用HTML5实现橡皮擦的涂抹效果”吧!最近项目刚好...
    99+
    2024-04-02
  • HTML5 Canvas怎么实现橡皮擦功能
    本篇内容主要讲解“HTML5 Canvas怎么实现橡皮擦功能”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“HTML5 Canvas怎么实现橡皮擦功能”吧!在现实...
    99+
    2024-04-02
  • JS如何实现canvas仿ps橡皮擦刮卡效果
    这篇文章主要介绍了JS如何实现canvas仿ps橡皮擦刮卡效果,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。效果演示:主要JS代码实现 <div c...
    99+
    2023-06-25
  • JS实现canvas仿ps橡皮擦刮卡效果详解
    目录效果演示: 主要JS代码实现 <div class="box" id="bb"> <canvas id="cas" width="136...
    99+
    2024-04-02
  • Android怎么实现橡皮筋回弹和平移缩放效果
    这篇文章主要介绍“Android怎么实现橡皮筋回弹和平移缩放效果”,在日常操作中,相信很多人在Android怎么实现橡皮筋回弹和平移缩放效果问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Android怎么实现...
    99+
    2023-06-30
  • Vue怎么实现多点涂鸦效果
    这篇文章主要介绍“Vue怎么实现多点涂鸦效果”,在日常操作中,相信很多人在Vue怎么实现多点涂鸦效果问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Vue怎么实现多点涂鸦效果”的疑惑有所帮助!接下来,请跟着小编...
    99+
    2023-07-05
  • 使用canvas怎么实现橡皮筋式线条绘图应用
    使用canvas怎么实现橡皮筋式线条绘图应用?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。什么叫橡皮筋式指画图时橡皮筋一样伸缩自如。。例子如下:point_down:思路思路很...
    99+
    2023-06-09
  • HTML5怎么实现波的效果
    本文小编为大家详细介绍“HTML5怎么实现波的效果”,内容详细,步骤清晰,细节处理妥当,希望这篇“HTML5怎么实现波的效果”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。 一....
    99+
    2024-04-02
  • html5缓动效果怎么实现
    这篇文章主要介绍“html5缓动效果怎么实现”,在日常操作中,相信很多人在html5缓动效果怎么实现问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”html5缓动效果怎么实现”...
    99+
    2024-04-02
  • 怎么用HTML5实现圣诞树效果
    这篇文章主要为大家展示了“怎么用HTML5实现圣诞树效果”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“怎么用HTML5实现圣诞树效果”这篇文章吧。   htm...
    99+
    2024-04-02
  • html5怎么实现的图片墙效果
    这篇文章主要讲解了“html5怎么实现的图片墙效果”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“html5怎么实现的图片墙效果”吧!本文实例讲述了基于htm...
    99+
    2024-04-02
  • HTML5中怎么实现动画效果
    这期内容当中小编将会给大家带来有关HTML5中怎么实现动画效果,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。1.canvas html代码: 代码如下:<html&...
    99+
    2024-04-02
  • html5怎么实现波浪效果图
    这篇文章主要介绍了html5怎么实现波浪效果图的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇html5怎么实现波浪效果图文章都会有所收获,下面我们一起来看看吧。   先画个最...
    99+
    2024-04-02
  • HTML5怎么实现歌词同步的效果
    本篇内容主要讲解“HTML5怎么实现歌词同步的效果 ”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“HTML5怎么实现歌词同步的效果 ”吧! ...
    99+
    2024-04-02
  • HTML5怎么实现有趣的海浪效果
    今天小编给大家分享一下HTML5怎么实现有趣的海浪效果的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了...
    99+
    2024-04-02
  • 怎么利用HTML5+CSS3实现3D转换效果
    这篇文章主要为大家展示了“怎么利用HTML5+CSS3实现3D转换效果”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“怎么利用HTML5+CSS3实现3D转换效果...
    99+
    2024-04-02
  • 怎么用html5 canvas实现漫天飞雪效果
    这篇文章主要讲解了“怎么用html5 canvas实现漫天飞雪效果”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么用html5 canvas实现漫天飞雪效...
    99+
    2024-04-02
  • html5中怎么利用canvas实现阴影效果
    这篇文章将为大家详细讲解有关html5中怎么利用canvas实现阴影效果,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。代码如下:<!DOCTYPE h...
    99+
    2024-04-02
  • Html5中怎么实现百叶窗效果
    这篇文章给大家介绍Html5中怎么实现百叶窗效果,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。1,百叶窗布局 用定位(position: absolute)覆盖在content布局之上...
    99+
    2024-04-02
  • html5怎么实现Photoshop渐变色效果
    这篇文章主要讲解了“html5怎么实现Photoshop渐变色效果”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“html5怎么实现Photoshop渐变色效...
    99+
    2024-04-02
软考高级职称资格查询
推荐阅读
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作