案例解析
這次要分析的是在法國的設計師 Louis Ansa 的作品集網頁,在 Louis Ansa 的網頁中開始的載入與關於頁面都有出現的遮罩效果,讓老闆帶著大家來看看這是如何實現的吧!
分析思路
這一頁的動畫主要有三個部分組成:
- 球型遮罩:在圓形裡面的文字會呈現不同的背景色與內文顏色
- 移動的球體:兩個球體的圓心沿著畫面的中心做圓周運動
- 變動的球體形狀:球體的邊界呈現不規則波動
首先針對1.的部分,要改變特定區域內的顏色效果,依據需要達成的效果不同,我們可以直接選擇改變區域內的顏色內容;或是使用兩層圖片,再將區域內不需要的上層元素移除掉。
如果只需要處理顏色的變換,沒有非常複雜的動畫,可以使用 css 的 mix-blend-mode
屬性來實現,mix-blend-mode
提供了saturation
、hue
、difference
等條件直接處理顏色。但是考量到後續如果需要做出多個物件、比較複雜的變形以及效能問題,從 Canvas 下手就會更靈活。
實作
1. 創建兩層 canvas
首先先使用兩層的canvas,並做出完全相同的長寬、內文、行高與字體大小的圖片:
canvas.draw = function() {
ctx = this.ctx;
requestAnimationFrame(() => {
this.draw(ctx);
});
ctx.fillStyle = this.backgroundColor;
ctx.fillRect(0, 0, window.innerWidth, window.innerHeight);
ctx.beginPath();
ctx.fillStyle = this.fillStyle;
ctx.lineWidth = 5;
ctx.font = "bold 100px Montserrat";
// 這邊先給出一個 text 的變數是用來測量行高,以便換行書寫、定位圖形中心
text = "Monoame";
var textWidth = ctx.measureText(text).width;
var textHeight = parseInt(ctx.font.match(/\d+/), 10);
ctx.fillText("Monoame", this.cx - textWidth / 2, this.cy - textHeight / 2);
ctx.fillText("Studio", this.cx - textWidth / 2, this.cy + textHeight / 2);
};
2. 設定遮罩圖形與透視的效果
這個步驟是整個案例的核心,我們使用到 canvas 的 globalCompositeOperation
屬性,globalCompositeOperation
可以指定 canvas 針對當前繪製圖形與背景的交互效果。
舉例來說,預設的值是 source-over
即是直接覆蓋過背景的圖層,畫上新的路徑;而我們使用到的是 destination-out
,可以將新舊圖形重疊的區域設定為透明,只在沒有重疊的的部分畫出圖形。
左圖片中,藍色方形是背景的原始圖形,紅色圓形是新繪製的圖形,我們可以比較一下兩種形式對於背景圖形的影響。關於 globalCompositeOperation
的更多選項與說明可以參考 MDN 的 Canvas 教學。
實作中先在上層的 canvas 中繪製出作為遮罩的圓形,並在繪製圓形之前將 ctx.globalCompositeOperation
設定為"destination-out"
,如此一來,在這個圓形的範圍內,原本被上層遮住的黑底紅字的底層就會顯示出來。最後也別忘了,在繪製完遮罩的部分之後將參數設定回原本的 "source-over"
,這樣第一個部分就大功告成了!
ctx.globalCompositeOperation = "destination-out";
ctx.arc(mousePos.x, mousePos.y, this.r, 0, Math.PI * 2, false);
ctx.fill();
ctx.globalCompositeOperation = "source-over";
3. 創建圍繞著圖片中心轉動的動態效果
有了圓形遮罩之後,我們要如何讓他繞著圖形的中心點做圓周運動呢?
這裡我們可以活用 canvas 的 translate
與 rotate
方法,在每一次渲染的時候,先使用rotate
將畫布旋轉 1 度,之後再使用translate
移動到遮罩的圓心位置,這樣就可以創造出像是衛星環繞的圓周運動效果了。這裡有個小地方要注意,就是我們在移動圍繞的中心點跟旋轉畫布之前,要先用 ctx.save()
將目前的畫布資訊存起來,等到繪製完成之後,再使用 ctx.restore()
回復到旋轉跟移動之前的位置,才不會影響到其他部分的圖形繪製喔。
ctx.clearRect(0, 0, canvas.width, canvas.height);
angle = (angle+1) % 360;
ctx.save();
ctx.translate(centerX, centerY);
ctx.rotate(2* Math.PI* angle/360);
ctx.beginPath();
ctx.arc(0, 0, dotR, 0, 2 * Math.PI, false);
ctx.fillStyle = "#000000";
ctx.closePath();
ctx.fill();
ctx.translate(100, 0);
ctx.fillStyle = "#FF0000";
ctx.beginPath();
ctx.arc(0, 0, circleR, 0, 2 * Math.PI, false);
ctx.closePath();
ctx.fill();
ctx.restore();
旋轉的部分可以參考這個範例:
See the Pen Canvas rotate around point by Ankycheng (@ankycheng) on CodePen.
登愣,老闆上菜啦!
結合以上的步驟,最後的成品就是這樣:
See the Pen canvas mask effect by Ankycheng (@ankycheng) on CodePen.
這個案例使用遮罩加上簡單的動態實現靈活變動的效果,關於遮罩的應用還有很多,像是這個案例就使用了一樣的 canvas 特性做出類似刮刮卡的效果:https://codepen.io/dudleystorey/pen/yJQxLX。而形狀的部分,除了使用單純的圓形,我們也可以模擬原版中抖動的邊框,或是不同形狀的靈活變化。
有什麼有趣的想法都歡迎在留言告訴老闆,或是你覺得這樣的特性還有哪些可以靈活運用的地方呢?如果這篇文章超過 15 個留言的話,老闆將會進一步解密如何做出原本中華麗的動態球體!老闆的網頁實驗室,我們下回見囉!
參考資料:
CSS: mix-blend-mode
Canvas: globalCompositeOperation
課程推薦
老闆在Hahow好學校開了與互動網頁有關的兩門課,其中,動畫互動網頁程式入門(HTML/CSS/JS)以簡單例子帶你入門網站的基礎架構及開發,用素材刻出簡單有趣又美觀的網頁和動畫,享受做出獨一無二的網頁所帶來的成就感,在職場上與設計師和工程師合作無間。
打好基本的互動網頁基礎之後,可以進階動畫互動網頁特效入門(JS/CANVAS),紮實掌握JavaScript 程式與動畫基礎以及進階互動,整合應用掌控資料與顯示的Vue.js前端框架,完成具有設計感的互動網站。期待在課程裡見到你!