代碼文件git
每週一點canvas動畫系列文章目前已經更新了12篇,今天給你們發個福利。咱們使用canvas來製做一個小的效果。這個小效果是我從codePen上看到的,我對其作了些修改加強,添加了一些新的功能。UI界面就以下圖中看到的樣子。咱們要實現的效果就如我在圖中操做的那樣,在輸入框中輸入文字(無論中文,仍是英文,仍是各類表情也好)均可以在canvas畫布中經過衆多的粒子組成,在側邊欄中還有不少控件,它們能夠控制粒子的各方面屬性,以此來造成各類不一樣的絢麗效果。github
UI界面的組成很簡單,主要有側邊欄控制檯
和canvas畫布
兩部分組成canvas
<canvas id="canvas"></canvas> <div id="control">...</div>
在側邊欄中有一系列的控制條,他們控制着粒子的各類屬性,包括文字輸入框:數組
<input type="text" id="message" value="hahaha" onchange="change()">
控制條bash
<input type="range" id="gra" value="0" min="-1" max="1" step="0.1" onchange="changeV()">
粒子選擇ide
<p style="margin: 0 0 20px 10px;"> <span id="ball">圓形</span> <span id="rect">方塊</span> </p>
在這我就不一一列舉了!CSS樣式文件主要是對UI界面的佈局和樣式處理,具體請查看代碼文件。函數
當點擊菜單按鈕時,側邊欄滑出,再次點擊縮回。採用classList
來切換滑出和縮回的class,在sidebar.js
中佈局
var btn = document.getElementById("btn"); var control = document.getElementById("control"); btn.addEventListener('click', function(e){ control.classList.toggle("slide"); }, false)
這樣咱們的基礎界面就搭建完成。下面就到了咱們這個動畫的核心思想字體
首先,咱們在咱們的index.js
文件中定義咱們須要的一些變量動畫
var canvas = document.getElementById('canvas'); context = canvas.getContext('2d'); W = canvas.width = window.innerWidth; H = canvas.height = window.innerHeight; gridY = 7, gridX = 7; type = "ball"; var message = document.getElementById('message'), gravity = document.getElementById('gra'), duration = document.getElementById('dur'), speed = document.getElementById('speed'), radius = document.getElementById('rad'), resolution = document.getElementById('res'); graVal = parseFloat(gravity.value); durVal = parseFloat(duration.value); spdVal = parseFloat(speed.value); radVal = parseFloat(radius.value); resVal = parseFloat(resolution.value); colors = [ '#f44336', '#e91e63', '#9c27b0', '#673ab7', '#3f51b5', '#2196f3', '#03a9f4', '#00bcd4', '#009688', '#4CAF50', '#8BC34A', '#CDDC39', '#FFEB3B', '#FFC107', '#FF9800', '#FF5722' ]; function change(){ 。。。 } function changeV() { 。。。 } (function drawFrame(){ window.requestAnimationFrame(drawFrame, canvas); context.clearRect(0, 0, W, H); 。。。 }())
注意這裏的的context, W, H等咱們定義的是全局變量。
這裏有兩個變量可能你不知道他是幹什麼的gridX
和gridY
,以後我會詳細介紹。
這個文件是咱們整個動畫效果的核心,只有理解了它,你才能瞭解這個效果的實現原理。由於不是很長,這裏我把文件所有列出:
function Shape(x, y, texte){ this.x = x; this.y = y; this.size = 200; this.text = texte; this.placement = []; } Shape.prototype.getValue = function(){ context.textAlign = "center"; context.font = this.size + "px arial"; context.fillText(this.text, this.x, this.y); var idata = context.getImageData(0, 0, W, H); var buffer32 = new Uint32Array(idata.data.buffer); for(var j=0; j < H; j += gridY){ for(var i=0 ; i < W; i += gridX){ if(buffer32[j * W + i]){ var particle = new Particle(i, j, type); this.placement.push(particle); } } } context.clearRect(0, 0, W, H); }
接下來,我就詳細的解一下該文件的代碼!首先咱們新建了一個構造函數Shape
,該構造函數有3個參數:
x , y: 要繪製的文字的位置
texte: 要繪製的文字
咱們設置了文字的大小爲200px, 而且定義了一個屬性placement
,這個屬性是一個數組。so wired!它是幹什麼的呢?別急,繼續往下走。
接下來咱們在原型對象上定義了一個方法getValue
.這幾行代碼:
context.textAlign = "center"; context.font = this.size + "px arial"; context.fillText(this.text, this.x, this.y);
很簡單,設置文字對其方式,字體大小,而且經過fillText()
在canvas上繪製文字。若是此時在控制欄中輸入文字,在index.js中新建一個shape對象,並把文字傳入,再調用getValue方法就能夠看到你已經把輸入的文字繪製到了canvas中,固然這時候忽略下面的代碼啊!
回到正題,接下來咱們調用了context.getImageData()
,它是canvas繪製圖片的API接口,經過它咱們能夠獲得須要繪製的圖片的數據內容。也許你會問,它是用來獲取canvas上繪製的圖片的數據內容,但是咱們這並無繪製圖片啊?
其實,該方法的做用並不僅是侷限於獲取圖片的內容。只要canvas上有內容,不論是繪製的文字,仍是圖形它都能獲取,甚至是空白的canvas它也能獲取,只不過此時的數據都是0。
那麼經過該API獲取的內容是什麼樣的呢?首先,咱們嘗試獲取一張空canvas的內容
var canvas = document.getElementById('canvas'), context = canvas.getContext('2d'); var imgData = context.getImageData(0, 0, canvas.width, canvas.height); console.log(imgData);
結果以下:
咱們看到,這裏的imgData
是一個對象,該對象的第一個屬性就是data
,是一個8位無符號整數的類型化數組Uint8ClampedArray。打開data看看都有什麼,這裏我隨便打開其中的一個看看。
由於canvas爲空,因此數據都爲零。如今咱們換一下,在canvas中畫一個藍色的矩形。
context.fillStyle = "#49f"; context.fillRect(0, 0, canvas.width,canvas.height); var imgData = context.getImageData(0, 0, canvas.width, canvas.height); console.log(imgData);
看看,咱們的的數據是否是不爲零了!OK! 原理我在這解釋的都差很少了,咱們回到正題,看下一行代碼。
var buffer32 = new Uint32Array(idata.data.buffer);
idata.data.buffer
,在這裏咱們調用Uint8ClampedArray對象的buffer屬性,獲取此數組引用的 ArrayBuffer
。而後將它傳入Uint32Array對象(32位無符號整數值的類型化數組)。此時,咱們看看上面繪製藍色矩形的數據變成什麼樣了,首先數組長度變爲[160000],恰好是上面的8位的四分之一
內容變爲
至關於咱們把一張圖片的分辨率縮小了,之前有640000個數據, 如今只有160000個數據。固然,在本文中數據的內容不是咱們所關心的。咱們所關心的是在哪有數據。
因此,接下來,就是在有數據的地方,放上咱們的粒子
for(var j=0; j < H; j += gridY){ for(var i=0 ; i < W; i += gridX){ if(buffer32[j * W + i]){ var particle = new Particle(i, j, type); this.placement.push(particle); } } } context.clearRect(0, 0, W, H); //清除所畫內容
咱們遍歷整個canvas, 經過buffer32[j * W + i]
來判斷這個位置的數據是否爲空,若是不爲空,那麼,在這繪製一個粒子。粒子的位置爲(i,j)咱們做爲參數傳入。固然你也能夠在數據爲空的地方放上粒子,看看會出現什麼樣的效果。
這裏用到了gridX和gridY,它們的做用是來判斷每一個多少個距離取一次數據。學過信號抽樣的同窗應該很好理解,若是你間隔大,抽樣獲得的數據就小,反之若是你設定的間隔小,那麼抽到的數據就多。在咱們的效果中,咱們繪製的是文字,一樣的道理,間隔小獲取的數據就多,粒子就多,組成的文字就完整。間隔大獲取的就少。那麼粒子組成的文字就不那麼完整,這兩個變量的值,經過分辨率控件來綁定。思來想去仍是上張圖吧!
該文件就是咱們的粒子文件,我就不作過多解釋了,不懂得歡迎提問。
粒子切換的代碼在slide.js
中,很簡單,就是綁定了兩個事件。
/粒子切換 var ball = document.getElementById("ball"); var rect = document.getElementById("rect"); function chose(particleName){ particleName.addEventListener('click', function(e){ this.style.backgroundColor = "orange"; (particleName == ball ? rect : ball).style.backgroundColor = "rgba(0, 0, 0, 0)"; type = (type === "ball" ? "rect" : "ball"); change(); }, false) } chose(ball); chose(rect);
Ok!這個效果的關鍵點,基本都已經講完了,有興趣本身看看吧!!!