loading動畫的效能優化-摒棄Canvas,擁抱CSS3-實現點選特效

carr_y發表於2018-11-08

click-colorful
click-colorful

demo演示 coldday.github.io/click-color…

專案中有個活動頁載入時有個loading動畫,效果如上圖,連續綻放小球,問題就是太卡,經常出現一堆小球抱團卡住,遂進行優化

注意:(僅在頁面載入時卡頓,載入完畢,點選的時候不卡頓)

檢視程式碼發現動畫借用的animejs 官網中的效果,通過canvas實現的,按理說canvas應該時效能很好的,不應該出現卡頓,問題在我的活動頁中就是非常卡頓

思考再三,把最終原因歸結於首頁載入時候,瀏覽器要載入資源,渲染dom,耗費cpu,而canvas做幀動畫會做大量的計算,這對cpu肯定是一種消耗,給瀏覽器造成一定壓力,個人見解,不一定準確

我的解決辦法是砍掉canvas,藉助CSS3並觸發硬體加速,只在建立小球的時候做一次運算,幀動畫通過transform集合transition實現,這樣可以大大減少js的運算

外掛封裝好並開源 github click-colorful ,全部程式碼不到100行,有興趣可以看一下,覺得可以就 Star 一下,算是鼓勵

下面是核心程式碼實現

定義預設引數

var defaultParams = {
		colors: ['#eb125f', '#6eff8a', '#6386ff', '#f9f383'],
		size: 30,
		maxCount: 30
  	}
複製程式碼

小球的顏色,大小,數量都可以隨意配置,非常靈活

建構函式

function colorBall(params) {
		this.params = Object.assign({}, defaultParams, params)
	}
複製程式碼

引數的賦值

綻放函式

colorBall.prototype.fly = function (x, y, playCount, loopTimer) {
		if (!loopTimer) loopTimer = 300
		var ballElements = []
		var fragment = document.createDocumentFragment()
		
		var ballNum = this.params.maxCount;
		// 修改輪換播放實現方式,改為一次建立所有,通過延遲執行動畫實現
		if(playCount) {
			ballNum = ballNum * playCount;
		} 
		var loop = 0
		for(var i=0; i<ballNum; i++) {
			var curLoop = parseInt(i/this.params.maxCount)
			var ball = doc.createElement('i');
			ball.className = 'color-ball ball-loop-' + curLoop;
			var blurX = Math.random() * 10
			if (Math.random() > 0.5) blurX = blurX* -1
			var blurY = Math.random() * 10
			if (Math.random() > 0.5) blurY = blurY* -1
			ball.style.left = (x) + 'px';
			ball.style.top = (y) + 'px';
			ball.style.width = this.params.size + 'px';
			ball.style.height = this.params.size + 'px';
			ball.style.position = 'fixed';
			ball.style.borderRadius = '1000px';
			ball.style.boxSizing = 'border-box';
			ball.style.zIndex = 9999;
			ball.style.opacity = 0;
			if (curLoop === 0) ball.style.opacity = 1;
			ball.style.transform = 'translate3d(0px, 0px, 0px) scale(1)';
			ball.style.webkitTransform = 'translate3d(0px, 0px, 0px) scale(1)';
			ball.style.transition = 'transform 1s ' + curLoop * loopTimer / 1000 + 's ease-out';
			ball.style.webkitTransition = 'transform 1s ' + curLoop * loopTimer / 1000 + 's ease-out';
			ball.style.backgroundColor = getOneRandom(this.params.colors);
			fragment.appendChild(ball);
			ballElements.push(ball)
			// 效能優化終極版
			if (curLoop !== loop) {
				(function(num){
					setTimeout(function(){
						var loopBalls = document.getElementsByClassName('ball-loop-' + num)
						for(var j = 0; j < loopBalls.length; j++) {
							loopBalls[j].style.opacity = 1
						}
						if (num === loop) {
							_clear(ballElements)
						}
					}, num * loopTimer + 30)
				})(curLoop)
				loop = curLoop
			}
		}
		
		doc.body.appendChild(fragment);
		// 延遲刪除
		!playCount &&  _clear(ballElements)
		// 執行動畫
		setTimeout(function () {
			for(var i=0; i<ballElements.length; i++){
				_run(ballElements[i])
			}	
		}, 10)
	}
	//  隨機賦值運動軌跡
	function _run(ball) {
		var randomXFlag = Math.random() > 0.5
		var randomYFlag = Math.random() > 0.5
		var randomX = parseInt(Math.random() * 160);
		var randomY = parseInt(Math.random() * 160);
		if (randomXFlag) {
				randomX = randomX * -1;
		}
		if (randomYFlag) {
				randomY = randomY * -1
		}
		var transform = 'translate3d('+randomX+'px,' + randomY + 'px, 0) scale(0)';
		ball.style.webkitTransform = transform;
		ball.style.MozTransform = transform;
		ball.style.msTransform = transform;
		ball.style.OTransform = transform;
		ball.style.transform = transform;
	}
複製程式碼

fly函式接受四個引數,x,y程式碼螢幕座標,playCount是執行一次播放幾輪, loopTimer是間隔

通過delay實現迴圈播放,而不是遞迴分批建立dom,減少頁面迴流,效能很高

_run函式中通過random實現小球隨機向四面八方運動

可以看到這個函式js計算非常少,只有在建立和刪除做了一些迴圈。而且用了createDocumentFragment儘可能的減少了dom操作,對效能提升也有很大幫助

整體結構

function (win, doc) {
	"use strict";
	var defaultParams = {
		colors: ['#eb125f', '#6eff8a', '#6386ff', '#f9f383'],
		size: 30,
		maxCount: 50
  	}
	function colorBall(params) {
		
	}
	colorBall.prototype.fly = function (x, y, playCount, loopTimer) {
		
	}
	//相容CommonJs規範 
	if (typeof module !== 'undefined' && module.exports) {
		module.exports = colorBall;
	};
	//相容AMD/CMD規範
	if (typeof define === 'function') define(function() { 
		return colorBall; 
	});
	//註冊全域性變數,相容直接使用script標籤引入外掛
	win.colorBall = colorBall;
 })(window, document)
複製程式碼

至此外掛封裝完畢,下面是使用方式

可選引數配置

var params = {
        colors: ["#eb125f", "#6eff8a", "#6386ff", "#f9f383"], // 自定義顏色
        size: 30, // 小球大小
        maxCount: 30 // 小球的數量
  }
複製程式碼

使用方式

  • 引入click-colorful.js <script src="click-colorful.js"></script>
  • 實力化外掛
//params不傳,則走預設配置
var color = new colorBall(params)
// 綻放一次
color.fly(x, y)
// 綻放5次,間隔300ms
color.fly(x, y, 5, 300)
複製程式碼

宣告: 並不是說canvas不好,只是有的場景不適合用canvas

如果文章哪裡寫的有問題請指出,謝謝

相關文章