HTML5 Canvas-Optimierung: Ein praktisches Beispiel
() translation by (you can also view the original English article)
Wenn Sie getan haben JavaScript-Entwicklung lange genug, haben Sie wahrscheinlich crashed Sie Ihren browser ein paar mal. Das problem in der Regel stellt sich heraus, dass einige JavaScript-Fehler, wie eine endlose while-Schleife; wenn nicht, ist der nächste verdächtige ist die Seite von Transformationen oder Animationen - die Art, die beinhalten das hinzufügen und entfernen von Elemente aus der Webseite oder das animieren von CSS-Eigenschaften. Dieses tutorial konzentriert sich auf die Optimierung der Animationen erzeugt mit JS und HTML5 - - element.
Dieses tutorial beginnt und endet mit dem, was die HTML5-animation widget, das Sie unten sehen:
Wir nehmen Sie mit auf eine Reise, die Erkundung der verschiedenen schwellen-Leinwand-Optimierung Tipps und Techniken und wenden Sie Sie auf die widget-JavaScript-source-code. Das Ziel ist die Verbesserung auf dem widget die Ausführung, die Geschwindigkeit und am Ende eine glattere, mehr Flüssigkeit animation widget, angetrieben durch schlankere, effizientere JavaScript.
Der source-download-enthält die HTML-und JavaScript von jedem Schritt im tutorial, so können Sie entlang Folgen von jedem Punkt.
Nehmen wir den ersten Schritt.
Schritt 1: Spielen Sie den Film-Anhänger
Das widget oben ist auf der Grundlage der Film-trailer zu Sintel, ein 3D-Animationsfilm der Blender Foundation. Es ist gebaut mit Hilfe von zwei HTML5 beliebtesten Ergänzungen: das und - Elemente.
Das - lädt und spielt die Sintel video-Datei, während das erzeugt seine eigene animation Sequenz von Momentaufnahmen der Wiedergabe von Videos und mischen es mit text und anderen Grafiken. Wenn Sie klicken, um das video abzuspielen, wird die Leinwand zu neuem Leben erweckt, mit einem dunklen hintergrund, der eine größere schwarze und weiße Kopie der Wiedergabe von video. Kleinere, farbige screen-shots des Videos kopiert werden, um die Szene, und gleiten über die es als Teil einer Filmrolle, illustration.



