簡單的canvas翻角效果

因爲工做需求 , 須要寫一個翻角效果;
翻角效果圖css

demo連接canvas

右上角須要從無的狀態撕開一個標記 , 且有動畫過程 , 上圖是實現的效果圖 , 不是gifapi

對這個翻角效果的難點在於沒有翻開的時候露出的是dom下面的內容 , 實現角度來講 純dom + css動畫的設計方案並無相出一個好的對策 ; 因而撿起了很久以前學的入門級別的canvas;dom

下面說一下實現思路:函數

  1. 動畫拆分 :
    將此動畫分解成兩部分 , 一部分是翻頁出現的黑色三角區域 , 另外一個是露出的橘色展現內容
    對於橘色的展現內容區域相對好一些 , 由於是一個規則圖形 , 而黑色區域相對較難;工具

先從基礎canvas使用方法提及 :佈局

<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對象 ,動畫

var canvas = document.querySelector('.myCanvas'); //獲取canvas對應dom
var ctx = canvas.getContext('2d'); //此方法較爲基礎 , 意爲獲取canvas繪畫2d內容的工具(上下文)
var cw = 100; //分辨率 , 其實直接從dom上獲取可能更好些
var ch = 100; //分辨率 , 其實直接從dom上獲取可能更好些

ctx這個繪畫上下文在這個教程中起到的做用相當重要 ; 它提供了很是強大的api , 好比用於畫線 , 填充 , 寫文字等 , 這樣看來理解爲畫筆會更爲簡明一些;spa

此處效果須要用到的api以下 ( 不作詳細解釋 , 可w3c自行查詢 );

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. 左上角向右下的半弧 ╮

  2. 而後是豎直向下的豎線 |

  3. 而後是向右的半圓 ╰

  4. 再而後是向右的橫線

  5. 接着仍是向右下的半弧 ╮

  6. 最後是將線鏈接會起點

因而第一步 咱們要先將畫筆移動到 起始位置

ctx.moveTo(50,0);

而後

ctx.quadraticCurveTo(55 , 5 , 55 , 25); // 能夠理解爲從(50,0)這個點劃線到(55,25)這個點 , 中間會受到(55,5)這個點將直線想磁鐵同樣"吸"成曲線;

因而第一個向右下的半弧完成 , 此時canvas上沒有任何繪製內容 , 由於尚未執行過繪製方法例如stroke或fill,

接下來直線向下就是簡單的移動

ctx.lineTo(55 , 40);

這個時候咱們接下來應該畫向右的半圓 , 這個時候再用貝塞爾曲線繪製 實在有些不太合適 , 由於從圖上來看 , 這裏徹底是1/4的圓 , 因此要使用canvas提供的畫圓的api

ctx.arc(60 , 40 , 5 , Math.PI , Math.PI / 2 , true);

上述畫圓的代碼意爲 : 以(60,40)點爲圓心 , 5爲半徑 , 逆時針從 180度繪製到90度 , 180度就是圓心的水平向左 到達點(55,40) , 與上一步鏈接上 , 而後又由於屏幕向下爲正 , 90度在圓心正下方 , 因此繪製出此半圓

因而按照相同的步驟 水平向右

ctx.lineTo(75 , 45);

而後再次使用貝塞爾曲線用第一步的思路畫出向右下的弧;

ctx.quadraticCurveTo( 95 , 45 , 100 , 50 );

同理 上述貝塞爾曲線能夠理解爲一條從( 75 , 45 ) 到 ( 100 , 50 )的線被 ( 95 , 45 )"吸"成曲線

最後連接起點 , 閉合繪畫區域

ctx.lineTo(50 , 0);

這個時候黑色區域的翻頁就畫完了 , 而後此時開始填充顏色 ;

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 ; 建立高光效果;
而後填充:

ctx.fillStyle = gradient;
ctx.fill();

因而翻頁效果的一半就算完成了。

至此 , 我要說一點我領悟的canvas的繪畫"套路";

對於上述教程中 , 有一步咱們使用了一個詞叫作 閉合 , 閉合的概念在canvas中是真是存在的 , 對於fill方法來講 填充的區間是有一個空間尺寸才能夠的 , 好比咱們繪畫的這個黑色的三角形 , 加入咱們最後沒有將終點與起點相鏈接 , 一樣canvas會自動幫咱們連接最後一筆繪畫的位置到起點 , 強制行程閉合空間 , 而這樣咱們想再多畫幾個新的閉合空間就麻煩了 , 因此canvas提供了以下api 新建閉合路徑:

ctx.beginPath(); //新建路徑
ctx.closePath(); //閉合路徑

因此對於咱們接下來要繪製右上角橘色區域來講 , 咱們在繪製黑色區域以前首先要作的是

ctx.beginPath();
...

而後在fill以前 咱們應該

ctx.closePath();

也就是說beginPath 到 closePath之間標識着咱們本身的一個完整的繪畫階段.

那麼接下來繪製右上角的橘色區域就簡單不少了:

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簡單的文本繪製 , 代碼以下:

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的畫筆作出了很是大的修改

好比咱們修改了旋轉角度以及畫布圓點 , 這種操做或許只在咱們須要繪製傾斜的new 的時候須要 , 後期可能就不須要使用了 ,

還好canvas的畫筆是存在"狀態"的, 經過ctx.save();能夠保存當前畫筆的狀態 , 經過ctx.restore();能夠恢復到上次畫筆保存的狀態.

因而我我的理解到 , 在開發canvas動畫時 , 一個較好的習慣就是 , 在beginPath以前先ctx.save();保存畫筆狀態 , 在closePath後ctx.restore();恢復以前的畫筆狀態 , 這樣咱們的每個繪製階段對於畫筆的修改都將是不會有影響的.( 我的經驗 )

ctx.globalCompositeOperation = 'source-atop'; //canvas層疊模式

代碼中這部分是指 咱們繪製的文字new 與 橘色三角形區域的重疊關係 , 此方法取值較多 , 此處不作過多介紹 , source-atop值可使重疊區域保留 , 新繪製的內容在重疊區域之外的部分消失 , 以此達到new在裏面的效果

到這裏咱們就開發好了翻角效果的徹底展現的狀態 , 那麼如何讓這個區域動起來呢?

此處須要使用h5提供的用於刷幀的函數 requestAnimationFrame ;

此方法可簡單理解爲 16毫秒的定時器 , 可是厲害的是能夠再各個環境中自動匹配到可達到的相對順暢的幀率 , 實際並非定時器哈~

咱們須要在這個循環執行的函數中 , 將上述的繪製內容重複繪製 , 例如 :

function draw(){
    drawMethod(); //繪製三角等內容
    window.requestAnimationFrame(function(){
        draw();
    })
}
function drawMethod(){
    //...
}

這樣咱們就能夠達到刷幀的效果了 , 因而接着咱們要作的就是控制繪製時各個數值的參數.

好比咱們是以 (50,0)爲起點 , ( 100 , 50 )爲終點這樣的兩個移動點爲繪製標記的 , 若是咱們將兩個點進行存儲 , 而且每次執行drawMethod的時候更新點的位置 , 而後清空canvas ,再繪製新的點 那麼就能夠達到canvas動起來的目的了;

實際效果連接在這裏

在上面的demo連接中 , 本身定義了一個速度與加速度的關係 , 好比每次繪製一次canvas後 , 將存儲的點座標進行增長一個speed值 , 而後speed值也增長 , 這樣speed對應的概念就是速度 , 而speed的增長值對應的就是加速度. 因此就呈現了一種加速運動的狀態;

以上內容純屬我的理解內容 , 若果有哪裏理解錯了 歡迎各位大大指點 , 另demo連接失效可私信.

相關文章
相關標籤/搜索