刮刮卡效果實現

公司國慶搞了個集卡、抽獎小活動。抽獎需要刮刮卡的效果,感覺 css 是實現不了。看我使用 canvas 如何實現刮刮卡效果。
廢話不多說,線上效果 jsrun-測試地址lilnong.top-測試地址

實現方案都有什麼

  1. clearRect 這是我第一個找到的 API,作用是清除一個矩形區域內的內容。缺點是矩形
  2. globalCompositeOperation 基於上面的缺點,我發現了他 destination-out 是我需要的,作用是改變canvas圖形的混合模式

我們期望有什麼功能

  1. 底部內容自定義(我們將 canvas 浮在內容上即可)
  2. 自定義蒙版顏色(canvas 繪製畫布設置一下顏色即可 context.fillStyle = color;
  3. 自定義蒙版圖片(使用drawImage來繪製畫布)
  4. 自定義筆觸

    1. clearRect爲矩形
    2. arc配合globalCompositeOperation來實現圓形
    3. drawImage配合globalCompositeOperation來自定義筆觸
  5. 自動刮開&自動刮開軌道生成器
  6. 刮開面積佔比

底部內容自定義

兩個方案的差距不大,根據個人喜好選擇即可。

  1. 方案一 DOM 層級(擁有更多的能力)效果查看-傳送門

       <style type="text/css">
           #app1{width:300px;height:100px;position: relative;border: 1px solid #f00;}
           #app1Content{width:300px;height:100px;position: absolute;left:0;top:0;z-index: 1}
           #app1Mark{width:300px;height:100px;position: absolute;left:0;top:0;z-index: 2}
       </style>
       <div id="app1">
           <div id="app1Content" style="background:url(https://www.lilnong.top/static/img/ml-btn6.png) left center no-repeat">我是自定義內容</div>
           <canvas id="app1Mark"></canvas>
       </div>
  2. 方案二 canvas 背景圖(使用簡單)效果查看-傳送門

    <style type="text/css">
        #app2{width:300px;height:100px;position: relative;border: 1px solid #f00;}
        #app2Mark{width:300px;height:100px;position: absolute;left:0;top:0;z-index: 2}
    </style>
    <div id="app2">
        <canvas id="app2Mark"  style="background:url(https://www.lilnong.top/static/img/ml-btn6.png) left center no-repeat"></canvas>
    </div>

蒙版顏色

ctx.fillStyle = '#0cc';主要通過這個來設置填充樣式效果查看-傳送門
感興趣可以去看看,我這裏沒用到就不細說了。傳送門 - Canvas API中文網

  1. 色值填充

    1. RGB色值:RGB(255, 0, 0)
    2. HSL色值:HSL(360, 100%, 50%)
    3. RGBA色值:RGBA(255, 0, 0, .5)
    4. HSLA色值:HSLA(360, 100%, 50%, .5)
    5. HEX色值:#FF0000
  2. 線性漸變 createLinearGradient()
  3. 徑向漸變 createRadialGradient()
  4. 圖案 createPattern
ctx = app3Mark.getContext('2d');
app3Mark.width = 300;
app3Mark.height = 100;
ctx.moveTo(0, 0);
ctx.lineTo(app3Mark.width, 0);
ctx.lineTo(app3Mark.width, app3Mark.height);
ctx.lineTo(0, app3Mark.height);
ctx.fillStyle = '#0cc';
ctx.fill();

蒙版圖片

drawImage() 使用這個方法來繪製一個圖片,需要等圖片onload之後再使用
效果查看-傳送門

ctx = app4Mark.getContext('2d');
app4Mark.width = 300;
app4Mark.height = 100;
var imageFill = new Image();
imageFill.src = 'https://www.lilnong.top/static/img/defaultmatch.png'
app4MarkCtx = ctx;
imageFill.onload = function(){
  app4MarkCtx.drawImage(this,0,0,300,100)
}

刮獎筆觸自定義(重要)

  1. clearRect 方案 效果傳送門
    context.clearRect(x, y, width, height); 將畫布某一塊矩形區域內容清除
    使用起來還是蠻簡單的,糾正一下中心點即可。但是只能是矩形,看上去鋸齒也很嚴重
  2. globalCompositeOperation 方案 效果傳送門
    設置繪製時,圖形的混合模式。傳送門-canvas api其實好多我都沒用過,太難了,PS中的我不怎麼會用
    destination-* 區別是動作的主體是新內容還是原內容source-* 系列是新內容,而destination-*系列動作主體是原內容。

    1. source-over 默認值,表現爲覆蓋。純視覺覆蓋。
    2. source-in 表現爲在原內容上繪製,顯示重疊部分,透明度疊加。
    3. source-outsource-in對應,表現爲減去原內容。
    4. source-atop 表現爲在原內容上繪製,使用原內容透明度。
    5. destination-over 表現爲原內容在上方,新內容在下方繪製。
    6. destination-in 同理,表現爲透明度疊加,顯示重疊部分。
    7. destination-out 同理,隱藏原內容和新內容重疊的部分。
    8. destination-atop 原內容只顯示和新內容重疊的部分,同時新內容在下方顯示
    9. lighter 自然光混合效果
    10. copy 只顯示新內容
    11. xor 重疊區域爲透明
    12. multiply 正片疊底。頂層的像素與底層的對應像素相乘。結果是一幅更黑暗的圖畫
    13. screen 濾色。像素反轉,相乘,然後再反轉。最終得到更淡的圖形(和 multiply 相反)
    14. overlay 疊加。 multiply 和 screen 組合效果。基礎圖層上暗的部分更暗,亮的部分更亮
    15. darken 變暗。保留原內容和新內容中最暗的像素
    16. lighten 變亮。保留原內容和新內容中最亮的像素
    17. color-dodge 顏色減淡。底部圖層色值除以頂部圖層的反相色值
    18. color-burn 顏色加深。底部圖層的色值除以頂部圖層色值,得到的結果再反相
    19. hard-light 強光。類似 overlay,是 multiply 和 screen 組合效果。只不過底層和頂層位置交換下
    20. soft-light 柔光。hard-light 的柔和版本。純黑色或白色不會生成爲純黑色或白色。
    21. difference 差異。頂層色值減去底層色值的絕對值。如果都是白色,則最後是黑色,因爲值爲0;什麼時候是白色呢,例如RGB(255,0,0)和RGB(0,255,255),色值相減後絕對值是RGB(255,255,255)。
    22. exclusion 排除。類似difference,不過對比度較低。
    23. hue 色調。最終的顏色保留底層的亮度和色度,同時採用頂層的色調。
    24. saturation 飽和度。最終的顏色保留底層的亮度和色調,同時採用頂層的色度。
    25. color 色值。最終的顏色保留底層的亮度,同時採用頂層的色調和色度。
    26. luminosity 亮度。最終的顏色保留底層的色調和色度,同時採用頂層的亮度。

軌道生成器

先來看我們刪除的代碼。其實依靠的就是xy的座標值。那麼我們抽一個clear的方法,傳遞座標。把每次單擊的點都記錄一下,然後循環把點推進去不就是自動的了嗎?

app6Mark.addEventListener('mousedown', function(e){
  app6MarkMouseDownTag = 1
  app6MarkCtx.fillText('lilnong.top', e.offsetX - 50, e.offsetY+10);
})

刮開開面積佔比

  1. getImageData 返回一個ImageData對象,其中包含Canvas畫布部分或完整的像素點信息
    四個值標識一個像素點信息分別爲 (r,g,b,a)。這裏我們只判斷透明通道即可
  2. 之後我們統計 通道透明數/總循環數 就是刮開面積佔比
        var imageData = ctx.getImageData(0, 0, 300, 200).data;
          var eraseTotal = 0;
          var step = 1 * 4;//步長精度
          for (var i = 3, len = imageData.length; i < len; i += step) {
            if (imageData[i] === 0) {
                eraseTotal++;
            }
          }
          eraseRate.innerText = eraseTotal/(len/step)

微信公衆號:前端linong

clipboard.png

總結

  1. 這個可以考慮不兼容canvas的情況,單擊時直接將上面的層透明掉。
  2. mousemove 要注意節流
  3. 使用透明圖片自定義筆觸還是蠻有意思的。
  4. 好了,今天就到這裏吧。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章