神奇的canvas——點與線繪制的絢麗動畫效果


代碼地址如下:
http://www.demodashi.com/demo/11636.html

前言

之前在某網站上看到了一個canvas繪制的動畫效果,雖然組成的元素很簡單,只有點和線,但是視覺效果卻非常炫麗,當下就非常想自己把他實現一遍。因為工作原因這個想法擱置了一段時間,前不久忽然想起來,就抽空完成了這個demo,下面是demo的截圖,想要看動態效果的小伙伴可以戳旁邊的鏈接:canvas繪制絢麗的點線動畫效果

運行效果圖

free-dots.png

下面就簡單介紹一下完成這個demo的思路

需要掌握的基礎知識

  1. canvas繪制點與線的api
  2. 理解JavaScript中“類”

需求分析

  1. 隨機產生並向隨機方向以隨機的速度勻速移動
  2. 未點擊時,點的總數保持不變;點擊時在點擊的位置產生數個新的點
  3. 點與點之間在一定距離內有細線連接
  4. 鼠標在畫面中移動時,能夠與其他點產生互動

編寫代碼(文章末尾有源碼)

點的實現

由於在整個demo中需要使用到大量的點,所以使用一個Dot類來負責產生點的實例以及這個點的所有行為

// 聲明一個Dot對象
var Dots = function () {
    this.canvas; // canvas節點
    this.ctx; // canvas繪圖上下文
    this.x; // 橫向坐標
    this.y; // 縱向坐標
    this.r; // dot半徑
    this.sx; // 單位時間水平移動距離
    this.sy; // 單位時間縱向移動距離
};

Dot的原型鏈中需要有一下兩個方法:init() 與 update()

Dots.prototype = {
	   init: function(){...},
	   update: function(){...}
}

init()

Dots實例的初始化方法,在canvas中繪制一個點,並確定這個Dots實例移動的方向與速度(由sx與sy決定,即確定sx與sy的值)

update()

更新dot的位置,通過不斷調用其的update方法,使其產生運動的效果,並且判斷dot所處的位置是否已經超出canvas的邊界,若超出則調用其init()方法,使其重生在canvas內

如何繪制一個點?

點的本質是一個實心圓,所以繪制一個點就是繪制一個填充了顏色的圓

this.ctx.beginPath(); // 開啟繪制路徑
this.ctx.arc(this.x, this.y, this.r, 0, 2*Math.PI); // 繪制圓 參數依次為 圓的橫坐標/縱坐標/半徑/繪制圓的起始位置/繪制圓的弧度大小
this.ctx.fillStyle = "rgba(255,255,255,.8)"; // 設置填充顏色
this.ctx.fill(); // 填充顏色
this.ctx.closePath(); // 關閉繪制路徑

讓點移動起來

如果讓你實現一個動畫,你可能會想到通過定時器setTimeout或者setInterval的方式來實現,時間設定越短動畫也就越流暢,但是使用定時器會有這么幾個問題出現:

  1. 當有耗時任務時,定時器任務會等待耗時任務結束,js引擎空閑時再去執行
  2. 當設定時間非常短時,可能會出現掉幀現象,產生動畫不連貫的感受

那么有什么方法可以解決這個問題呢?答案是使用全局函數requestAnimFrame()

requestAnimFrame的字面意思是“請求動畫幀”,作用是根據GPU的渲染頻率來執行方法內的js代碼,這樣就不會出現上面使用定時器而導致的兩個可能的問題

// dot移動效果
function animateUpdate() {
    dot.update(); // 更新dot的當前位置
    requestAnimFrame(animateUpdate);
}

requestAnimFrame(animateUpdate);

繪制點與點之間的連線

將產生的點存放在一個數組中,就得到了一個當前所有點實例的集合,通過for循環的嵌套,將數組中的每個點進行兩兩比較,當點與點之間的距離達到預先設置的臨界值時,即可繪制連線

如何繪制一條線?

ctx.beginPath(); // 開啟繪制路徑
ctx.moveTo(x, y); // 設置線的起始位置
ctx.lineTo(dx, dy); // 設置線的結束位置
ctx.strokeStyle = 'rgb(255,255,255)'; // 設置繪制線條的顏色
ctx.strokeWidth = 1; // 設置繪制線條的寬度
ctx.stroke(); // 繪制
ctx.closePath(); // 關閉繪制路徑