In der linken oberen Ecke, wir haben den Titel und ein paar Zeilen beschreibenden text, fade in und out, während die animation spielt. Das Skript ist Leistung, Geschwindigkeit und verwandten Metriken sind Teil der animation, in der kleinen schwarzen box in der linken unteren Ecke mit einem Diagramm und lebendigen text. Wir werden uns an dieser besonderen Sache im detail später.
Schließlich gibt es einen großen, sich drehenden Klinge, die fliegt über die Szene am Anfang der animation, dessen Grafik geladen wird, eine externe PNG-Bild-Datei.
Schritt 2: die Quelle Anzeigen
Der Quellcode enthält die übliche Mischung von HTML, CSS und Javascript. Die HTML ist spärlich: nur die und - tags eingeschlossen in einem container :
1 |
<div id="animationWidget" > |
2 |
<canvas width="368" height="208" id="mainCanvas" ></canvas> |
3 |
<video width="184" height="104" id="video" autobuffer="autobuffer" controls="controls" poster="poster.jpg" > |
4 |
<source src="sintel.mp4" type="video/mp4" ></source> |
5 |
<source src="sintel.webm" type="video/webm" ></source> |
6 |
</video>
|
7 |
</div>
|
Der container eine ID (animationWidget), die fungiert als einen Haken für alle CSS-Regeln angewendet und Ihr Inhalt (unten).
1 |
|
2 |
#animationWidget{ |
3 |
border:1px #222 solid; |
4 |
position:relative; |
5 |
width: 570px; |
6 |
height: 220px; |
7 |
}
|
8 |
#animationWidget canvas{ |
9 |
border:1px #222 solid; |
10 |
position:absolute; |
11 |
top:5px; |
12 |
left:5px; |
13 |
}
|
14 |
#animationWidget video{ |
15 |
position:absolute; |
16 |
top:110px; |
17 |
left:380px; |
18 |
}
|
Während HTML und CSS sind Sie die Marinade Gewürze und Gewürze, die die JavaScript, die das Fleisch des widgets.
- An der Spitze, haben wir die wichtigsten Objekte, die oft benutzt werden durch das Skript, einschließlich der Verweise auf das canvas-element und sein 2D-Kontext.
- Die init () - Funktion wird aufgerufen, wenn die video-Wiedergabe beginnt, und setzt alle Objekte in dem Skript verwendet.
- Die sampleVideo () - Funktion erfasst den aktuellen frame der Wiedergabe von video, während setBlade() lädt eine externe Bild erforderlich, die von der animation.
- Das Tempo und die Inhalte des canvas-animation gesteuert werden, die von der main () - Funktion, die wie das Skript Herzschlag. Führen Sie in regelmäßigen Abständen, einmal das video zu spielen beginnt, sind es Malt jedes Bild der animation, indem Sie zunächst das clearing der Leinwand, dann ruft jeder von dem Skript die fünf Funktionen zum zeichnen:
drawBackground()
drawFilm()
drawTitle()
drawDescription()
drawStats()
Wie die Namen bereits andeuten, jede Zeichnung Funktion ist verantwortlich für die Zeichnung ein Element in der animation Szene. Die Strukturierung der code auf diese Weise erhöht die Flexibilität und macht zukünftige Wartung zu erleichtern.
Das volle Skript ist unten gezeigt. Nehmen Sie sich einen moment Zeit, um zu bewerten, und sehen, ob Sie können vor Ort alle änderungen, die Sie machen würde, um ihn zu beschleunigen.
1 |
(function(){ |
2 |
if( !document.createElement("canvas").getContext ){ return; } //the canvas tag isn't supported |
3 |
|
4 |
var mainCanvas = document.getElementById("mainCanvas"); // points to the HTML canvas element above |
5 |
var mainContext = mainCanvas.getContext('2d'); //the drawing context of the canvas element |
6 |
var video = document.getElementById("video"); // points to the HTML video element |
7 |
var frameDuration = 33; // the animation's speed in milliseconds |
8 |
video.addEventListener( 'play', init ); // The init() function is called whenever the user presses play & the video starts/continues playing |
9 |
video.addEventListener( 'ended', function(){ drawStats(true); } ); //drawStats() is called one last time when the video end, to sum up all the statistics |
10 |
|
11 |
var videoSamples; // this is an array of images, used to store all the snapshots of the playing video taken over time. These images are used to create the 'film reel' |
12 |
var backgrounds; // this is an array of images, used to store all the snapshots of the playing video taken over time. These images are used as the canvas background |
13 |
var blade; //An canvas element to store the image copied from blade.png |
14 |
var bladeSrc = 'blade.png'; //path to the blade's image source file |
15 |
|
16 |
var lastPaintCount = 0; // stores the last value of mozPaintCount sampled |
17 |
var paintCountLog = []; // an array containing all measured values of mozPaintCount over time |
18 |
var speedLog = []; // an array containing all the execution speeds of main(), measured in milliseconds |
19 |
var fpsLog = []; // an array containing the calculated frames per secong (fps) of the script, measured by counting the calls made to main() per second |
20 |
var frameCount = 0; // counts the number of times main() is executed per second. |
21 |
var frameStartTime = 0; // the last time main() was called |
22 |
|
23 |
// Called when the video starts playing. Sets up all the javascript objects required to generate the canvas animation and measure perfomance
|
24 |
function init(){ |
25 |
if( video.currentTime > 1 ){ return; } |
26 |
|
27 |
bladeSrc = new Image(); |
28 |
bladeSrc.src = "blade.png"; |
29 |
bladeSrc.onload = setBlade; |
30 |
|
31 |
backgrounds = []; |
32 |
videoSamples = []; |
33 |
fpsLog = []; |
34 |
paintCountLog = []; |
35 |
if( window.mozPaintCount ){ lastPaintCount = window.mozPaintCount; } |
36 |
speedLog = []; |
37 |
frameCount = 0; |
38 |
frameStartTime = 0; |
39 |
main(); |
40 |
setTimeout( getStats, 1000 ); |
41 |
}
|
42 |
|
43 |
// As the scripts main function, it controls the pace of the animation
|
44 |
function main(){ |
45 |
setTimeout( main, frameDuration ); |
46 |
if( video.paused || video.ended ){ return; } |
47 |
|
48 |
var now = new Date().getTime(); |
49 |
if( frameStartTime ){ |
50 |
speedLog.push( now - frameStartTime ); |
51 |
}
|
52 |
frameStartTime = now; |
53 |
if( video.readyState < 2 ){ return; } |
54 |
|
55 |
frameCount++; |
56 |
mainCanvas.width = mainCanvas.width; //clear the canvas |
57 |
drawBackground(); |
58 |
drawFilm(); |
59 |
drawDescription(); |
60 |
drawStats(); |
61 |
drawBlade(); |
62 |
drawTitle(); |
63 |
}
|
64 |
|
65 |
// This function is called every second, and it calculates and stores the current frame rate
|
66 |
function getStats(){ |
67 |
if( video.readyState >= 2 ){ |
68 |
if( window.mozPaintCount ){ //this property is specific to firefox, and tracks how many times the browser has rendered the window since the document was loaded |
69 |
paintCountLog.push( window.mozPaintCount - lastPaintCount ); |
70 |
lastPaintCount = window.mozPaintCount; |
71 |
}
|
72 |
|
73 |
fpsLog.push(frameCount); |
74 |
frameCount = 0; |
75 |
}
|
76 |
setTimeout( getStats, 1000 ); |
77 |
}
|
78 |
|
79 |
// create blade, the ofscreen canavs that will contain the spining animation of the image copied from blade.png
|
80 |
function setBlade(){ |
81 |
blade = document.createElement("canvas"); |
82 |
blade.width = 400; |
83 |
blade.height = 400; |
84 |
blade.angle = 0; |
85 |
blade.x = -blade.height * 0.5; |
86 |
blade.y = mainCanvas.height/2 - blade.height/2; |
87 |
}
|
88 |
|
89 |
// Creates and returns a new image that contains a snapshot of the currently playing video.
|
90 |
function sampleVideo(){ |
91 |
var newCanvas = document.createElement("canvas"); |
92 |
newCanvas.width = video.width; |
93 |
newCanvas.height = video.height; |
94 |
newCanvas.getContext("2d").drawImage( video, 0, 0, video.width, video.height ); |
95 |
return newCanvas; |
96 |
}
|
97 |
|
98 |
// renders the dark background for the whole canvas element. The background features a greyscale sample of the video and a gradient overlay
|
99 |
function drawBackground(){ |
100 |
var newCanvas = document.createElement("canvas"); |
101 |
var newContext = newCanvas.getContext("2d"); |
102 |
newCanvas.width = mainCanvas.width; |
103 |
newCanvas.height = mainCanvas.height; |
104 |
newContext.drawImage( video, 0, video.height * 0.1, video.width, video.height * 0.5, 0, 0, mainCanvas.width, mainCanvas.height ); |
105 |
|
106 |
var imageData, data; |
107 |
try{ |
108 |
imageData = newContext.getImageData( 0, 0, mainCanvas.width, mainCanvas.height ); |
109 |
data = imageData.data; |
110 |
} catch(error){ // CORS error (eg when viewed from a local file). Create a solid fill background instead |
111 |
newContext.fillStyle = "yellow"; |
112 |
newContext.fillRect( 0, 0, mainCanvas.width, mainCanvas.height ); |
113 |
imageData = mainContext.createImageData( mainCanvas.width, mainCanvas.height ); |
114 |
data = imageData.data; |
115 |
}
|
116 |
|
117 |
//loop through each pixel, turning its color into a shade of grey
|
118 |
for( var i = 0; i < data.length; i += 4 ){ |
119 |
var red = data[i]; |
120 |
var green = data[i + 1]; |
121 |
var blue = data[i + 2]; |
122 |
var grey = Math.max( red, green, blue ); |
123 |
|
124 |
data[i] = grey; |
125 |
data[i+1] = grey; |
126 |
data[i+2] = grey; |
127 |
}
|
128 |
newContext.putImageData( imageData, 0, 0 ); |
129 |
|
130 |
//add the gradient overlay
|
131 |
var gradient = newContext.createLinearGradient( mainCanvas.width/2, 0, mainCanvas.width/2, mainCanvas.height ); |
132 |
gradient.addColorStop( 0, '#000' ); |
133 |
gradient.addColorStop( 0.2, '#000' ); |
134 |
gradient.addColorStop( 1, "rgba(0,0,0,0.5)" ); |
135 |
newContext.fillStyle = gradient; |
136 |
newContext.fillRect( 0, 0, mainCanvas.width, mainCanvas.height ); |
137 |
|
138 |
mainContext.save(); |
139 |
mainContext.drawImage( newCanvas, 0, 0, mainCanvas.width, mainCanvas.height ); |
140 |
|
141 |
mainContext.restore(); |
142 |
}
|
143 |
|
144 |
// renders the 'film reel' animation that scrolls across the canvas
|
145 |
function drawFilm(){ |
146 |
var sampleWidth = 116; // the width of a sampled video frame, when painted on the canvas as part of a 'film reel' |
147 |
var sampleHeight = 80; // the height of a sampled video frame, when painted on the canvas as part of a 'film reel' |
148 |
var filmSpeed = 20; // determines how fast the 'film reel' scrolls across the generated canvas animation. |
149 |
var filmTop = 120; //the y co-ordinate of the 'film reel' animation |
150 |
var filmAngle = -10 * Math.PI / 180; //the slant of the 'film reel' |
151 |
var filmRight = ( videoSamples.length > 0 )? videoSamples[0].x + videoSamples.length * sampleWidth : mainCanvas.width; //the right edge of the 'film reel' in pixels, relative to the canvas |
152 |
|
153 |
//here, we check if the first frame of the 'film reel' has scrolled out of view
|
154 |
if( videoSamples.length > 0 ){ |
155 |
var bottomLeftX = videoSamples[0].x + sampleWidth; |
156 |
var bottomLeftY = filmTop + sampleHeight; |
157 |
bottomLeftX = Math.floor( Math.cos(filmAngle) * bottomLeftX - Math.sin(filmAngle) * bottomLeftY ); // the final display position after rotation |
158 |
|
159 |
if( bottomLeftX < 0 ){ //the frame is offscreen, remove it's refference from the film array |
160 |
videoSamples.shift(); |
161 |
}
|
162 |
}
|
163 |
|
164 |
// add new frames to the reel as required
|
165 |
while( filmRight <= mainCanvas.width ){ |
166 |
var newFrame = {}; |
167 |
newFrame.x = filmRight; |
168 |
newFrame.canvas = sampleVideo(); |
169 |
videoSamples.push(newFrame); |
170 |
filmRight += sampleWidth; |
171 |
}
|
172 |
|
173 |
// create the gradient fill for the reel
|
174 |
var gradient = mainContext.createLinearGradient( 0, 0, mainCanvas.width, mainCanvas.height ); |
175 |
gradient.addColorStop( 0, '#0D0D0D' ); |
176 |
gradient.addColorStop( 0.25, '#300A02' ); |
177 |
gradient.addColorStop( 0.5, '#AF5A00' ); |
178 |
gradient.addColorStop( 0.75, '#300A02' ); |
179 |
gradient.addColorStop( 1, '#0D0D0D' ); |
180 |
|
181 |
mainContext.save(); |
182 |
mainContext.globalAlpha = 0.9; |
183 |
mainContext.fillStyle = gradient; |
184 |
mainContext.rotate(filmAngle); |
185 |
|
186 |
// loops through all items of film array, using the stored co-ordinate values of each to draw part of the 'film reel'
|
187 |
for( var i in videoSamples ){ |
188 |
var sample = videoSamples[i]; |
189 |
var punchX, punchY, punchWidth = 4, punchHeight = 6, punchInterval = 11.5; |
190 |
|
191 |
//draws the main rectangular box of the sample
|
192 |
mainContext.beginPath(); |
193 |
mainContext.moveTo( sample.x, filmTop ); |
194 |
mainContext.lineTo( sample.x + sampleWidth, filmTop ); |
195 |
mainContext.lineTo( sample.x + sampleWidth, filmTop + sampleHeight ); |
196 |
mainContext.lineTo( sample.x, filmTop + sampleHeight ); |
197 |
mainContext.closePath(); |
198 |
|
199 |
//adds the small holes lining the top and bottom edges of the 'fim reel'
|
200 |
for( var j = 0; j < 10; j++ ){ |
201 |
punchX = sample.x + ( j * punchInterval ) + 5; |
202 |
punchY = filmTop + 4; |
203 |
mainContext.moveTo( punchX, punchY + punchHeight ); |
204 |
mainContext.lineTo( punchX + punchWidth, punchY + punchHeight ); |
205 |
mainContext.lineTo( punchX + punchWidth, punchY ); |
206 |
mainContext.lineTo( punchX, punchY ); |
207 |
mainContext.closePath(); |
208 |
punchX = sample.x + ( j * punchInterval ) + 5; |
209 |
punchY = filmTop + 70; |
210 |
mainContext.moveTo( punchX, punchY + punchHeight ); |
211 |
mainContext.lineTo( punchX + punchWidth, punchY + punchHeight ); |
212 |
mainContext.lineTo( punchX + punchWidth, punchY ); |
213 |
mainContext.lineTo( punchX, punchY ); |
214 |
mainContext.closePath(); |
215 |
}
|
216 |
mainContext.fill(); |
217 |
}
|
218 |
|
219 |
//loop through all items of videoSamples array, update the x co-ordinate values of each item, and draw its stored image onto the canvas
|
220 |
mainContext.globalCompositeOperation = 'lighter'; |
221 |
for( var i in videoSamples ){ |
222 |
var sample = videoSamples[i]; |
223 |
sample.x -= filmSpeed; |
224 |
mainContext.drawImage( sample.canvas, sample.x + 5, filmTop + 10, 110, 62 ); |
225 |
}
|
226 |
|
227 |
mainContext.restore(); |
228 |
}
|
229 |
|
230 |
// renders the canvas title
|
231 |
function drawTitle(){ |
232 |
mainContext.save(); |
233 |
mainContext.fillStyle = 'black'; |
234 |
mainContext.fillRect( 0, 0, 368, 25 ); |
235 |
mainContext.fillStyle = 'white'; |
236 |
mainContext.font = "bold 21px Georgia"; |
237 |
mainContext.fillText( "SINTEL", 10, 20 ); |
238 |
mainContext.restore(); |
239 |
}
|
240 |
|
241 |
// renders all the text appearing at the top left corner of the canvas
|
242 |
function drawDescription(){ |
243 |
var text = []; //stores all text items, to be displayed over time. the video is 60 seconds, and each will be visible for 10 seconds. |
244 |
text[0] = "Sintel is an independently produced short film, initiated by the Blender Foundation."; |
245 |
text[1] = "For over a year an international team of 3D animators and artists worked in the studio of the Amsterdam Blender Institute on the computer-animated short 'Sintel'."; |
246 |
text[2] = "It is an epic short film that takes place in a fantasy world, where a girl befriends a baby dragon."; |
247 |
text[3] = "After the little dragon is taken from her violently, she undertakes a long journey that leads her to a dramatic confrontation."; |
248 |
text[4] = "The script was inspired by a number of story suggestions by Martin Lodewijk around a Cinderella character (Cinder in Dutch is 'Sintel'). "; |
249 |
text[5] = "Screenwriter Esther Wouda then worked with director Colin Levy to create a script with multiple layers, with strong characterization and dramatic impact as central goals."; |
250 |
text = text[Math.floor( video.currentTime / 10 )]; //use the videos current time to determine which text item to display. |
251 |
|
252 |
mainContext.save(); |
253 |
var alpha = 1 - ( video.currentTime % 10 ) / 10; |
254 |
mainContext.globalAlpha = ( alpha < 5 )? alpha : 1; |
255 |
mainContext.fillStyle = '#fff'; |
256 |
mainContext.font = "normal 12px Georgia"; |
257 |
|
258 |
//break the text up into several lines as required, and write each line on the canvas
|
259 |
text = text.split(' '); |
260 |
var colWidth = mainCanvas.width * .75; |
261 |
var line = ''; |
262 |
var y = 40; |
263 |
for(var i in text ){ |
264 |
line += text[i] + ' '; |
265 |
if( mainContext.measureText(line).width > colWidth ){ |
266 |
mainContext.fillText( line, 10, y ); |
267 |
line = ''; |
268 |
y += 12; |
269 |
}
|
270 |
}
|
271 |
mainContext.fillText( line, 10, y ); |
272 |
|
273 |
mainContext.restore(); |
274 |
}
|
275 |
|
276 |
//updates the bottom-right potion of the canvas with the latest perfomance statistics
|
277 |
function drawStats( average ){ |
278 |
var x = 245.5, y = 130.5, graphScale = 0.25; |
279 |
|
280 |
mainContext.save(); |
281 |
mainContext.font = "normal 10px monospace"; |
282 |
mainContext.textAlign = 'left'; |
283 |
mainContext.textBaseLine = 'top'; |
284 |
mainContext.fillStyle = 'black'; |
285 |
mainContext.fillRect( x, y, 120, 75 ); |
286 |
|
287 |
//draw the x and y axis lines of the graph
|
288 |
y += 30; |
289 |
x += 10; |
290 |
mainContext.beginPath(); |
291 |
mainContext.strokeStyle = '#888'; |
292 |
mainContext.lineWidth = 1.5; |
293 |
mainContext.moveTo( x, y ); |
294 |
mainContext.lineTo( x + 100, y ); |
295 |
mainContext.stroke(); |
296 |
mainContext.moveTo( x, y ); |
297 |
mainContext.lineTo( x, y - 25 ); |
298 |
mainContext.stroke(); |
299 |
|
300 |
// draw the last 50 speedLog entries on the graph
|
301 |
mainContext.strokeStyle = '#00ffff'; |
302 |
mainContext.fillStyle = '#00ffff'; |
303 |
mainContext.lineWidth = 0.3; |
304 |
var imax = speedLog.length; |
305 |
var i = ( speedLog.length > 50 )? speedLog.length - 50 : 0 |
306 |
mainContext.beginPath(); |
307 |
for( var j = 0; i < imax; i++, j += 2 ){ |
308 |
mainContext.moveTo( x + j, y ); |
309 |
mainContext.lineTo( x + j, y - speedLog[i] * graphScale ); |
310 |
mainContext.stroke(); |
311 |
}
|
312 |
|
313 |
// the red line, marking the desired maximum rendering time
|
314 |
mainContext.beginPath(); |
315 |
mainContext.strokeStyle = '#FF0000'; |
316 |
mainContext.lineWidth = 1; |
317 |
var target = y - frameDuration * graphScale; |
318 |
mainContext.moveTo( x, target ); |
319 |
mainContext.lineTo( x + 100, target ); |
320 |
mainContext.stroke(); |
321 |
|
322 |
// current/average speedLog items
|
323 |
y += 12; |
324 |
if( average ){ |
325 |
var speed = 0; |
326 |
for( i in speedLog ){ speed += speedLog[i]; } |
327 |
speed = Math.floor( speed / speedLog.length * 10) / 10; |
328 |
}else { |
329 |
speed = speedLog[speedLog.length-1]; |
330 |
}
|
331 |
mainContext.fillText( 'Render Time: ' + speed, x, y ); |
332 |
|
333 |
// canvas fps
|
334 |
mainContext.fillStyle = '#00ff00'; |
335 |
y += 12; |
336 |
if( average ){ |
337 |
fps = 0; |
338 |
for( i in fpsLog ){ fps += fpsLog[i]; } |
339 |
fps = Math.floor( fps / fpsLog.length * 10) / 10; |
340 |
}else { |
341 |
fps = fpsLog[fpsLog.length-1]; |
342 |
}
|
343 |
mainContext.fillText( ' Canvas FPS: ' + fps, x, y ); |
344 |
|
345 |
// browser frames per second (fps), using window.mozPaintCount (firefox only)
|
346 |
if( window.mozPaintCount ){ |
347 |
y += 12; |
348 |
if( average ){ |
349 |
fps = 0; |
350 |
for( i in paintCountLog ){ fps += paintCountLog[i]; } |
351 |
fps = Math.floor( fps / paintCountLog.length * 10) / 10; |
352 |
}else { |
353 |
fps = paintCountLog[paintCountLog.length-1]; |
354 |
}
|
355 |
mainContext.fillText( 'Browser FPS: ' + fps, x, y ); |
356 |
}
|
357 |
|
358 |
mainContext.restore(); |
359 |
}
|
360 |
|
361 |
//draw the spining blade that appears in the begining of the animation
|
362 |
function drawBlade(){ |
363 |
if( !blade || blade.x > mainCanvas.width ){ return; } |
364 |
blade.x += 2.5; |
365 |
blade.angle = ( blade.angle - 45 ) % 360; |
366 |
|
367 |
//update blade, an ofscreen canvas containing the blade's image
|
368 |
var angle = blade.angle * Math.PI / 180; |
369 |
var bladeContext = blade.getContext('2d'); |
370 |
blade.width = blade.width; //clear the canvas |
371 |
bladeContext.save(); |
372 |
bladeContext.translate( 200, 200 ); |
373 |
bladeContext.rotate(angle); |
374 |
bladeContext.drawImage( bladeSrc, -bladeSrc.width/2, -bladeSrc.height/2 ); |
375 |
bladeContext.restore(); |
376 |
|
377 |
mainContext.save(); |
378 |
mainContext.globalAlpha = 0.95; |
379 |
mainContext.drawImage( blade, blade.x, blade.y + Math.sin(angle) * 50 ); |
380 |
mainContext.restore(); |
381 |
}
|
382 |
})();
|
Schritt 3: Code-Optimierung: die Regeln Kennen
Die erste Regel der code-performance Optimierung ist: nicht.
Der Sinn dieser Regel ist zu entmutigen Optimierung für die Optimierung Willen, da der Prozess kommt zu einem Preis.
Ein optimiertes Skript wird leichter sein, für die browser Parsen und verarbeiten, aber in der Regel mit einer Belastung für die Menschen, die finden es schwieriger zu Folgen und pflegen. Wenn Sie entscheiden, dass einige Optimierung erforderlich ist, setzen einige Ziele, die vorher, so dass Sie nicht auf Abwege geraten durch den Prozess und es übertreiben.
Das Ziel bei der Optimierung dieses widget wird an die main () - Funktion ausgeführt, die in weniger als 33 Millisekunden, wie es sollte, die passt die frame-rate der Wiedergabe von video-Dateien (sintel.mp4 und sintel.webm). Diese Dateien verschlüsselt wurden, an eine Wiedergabe-Geschwindigkeit von 30 (dreißig Bilder pro Sekunde), was übersetzt etwa 0.33 Sekunden oder 33 Millisekunden pro Bild ( 1 Sekunde ÷ 30 frames ).
Da JavaScript zeichnet eine neue animation frame auf die Leinwand jedes mal die main () - Funktion aufgerufen, die das Ziel der Optimierung wird der Prozess sein, um diese Funktion nutzen 33 Millisekunden oder weniger jedes mal, wenn es läuft. Diese Funktion wiederholt sich mit einem setTimeout (), Javascript-timer, wie unten gezeigt.
1 |
|
2 |
var frameDuration = 33; // set the animation's target speed in milliseconds |
3 |
function main(){ |
4 |
if( video.paused || video.ended ){ return false; } |
5 |
setTimeout( main, frameDuration ); |
.
Diese Regel unterstreicht den Punkt, dass die Optimierung sollte immer durchgeführt werden, am Ende des Entwicklungsprozesses, wenn Sie bereits konkretisiert einige komplette, funktionierende code. Die Optimierung der Polizei lassen Sie uns gehen Sie auf dieser ein, da die widget-Skript ist ein perfektes Beispiel für die komplette, Arbeits-Programm, das ist bereit für den Prozess.
Die Dritte Regel: nicht doch, und Profil den ersten.
Diese Regel ist zu verstehen, Ihr Programm in Bezug auf die Laufzeit-performance. Profiling hilft Ihnen, zu wissen, anstatt zu erraten, welche Funktionen oder Bereiche der Skript nehmen sich die meisten Zeit oder am häufigsten verwendet werden, so dass Sie sich auf diejenigen, die in den Optimierungsprozess. Es ist wichtig genug, um in führende Browser-Schiff mit eingebauten JavaScript-Profiler, oder haben Erweiterungen, die diese Dienstleistung erbringen.
Ich lief das widget unter dem profiler in Firebug, und unten ist ein screenshot der Ergebnisse.



Schritt 4: Legen Sie Sich Einige Der Performance-Metriken
Als Sie lief das widget, ich bin sicher, Sie finden alle Sintel Sachen in Ordnung, und waren absolut hin und Weg von dem Punkt auf der rechten unteren Ecke der Leinwand, die man mit einer schönen Grafik und glänzend text.

Es ist nicht nur ein hübsches Gesicht, dass die box liefert auch einige real-time-performance-Statistiken über das laufende Programm. Es ist eigentlich eine einfache, bare-bones-Javascript-profiler. Das ist richtig! Yo, ich hörte Sie wie profiling, also habe ich ein profiler in Ihren Film, so dass Sie Profil es, während Sie beobachten.
Die Grafik tracks die Render-Zeit, ermittelt durch die Messung, wie lange jeder ausführen von main() übernimmt in Millisekunden. Da diese Funktion zeichnet jedes frame der animation, es ist effektiv der animation frame rate. Jede vertikale Blaue Linie im Diagramm zeigt die Zeit, die ein frame. Die rote horizontale Linie ist die soll-Geschwindigkeit, die wir bei 33ms, passen Sie die video-Datei der frame-raten. Direkt unter der Grafik, die Geschwindigkeit von dem letzten Aufruf von main() ist in Millisekunden angegeben.
Der profiler ist auch eine praktische browser-rendering-Geschwindigkeit testen. Im moment, die Durchschnittliche render-Zeit in Firefox ist 55 MS, 90 MS, im IE 9, 41ms in Chrom, 148ms in Oper und 63ms in Safari. Alle Browser wurden unter Windows XP, außer für den IE 9, die profiliert wurde auf Windows Vista.
Die nächste Metrische unten, die Leinwand FPS (Leinwand Bilder pro Sekunde), die durch das zählen, wie viele Male main() aufgerufen wird pro Sekunde. Der profiler zeigt die neuesten Leinwand-FPS-rate, wenn das video noch abgespielt wird, und wenn es endet, es zeigt die Durchschnittliche Geschwindigkeit aller Aufrufe von main().
Die Letzte Metrik ist Browser-FPS, die misst, wie viele der browser aktualisiert das aktuelle Fenster in jeder Sekunde. Dieser ist nur verfügbar, wenn Sie das widget in Firefox, da kommt es auf ein feature derzeit nur in diesem browser-Fenster genannt.mozPaintCount., eine JavaScript-Eigenschaft, die Spur hält, wie viele Male das browser-Fenster neu gestrichen wurde, da die Webseite zum ersten mal geladen wird.
Die repaints in der Regel auftreten, wenn ein Ereignis oder eine Aktion, ändert sich das Aussehen einer Seite Auftritt, wie wenn Sie nach unten Blättern oder mit der Maus über einen link. Es ist effektiv die browser-Echtzeit-frame-rate, die bestimmt ist von, wie beschäftigt die aktuelle Webseite ist.
Um welche Auswirkungen abschätzen die UN-optimierte Canvas-Animation auf MozPaintCount hatte, entfernte ich das Canvas-Tag und alle JavaScript, um die Browser-Frame-Rate zu verfolgen, wenn nur das Video abgespielt. Meine Tests erfolgten in Firebug Konsole, mit der folgenden Funktion:
1 |
var lastPaintCount = window.mozPaintCount; |
2 |
setInterval( function(){ |
3 |
console.log( window.mozPaintCount - lastPaintCount ); |
4 |
lastPaintCount = window.mozPaintCount; |
5 |
}, 1000); |
Die Ergebnisse: Der browser frame-rate war zwischen 30 und 32 FPS, wenn das video abgespielt wurde, und fiel auf 0-1 FPS, wenn das video beendet. Dies bedeutet, dass Firefox war seine Einstellung Fenster repaint Frequenz, mit der die Wiedergabe von video, codiert mit 30fps. Wenn der test wurde mit der un-optimiert canvas-animation und video-spielen zusammen, es verlangsamt 16fps, wie der browser wurde jetzt kämpfen, um ausführen alle JavaScript-und immer noch neu streichen der Fenster auf, so dass sowohl die video-Wiedergabe und canvas-Animationen träge.
Wir werden jetzt beginnen, tweaking, unser Programm, und wie wir das tun, werden wir merken, in die Render-Zeit, Canvas FPS und Browser-FPS zu Messen, die Auswirkungen unserer änderungen.
Schritt 5: Verwenden Sie requestAnimationFrame()
Die letzten zwei JavaScript-Schnipsel oben verwenden Sie die setTimeout() und setInterval () - timer-Funktionen. Um diese Funktionen zu verwenden, geben Sie einen Zeitintervall in Millisekunden und die callback-Funktion, die Sie wollen ausgeführt, wenn die Zeit verstreicht. Der Unterschied zwischen den beiden ist, dass setTimeout() ruft die Funktion nur einmal, während setInterval() wird es wiederholt.
Während diese Funktionen schon immer unverzichtbaren Werkzeugen in der JavaScript animator ' s kit, haben Sie ein paar Fehler:
Erste, der Zeitintervall ist nicht immer zuverlässig. Wenn das Programm immer noch in der Mitte der Ausführung etwas anderes, wenn das Intervall verstreicht, wird die callback-Funktion wird ausgeführt, später als ursprünglich festgelegt, sobald der browser wird nicht mehr beschäftigt. In der main () - Funktion, setzen wir das Intervall auf 33 Millisekunden, aber als der profiler zeigt, ist die Funktion tatsächlich aufgerufen wird jedes 148 Millisekunden in der Oper.
Zweitens, es gibt ein Problem mit dem browser aktualisiert. Hätten wir eine callback-Funktion generiert, dass die 20 frames einer animation pro Sekunde, während der browser neu gestrichen-Fenster wird nur 12 mal in der Sekunde, 8-Aufrufe zu dieser Funktion wird verschwendet werden, da der Anwender nie zu sehen bekommt, die Ergebnisse.
Endlich, der browser hat keine Möglichkeit zu wissen, dass die Funktion aufgerufen wird, wird die Animation der Elemente im Dokument. Dies bedeutet, dass, wenn diese Elemente scrollen aus der Ansicht oder klickt der Benutzer auf einen anderen tab, wird der Rückruf wird immer noch wiederholt ausgeführt, Verschwendung von CPU-Zyklen.
Mit requestAnimationFrame() löst die meisten dieser Probleme, und es kann verwendet werden, anstelle der timer-Funktionen im HTML5-Animationen. Statt der Angabe einer Zeitspanne, requestAnimationFrame() synchronisiert die Funktion ruft mit dem browser-Fenster aktualisiert. Dies führt zu mehr Flüssigkeit, konsistent animation, da keine frames verworfen werden, und die browser können weitere interne Optimierungen zu wissen, eine animation im Gange ist.
Ersetzen Sie setTimeout() mit requestAnimationFrame in unserem widget, die wir zuerst fügen Sie die folgende Zeile an der Spitze unseres Skripts:
1 |
requestAnimationFrame = window.requestAnimationFrame || |
2 |
window.mozRequestAnimationFrame || |
3 |
window.webkitRequestAnimationFrame || |
4 |
window.msRequestAnimationFrame || |
5 |
setTimeout; |
Als Spezifikation ist noch Recht neu, manche Browser oder browser-Versionen haben Ihre eigenen experimentellen Implementierungen, diese Zeile stellt sicher, dass der name der Funktion Punkte für die richtige Methode, wenn es verfügbar ist, und fällt zurück auf setTimeout (), wenn nicht. Dann in der main () - Funktion, ändern wir diese Zeile:
1 |
|
2 |
setTimeout( main, frameDuration ); |
...:
1 |
requestAnimationFrame( main, canvas ); |
Der erste parameter nimmt die callback-Funktion, die in diesem Fall ist die main () - Funktion. Der zweite parameter ist optional und gibt das DOM-element mit der animation. Es soll verwendet werden, um zu berechnen, weitere Optimierungen.
Beachten Sie, dass die getStats () - Funktion verwendet auch ein setTimeout(), aber das lassen wir, dass man im Ort auf, da diese spezielle Funktion nichts zu tun hat mit dem animieren der Szene. requestAnimationFrame() wurde speziell für Animationen, so dass, wenn Sie Ihre callback-Funktion, die nicht tun, animation, können Sie setTimeout() oder setInterval().
Schritt 6: Benutzen Sie die Page-Visibility-API
Im letzten Schritt haben wir gemacht requestAnimationFrame macht den canvas animation, und jetzt haben wir ein neues problem. Wenn wir beginnen, von dem widget, dann minimieren Sie das browser-Fenster oder wechseln zu einem neuen tab, wird das widget-Fenster repaint-rate drosselt runter um Strom zu sparen. Dadurch verlangsamt sich auch die canvas-animation da ist es jetzt synchronisiert mit dem repaint-rate - das wäre perfekt, wenn das video nicht spielen, auf der Ende.
Wir brauchen eine Möglichkeit, zu erkennen, wenn die Seite nicht angezeigt wird, so dass wir anhalten der Wiedergabe von Videos; das ist, wo die Page-Visibility-API kommt zur Rettung.
Die API enthält eine Reihe von Eigenschaften, Funktionen und Ereignisse, die wir verwenden können, um zu erkennen, ob eine Webseite in der Ansicht oder ausgeblendet werden. Wir können dann code hinzufügen, passt sich unser Verhalten entsprechend. Wir verwenden diese API, um zu pausieren das widget die Wiedergabe von video, wenn die Seite inaktiv ist.
Wir beginnen, indem Sie eine neue Ereignis-listener, um unser Skript:
1 |
|
2 |
document.addEventListener( 'visibilitychange', onVisibilityChange, false); |
Als Nächstes kommt die event-handler-Funktion:
1 |
|
2 |
// Adjusts the program behavior, based on whether the webpage is active or hidden
|
3 |
function onVisibilityChange() { |
4 |
if( document.hidden && !video.paused ){ |
5 |
video.pause(); |
6 |
}else if( video.paused ){ |
7 |
video.play(); |
8 |
}
|
9 |
}
|
Schritt 7: Für Kundenspezifische Formen, Ziehen den Ganzen Weg Auf Einmal
Pfade werden verwendet, um zu erstellen, und zeichnen Sie eigene Formen und Umrisse auf dem - element, das zu allen Zeiten haben einen aktiven Pfad.
Ein Pfad enthält eine Liste von Nebenpfade und jedes Sub-Pfad setzt sich aus Leinwand koordinieren Punkte durch eine Linie oder einen Bogen miteinander verbunden. Der Weg machen und Zeichenfunktionen sind Eigenschaften von der Leinwand Kontextobjekt und können in zwei Gruppen eingeteilt werden.
Es gibt die Unterpfad-making-Funktionen, verwendet um zu definieren, einen Unterpfad und gehören lineTo(), quadraticCurveTo(), bezierCurveTo () und arc(). Dann haben wir die stroke() und fill () der Pfad/Unterpfad Zeichnung Funktionen. Mit stroke() wird ein Umriss, während die fill () - erzeugt eine Form gefüllt, indem Sie entweder eine Farbe, Farbverlauf oder Muster.
Beim zeichnen von Formen und umrissen auf die Leinwand, es ist effizienter erstellen Sie den gesamten Pfad zuerst, dann nur stroke() oder fill() ist es einmal, eher als die Definition und Zeichnung der einzelnen supbath zu einer Zeit. Unter der profiler-Graphen beschrieben, der in Schritt 4 als ein Beispiel, jede einzelne vertikale Blaue Linie ist ein Unterpfad, während Sie alle zusammen machen bis der ganze Strom Weg.

Der stroke () - Methode wird derzeit aufgerufen, innerhalb einer Schleife, die definiert jeder Unterpfad:
1 |
|
2 |
mainContext.beginPath(); |
3 |
for( var j = 0; i < imax; i++, j += 2 ){ |
4 |
mainContext.moveTo( x + j, y ); // define the subpaths starting point |
5 |
mainContext.lineTo( x + j, y - speedLog[i] * graphScale ); // set the subpath as a line, and define its endpoint |
6 |
mainContext.stroke(); // draw the subpath to the canvas |
7 |
}
|
Dieser graph gezeichnet werden können, wesentlich effizienter zu gestalten, definieren Sie zunächst alle Unterpfade, dann ist nur die Erstellung der gesamten aktuellen Pfad auf einmal, wie unten gezeigt.
1 |
|
2 |
mainContext.beginPath(); |
3 |
for( var j = 0; i < imax; i++, j += 2 ){ |
4 |
mainContext.moveTo( x + j, y ); // define the subpaths starting point |
5 |
mainContext.lineTo( x + j, y - speedLog[i] * graphScale ); // set the subpath as a line, and define its endpoint |
6 |
}
|
7 |
mainContext.stroke(); // draw the whole current path to the mainCanvas. |
Schritt 8: Verwenden Sie eine Off-Screen-Leinwand Zum Erstellen der Szene
Diese Optimierung der Technik ist in Bezug auf die in der vorherigen Schritt, dass Sie beide basieren auf dem gleichen Prinzip der Minimierung der Webseite aktualisiert.
Wann immer etwas passiert, dass änderungen eines Dokuments suchen oder Inhalte, die der browser hat, um einen Zeitplan repaint Betrieb bald nach, dass ein update der Benutzeroberfläche. Neu kann eine teure operation sein in Bezug auf die CPU-Zyklen und Kraft, vor allem für den dichten Seiten mit einer Menge von Elementen und animation geht. Wenn Sie den Aufbau einer komplexen animation, Szene, indem viele Elemente ein zu einer Zeit, die , jede neue Ergänzung darf nur der Auslöser einer ganzen repaint.
Es ist besser und viel schneller zu bauen die Szene auf einem Bildschirm angezeigt (in der Erinnerung) , und sobald Sie dies getan haben, malen Sie die ganze Szene nur einmal auf dem Bildschirm sichtbar .
Einfach unten den code, erhält Referenz auf das widget - und seinem Kontext, wir werden das hinzufügen von fünf neuen Linien, erstellen Sie eine off-screen-Leinwand-DOM-Objekt und entsprechen seine Abmessungen mit dem original, sichtbar .
1 |
|
2 |
var mainCanvas = document.getElementById("mainCanvas"); // points to the on-screen, original HTML canvas element |
3 |
var mainContext = mainCanvas.getContext('2d'); // the drawing context of the on-screen canvas element |
4 |
var osCanvas = document.createElement("canvas"); // creates a new off-screen canvas element |
5 |
var osContext = osCanvas.getContext('2d'); //the drawing context of the off-screen canvas element |
6 |
osCanvas.width = mainCanvas.width; // match the off-screen canvas dimensions with that of #mainCanvas |
7 |
osCanvas.height = mainCanvas.height; |
Wir werden dann tun, wie suchen und ersetzen in allen Zeichenfunktionen für alle Verweise auf "mainCanvas" und ändern Sie Sie auf "osCanvas". Verweise auf "mainContext" wird ersetzt mit "osContext". Alles wird nun erstellt, um die neue off-screen-Leinwand, anstelle von den original - .
Schließlich, fügen wir eine weitere Zeile in main (), die Malt, was derzeit auf den off-screen - - in unserem original .
1 |
|
2 |
// As the scripts main function, it controls the pace of the animation
|
3 |
function main(){ |
4 |
requestAnimationFrame( main, mainCanvas ); |
5 |
if( video.paused || video.currentTime > 59 ){ return; } |
6 |
|
7 |
var now = new Date().getTime(); |
8 |
if( frameStartTime ){ |
9 |
speedLog.push( now - frameStartTime ); |
10 |
}
|
11 |
frameStartTime = now; |
12 |
if( video.readyState < 2 ){ return; } |
13 |
|
14 |
frameCount++; |
15 |
osCanvas.width = osCanvas.width; //clear the offscreen canvas |
16 |
drawBackground(); |
17 |
drawFilm(); |
18 |
drawDescription(); |
19 |
drawStats(); |
20 |
drawBlade(); |
21 |
drawTitle(); |
22 |
mainContext.drawImage( osCanvas, 0, 0 ); // copy the off-screen canvas graphics to the on-screen canvas |
23 |
}
|
Schritt 9: Cache-Pfade, Die Als Bitmap-Bilder, Wenn Möglich
Für viele Arten von Grafiken, die Verwendung der Methode drawImage() wird viel schneller als konstruieren Sie das gleiche Bild auf Leinwand mithilfe von Pfaden. Wenn Sie feststellen, dass eine große potion von deinem script ausgegeben wird, zeichnen der wiederholt die gleichen Formen und Konturen immer und immer wieder, Sie können speichern die browser einige arbeiten durch die Zwischenspeicherung der resultierenden Grafik als bitmap-Bild, dann malen Sie nur einmal auf die Leinwand, wenn erforderlich, mit der Methode drawImage().
Es gibt zwei Möglichkeiten, dies zu tun.
Die erste ist durch die Schaffung einer externen Bild-Datei als JPG -, GIF-oder PNG-Bild, dann laden Sie es dynamisch mit JavaScript und kopieren es auf Ihre Leinwand. Der einzige Nachteil dieser Methode ist die zusätzliche Dateien, wird Ihr Programm zum herunterladen aus dem Netz, aber je nach Art der Grafik oder was Ihre Anwendung tut, könnte dies tatsächlich eine gute Lösung. Die animation widget verwendet diese Methode zum laden der spinning blade Grafik -, was unmöglich gewesen wäre, neu zu erstellen mit nur den canvas-Pfad zeichnen-Funktionen.



Die zweite Methode beinhaltet die Zeichnung nur die Grafik einmal, um eine off-screen-Leinwand, anstatt das laden eines externen Bilds. Wir verwenden diese Methode, um cache-der Titel der animation widget. Zuerst erstellen wir eine variable als Verweis auf die neue off-screen-canvas-element erstellt werden. Der Standardwert ist false, so dass wir sagen können, ob oder nicht eine Bild-cache wurde erstellt und gespeichert, sobald das script gestartet wird:
1 |
|
2 |
var titleCache = false; // points to an off-screen canvas used to cache the animation scene's title |
Wir Bearbeiten Sie dann die drawTitle () - Funktion zuerst überprüfen, ob das titleCache Leinwand Bild erstellt wurde. Wenn nicht, erstellt es ein Offscreen-image und speichert einen Verweis darauf in titleCache:
1 |
|
2 |
// renders the canvas title
|
3 |
function drawTitle(){ |
4 |
if( titleCache == false ){ // create and save the title image |
5 |
titleCache = document.createElement('canvas'); |
6 |
titleCache.width = osCanvas.width; |
7 |
titleCache.height = 25; |
8 |
|
9 |
var context = titleCache.getContext('2d'); |
10 |
context.fillStyle = 'black'; |
11 |
context.fillRect( 0, 0, 368, 25 ); |
12 |
context.fillStyle = 'white'; |
13 |
context.font = "bold 21px Georgia"; |
14 |
context.fillText( "SINTEL", 10, 20 ); |
15 |
}
|
16 |
|
17 |
osContext.drawImage( titleCache, 0, 0 ); |
18 |
}
|
Schritt 10: Löschen Sie die Leinwand Mit clearRect()
Der erste Schritt in der Zeichnung eine neue animation frame ist, deaktivieren Sie die Leinwand von der aktuellen. Dies kann entweder geschehen, indem das zurücksetzen der Breite des canvas-element, oder verwenden Sie die clearRect () - Funktion.
Zurücksetzen der Breite hat auch einen Nebeneffekt löschen des aktuellen canvas-Kontext zurück zu seinem default-Zustand ist, die Dinge verlangsamen. Mit clearRect() ist immer der schnellere und bessere Weg zum löschen der Leinwand.
In der main () - Funktion, wir werden das ändern:
1 |
|
2 |
osCanvas.width = osCanvas.width; //clear the off-screen canvas |
...:
1 |
|
2 |
osContext.clearRect( 0, 0, osCanvas.width, osCanvas.height ); //clear the offscreen canvas |
Schritt 11: Umsetzen Schichten
Wenn Sie gearbeitet haben, mit Bild oder video-editing-software wie Gimp oder Photoshop vor, dann sind Sie bereits vertraut mit dem Konzept von Schichten, auf denen ein Bild zusammengesetzt ist, die durch stapeln viele Bilder übereinander, und jeder kann gewählt werden und separat bearbeitet.
Angewendet auf eine Leinwand, animation Szene, jede Ebene eine separate canvas-element, platziert auf der Oberseite jeder anderen Verwendung von CSS, um die illusion von einem einzigen element. Als eine Optimierung der Technik, es funktioniert am besten, wenn es eine klare Unterscheidung zwischen Vordergrund-und hintergrund-Elemente einer Szene, mit der die meisten der Aktion stattfindet, in den Vordergrund. Der hintergrund kann dann gezeichnet werden auf einem canvas-element, ändert sich nicht viel zwischen den frames einer animation, und die Vordergrundfarbe auf eine weitere dynamische canvas-element oben. Auf diese Weise wird die gesamte Szene muss nicht neu gezeichnet werden wieder für jedes Animations-frame.
Leider ist die animation widget ist ein gutes Beispiel für eine Szene, wo können wir sinnvoll die Anwendung dieser Technik, da sowohl die Vordergrund-und hintergrund-Elemente sind sehr animiert.



Schritt 12: Aktualisierung Nur Die sich Ändernden Bereiche einer Animation Szene
Dies ist eine weitere Optimierung der Technik, hängt stark von der animation Szene Komposition. Es kann verwendet werden, wenn der Szene-animation ist konzentriert um einen bestimmten rechteckigen Bereich auf der Leinwand. Wir könnten dann löschen und neu zeichnen, nur neu gezeichnet, die region.
Zum Beispiel, die Sintel-Titel bleibt unverändert, während die meisten der animation, so dass wir verlassen konnte, dass dieser Bereich intakt, wenn das clearing der Leinwand für den nächsten Animations-frame.



Diese Technik implementieren, ersetzen wir die Zeile, fordert die Titel-Zeichnung Funktion in main() mit den folgenden block:
1 |
|
2 |
if( titleCache == false ){ // If titleCache is false, the animation's title hasn't been drawn yet |
3 |
drawTitle(); // we draw the title. This function will now be called just once, when the program starts |
4 |
osContext.rect( 0, 25, osCanvas.width, osCanvas.height ); // this creates a path covering the area outside by the title |
5 |
osContext.clip(); // we use the path to create a clipping region, that ignores the title's region |
6 |
}
|
Schritt 13: Setzen Sie Das Sub-Pixel-Rendering
Sub-pixel-rendering oder anti-aliasing erfolgt, wenn der browser automatisch für die Grafik-Effekte zum entfernen von gezackten Kanten. Es führt zu einer glatteren Suche Bilder und Animationen, und wird automatisch aktiviert, wenn Sie angeben, Bruch-Koordinaten, sondern als ganze Zahl, wenn die Zeichnung auf die Leinwand.
Jetzt gibt es keinen standard, genau wie es getan werden sollte, so subpixel-rendering ist ein wenig inkonsistent zu allen Browsern in Bezug auf die gerenderte Ausgabe. Es verlangsamt die rendering-Geschwindigkeit im browser zu tun einige Berechnungen zu generieren, die Wirkung. Als Leinwand anti-aliasing werden nicht direkt abgeschaltet, der einzige Weg, um zu bekommen es ist immer mit ganzen zahlen in der Zeichnung, koordiniert.
Wir verwenden Math.floor (), um sicherzustellen, ganzen zahlen in unserem Skript immer anwendbar. Beispielsweise die folgende Zeile in drawFilm():
1 |
|
2 |
punchX = sample.x + ( j * punchInterval ) + 5; // the x co-ordinate |
...ist auch so geschrieben:
1 |
|
2 |
punchX = sample.x + ( j * punchInterval ) + 5; // the x co-ordinate |
Schritt 14: die Ergebnisse Messen
Wir habe schon ein paar canvas-animation-Optimierung Techniken, und es jetzt Zeit, um das Ergebnis zu überprüfen.



Diese Tabelle zeigt die vor und nach der durchschnittlichen Render-Zeiten und Leinwand FPS. Wir sehen einige signifikante Verbesserungen auf allen Browsern, obwohl es nur Chrome, die wirklich nahe kommt, um unsere ursprüngliche Ziel, eine maximale 33ms Renderzeit. Dies bedeutet, es ist noch viel Arbeit getan werden, um das gewählte Ziel.
Wir weiterfahren konnten durch die Anwendung allgemeiner JavaScript-Optimierung Techniken, und wenn das immer noch fehlschlägt, vielleicht betrachten toning down die animation, indem einige Glocken und Pfeifen. Aber wir werden nicht auf der Suche auf alle diese Techniken heute, da der Fokus lag hier auf Optimierungen für das - animation.
Die Canvas-API ist noch sehr neu und wächst jeden Tag, so halten Sie Experimentieren, testen, erforschen und weitergeben. Vielen Dank für das Lesen der Anleitung.