Heim  >  Artikel  >  Web-Frontend  >  Eine kurze Diskussion über die Verwendung des Caches zur Optimierung der Leistung von HTML5 Canvas. Programm_HTML5-Tutorial-Fähigkeiten

Eine kurze Diskussion über die Verwendung des Caches zur Optimierung der Leistung von HTML5 Canvas. Programm_HTML5-Tutorial-Fähigkeiten

WBOY
WBOYOriginal
2016-05-16 15:46:482564Durchsuche

Wenn Sie zu viel mit der Leinwand herumspielen, werden Sie automatisch über Leistungsprobleme nachdenken. Wie optimiert man die Leinwandanimation?

【Cache verwenden】

Cache verwenden bedeutet, für das Vorrendern eine Off-Screen-Leinwand zu verwenden. Das Prinzip ist sehr einfach: Zeichnen Sie zuerst in eine Off-Screen-Leinwand und zeichnen Sie dann die Off-Screen-Leinwand über drawImage in die Haupt-Leinwand. Viele Leute verstehen das vielleicht falsch. Ist das nicht ein doppelter Puffermechanismus, der in Spielen häufig verwendet wird?

Tatsächlich wird der doppelte Puffermechanismus in der Spielprogrammierung verwendet, um ein Flackern des Bildschirms zu verhindern. Daher wird vor dem Benutzer eine Leinwand angezeigt und beim Zeichnen wird der Bildschirminhalt zuerst angezeigt Hintergrund-Leinwand, und dann wird die Hintergrund-Leinwand gezeichnet. Die Daten in der Leinwand werden auf die vordere Leinwand gezeichnet. Dies ist eine doppelte Pufferung, aber in Canvas gibt es keine doppelte Pufferung, da moderne Browser grundsätzlich über einen integrierten Doppelpufferungsmechanismus verfügen. Daher stellt die Verwendung von Off-Screen-Canvas keine doppelte Pufferung dar, sondern behandelt Off-Screen-Canvas als Cache-Bereich. Zwischenspeichern Sie Bildschirmdaten, die wiederholt gezeichnet werden müssen, um den Verbrauch beim Aufrufen der Canvas-API zu reduzieren.

Wie wir alle wissen, verbraucht der Aufruf der Canvas-API die Leistung, wenn wir einige wiederholte Bildschirmdaten zeichnen möchten. Sie können sich die folgende DEMO ansehen

1. Es wird kein Cache verwendet

 2. Cache wird verwendet, aber die Breite und Höhe der Off-Screen-Leinwand sind nicht festgelegt

3. Cache wird verwendet, aber die Breite und Höhe der Off-Screen-Leinwand sind nicht festgelegt

 4. Verwenden Sie Caching und legen Sie die Breite und Höhe der Off-Screen-Leinwand fest

