使用canvas和requestAnimationFrame的高CPU使用率问题

时间:2018-04-06 13:49:09

标签: javascript animation canvas requestanimationframe

我发现了一个带有requestAnimationFrame的画布动画,我试图改变我的需求,并且我有一个巨大的cpu使用问题..高达80%,我开始削减我不想要或不需要的东西。我现在降低到40-50%的CPU使用率,所以我想要一些帮助,我可以做些什么来优化这些代码并减少CPU使用率。

我已经阅读过requestAnimationFrame,并且它以60fps甚至更高的速度运行,并且mabye在性能方面有很大的作用,也许我可以在那里做些什么?

/**
 * Stars
 * Inspired by Steve Courtney's poster art for Celsius GS's Drifter - http://celsiusgs.com/drifter/posters.php
 * by Cory Hughart - http://coryhughart.com
 */

// Settings
var particleCount = 40,
	flareCount = 0,
	motion = 0.05,
	tilt = 0.05,
	color = '#00FF7B',
	particleSizeBase = 1,
	particleSizeMultiplier = 0.5,
	flareSizeBase = 100,
	flareSizeMultiplier = 100,
	lineWidth = 1,
	linkChance = 75, // chance per frame of link, higher = smaller chance
	linkLengthMin = 5, // min linked vertices
	linkLengthMax = 7, // max linked vertices
	linkOpacity = 0.25; // number between 0 & 1
	linkFade = 90, // link fade-out frames
	linkSpeed = 0, // distance a link travels in 1 frame
	glareAngle = -60,
	glareOpacityMultiplier = 0.05,
	renderParticles = true,
	renderParticleGlare = true,
	renderFlares = false,
	renderLinks = false,
	renderMesh = false,
	flicker = false,
	flickerSmoothing = 15, // higher = smoother flicker
	blurSize = 0,
	orbitTilt = true,
	randomMotion = true,
	noiseLength = 1000,
	noiseStrength = 1;

var canvas = document.getElementById('stars'),
	context = canvas.getContext('2d'),
	mouse = { x: 0, y: 0 },
	m = {},
	r = 0,
	c = 1000, // multiplier for delaunay points, since floats too small can mess up the algorithm
	n = 0,
	nAngle = (Math.PI * 2) / noiseLength,
	nRad = 100,
	nScale = 0.5,
	nPos = {x: 0, y: 0},
	points = [],
	vertices = [],
	triangles = [],
	links = [],
	particles = [],
	flares = [];

function init() {
	var i, j, k;

	// requestAnimFrame polyfill
	window.requestAnimFrame = (function(){
		return  window.requestAnimationFrame ||
				window.webkitRequestAnimationFrame ||
				window.mozRequestAnimationFrame ||
				function( callback ){

				};
	})();


	// Size canvas
	resize();

	mouse.x = canvas.clientWidth / 2;
	mouse.y = canvas.clientHeight / 2;

	// Create particle positions
	for (i = 0; i < particleCount; i++) {
		var p = new Particle();
		particles.push(p);
		points.push([p.x*c, p.y*c]);
	}


	vertices = Delaunay.triangulate(points);


	var tri = [];
	for (i = 0; i < vertices.length; i++) {
		if (tri.length == 3) {
			triangles.push(tri);
			tri = [];
		}
		tri.push(vertices[i]);
	}


	// Tell all the particles who their neighbors are
	for (i = 0; i < particles.length; i++) {
		// Loop through all tirangles
		for (j = 0; j < triangles.length; j++) {
			// Check if this particle's index is in this triangle
			k = triangles[j].indexOf(i);
			// If it is, add its neighbors to the particles contacts list
			if (k !== -1) {
				triangles[j].forEach(function(value, index, array) {
					if (value !== i && particles[i].neighbors.indexOf(value) == -1) {
						particles[i].neighbors.push(value);
					}
				});
			}
		}
	}


	// Animation loop
	(function animloop(){
		requestAnimFrame(animloop);
		resize();
		render();
	})();
}

