HTML Canvas производительность при изменении размера
Мне удалось перевести старый эскиз OO Processing на HTML5 Canvas: https://gist.github.com/sindiploma/ce1bb3b8424c32fb8d9e094f8944a789
Я добавил библиотеку Stats для mr.Doob, чтобы записать представление холста.
Он работает, как и ожидалось, при загрузке документа, но при попытке изменить размер окна. частота кадров резко падает.
Любые советы о том, как повысить производительность при изменении размера события?
Вы также можете проверить это живое демо:
(function(f,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"===typeof define&&define.amd?define(e):f.Stats=e()})(this,function(){var f=function(){function e(a){c.appendChild(a.dom);return a}function u(a){for(var d=0;d<c.children.length;d++)c.children[d].style.display=d===a?"block":"none";l=a}var l=0,c=document.createElement("div");c.style.cssText="position:fixed;top:0;left:0;cursor:pointer;opacity:0.9;z-index:10000";c.addEventListener("click",function(a){a.preventDefault();
u(++l%c.children.length)},!1);var k=(performance||Date).now(),g=k,a=0,r=e(new f.Panel("FPS","#0ff","#002")),h=e(new f.Panel("MS","#0f0","#020"));if(self.performance&&self.performance.memory)var t=e(new f.Panel("MB","#f08","#201"));u(0);return{REVISION:16,dom:c,addPanel:e,showPanel:u,begin:function(){k=(performance||Date).now()},end:function(){a++;var c=(performance||Date).now();h.update(c-k,200);if(c>g+1E3&&(r.update(1E3*a/(c-g),100),g=c,a=0,t)){var d=performance.memory;t.update(d.usedJSHeapSize/
1048576,d.jsHeapSizeLimit/1048576)}return c},update:function(){k=this.end()},domElement:c,setMode:u}};f.Panel=function(e,f,l){var c=Infinity,k=0,g=Math.round,a=g(window.devicePixelRatio||1),r=80*a,h=48*a,t=3*a,v=2*a,d=3*a,m=15*a,n=74*a,p=30*a,q=document.createElement("canvas");q.width=r;q.height=h;q.style.cssText="width:80px;height:48px";var b=q.getContext("2d");b.font="bold "+9*a+"px Helvetica,Arial,sans-serif";b.textBaseline="top";b.fillStyle=l;b.fillRect(0,0,r,h);b.fillStyle=f;b.fillText(e,t,v);
b.fillRect(d,m,n,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d,m,n,p);return{dom:q,update:function(h,w){c=Math.min(c,h);k=Math.max(k,h);b.fillStyle=l;b.globalAlpha=1;b.fillRect(0,0,r,m);b.fillStyle=f;b.fillText(g(h)+" "+e+" ("+g(c)+"-"+g(k)+")",t,v);b.drawImage(q,d+a,m,n-a,p,d,m,n-a,p);b.fillRect(d+n-a,m,a,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d+n-a,m,a,g((1-h/w)*p))}}};return f});
var canvas = document.getElementById("inner_heading-canvas");
if (canvas.getContext) {
var context = canvas.getContext("2d");
}
window.addEventListener("load", init, false);
window.addEventListener(
"resize",
function() {
clearTimeout(init);
setTimeout(init, 500);
},
false
);
// Init
function init() {
var net = undefined;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var nodesLength = Math.floor(canvas.width * canvas.height / 2000);
// Nodes
net = new Net();
net.populate(nodesLength);
window.requestAnimationFrame(render);
function render() {
net.update();
net.draw();
net.connect(50);
window.requestAnimationFrame(render);
}
}
// Net
class Net {
constructor() {
this.nodes = [];
this.length = undefined;
}
populate(length) {
this.length = length;
for (var i = 0; i < length; i++) {
var xPos = Math.floor(getRandom(0, canvas.width));
var yPos = Math.floor(getRandom(0, canvas.height));
this.nodes.push(new Node(xPos, yPos));
}
}
update() {
for (var i = 0; i < this.length; i++) {
this.nodes[i].update();
}
}
draw() {
context.fillStyle = "#000000";
context.fillRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < this.length; i++) {
this.nodes[i].draw();
}
}
connect(distanceMax) {
for (var i = 0; i < this.length - 1; i++) {
this.nodes[i].connections = [];
for (var j = 0; j < this.length - 1; j++) {
var a = this.nodes[j].x - this.nodes[i].x;
var b = this.nodes[j].y - this.nodes[i].y;
var c = Math.sqrt(a * a + b * b);
if (c < distanceMax) {
this.nodes[i].connections.push(j);
}
}
for (var k = 0; k < this.nodes[i].connections.length; k++) {
context.beginPath();
context.moveTo(this.nodes[i].x, this.nodes[i].y);
context.lineTo(
this.nodes[this.nodes[i].connections[k]].x,
this.nodes[this.nodes[i].connections[k]].y
);
context.strokeStyle = "rgba(255,255,255,.15)";
context.stroke();
}
}
}
}
// Node
class Node {
constructor(_x, _y) {
this.x = _x;
this.y = _y;
this.radius = 2;
this.depth = Math.floor(getRandom(1, 10)) / 10;
}
update() {
var velocity = (1 - this.depth) / 10;
this.x = this.x + velocity;
if (this.x > canvas.width || this.x < 0) {
this.x = 0;
}
}
draw() {
var alpha = 1 - this.depth;
context.beginPath();
context.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, false);
context.fillStyle = "rgba(255,255,255," + alpha + ")";
context.fill();
}
}
// Helpers
function getRandom(min, max) {
return Math.random() * (max - min) + min;
}
// Stats
var stats = new Stats();
stats.showPanel(0);
document.body.appendChild(stats.dom);
function animate() {
stats.begin();
stats.end();
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
body {
margin: 0;
}
<html>
<head></head>
<body>
<canvas id="inner_heading-canvas" width="600" height="600"></canvas>
</body>
</html>
1 ответ
Самый важный импульс, который вы получаете от очистки requestAnimationFrame:
var rafID = null; //scoping requestAnimationFrame request-ID
//...
//inside resize-callback
window.cancelAnimationFrame(rafID);
...
//inside init() + inside render()
rafID = window.requestAnimationFrame(render);
Таким образом, мы гарантируем, что браузер все еще не пытается обновить старые экземпляры. Я добавил его в фрагмент кода ниже, где я также убедился, что время ожидания работает (для устранения неполадок).
(function(f,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"===typeof define&&define.amd?define(e):f.Stats=e()})(this,function(){var f=function(){function e(a){c.appendChild(a.dom);return a}function u(a){for(var d=0;d<c.children.length;d++)c.children[d].style.display=d===a?"block":"none";l=a}var l=0,c=document.createElement("div");c.style.cssText="position:fixed;top:0;left:0;cursor:pointer;opacity:0.9;z-index:10000";c.addEventListener("click",function(a){a.preventDefault();
u(++l%c.children.length)},!1);var k=(performance||Date).now(),g=k,a=0,r=e(new f.Panel("FPS","#0ff","#002")),h=e(new f.Panel("MS","#0f0","#020"));if(self.performance&&self.performance.memory)var t=e(new f.Panel("MB","#f08","#201"));u(0);return{REVISION:16,dom:c,addPanel:e,showPanel:u,begin:function(){k=(performance||Date).now()},end:function(){a++;var c=(performance||Date).now();h.update(c-k,200);if(c>g+1E3&&(r.update(1E3*a/(c-g),100),g=c,a=0,t)){var d=performance.memory;t.update(d.usedJSHeapSize/
1048576,d.jsHeapSizeLimit/1048576)}return c},update:function(){k=this.end()},domElement:c,setMode:u}};f.Panel=function(e,f,l){var c=Infinity,k=0,g=Math.round,a=g(window.devicePixelRatio||1),r=80*a,h=48*a,t=3*a,v=2*a,d=3*a,m=15*a,n=74*a,p=30*a,q=document.createElement("canvas");q.width=r;q.height=h;q.style.cssText="width:80px;height:48px";var b=q.getContext("2d");b.font="bold "+9*a+"px Helvetica,Arial,sans-serif";b.textBaseline="top";b.fillStyle=l;b.fillRect(0,0,r,h);b.fillStyle=f;b.fillText(e,t,v);
b.fillRect(d,m,n,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d,m,n,p);return{dom:q,update:function(h,w){c=Math.min(c,h);k=Math.max(k,h);b.fillStyle=l;b.globalAlpha=1;b.fillRect(0,0,r,m);b.fillStyle=f;b.fillText(g(h)+" "+e+" ("+g(c)+"-"+g(k)+")",t,v);b.drawImage(q,d+a,m,n-a,p,d,m,n-a,p);b.fillRect(d+n-a,m,a,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d+n-a,m,a,g((1-h/w)*p))}}};return f});
var canvas = document.getElementById("inner_heading-canvas");
var timeoutID = null;
var rafID = null;
if (canvas.getContext) {
var context = canvas.getContext("2d");
}
window.addEventListener("load", init, false);
window.addEventListener(
"resize",
function() {
clearTimeout(timeoutID);
window.cancelAnimationFrame(rafID);
timeoutID = setTimeout(init, 500);
},
false
);
// Init
function init() {
var net = undefined;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var nodesLength = Math.floor(canvas.width * canvas.height / 2000);
// Nodes
net = new Net();
net.populate(nodesLength);
rafID = window.requestAnimationFrame(render);
function render() {
net.update();
net.draw();
net.connect(50);
rafID = window.requestAnimationFrame(render);
}
}
// Net
class Net {
constructor() {
this.nodes = [];
this.length = undefined;
}
populate(length) {
this.length = length;
for (var i = 0; i < length; i++) {
var xPos = Math.floor(getRandom(0, canvas.width));
var yPos = Math.floor(getRandom(0, canvas.height));
this.nodes.push(new Node(xPos, yPos));
}
}
update() {
for (var i = 0; i < this.length; i++) {
this.nodes[i].update();
}
}
draw() {
context.fillStyle = "#000000";
context.fillRect(0, 0, canvas.width, canvas.height);
for (var i = 0; i < this.length; i++) {
this.nodes[i].draw();
}
}
connect(distanceMax) {
for (var i = 0; i < this.length - 1; i++) {
this.nodes[i].connections = [];
for (var j = 0; j < this.length - 1; j++) {
var a = this.nodes[j].x - this.nodes[i].x;
var b = this.nodes[j].y - this.nodes[i].y;
var c = Math.sqrt(a * a + b * b);
if (c < distanceMax) {
this.nodes[i].connections.push(j);
}
}
for (var k = 0; k < this.nodes[i].connections.length; k++) {
context.beginPath();
context.moveTo(this.nodes[i].x, this.nodes[i].y);
context.lineTo(
this.nodes[this.nodes[i].connections[k]].x,
this.nodes[this.nodes[i].connections[k]].y
);
context.strokeStyle = "rgba(255,255,255,.15)";
context.stroke();
}
}
}
}
// Node
class Node {
constructor(_x, _y) {
this.x = _x;
this.y = _y;
this.radius = 2;
this.depth = Math.floor(getRandom(1, 10)) / 10;
}
update() {
var velocity = (1 - this.depth) / 10;
this.x = this.x + velocity;
if (this.x > canvas.width || this.x < 0) {
this.x = 0;
}
}
draw() {
var alpha = 1 - this.depth;
context.beginPath();
context.arc(this.x, this.y, this.radius, 0, 2 * Math.PI, false);
context.fillStyle = "rgba(255,255,255," + alpha + ")";
context.fill();
}
}
// Helpers
function getRandom(min, max) {
return Math.random() * (max - min) + min;
}
// Stats
var stats = new Stats();
stats.showPanel(0);
document.body.appendChild(stats.dom);
function animate() {
stats.begin();
stats.end();
requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
body {
margin: 0;
}
<html>
<head></head>
<body>
<canvas id="inner_heading-canvas" width="600" height="600"></canvas>
</body>
</html>
Помимо этого быстрого исправления, есть много других общих способов сделать работу по изменению размера лучше. Выполняя поиск по стеку, вы можете изучить несколько методов: