右上角需要從無的狀態撕開一個標記 , 且有動畫過程 , 上圖是實現的效果圖 , 不是gif
對這個翻角效果的難點在於沒有翻開的時候露出的是dom下面的內容 , 實現角度來說 純dom + css動畫的設計方案並沒有相出一個好的對策 ; 於是撿起了好久之前學的入門級別的canvas;
下面說一下實現思路:
動畫拆分 :
將此動畫分解成兩部分 , 一部分是翻頁出現的黑色三角區域 , 另一個是露出的橘色展示內容
對於橘色的展示內容區域相對好一些 , 因爲是一個規則圖形 , 而黑色區域相對較難;
先從基礎canvas使用方法說起 :
1 2 3 | <div class="container"> <canvas class="myCanvas" width="100" height="100"></canvas> </div> |
佈局如上 , 這裏要說一點踩過的坑是 , canvas必須要設置上width 與 height , 此處並非爲css中的width與height;而是寫在dom上的屬性 ; 因爲dom上的width與height標識了canvas的分辨率(個人理解); 所以此canvas畫布分辨率爲100*100 , 而展示尺寸是可以通過css控制;
js中首先要做的是獲取canvas對象 ,
1 2 3 4 | var canvas = document.querySelector('.myCanvas'); //獲取canvas對應dom var ctx = canvas.getContext('2d'); //此方法較爲基礎 , 意爲獲取canvas繪畫2d內容的工具(上下文) var cw = 100; //分辨率 , 其實直接從dom上獲取可能更好些 var ch = 100; //分辨率 , 其實直接從dom上獲取可能更好些 |
ctx這個繪畫上下文在這個教程中起到的作用至關重要 ; 它提供了非常強大的api , 比如用於畫線 , 填充 , 寫文字等 , 這樣看來理解爲畫筆會更爲簡明一些;
此處效果需要用到的api如下 ( 不做詳細解釋 , 可w3c自行查詢 );
1 2 3 4 5 6 7 8 9 10 11 12 | ctx.save() //保存上下文狀態 (比如畫筆尺寸 顏色 旋轉角度) ctx.restore() //返回上次保存的上下文狀態 ctx.moveTo(x,y) //上下文移動到具體位置 ctx.lineTo(x,y) //上下文以劃線的形式移動到某位置 ctx.stroke() // 畫線動作 ctx.quadraticCurveTo() //上下文(畫筆)按貝塞爾曲線移動(簡單理解爲可控的曲線即可) ctx.arc() //畫圓 ctx.beginPath() //開啓新的畫筆路徑 ctx.closePath() //關閉當前畫筆路徑 ctx.createLinearGradient() //創建canvas漸變對象 ctx.fill() //對閉合區域進行填充 ctx.globalCompositeOperation //畫筆的重疊模式 |
可能方法列舉的不夠詳盡 , 見諒.
首先是繪製黑色翻出的部分 , 圖形分解爲如下幾部分(請根據上圖腦補)
左上角向右下的半弧 ╮
然後是豎直向下的豎線 |
然後是向右的半圓 ╰
再然後是向右的橫線
接着還是向右下的半弧 ╮
最後是將線連接會起點
於是第一步 我們要先將畫筆移動到 起始位置
1 | ctx.moveTo(50,0); |
然後
1 | ctx.quadraticCurveTo(55 , 5 , 55 , 25); // 可以理解爲從(50,0)這個點劃線到(55,25)這個點 , 中間會受到(55,5)這個點將直線想磁鐵一樣"吸"成曲線; |
於是第一個向右下的半弧完成 , 此時canvas上沒有任何繪製內容 , 因爲還沒有執行過繪製方法例如stroke或fill,
接下來直線向下就是簡單的移動
1 | ctx.lineTo(55 , 40); |
這個時候我們接下來應該畫向右的半圓 , 這個時候再用貝塞爾曲線繪製 實在有些不太合適 , 因爲從圖上來看 , 這裏完全是1/4的圓 , 所以要使用canvas提供的畫圓的api
1 | ctx.arc(60 , 40 , 5 , Math.PI , Math.PI / 2 , true); |
上述畫圓的代碼意爲 : 以(60,40)點爲圓心 , 5爲半徑 , 逆時針從 180度繪製到90度 , 180度就是圓心的水平向左 到達點(55,40) , 與上一步連接上 , 然後又因爲屏幕向下爲正 , 90度在圓心正下方 , 所以繪製出此半圓
於是按照相同的步驟 水平向右
1 | ctx.lineTo(75 , 45); |
然後再次使用貝塞爾曲線用第一步的思路畫出向右下的弧;
1 | ctx.quadraticCurveTo( 95 , 45 , 100 , 50 ); |
同理 上述貝塞爾曲線可以理解爲一條從( 75 , 45 ) 到 ( 100 , 50 )的線被 ( 95 , 45 )”吸”成曲線
最後鏈接起點 , 閉合繪畫區域
1 | ctx.lineTo(50 , 0); |
這個時候黑色區域的翻頁就畫完了 , 然後此時開始填充顏色 ;
1 2 3 4 | var gradient = ctx.createLinearGradient(50 , 50 , 75 , 75); gradient.addColorStop(0 , '#ccc'); gradient.addColorStop(0.7 , '#111'); gradient.addColorStop(1 , '#000'); |
我們通過上述代碼創建一個 從( 50 , 50 )點到(75 , 75)點的線性漸變 , 顏色從 #ccc 到 #111 到 #000 ; 創建高光效果;
然後填充:
1 2 | ctx.fillStyle = gradient; ctx.fill(); |
於是翻頁效果的一半就算完成了。
至此 , 我要說一點我領悟的canvas的繪畫”套路”;
對於上述教程中 , 有一步我們使用了一個詞叫做 閉合 , 閉合的概念在canvas中是真是存在的 , 對於fill方法來說 填充的區間是有一個空間尺寸纔可以的 , 比如我們繪畫的這個黑色的三角形 , 加入我們最後沒有將終點與起點相連接 , 同樣canvas會自動幫我們鏈接最後一筆繪畫的位置到起點 , 強制行程閉合空間 , 而這樣我們想再多畫幾個新的閉合空間就麻煩了 , 所以canvas提供瞭如下api 新建閉合路徑:
1 2 | ctx.beginPath(); //新建路徑 ctx.closePath(); //閉合路徑 |
所以對於我們接下來要繪製右上角橘色區域來說 , 我們在繪製黑色區域之前首先要做的是
1 2 | ctx.beginPath(); ... |
然後在fill之前 我們應該
1 | ctx.closePath(); |
也就是說beginPath 到 closePath之間標識着我們自己的一個完整的繪畫階段.
那麼接下來繪製右上角的橘色區域就簡單很多了:
1 2 3 4 5 6 7 8 | ctx.beginPath(); ctx.moveTo(50,0); ctx.lineTo(100,50); ctx.lineTo(100,0); ctx.lineTo(50,0); ctx.closePath(); ctx.fillStyle = '#ff6600'; ctx.fill(); |
於是右上角的橘色區域我們就繪製完成了;
文字繪製
接下來繪製”new” , 實際上是使用canvas簡單的文本繪製 , 代碼如下:
1 2 3 4 5 6 7 8 9 10 | var deg = Math.PI / 180; ctx.globalCompositeOperation = 'source-atop'; //canvas層疊模式 ctx.beginPath(); ctx.font = '14px Arial'; //設置字體大小 字體 ctx.textAlign = 'center'; // 字體對齊方式 ctx.translate(78 , 22); // 移動canvas畫布圓點 ctx.rotate(45 * deg); // 旋轉畫布 ctx.fillStyle = '#fff'; // 設置文字顏色 ctx.fillText('NEW' , 0 , 0); //文字繪製動作 ctx.closePath(); |
對於上述代碼中 , 文字的相關api是屬於沒有難度的 , 只是設置而已 , 需要理解的部分在於 translate和rotate,
這兩個方法中 translate的意思爲移動canvas畫布的( 0 , 0 )點到 (78,22),然後旋轉45度, 再將文字渲染在原點 , 實際就是 ( 78 , 22 ) 這個點上, 此時我們對canvas的畫筆做出了非常大的修改