function render() {
	if (randomMotion) {
		n++;
		if (n >= noiseLength) {
			n = 0;
		}

		nPos = noisePoint(n);

	}



	if (renderParticles) {
		// Render particles
		for (var i = 0; i < particleCount; i++) {
			particles[i].render();
		}
	}


}

function resize() {
	canvas.width = window.innerWidth * (window.devicePixelRatio || 1);
	canvas.height = canvas.width * (canvas.clientHeight / canvas.clientWidth);
}



// Particle class
var Particle = function() {
	this.x = random(-0.1, 1.1, true);
	this.y = random(-0.1, 1.1, true);
	this.z = random(0,4);
	this.color = color;
	this.opacity = random(0.1,1,true);
	this.flicker = 0;
	this.neighbors = []; // placeholder for neighbors
};
Particle.prototype.render = function() {
	var pos = position(this.x, this.y, this.z),
		r = ((this.z * particleSizeMultiplier) + particleSizeBase) * (sizeRatio() / 1000),
		o = this.opacity;



	context.fillStyle = this.color;
	context.globalAlpha = o;
	context.beginPath();

	context.fill();
	context.closePath();

	if (renderParticleGlare) {
		context.globalAlpha = o * glareOpacityMultiplier;

		context.ellipse(pos.x, pos.y, r * 100, r, (glareAngle - ((nPos.x - 0.5) * noiseStrength * motion)) * (Math.PI / 180), 0, 2 * Math.PI, false);
		context.fill();
		context.closePath();
	}

	context.globalAlpha = 1;
};

// Flare class


// Link class
var Link = function(startVertex, numPoints) {
	this.length = numPoints;
	this.verts = [startVertex];
	this.stage = 0;
	this.linked = [startVertex];
	this.distances = [];
	this.traveled = 0;
	this.fade = 0;
	this.finished = false;
};




// Utils

function noisePoint(i) {
	var a = nAngle * i,
		cosA = Math.cos(a),
		sinA = Math.sin(a),


		rad = nRad;
	return {
		x: rad * cosA,
		y: rad * sinA
	};
}

function position(x, y, z) {
	return {
		x: (x * canvas.width) + ((((canvas.width / 2) - mouse.x + ((nPos.x - 0.5) * noiseStrength)) * z) * motion),
		y: (y * canvas.height) + ((((canvas.height / 2) - mouse.y + ((nPos.y - 0.5) * noiseStrength)) * z) * motion)
	};
}

function sizeRatio() {
	return canvas.width >= canvas.height ? canvas.width : canvas.height;
}

function random(min, max, float) {
	return float ?
		Math.random() * (max - min) + min :
		Math.floor(Math.random() * (max - min + 1)) + min;
}


// init
if (canvas) init();
html,
body {
	margin: 0;
	padding: 0;
  height: 100%;
}

body {
   background: #375848;
  background-image: linear-gradient(-180deg, rgba(0,0,0,0.00) 0%, #000000 100%);
}

#stars {
  display: block;
  position: relative;
  width: 100%;
  height: 16rem;
  height: 100vh;
  z-index: 1;
  position: absolute;
}
<script src="https://rawgit.com/ironwallaby/delaunay/master/delaunay.js"></script>
<script src="http://requirejs.org/docs/release/2.1.15/minified/require.js"></script>
<canvas id="stars" width="300" height="300"></canvas>

1 个答案:

答案 0 :(得分:0)

我有两件事需要建议,其中一个人已经在评论中提到过 - 只在调整大小事件时调整画布大小。

另一种方法是将每次调用动画循环的时间与上次调用的时间进行比较,并且只在经过一定时间后再次渲染 - 比如说大约60 fps的速率就是16毫秒。 / p>

var lastRender = 0;

(function animloop(){
    requestAnimFrame(animloop);
    var now = Date.now();
    if (now >= lastRender + 16) {
        render();
        lastRender = now;
    }
})();