Sie können sehen, dass die Leistung der obigen DEMO unterschiedlich ist. Lassen Sie uns die folgenden Gründe analysieren: Um den Stil jedes Kreises zu erreichen, habe ich beim Zeichnen von Kreisen das Schleifenzeichnen verwendet Kreise auf der Seite Wenn ein bestimmter Punkt erreicht ist, ist für jeden Frame der Animation eine große Anzahl von Canvas-API-Aufrufen und eine große Menge an Berechnungen erforderlich, sodass er unabhängig von der Qualität des Browsers angezeigt wird runter.
XML/HTML-CodeInhalt in die Zwischenablage kopieren

  1. ctx.save();   
  2.                         var j=0;   
  3.                                                     for(var i=
  4. 1
  5. ;i<this.r;i =borderWidth){                                ctx.beginPath();                                   
  6.                            ctx.arc(this.x , this.y , i , 0 , 2*Math.PI);   
  7.                             ctx.Stroke();                                j ;                            }   
  8.                         ctx.restore();  
  9.   所以,我的方法很简单,每个圈圈对象里面给他一个离屏canvas作缓存区.
  10.   除了创建离屏canvas作为缓存之外,下面的代码中有一点很关键,就是要设置离屏canvas的宽度和高度,Leinwand生成后的默认大小是300X150;对于我的代码中每个缓存起来圈圈对象半径最大也就不超过80, 所以300X150的大小明显会造成很多空白区域, 会造成资源浪费, 所以就要设置一下离屏Leinwand的宽度和高度, 让它跟缓存起来的元素大小一致上面的四个demo很明显的显示出了性能差距,如果没有设置宽高,当页面超过400个圈圈对象时就会卡的不行了,而设置了宽高1000个圈圈对象也不觉得卡.
  11. XML/HTML-Code
  12. 复制内容到剪贴板
    1. var ball = function(x , y , vx , vy , useCache){   
    2.                 this.x = x;   
    3.                 this.y = y;   
    4.                 this.vx = vx;   
    5.                 this.vy = vy;   
    6.                                     this.color = [];   
    7.                                     
    8. this
    9. this.cacheCtx = this.cacheCanvas.getContext("2d");                    this.cacheCanvas.width
    10.  = 
    11. 2*this.r;                    
    12. this.cacheCanvas.height
    13.  = 2*this.r;                    var 
    14. num
    15.  = getZ(this.r/borderWidth);                    for(var 
    16. j
    17. =0;j<num
    18. ;j ){   
    19.                     this.color.push("rgba(" getZ(getRandom(0,255)) "," getZ(getRandom(0,255)) "," getZ(getRandom(0,255)) ",1)");                    }                    this.useCache = useCache;   
    20.                 if(useCache){   
    21.                     this.cache();   
    22.                 }                }  
    23. Wenn ich das Kreisobjekt instanziiere, rufe ich direkt die Cache-Methode auf, zeichne den komplexen Kreis direkt in die Off-Screen-Leinwand des Kreisobjekts und speichere ihn.

      XML/HTML-CodeInhalt in die Zwischenablage kopieren
      1. cache:function(){
      2. this.cacheCtx.save();
      3. var j=0;
      4. this.cacheCtx.lineWidth = borderWidth; for(var
      5. i
      6. =1;i<this.r;i =borderWidth){ this.cacheCtx.beginPath();
      7.  
      8. this
      9. this.cacheCtx.StrokeStyle = this.color[j]; this.cacheCtx.arc(this.r, this.r, i, 0, 2*Math.PI);
      10. this.cacheCtx.Stroke();
      11. j ;
      12.                                                                     
      13. this.cacheCtx.restore();
      14.                                                                                  
      15. Dann muss ich in der nächsten Animation nur die Off-Screen-Leinwand des Kreisobjekts in die Hauptleinwand zeichnen. Auf diese Weise enthält die in jedem Frame aufgerufene CanvasAPI nur diesen Satz:
      16. XML/HTML-Code
      Inhalt in die Zwischenablage kopieren

      ctx.drawImage(this.cacheCanvas, this.x-this.r, this.y-this.r);
      Im Vergleich zum vorherigen for-Schleifenzeichnen ist es wirklich viel schneller. Wenn wir also wiederholt Vektorgrafiken oder mehrere Bilder zeichnen müssen, können wir sinnvollerweise die Off-Screen-Leinwand verwenden, um die Bilddaten im Voraus zwischenzuspeichern, was den unnötigen Leistungsverbrauch bei jedem nachfolgenden Bildvorgang reduzieren kann.
      1. Der Smooth-Versionscode für 1000 Kreisobjekte ist unten aufgeführt:  
      2. XML/HTML-Code
      Inhalt in die Zwischenablage kopieren

      1. >  
      2. <html lang="en" >  
      3. <Kopf>  
      4.     <meta charset="UTF- 8">  
      5.     <Stil>  
      6.         Körper{   
      7.             padding:0;   
      8.             marge:0;   
      9.             Überlauf: versteckt;   
      10.         }   
      11.         #cas{   
      12.             display: block;   
      13.             background-color:rgba(0,0,0,0);   
      14.             margin:auto;   
      15.             border:1px solid;   
      16.         }
      17.  Stil> 
      18.  <Titel>TestTitel>
      19. Kopf>
      20. <Körper>
      21. <div >
      22.  <canvas id='cas' Breite="800" Höhe="600">Der Browser unterstützt kein CanvasCanvas > ;
      23.  <div style="text- align:center">1000 Kreisobjekte bleiben nicht hängendiv>
      24.  div> 
      25.  <Skript> 
      26. var testBox = function(){
      27. var canvas = document.getElementById("cas"),
      28. ctx = canvas.getContext('2d'),
      29.  borderWidth = 2,
      30. Bälle = [];
      31. var
      32. ball = function(x, y, vx, vy, useCache){
      33.  
      34. this.x = x;  
      35. this.y
      36. = y;  this.vx
      37. = vx;
      38.                 this.vy = vy;   
      39.                                     this.color = [];   
      40.                                     
      41. this
      42. this.cacheCtx = this.cacheCanvas.getContext("2d");                    this.cacheCanvas.width
      43.  = 
      44. 2*this.r;                    
      45. this.cacheCanvas.height
      46.  = 2*this.r;                    var 
      47. num
      48.  = getZ(this.r/borderWidth);                    for(var 
      49. j
      50. =0;j<num
      51. ;j ){   
      52.                     this.color.push("rgba(" getZ(getRandom(0,255)) "," getZ(getRandom(0,255)) "," getZ(getRandom(0,255)) ",1)");                    }                    this.useCache = useCache;   
      53.                 if(useCache){   
      54.                     this.cache();   
      55.                 }                }  
      56.   
      57.             function getZ(num){   
      58.                 var gerundet;   
      59.                 gerundet = (0,5   num) | 0;   
      60.                 // Ein double bitwise not.   
      61.                 gerundet = ~~ (0,5   num);   
      62.                 // Schließlich eine bitweise Linksverschiebung.   
      63.                 🎜>0;                       return gerundet;                }  
      64.   
      65.             ball.prototype = {   
      66.                 paint:function(ctx){   
      67.                     if(!this.useCache){   
      68.                         ctx.save();   
      69.                         var j=0;   
      70.                                                     for(var i=
      71. 1
      72. ;i<this.r;i =borderWidth){                                ctx.beginPath();                                   
      73.                            ctx.arc(this.x , this.y , i , 0 , 2*Math.PI);   
      74.                             ctx.Stroke();                                j ;                            }   
      75.                         ctx.restore();   
      76.                     } else{   
      77.                         ctx.drawImage(this.cacheCanvas , this.x-this.r , this.y-this.r);   
      78.                     }   
      79.                 },   
      80.   
      81.                 cache:function(){   
      82.                     this.cacheCtx.save();   
      83.                     var j=0;   
      84.                                             for(var i=
      85. 1
      86. ;i<this.r;i =borderWidth){                            this.cacheCtx.beginPath();                               
      87.                         this.cacheCtx.arc(this.r , this.r , i , 0 , 2*Math.PI);   
      88.                         this.cacheCtx.Stroke();                            j ;   
      89.                     }   
      90.                     this.cacheCtx.restore();   
      91.                 },   
      92.   
      93.                 move:function(){   
      94.                     this.x  = this.vx;   
      95.                     this.y  = this.vy;   
      96.                     if(this.x
      97. >
      98. (canvas.width-this.r)||this.x
      99. <
      100. this.r
      101. ){   
      102.                         🎜>?this.r:(canvas.width-this.r);                             this.vx = -this.vx;   
      103.                     }  
      104.                     if(this.y>(canvas.height-this.r)||this.y< this.r){   
      105.                         🎜>?this.r:(canvas.height-this.r);                             this.vy = -this.vy;                        }   
      106.                        this.paint(ctx);   
      107.                 }   
      108.             }   
      109.   
      110.             var 
      111. Spiel = {   
      112.                 init:function(){   
      113.                     for(var i=
      114. 0;i
      115. <1000;i ){                            var b = new ball(getRandom(0,canvas.width) , getRandom(0,canvas .height) , getRandom(-10 , 10) ,  getRandom(-10 , 10) , true)   
      116.                         Balls.push(b);                        }                    },   
      117.   
      118.                 update:function(){   
      119.                     ctx.clearRect(0,0,canvas.width,canvas.height);   
      120.                     for(var 
      121. i=
      122. 0;i
      123. <Kugellänge;i ){                            Balls[i].move();                        }                    },   
      124.   
      125.                 loop:function(){   
      126.                     var _this = this;   
      127.                     this.update();   
      128.                     RAF(function(){   
      129.                         _this.loop();   
      130.                     })   
      131.                 },   
      132.   
      133.                 start:function(){   
      134.                     this.init();   
      135.                     this.loop();   
      136.                 }   
      137.             }   
      138.   
      139.             window.RAF = (function(){   
      140.                 return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || Funktion (Rückruf) {window.setTimeout(callback, 1000 / 60); };   
      141.             })();   
      142.   
      143.             return Game;   
      144.         }();   
      145.   
      146.         function getRandom(a , b){   
      147.             return Math.random()*(b-a) a;   
      148.         }   
      149.   
      150.         window.onload = function(){   
      151.             testBox.start();   
      152.         }   
      153.     Skript>  
      154. Körper>  
      155. html>  

    Es gibt noch einen weiteren Hinweis zur Off-Screen-Leinwand: Wenn der Effekt, den Sie erzielen, darin besteht, kontinuierlich Objekte zu erstellen und zu zerstören, verwenden Sie die Off-Screen-Leinwand bitte mit Vorsicht. Binden Sie zumindest nicht die Attribute jedes Objekts, wie ich oben geschrieben habe. Stellen Sie eine Leinwand außerhalb des Bildschirms ein.

    Denn wenn das Objekt auf diese Weise gebunden wird, wird auch die Leinwand außerhalb des Bildschirms zerstört, und es wird ständig eine große Anzahl von Leinwänden außerhalb des Bildschirms erstellt und zerstört, was dazu führt, dass der Leinwandpuffer a verbraucht Sie beanspruchen viele GPU-Ressourcen und führen leicht zum Absturz des Browsers oder zu ernsthaften Frame-Einfrierungen. Die Lösung besteht darin, ein Off-Screen-Canvas-Array zu erstellen, eine ausreichende Anzahl von Off-Screen-Canvas vorab zu laden, nur die noch lebenden Objekte zwischenzuspeichern und sie aus dem Cache zu entfernen, wenn die Objekte zerstört werden. Dadurch wird die Leinwand außerhalb des Bildschirms nicht zerstört.

    【RequestAnimationFrame verwenden】

    Ich werde das nicht im Detail erklären. Ich denke, viele Leute wissen, dass dies die beste Schleife für Animationen ist, nicht setTimeout oder setInterval. Veröffentlichen Sie direkt die Schreibmethode für die Kompatibilität:

    XML/HTML-CodeInhalt in die Zwischenablage kopieren
    1. window.RAF = (function(){
    2. window.requestAnimationFrame ||. window.mozRequestAnimationFrame ||. window.msRequestAnimationFrame ||.
    3. })();

     

    【Gleitkommaoperationen vermeiden】

    Obwohl JavaScript einige sehr praktische Rundungsmethoden bereitstellt, wie z. B. Math.floor, Math.ceil und parseInt, haben ausländische Freunde Tests durchgeführt und die parseInt-Methode erledigt einige zusätzliche Arbeiten (z. B. das Erkennen, ob die Daten ein gültiger Wert sind, parseInt). konvertiert den Parameter sogar zuerst in einen String!), daher ist die direkte Verwendung von parseInt relativ leistungsintensiver. Um es zusammenzufassen: Sie können direkt eine sehr clevere Methode verwenden, die von Ausländern geschrieben wurde:

      JavaScript-CodeInhalt in die Zwischenablage kopieren

      1.gerundet = (0,5 Somenum) | 2.gerundet = ~~ (0,5 Somenum); 3.gerundet = (0,5 Somenum)

      Wenn Sie die Operatoren nicht verstehen, können Sie einfach hier klicken: http://www.w3school.com.cn/js/pro_js_operators_bitwise.asp

      Darin finden Sie detaillierte Erklärungen
     
    【CanvasAPI-Aufrufe so weit wie möglich reduzieren】

    Versuchen Sie beim Erstellen von Partikeleffekten, so wenig Kreise wie möglich zu verwenden und verwenden Sie vorzugsweise Quadrate. Da die Partikel zu klein sind, ähneln Quadrate Kreisen. Der Grund dafür ist leicht zu verstehen: Wir benötigen drei Schritte, um einen Kreis zu zeichnen: Zuerst beginPath, dann verwenden Sie arc, um einen Bogen zu zeichnen, und verwenden Sie dann fill, um ihn zu füllen, um einen Kreis zu erstellen. Aber um ein Quadrat zu zeichnen, brauchen Sie nur ein fillRect. Obwohl es nur einen Unterschied zwischen zwei Aufrufen gibt, wird die Leistungslücke sichtbar, wenn die Anzahl der Partikelobjekte ein bestimmtes Niveau erreicht.

    Es gibt noch einige andere Dinge zu beachten, ich werde sie nicht alle auflisten, da es bei Google einige davon gibt. Dies kann als Aufzeichnung für mich selbst betrachtet werden, hauptsächlich um die Cache-Nutzung aufzuzeichnen. Wenn Sie die Leistung von Canvas verbessern möchten, ist es am wichtigsten, auf die Struktur des Codes zu achten, unnötige API-Aufrufe zu reduzieren, komplexe Vorgänge in jedem Frame zu reduzieren oder komplexe Vorgänge von einmal für jeden Frame auf einmal für mehrere zu ändern Rahmen. Gleichzeitig habe ich für die oben erwähnte Cache-Nutzung der Einfachheit halber für jedes Objekt eine Off-Screen-Leinwand verwendet. Wenn Sie dort zu viele Off-Screen-Leinwände verwenden Es wird zu Leistungsproblemen kommen. Versuchen Sie Ihr Bestes, die Leinwand außerhalb des Bildschirms zu nutzen.

    Quellcode-Adresse:

    https://github.com/whxaxes/canvas-test/tree/gh-pages/src/Other-demo/cache

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn