《每週一點canvas動畫》是一個系列文章,本文並不對canvas的API作過多的介紹,我默認你已經瞭解基本的canvas繪圖API,並在此告訴你如何使用簡單的數學與物理知識建立至關酷炫的動畫。一說到物理和數學知識各位騷年們是否是感受蛋疼(原諒我說髒話了),不過我要告訴你,咱們用到的數學和物理知識真的很簡單,一點都不可怕。html
canvas做爲H5中最爲重要的新增特性,使開發者能夠用它來創做各類使人驚歎的做品。但開發者最關心的問題確定是瀏覽器的支持狀況(圖片來自張鑫旭博客)!git
本系列文章主要介紹Canvas 2D動畫原理,以及簡單的如何在2D平面模仿3D效果的方法,中間可能會穿插一些與其餘技術相結合的DEMO和項目。首先,咱們創做動畫的基本文檔結構以下。github
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>canvas動畫序</title> </head> <body> <canvas id="canvas" width='500' height="500"> <P>you browser not support canvas!</P> </canvas> <script> window.onload = function(){ //咱們的代碼 } </script> </body> </html>
在此咱們須要明白動畫的基本概念:
動畫實際上是由不一樣的靜態畫面組成,每一幅靜態畫面咱們叫作一幀(frame),當衆多的靜態畫面按照必定的規則快速運動時,咱們的眼睛就會欺騙咱們的大腦,從而造成物體運動的假象。在這裏先給你們展現用Canvas創做的兩個酷炫動畫,看看它是否夠簡潔,夠酷炫!web
第一個動畫是一個用Canvas作的百分比加載動畫(根據讀者的建議,已經對錯誤的代碼作了修改)。codePen地址canvas
var canvas = document.getElementById('canvas'), //獲取canvas元素 context = canvas.getContext('2d'), //獲取畫圖環境,指明爲2d centerX = canvas.width/2, //Canvas中心點x軸座標 centerY = canvas.height/2, //Canvas中心點y軸座標 rad = Math.PI*2/100, //將360度分紅100份,那麼每一份就是rad度 speed = 0.1; //加載的快慢就靠它了 //繪製藍色外圈 function blueCircle(n){ context.save(); context.beginPath(); context.strokeStyle = "#49f"; context.lineWidth = 12; context.arc(centerX, centerY, 100 , -Math.PI/2, -Math.PI/2 + n*rad, false); context.stroke(); context.restore(); } //繪製白色外圈 function whiteCircle(){ context.save(); context.beginPath(); context.strokeStyle = "#A5DEF1"; context.lineWidth = 12; context.arc(centerX, centerY, 100 , 0, Math.PI*2, false); context.stroke(); context.closePath(); context.restore(); } //百分比文字繪製 function text(n){ context.save(); context.fillStyle = "#F47C7C"; context.font = "40px Arial"; context.textAlign = "center"; context.textBaseline = "middle"; context.fillText(n.toFixed(0)+"%", centerX, centerY); context.restore(); } //動畫循環 (function drawFrame(){ window.requestAnimationFrame(drawFrame, canvas); context.clearRect(0, 0, canvas.width, canvas.height); whiteCircle(); text(speed); blueCircle(speed); if(speed > 100) speed = 0; speed += 0.1; }()); </script> </body> </html>
在上面的代碼段中,咱們經過一個當即執行函數來執行咱們的動畫循環,並在內部經過數組
window.requestAnimationFrame(drawFrame, canvas);
循環調用自身,requestAnimationFrame是一個新的API,做用與setTimeInterval同樣,不一樣的是它會根據瀏覽器的刷新頻率自動調整動畫的時間間隔。在循環中咱們每次執行都會從新繪製藍色的圓弧,和白色的圓環和百分比加載的文字,因爲每次繪製的時間間隔很小,只有十幾毫秒(主要看電腦),因此咱們的肉眼是沒法清楚地分別每一幀的畫面,這樣就造成了咱們看到的動畫。該API的兼容處理以下:瀏覽器
if(!window.requestAnimationFrame){ window.requestAnimationFrame =(window.webkitRequestAnimationFrame|| window.mozRequestAnimationFrame|| window.oRequestAnimationFrame|| window.msRequestAnimationFrame|| function(callback){ return window.setTimeout(callback,1000/60); }); }
經典的黑客帝國效果,Geek們的最愛。目前,已經被我封裝成插件,可供你們方便使用。codePen地址 | 插件地址bash
具體代碼:dom
var canvas = document.querySelector('canvas'), context = canvas.getContext('2d'), w = canvas.width = window.innerWidth, h = canvas.height = window.innerHeight; //初始化 var clearColor = 'rgba(0, 0, 0, .1)', //用於繪製漸變陰影 wordColor = "#33ff33", //文字顏色 words = "0123456789qwertyuiopasdfghjklzxcvbnm,./;'\[]QWERTYUIOP{}ASDFGHJHJKL:ZXCVBBNM<>?", wordsArr = words.split(''), //將文字拆分進一個數組 font_size = 16, //字體大小 clumns = w / font_size, //文字降落的列數 drops = []; for(var i=0; i<clumns; i++){ drops[i] = 1; } function draw(){ context.save(); context.fillStyle = wordColor; context.font = font_size + "px arial"; //核心 for (var i = 0; i < drops.length; i++){ var text = wordsArr[Math.floor(Math.random() * wordsArr.length)]; context.fillText(text, i * font_size, drops[i] * font_size); if (drops[i] * font_size > h && Math.random() > 0.98){ drops[i] = 0; } drops[i]++; } context.restore(); } //動畫循環 (function drawFrame(){ window.requestAnimationFrame(drawFrame, canvas); context.fillStyle = clearColor; context.fillRect(0, 0, w, h); //注意這 draw(); }())
這段代碼有兩個比較核心的地方:
1,在初始化部分,咱們定義了一個變量clearColor = 'rgba(0, 0, 0, .1)',用於繪製陰影。其原理是:每當動畫繪製新的一幀,就在上面覆蓋一個透明度爲0.1的黑色矩形。隨着層數的疊加,文字就會被逐漸遮蓋造成了咱們看到的陰影。函數
2,在初始化的註釋處和核心模塊處。首先,設置了每一個字體的大小(font_size)。而後,用canvas的寬度除以字體的大小,就獲得了須要繪製的列數(clumns), 而後建立了一個數組drops,數組的長度爲clumns,而且每一個元素的值都爲1(drops在這有什麼用呢?繼續往下看)。在繪製部分,咱們採起的思路是一行一行的繪製,首先在循環中隨機的獲取文字,在文字繪製API部分注意這行代碼:
context.fillText(text, i * font_size, drops[i] * font_size);
咱們知道該API有三個參數,第一個是繪製的文字,第二,三是文字的座標。在X座標部分爲i * font_size
,也就是說在循環完成後每一個文字的X軸座標是(0, 16, 32,48...), 而Y座標爲drops[i] * font_size
因爲drops內元素的初始值都爲1,因此文字的Y座標爲(16, 16, 16, ...),這樣咱們就至關於先繪製了第一行的文字。那麼緊接着咱們繪製第二行只須要將drops中的元素加1便可,即(第二行的Y軸座標爲(32,32,32...)。
依次類推,咱們就繪製了滿屏的文字,經過漸變陰影咱們就能夠看到文字彷佛是向下運動的效果。爲了讓他們看上去運動的速度不一致,加上了這行代碼:
if (drops[i] * font_size > h && Math.random() > 0.98){ drops[i] = 0; }
這行代碼判斷的是當前繪製的這行文字的Y座標是否超過了canvas的高度,若是超過又從第一行開始繪製,那麼如何讓他們出現差別性呢!小祕密在Math.random() > 0.98
這,if中的兩個條件一個是判斷文字高度,另外一個是判斷一個隨機數是佛大於0.98,只有當兩個條件同時成立才能回到第一行從新繪製。因此,因爲第二個條件是隨機的,那麼差別性就天然而然的出現了!
看看,只須要這麼簡單的代碼就能寫出這麼酷炫的效果,是否是很贊!你也試試吧!不理解不要緊,這裏只是讓你看看canvas能作出多麼酷炫的效果。
下一節,咱們就正式開始咱們的Canvas動畫之旅!!!