如何實現線條的淡入淡出效果?

設置reba顏色值可實現帶透明度的顏色,透明度的計算方式為,(臨界值距離 - 實際距離) / 臨界值距離,這樣就可以實現兩點距離越遠線條顏色越淡。再通過動畫不停渲染,就可以造成視覺上淡入淡出的效果

ctx.strokeStyle = 'rgba(255,255,255,'+(dotsDistance-s)/dotsDistance+')';

實現鼠標交互

添加點擊事件 click 事件監聽器,當點擊時實例化多個Dots對象,並將其添加到上文保存點的數組中,這樣既可將新產生的點與原有的點產生聯系。需要注意的是,產生點的位置應該為點擊的位置,由於demo中的canvas是全屏顯示的,所以只需要獲取鼠標點的pageX / pageY,如果canvas並非全屏,則需要獲取到的點產生的位置應該是相對於canvas的位置,而不能直接使用pageX / pageY, 並且需要判斷是否在canvas內,如果不在則不產生新點

document.addEventListener('click', createDot); // 添加點擊事件
function createDot(e) { // 點擊事件方法
    var tx = e.pageX,
        ty = e.pageY; // 獲取點擊位置
    if ((tx > 0 && tx < width) && (ty > 0 && ty < height)) { // 判斷是都在canvas內 width和height為canvas寬高
		  for (var i = 0; i < 5; i ++) {
            var dot = new Dots();
            dotsArr.push(dot);
            dot.init(canvas, tx, ty);
        } // 循環創建5個點 並添加到數組中
    }
};

如何實現鼠標在canvas中移動的交互效果?

首先添加 mousemove 的事件監聽器,其他步驟與上面點擊的代碼相同,唯一不同的是,確定點擊在canvas內后不能創建新的點。現在為Dots對象添加一個新的原型鏈方法mouseDot()用於更新需要跟蹤鼠標移動的點的位置

mouseDot: function (x, y) {
    this.x = x * 2;
    this.y = y * 2; // 這里的2是屏幕的devicePixelRatio 是一個全局熟悉 在retain屏幕下 devicePixelRatio=2 標識瀏覽器會用兩個像素點去繪制原先的一個像素 這樣會導致繪圖不清晰 之后會專門寫一篇關於這個問題的文章 感興趣的小伙伴可以持續關注
    this.ctx.beginPath();
    this.ctx.arc(this.x, this.y, this.r + 0.5, 0, 2*Math.PI);
    this.ctx.fillStyle = "rgba(255,0,0,.8)";
    this.ctx.fill();
    this.ctx.closePath();
    }
document.addEventListener('mousemove', moveDot);
function moveDot(e) {
    var tx = e.pageX,
        ty = e.pageY;
    if ((tx > 0 && tx < width) && (ty > 0 && ty < height)) {
        dot.mouseDot(tx, ty); // 更新跟蹤點的位置 小伙伴們可以思考一下這里的dot對於的是dotsArr中的哪一個點
    }
};

性能優化

以上就是canvas實現絢麗點線效果的基本思路啦!但是還有一個問題需要優化,看過demo的小伙伴可能已經發現了,剛開始不斷點擊的時候會不斷產生點,但是當點的數量到達一定程度的時候就會發現:不管怎么點擊,畫面中的點的數量基本保持不變

其實在之前看到的網站上,點的數量是可以無上限增加的。但是點的數量不斷增加會嚴重消耗性能,導致動畫效果卡頓嚴重,無法直視,同時點太多也十分的不美觀,於是demo就對這一情況做了優化:

當點的數量增加到預設的最大值時,每新增一個點,就會舍棄掉點數組中最先添加進去的點

至於是如何實現的,小伙伴們可以在源碼中尋找答案哦!

代碼結構圖

文件結構類別如下


神奇的canvas——點與線繪制的絢麗動畫效果

代碼地址如下:
http://www.demodashi.com/demo/11636.html

注:本文著作權歸作者,由demo大師代發,拒絕轉載,轉載需要作者授權


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM