Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Stupid Canvas Tricks

9,237 views

Published on

A grab bag of tricks for

  • Be the first to comment

Stupid Canvas Tricks

  1. 1. Stupid Canvas Tricks
  2. 2. Who Am I? Dean Hudson, @deaneroBit Twiddler at Massively Fun
  3. 3. We make games withopen web technology. We’re hiring!
  4. 4. What this is?• Tips tricks and about the Canvas API• 2D Context• Focus on game programming• Focus on bitmaps• Me v. Me Lightning Talks
  5. 5. What this isn’t• A talk about WebGL• Terribly deep on any one topic• A JavaScript or Canvas API tutorial
  6. 6. But first: thedumbest trick Icould think of
  7. 7. Pro Tip: Use HSL instead of RGB when interpolating color.
  8. 8. 1) Basic Tools
  9. 9. A SimpleAnimation Loop
  10. 10. Outline• Call requestAnimationFrame• Handle queued UI events• Call update on your game entities• Render!
  11. 11. Outline• Call requestAnimationFrame• Handle queued UI events• Call update on your game entities• Render!
  12. 12. // game loopgameLoop = { run: function () { this.id = requestAnimationFrame(this.run); update(); draw(); }, stop: function () { cancelAnimationFrame(this.id); }};gameLoop.run();
  13. 13. RAF Shim• Lots of examples on the internet• Set RAF to whichever your browser has• Fallback to set timeout at ~16ms
  14. 14. buildRAFPolyfill = function () { window.requestAnimationFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (cb, elt) { window.setTimeout(function() { cb(+new Date()); }, 1000 / 60); };}
  15. 15. Profiling
  16. 16. chrome://tracing• Wrap things you care about in console.time() / console.timeEnd() calls• Bring up tracing in a tab• Downside: console spam
  17. 17. var profile = function (name, cb) { console.time(name); cb(); console.timeEnd(name);}// then later...profile(render, function () { // my render call. render();});
  18. 18. Testing?
  19. 19. Testing Canvas is hard.• PhantomJS (A little scary...)• Stub out the Canvas API
  20. 20. Testing Canvas is hard.• PhantomJS (A little scary...)• Stub out the Canvas API
  21. 21. 2) Caching Tricks
  22. 22. Basic Technique• Create a canvas element in your code• Get its context• Write pixels to that context• Use the canvas in a drawImage() call on another canvas context• YOU DON’T ADD THE CACHED CANVAS TO THE DOM!
  23. 23. var canvasCache = document.createElement(canvas), cacheCtx;canvasCache.setAttribute(width, 400);canvasCache.setAttribute(height, 400);cacheCtx = canvasCache.getContext(2d);cacheCtx.drawImage(someImage, 0, 0);// later...mainCtx.drawImage(canvasCache, 0, 0);
  24. 24. Double Buffering
  25. 25. Basic Idea• drawImage() (to screen) is expensive• Build a whole screen cache in a back buffer (in memory)• Draw entities to that cache with drawImage() during render• Write the entire cache to the screen with a single drawImage() call
  26. 26. Basic Idea• drawImage() (to screen) is expensive• Build a whole screen cache in a back buffer (in memory)• Draw entities to that cache with drawImage() during render• Write the entire cache to the screen with a single drawImage() call
  27. 27. var backBuffer = document.createElement(canvas), backBufferCtx;backBufferCtx = canvasCache.getContext(2d);// later...var render = function () { var i, ent; mainCtx.drawImage(backBuffer, 0, 0); for (i = 0; i > entities.length; i++) { ent = entities[i]; // this is not quite what youd do but... backBufferCtx.drawImage(ent.cache, ent.x, ent.y) }}
  28. 28. Layered Images
  29. 29. Similar thing...• Save drawImage() calls by compositing into a cache• Draw multiple images to the same cache in order• Attach the cache to a single game entity• Write the composited cache to your frame buffer in your draw call.
  30. 30. =+
  31. 31. ...and much more!
  32. 32. 3) Your Friend, ImageData
  33. 33. WTF is ImageData?• ImageData is what you get back from a getImageData() call• A single byte Array with ALL your pixel data• Pixel data is stored as R, G, B, A tuples
  34. 34. Single pixel Array offsets 0 1 2 3 R G B A 8 bits 8 bits 8 bits 8 bits 4 bytes
  35. 35. Hit Detection
  36. 36. What we want
  37. 37. Strategy• If (x, y) is in the bounding box for the image, check pixels.• getImageData() to get a pixel data array for your sprite at pixel x, y.• Check byte values for pixel.
  38. 38. var isPixelPainted = function (x, y) { ctx = this.imageCache; // this must be a canvas! thresh = this.transparencyThreshold || 100; // Grab a single pixel at x, y: if the alpha channel is greater // than the threshold, were in business. idata acts like a byte // array with each pixel occupying 4 slots (R, G, B , A). idata = ctx.getImageData(x, y, 1, 1); return idata.data[3] > thresh;}
  39. 39. DON’T FORGET: each idata.datavalue is between 0-255!
  40. 40. Image Filters
  41. 41. Since ImageData acts like a byte array....• We can iterate through the pixels and do arithmetic or bitwise manipulation on the data.• Use XOR, NOT, etc. Get weird!
  42. 42. var filter = function (x, y, w, h) { if (!this._cache) return; var ctx = this._cache.getContext(2d), idata = ctx.getImageData(x, y, w, h), i; for (i = 0; i < idata.data.length; i++) { idata.data[i] = (idata.data[i]++) % 255; }};
  43. 43. var filter = function (x, y, w, h) { if (!this._cache) return; var ctx = this._cache.getContext(2d), idata = ctx.getImageData(x, y, w, h), i; for (i = 0; i < idata.data.length; i++) { if (! i % 4 === 3) continue; idata.data[i] = (idata.data[i]++) % 255; }
  44. 44. Also possible, (but not necessarily recommended)• Masking (if the pixel is colored in one image...)• Pixel-wise composition.• Etc.!
  45. 45. Caveats• Cross-domain issues: if your data came from somewhere untrusted, the browser will not send it to the GPU.• Canvas calls exist for some of these things.• You can easily produce garbage.
  46. 46. Questions?
  47. 47. Thanks!Slides: http://j.mp/stupidcanvastricksdean@massivelyfun.com, @deanero http://massivelyfun.com/jobs

×