Canvas 橡皮擦效果

引子

解决了第一个问题图像灰度处理以后,接着就是作擦除的效果。javascript

思路

一开始想到 Canvas 的画布能够相互覆盖的特性,彩色原图做为背景,灰度图渲染到 Canvas 画布上,而后手指滑动的时候,把接触的部分清除掉,就显示出了背景图。关键的部分是怎么清除画布上已有图像,查询资料发现有两种方式:html

  1. 使用 clearRect 方法,会把指定范围全部像素变成透明,并擦除以前绘制的全部内容。
  2. 使用 globalCompositeOperation 属性,该属性设置在绘制新形状时应用合成操做的类型,值为 destination-out 时效果以下:

66-destination-out

在上面两种方式中,第一种方式正是功能实现所须要的,但擦除时的边角效果没有第二种方式理想。下面以第二种方式做为示例。html5

实现

在实现的过程当中,发现有几点须要注意:java

  • 手机端高清显示处理。
  • 注意 globalCompositeOperation 属性设置的时机,过早设置,可能画布上没法显示内容。
  • 擦除状态的控制,特别是在 PC 端,若是不设置相应状态,鼠标移动的时候就可能发生擦除。

这是示例页面,移动端访问以下:git

66-normal

优化一

通常用户都不会所有进行擦除,并且这种体验也很差,因此擦除面积达到必定百分比时,自动擦除剩余的部分。github

擦除效果其实是改变了已有图像的透明度,能够经过 getImageData 方法查看 Canvas 上图像的像素数据。经过统计符合透明度要求的像素所占百分比,就等效擦除面积的百分比。至于透明度多少算擦除有效,看实际应用场景和所需效果。下面是主要的实现:canvas

/** * 获取透明所占百分比,返回一个 <= 1 的值 * @param {object} context canvas 上下文对象 * @param {number} opacity 透明度参考值,初始参考透明值是 128 */
  function getOpacityPercentage (context, opacity = 128) {
    var imageData = context.getImageData(0,0,248,415);
    var colorDataArr = imageData.data;
    var colorDataArrLen = colorDataArr.length;
    var eraseArea = [];
    // rgba 显示的模式,因此一个像素表示有 4 个份量,透明度是最后一个份量
    for(var i = 0; i < colorDataArrLen; i += 4) {
      // 严格上来讲,判断像素点是否透明须要判断该像素点的 a 值是否等于0,
      if(colorDataArr[i + 3] < opacity) {
        eraseArea.push(colorDataArr[i + 3]);
      }
    }
    var divResult = eraseArea.length / (colorDataArrLen/4);
    // 处理除不尽的状况
    var pointIndex = String(divResult).indexOf('.');
    if (pointIndex>-1) {
      divResult = String(divResult).slice(0,pointIndex+5);
    }
    return Number(divResult).toFixed(2);

  }
复制代码

这是示例页面,移动端访问以下:post

66-percentage

优化二

优化一中擦除所有的效果是一会儿就不见了,但我在那个游戏里面看到的擦除效果是有一个过程的。因而再折腾了一下。优化

这是示例页面,移动端访问以下:ui

66-progress

参考资料

必应首页图片有些时候蛮不错的。

66-poster
相关文章
相关标签/搜索