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
相關文章
相關標籤/搜索