SlideShare a Scribd company logo
The Canvas API
 for Rubyists
  Cascadia Ruby, August 3rd, 2012
       Harry Dean Hudson
Who Am I?




 Dean Hudson, @deanero
Bit Twiddler at Massively Fun
We make games with
open web technology.
  (And are hiring).

http://massivelyfun.com
    @massivelyfun
Word * Word
What is this talk?
• Actually less about Canvas API and more
   about patterns in which it can be used...
• No Ruby! (Sorry Rubyists).
• Play along! https://github.com/massivelyfun/
   canvas-playground
• ...in 5 / 7 / 5.
Canvas API:
2D graphics in browser.
   It is quite simple.
Canvas is
• 2D, immediate mode, graphics API
• Well supported in modern browsers
• A collection of drawing calls
• Not Flash!
(function () {

    var ImageData = function (width, height) {

         this.width     = width   || 10;

         this.height = height || 10;

         numPixels = this.width * this.height;

         this.data = new Uint8Array(numPixels * 4);

    };



    var CanvasGradient = function () {};



    CanvasGradient.prototype.addColorStop = function (offset, color) {

         return undefined;

    };



    var CanvasPattern = function (image, repetitionStyle) {

         this.image = image;

         this.repetitionStyle = repetitionStyle;

    };



    var TextMetrics = function (ctx, text) {




                                           this...
         // quick and dirty style

         var fontSize = parseInt(ctx.font),

             chars = text.split().length;

         this.width = fontSize * chars;

    }



    var CanvasRenderingContext2D = function (canvas) {

         this.canvas                         = canvas;

         this.fillStyle                      = "rgb(0,0,0)";

         this.font                           = "10px sans-serif";

         this.globalAlpha                    = 1.0;

         this.globalCompositionOperation = "source-over";

         this.lineCap                        = "butt";

         this.lineJoin                       = "miter";

         this.lineWidth                      = 1.0;

         this.miterLimit                     = 10;

         this.textAlign                      = "start";

         this.textBaseLine                   = "alphabetic";

         this.shadowBlur                     = 0;

         this.shadowColor                    = "rgba(0,0,0,0)";

         this.shadowOffsetX                  = 0;

         this.shadowOffsetY                  = 0;

         this.strokeStyle                    = "rgb(0,0,0)";



         this.__width              = this.canvas.width;

         this.__height             = this.canvas.height;

         this.__imageData          = null; // don't do this until we need it, it's a memory hog.

                                           // new ImageData(this.__width, this.__height);

         this.__curX               = 0;

         this.__curY               = 0;
this.__openSubpath      = true;

     this.__initTime         = new Date();

     this.__lastUpdateTime = null;

     this.__lastFillTime     = null;

     this.__updateCount      = 0;

     this.__fillCount        = 0;

};



CanvasRenderingContext2D.prototype.__update = function () {

     var args = Array.prototype.slice.call(arguments);

     this.__lastUpdateTime = new Date();

     this.__updateCount++;

}

CanvasRenderingContext2D.prototype.__fill = function () {

     var args = Array.prototype.slice.call(arguments);

     this.__lastFillTime = new Date();

     this.__fillCount++;

}



// Stub out the real methods. I'm explicitly returning undefined

// in cases where the API calls for void return, so as to be clear

// about the intent. This is a simple sub-set of the API focused

// on operations for images. TODO: implement transforms and state.

CanvasRenderingContext2D.prototype.arc = function (x, y, radius, startAngle, endAngle, counterClockwise) {

     this.__openSubpath      = true;




...plus this...
     return undefined;

};

CanvasRenderingContext2D.prototype.arcTo = function (x1, y1, x2, y2, radius) {

     this.__openSubpath      = true;

     this.__curX = x2;

     this.__curY = y2;

     return undefined;

};

CanvasRenderingContext2D.prototype.beginPath = function () {

     this.__openSubpath = true;

     return undefined;

};

CanvasRenderingContext2D.prototype.bezierCurveTo = function (cpX1, cpY1, cpX2, cpY2, x, y) {

     this.__openSubpath      = true;

     this.__curX = x;

     this.__curY = y;

     return undefined;

};

CanvasRenderingContext2D.prototype.clearRect = function () {

     return undefined;

};

CanvasRenderingContext2D.prototype.clip = function () {

     return undefined;

};

CanvasRenderingContext2D.prototype.closePath = function () {

     this.__openSubpath = false;

     return undefined;

};

CanvasRenderingContext2D.prototype.createImageData = function () {

     var args = Array.prototype.slice.call(arguments);
CanvasRenderingContext2D.prototype.clip = function () {

     return undefined;

};

CanvasRenderingContext2D.prototype.closePath = function () {

     this.__openSubpath = false;

     return undefined;

};

CanvasRenderingContext2D.prototype.createImageData = function () {

     var args = Array.prototype.slice.call(arguments);



     if (args[0].hasOwnProperty('data') && typeof args[0].data !== 'undefined') {

         return new ImageData(args[0].data.length, 1);

     } else if (typeof args[0] === 'number' && typeof args[1] === 'number') {

         return new ImageData(args[0], args[1]);

     } else {

         throw new Error("Invalid arguments. createImageData() takes 1 or 2 args.");

     }

};

CanvasRenderingContext2D.prototype.createLinearGradient = function (xStart, yStart, xEnd, yEnd) {

     return new CanvasGradient();

};

CanvasRenderingContext2D.prototype.createPattern = function (image, repetitionStyle) {

     return new CanvasPattern(image, repetitionStyle);

};

CanvasRenderingContext2D.prototype.createRadialGradient = function (xStart, yStart, radiusStart, xEnd, yEnd, radiusEnd) {




...plus this...
     return new CanvasGradient();

};

CanvasRenderingContext2D.prototype.drawImage = function () {

     switch(arguments.length) {

     case 3:

         return CanvasRenderingContext2D.prototype.__drawImage3.apply(this, arguments);

     case 5:

         return CanvasRenderingContext2D.prototype.__drawImage5.apply(this, arguments);

     case 9:

         return CanvasRenderingContext2D.prototype.__drawImage9.apply(this, arguments);

     default:

         throw new Error("Invalid number of arguments. drawImage() takes, 3, 5, or 9 args.");

     }

};



// All that contortion and I don't do anything with it. I'm stubbing

// this out in case I want to at some point.

CanvasRenderingContext2D.prototype.__drawImage3 = function (image, dx, dy) {

     return undefined;

};

CanvasRenderingContext2D.prototype.__drawImage5 = function (image, dx, dy, dw, dh) {

     return undefined;

};

CanvasRenderingContext2D.prototype.__drawImage9 = function (image, sx, sy, sw, sh, dx, dy, dw, dh) {

     return undefined;

};

CanvasRenderingContext2D.prototype.fill = function () {

     return undefined;

};

CanvasRenderingContext2D.prototype.fillRect = function (x, y, width, height) {
return retImageData;

};

CanvasRenderingContext2D.prototype.isPointPath = function (x, y) {

     return true;

};

CanvasRenderingContext2D.prototype.lineTo = function (x, y) {

     this.__openSubpath      = true;

     this.__curX = x;

     this.__curY = y;

     return undefined;

};

CanvasRenderingContext2D.prototype.measureText = function (text) {

     return new TextMetrics(this, text);

};

CanvasRenderingContext2D.prototype.moveTo = function (x, y) {

     this.__curX = x;

     this.__curY = y;

     return undefined;

};

CanvasRenderingContext2D.prototype.putImageData = function (insertData, dx, dy, sx, sy, sw, sh) {

     if (arguments.length !== 7)

         throw new Error("putImageData requires 7 arguments")



     var imageData = this.__imageData || new ImageData(this.__width,   this.__height),




...plus this...
         startAt     = dx * dy * 4 + dx + 4,

         fromData,

         fromOffset = sx * sy * 4 + sx * 4,

         fromNumPixels = sw * sh * 4 + sw * 4,

         endAt = imageData.length - 1,

         howMany;



     if (typeof fromOffset === 'number' && typeof fromNumPixels === 'number') {

         fromData = insertData.data.slice(fromOffset, fromOffset + fromNumPixels);

     } else {

         fromData = insertData.data;

     }



     startAt + fromData.length > endAt ? howMany = endAt - startAt : howMany = startAt + fromData.length;

     imageData.data.splice(startAt, howMany, fromData);



     return undefined;

};

CanvasRenderingContext2D.prototype.quadraticCurveTo = function (cpX, cpY, x, y) {

     this.__curX = x;

     this.__curY = y;

     return undefined;

};

CanvasRenderingContext2D.prototype.rect = function (x, y, width, height) {

     this.__curX = x;

     this.__curY = y;

     return undefined;

};

CanvasRenderingContext2D.prototype.restore = function () {

     return undefined;
return undefined;

    };

    CanvasRenderingContext2D.prototype.restore = function () {

         return undefined;

    };

    CanvasRenderingContext2D.prototype.rotate = function (angle) {

         return undefined;

    };

    CanvasRenderingContext2D.prototype.save = function () {

         return undefined;

    };

    CanvasRenderingContext2D.prototype.scale = function (sx, sy) {

         return undefined;

    };

    CanvasRenderingContext2D.prototype.setTransform = function (a, b, c, d, e, f) {

         return undefined;

    };

    CanvasRenderingContext2D.prototype.stroke = function () {

         return undefined;

    };

    CanvasRenderingContext2D.prototype.strokeRect = function (x, y, width, height) {

         return undefined;

    };

    CanvasRenderingContext2D.prototype.strokeText = function (text, x, y, max) {

         return undefined;




           ...and this.
    };

    CanvasRenderingContext2D.prototype.transform = function (a, b, c, d, e, f) {

         return undefined;

    };

    CanvasRenderingContext2D.prototype.translate = function (dx, dy) {

         return undefined;

    };



    var Canvas = function () {

         this.width   = 10; // API default is 300 x 150, but that makes

         this.height = 10; // our ImageData a memory hog.

    };



    Canvas.prototype.getContext = function (cxtType) {

         return new CanvasRenderingContext2D(this);

    };



    Canvas.prototype.toDataURL = function () {

         var buf = new Buffer("Say hello to my little friend.");

         return "data:text/plain;base64," + buf.toString('base64');

    };



    module.exports = Canvas;

})();
The CanvasContext:
It has all the calls to draw
  Pixels to the screen.
Simple, no?
cvs = document.getElementById("canvas")
ctx = cvs.getContext("2D")

# ctx has all your draw methods...

ctx.fillText("Hello!", 100, 100, 200)
Simple, no?
cvs = document.getElementById("canvas")
ctx = cvs.getContext("2D")

# ctx has all your draw methods...

ctx.fillText("Hello!", 100, 100, 200)

     You’ll only see this from here out.
dean@dean:~$ grep prototype canvas.js |
                 wc -l
                   38

  You can learn the draw
    API in a weekend!
dean@dean:~$ grep prototype canvas.js |
                 wc -l
                   38

  You can learn the draw
     API in a weekend!
...so, on to bigger things!
Browser event loops
Are not well suited for games
   You must roll your own
Simple Game Loop
 • Handle queued UI/server events
 • Update state of “things” on screen.
 • Re-draw!
# a fake game loop
gameLoop =
  run: ->
    @handleQueuedEvents()
    @update()
    @draw()
    # loop somehow?
setInterval()?
setTimeout()?
NO!
16 ms !=
16.666...ms
    Text

 (60 FPS)
For accurate loops
requestAnimationFrame()
 Will bring you great joy
requestAnimationFrame
 allows the browser to
manage screen updates
      efficiently...
requestAnimationFrame
 allows the browser to
manage screen updates
      efficiently...

 ...but is not uniformly
        supported.
buildRAFPolyfill = ->
  requestAnimationFrame =
    requestAnimationFrame         ||
    webkitRequestAnimationFrame   ||
    mozRequestAnimationFrame      ||
    oRequestAnimationFrame        ||
    msRequestAnimationFrame       ||
    -> (cb, elt)
      setTimeout (cb) ->
        cb(+new Date())
      , 1000 / 60
class GameLoop
  constructor: (cvs) ->
    @ctx       = cvs.getContext("2D")
    @entities = []

  addEntity: (entity) ->
    @entities.push(entity)

  run: () =>
    tick = @frameId = requestAnimationFrame(@run)

    # Update entities
    entity.update(tick) for entity in @entities

    # Draw!
    entity.draw(@ctx) for entity in @entities

  stop: ->
    root.cancelAnimationFrame(@frameId)
class GameLoop
  constructor: (cvs) ->
    @ctx       = cvs.getContext("2D")
    @entities = []

  addEntity: (entity) ->
    @entities.push(entity)

  run: () =>
    tick = @frameId = requestAnimationFrame(@run)

    # Update entities
    entity.update(tick) for entity in @entities

    # Draw!
    entity.draw(@ctx) for entity in @entities

  stop: ->
    root.cancelAnimationFrame(@frameId)

requestAnimationFrame returns a int “id”
Entities and Rects:
The largest things are built
 From just these pieces.
Simple Game
   Entity
• Respond to update call
• Manage position, height, width
• Delegate draw calls
# Game level object
class Entity
  # Rect is a drawing primitive
  constructor: (options = {}) ->
    @rect     = options.rect ? null
    {x, y}    = options
    @position = {x: x, y: y}

  update: (tick) ->
    # override and do something interesting here.

  draw: (ctx) ->
    # delegate to your drawing primitive!
    @rect.draw(ctx)

module.exports = Entity
# Game level object
class Entity
  # Rect is a drawing primitive
  constructor: (options = {}) ->
    @rect     = options.rect ? null
    {x, y}    = options
    @position = {x: x, y: y}

  update: (tick) ->
    # override and do something interesting here.

  draw: (ctx) ->
    # delegate to your drawing primitive!
    @rect.draw(ctx)

module.exports = Entity

         Game level logic, once per tick
# Game level object
class Entity
  # Rect is a drawing primitive
  constructor: (options = {}) ->
    @rect     = options.rect ? null
    {x, y}    = options
    @position = {x: x, y: y}

  update: (tick) ->
    # override and do something interesting here.

  draw: (ctx) ->
    # delegate to your drawing primitive!
    @rect.draw(ctx)

module.exports = Entity

               DELEGATE!!!!!
Simple Draw
        Primitive
• Associated with Entity
• Responds to draw() call
• Has actual CanvasContext2D drawing calls
• You can determine containment here
  (image hit detection, for instance)
#
# Rect: Drawing primitive for canvas.

class Rect
  constructor: (options = {}) ->
    @width    = options?.width ? 1
    @height   = options?.height ? 1

    {x, y} = options

    @position = {x: x, y: y}

  draw: (canvasCtx) ->
    throw new Error("Implement draw()")

module.exports = Rect
#
# Rect: Drawing primitive for canvas.

class Rect
  constructor: (options = {}) ->
    @width    = options?.width ? 1
    @height   = options?.height ? 1

    {x, y} = options

    @position = {x: x, y: y}

  draw: (canvasCtx) ->
    throw new Error("Implement draw()")

module.exports = Rect

      Your *simple* interface
Rect = require "rect"

# Sometimes we just need the simple things.
# Make a simple box subclass.
class Box extends Rect
  constructor: (options = {}) ->
    super(options)

  draw: (ctx) ->
    {x, y} = @position.get()
    ctx.fillRect(x, y, @width, @height)

module.exports = Box
Rect = require "rect"

# Sometimes we just need the simple things.
# Make a simple box subclass.
class Box extends Rect
  constructor: (options = {}) ->
    super(options)

  draw: (ctx) ->
    {x, y} = @position.get()
    ctx.fillRect(x, y, @width, @height)

module.exports = Box


                Implement draw
Rect = require "rect"

# Sometimes we just need the simple things.
# Make a simple box subclass.
class Box extends Rect
  constructor: (options = {}) ->
    super(options)

  draw: (ctx) ->
    {x, y} = @position.get()
    ctx.fillRect(x, y, @width, @height)

module.exports = Box


      Do work on CanvasContext2D
Canvas tests are hard;
The one true way is to cheat.
Stub, Stub, stub the world.
CanvasRenderingContext2D.prototype.__update = function () {
    var args = Array.prototype.slice.call(arguments);
    this.__lastUpdateTime = new Date();
    this.__updateCount++;
}
CanvasRenderingContext2D.prototype.__fill = function () {
    var args = Array.prototype.slice.call(arguments);
    this.__lastFillTime = new Date();
    this.__fillCount++;
}

//   Stub out the real methods. I'm explicitly returning undefined
//   in cases where the API calls for void return, so as to be clear
//   about the intent. This is a simple sub-set of the API focused
//   on operations for images. TODO: implement transforms and state.

CanvasRenderingContext2D.prototype.arcTo = function (x1, y1, x2, y2, radius) {
    this.__openSubpath    = true;
    this.__curX = x2;
    this.__curY = y2;
    return undefined;



                               ...etc.
};
Questions?
Thanks!




dean@massivelyfun.com, @deanero
     http://massivelyfun.com

More Related Content

What's hot

openFrameworks 007 - GL
openFrameworks 007 - GL openFrameworks 007 - GL
openFrameworks 007 - GL
roxlu
 
TypeScript - All you ever wanted to know - Tech Talk by Epic Labs
TypeScript - All you ever wanted to know - Tech Talk by Epic LabsTypeScript - All you ever wanted to know - Tech Talk by Epic Labs
TypeScript - All you ever wanted to know - Tech Talk by Epic Labs
Alfonso Peletier
 
Hidden Gems in Swift
Hidden Gems in SwiftHidden Gems in Swift
Hidden Gems in Swift
Netguru
 
JavaScript Design Patterns
JavaScript Design PatternsJavaScript Design Patterns
JavaScript Design Patterns
Derek Brown
 
Css5 canvas
Css5 canvasCss5 canvas
Css5 canvas
Vadim Spiridenko
 
C# v8 new features - raimundas banevicius
C# v8 new features - raimundas baneviciusC# v8 new features - raimundas banevicius
C# v8 new features - raimundas banevicius
Raimundas Banevičius
 
Encoder + decoder
Encoder + decoderEncoder + decoder
Encoder + decoder
COMSATS Abbottabad
 
Exploring Canvas
Exploring CanvasExploring Canvas
Exploring CanvasKevin Hoyt
 
WebGL 2.0 Reference Guide
WebGL 2.0 Reference GuideWebGL 2.0 Reference Guide
WebGL 2.0 Reference Guide
The Khronos Group Inc.
 
Oxygine 2 d objects,events,debug and resources
Oxygine 2 d objects,events,debug and resourcesOxygine 2 d objects,events,debug and resources
Oxygine 2 d objects,events,debug and resources
corehard_by
 
Composite Pattern
Composite PatternComposite Pattern
Composite Pattern
Somenath Mukhopadhyay
 
Writing SOLID C++ [gbgcpp meetup @ Zenseact]
Writing SOLID C++ [gbgcpp meetup @ Zenseact]Writing SOLID C++ [gbgcpp meetup @ Zenseact]
Writing SOLID C++ [gbgcpp meetup @ Zenseact]
Dimitrios Platis
 
Js hacks
Js hacksJs hacks
openFrameworks 007 - graphics
openFrameworks 007 - graphicsopenFrameworks 007 - graphics
openFrameworks 007 - graphicsroxlu
 
JavaScript - i och utanför webbläsaren (2010-03-03)
JavaScript - i och utanför webbläsaren (2010-03-03)JavaScript - i och utanför webbläsaren (2010-03-03)
JavaScript - i och utanför webbläsaren (2010-03-03)
Anders Jönsson
 
Functional microscope - Lenses in C++
Functional microscope - Lenses in C++Functional microscope - Lenses in C++
Functional microscope - Lenses in C++
Alexander Granin
 
Cursor implementation
Cursor implementationCursor implementation
Cursor implementationvicky201
 
Say It With Javascript
Say It With JavascriptSay It With Javascript
Say It With Javascript
Giovanni Scerra ☃
 

What's hot (20)

openFrameworks 007 - GL
openFrameworks 007 - GL openFrameworks 007 - GL
openFrameworks 007 - GL
 
TypeScript - All you ever wanted to know - Tech Talk by Epic Labs
TypeScript - All you ever wanted to know - Tech Talk by Epic LabsTypeScript - All you ever wanted to know - Tech Talk by Epic Labs
TypeScript - All you ever wanted to know - Tech Talk by Epic Labs
 
Hidden Gems in Swift
Hidden Gems in SwiftHidden Gems in Swift
Hidden Gems in Swift
 
JavaScript Design Patterns
JavaScript Design PatternsJavaScript Design Patterns
JavaScript Design Patterns
 
Css5 canvas
Css5 canvasCss5 canvas
Css5 canvas
 
C# v8 new features - raimundas banevicius
C# v8 new features - raimundas baneviciusC# v8 new features - raimundas banevicius
C# v8 new features - raimundas banevicius
 
Encoder + decoder
Encoder + decoderEncoder + decoder
Encoder + decoder
 
Exploring Canvas
Exploring CanvasExploring Canvas
Exploring Canvas
 
WebGL 2.0 Reference Guide
WebGL 2.0 Reference GuideWebGL 2.0 Reference Guide
WebGL 2.0 Reference Guide
 
Oxygine 2 d objects,events,debug and resources
Oxygine 2 d objects,events,debug and resourcesOxygine 2 d objects,events,debug and resources
Oxygine 2 d objects,events,debug and resources
 
Composite Pattern
Composite PatternComposite Pattern
Composite Pattern
 
Writing SOLID C++ [gbgcpp meetup @ Zenseact]
Writing SOLID C++ [gbgcpp meetup @ Zenseact]Writing SOLID C++ [gbgcpp meetup @ Zenseact]
Writing SOLID C++ [gbgcpp meetup @ Zenseact]
 
Js hacks
Js hacksJs hacks
Js hacks
 
openFrameworks 007 - graphics
openFrameworks 007 - graphicsopenFrameworks 007 - graphics
openFrameworks 007 - graphics
 
JavaScript - i och utanför webbläsaren (2010-03-03)
JavaScript - i och utanför webbläsaren (2010-03-03)JavaScript - i och utanför webbläsaren (2010-03-03)
JavaScript - i och utanför webbläsaren (2010-03-03)
 
Functional microscope - Lenses in C++
Functional microscope - Lenses in C++Functional microscope - Lenses in C++
Functional microscope - Lenses in C++
 
Cursor implementation
Cursor implementationCursor implementation
Cursor implementation
 
Say It With Javascript
Say It With JavascriptSay It With Javascript
Say It With Javascript
 
Pointers
PointersPointers
Pointers
 
P1
P1P1
P1
 

Similar to The Canvas API for Rubyists

javascript Model- Render & canvas sample
javascript Model- Render & canvas samplejavascript Model- Render & canvas sample
javascript Model- Render & canvas sampleHika Maeng
 
Bindings: the zen of montage
Bindings: the zen of montageBindings: the zen of montage
Bindings: the zen of montageKris Kowal
 
Object-Oriented JavaScript
Object-Oriented JavaScriptObject-Oriented JavaScript
Object-Oriented JavaScript
kvangork
 
Object-Oriented Javascript
Object-Oriented JavascriptObject-Oriented Javascript
Object-Oriented Javascript
kvangork
 
TypeScript Introduction
TypeScript IntroductionTypeScript Introduction
TypeScript Introduction
Dmitry Sheiko
 
ES6 Overview
ES6 OverviewES6 Overview
ES6 Overview
Bruno Scopelliti
 
ES6 in Real Life
ES6 in Real LifeES6 in Real Life
ES6 in Real Life
Domenic Denicola
 
05 Geographic scripting in uDig - halfway between user and developer
05 Geographic scripting in uDig - halfway between user and developer05 Geographic scripting in uDig - halfway between user and developer
05 Geographic scripting in uDig - halfway between user and developer
Andrea Antonello
 
Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient
Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficientTh 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient
Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficientBin Shao
 
Player x 0 y ga.docx
Player x 0 y ga.docxPlayer x 0 y ga.docx
Player x 0 y ga.docx
mattjtoni51554
 
Jeroen Vloothuis Bend Kss To Your Will
Jeroen Vloothuis   Bend Kss To Your WillJeroen Vloothuis   Bend Kss To Your Will
Jeroen Vloothuis Bend Kss To Your Will
Vincenzo Barone
 
Webgl para JavaScripters
Webgl para JavaScriptersWebgl para JavaScripters
Webgl para JavaScriptersgerbille
 
need help with code I wrote. This code is a maze gui, and i need hel.pdf
need help with code I wrote. This code is a maze gui, and i need hel.pdfneed help with code I wrote. This code is a maze gui, and i need hel.pdf
need help with code I wrote. This code is a maze gui, and i need hel.pdf
arcotstarsports
 
Using Arbor/ RGraph JS libaries for Data Visualisation
Using Arbor/ RGraph JS libaries for Data VisualisationUsing Arbor/ RGraph JS libaries for Data Visualisation
Using Arbor/ RGraph JS libaries for Data Visualisation
Alex Hardman
 
JavaScript Refactoring
JavaScript RefactoringJavaScript Refactoring
JavaScript Refactoring
Krzysztof Szafranek
 
Sencha Touch
Sencha TouchSencha Touch
Sencha Touch
Craig Walker
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ Etsy
Nishan Subedi
 
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
DroidConTLV
 
Developing web-apps like it's 2013
Developing web-apps like it's 2013Developing web-apps like it's 2013
Developing web-apps like it's 2013
Laurent_VB
 
Exploring fractals in CSS, @fronttrends, Warsaw, 2015
Exploring fractals in CSS, @fronttrends, Warsaw, 2015Exploring fractals in CSS, @fronttrends, Warsaw, 2015
Exploring fractals in CSS, @fronttrends, Warsaw, 2015
pixelass
 

Similar to The Canvas API for Rubyists (20)

javascript Model- Render & canvas sample
javascript Model- Render & canvas samplejavascript Model- Render & canvas sample
javascript Model- Render & canvas sample
 
Bindings: the zen of montage
Bindings: the zen of montageBindings: the zen of montage
Bindings: the zen of montage
 
Object-Oriented JavaScript
Object-Oriented JavaScriptObject-Oriented JavaScript
Object-Oriented JavaScript
 
Object-Oriented Javascript
Object-Oriented JavascriptObject-Oriented Javascript
Object-Oriented Javascript
 
TypeScript Introduction
TypeScript IntroductionTypeScript Introduction
TypeScript Introduction
 
ES6 Overview
ES6 OverviewES6 Overview
ES6 Overview
 
ES6 in Real Life
ES6 in Real LifeES6 in Real Life
ES6 in Real Life
 
05 Geographic scripting in uDig - halfway between user and developer
05 Geographic scripting in uDig - halfway between user and developer05 Geographic scripting in uDig - halfway between user and developer
05 Geographic scripting in uDig - halfway between user and developer
 
Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient
Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficientTh 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient
Th 0230 turbo_chargeyourui-howtomakeyourandroidu_ifastandefficient
 
Player x 0 y ga.docx
Player x 0 y ga.docxPlayer x 0 y ga.docx
Player x 0 y ga.docx
 
Jeroen Vloothuis Bend Kss To Your Will
Jeroen Vloothuis   Bend Kss To Your WillJeroen Vloothuis   Bend Kss To Your Will
Jeroen Vloothuis Bend Kss To Your Will
 
Webgl para JavaScripters
Webgl para JavaScriptersWebgl para JavaScripters
Webgl para JavaScripters
 
need help with code I wrote. This code is a maze gui, and i need hel.pdf
need help with code I wrote. This code is a maze gui, and i need hel.pdfneed help with code I wrote. This code is a maze gui, and i need hel.pdf
need help with code I wrote. This code is a maze gui, and i need hel.pdf
 
Using Arbor/ RGraph JS libaries for Data Visualisation
Using Arbor/ RGraph JS libaries for Data VisualisationUsing Arbor/ RGraph JS libaries for Data Visualisation
Using Arbor/ RGraph JS libaries for Data Visualisation
 
JavaScript Refactoring
JavaScript RefactoringJavaScript Refactoring
JavaScript Refactoring
 
Sencha Touch
Sencha TouchSencha Touch
Sencha Touch
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ Etsy
 
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
Tricks to Making a Realtime SurfaceView Actually Perform in Realtime - Maarte...
 
Developing web-apps like it's 2013
Developing web-apps like it's 2013Developing web-apps like it's 2013
Developing web-apps like it's 2013
 
Exploring fractals in CSS, @fronttrends, Warsaw, 2015
Exploring fractals in CSS, @fronttrends, Warsaw, 2015Exploring fractals in CSS, @fronttrends, Warsaw, 2015
Exploring fractals in CSS, @fronttrends, Warsaw, 2015
 

More from deanhudson

Seattle.rb 6.4
Seattle.rb 6.4Seattle.rb 6.4
Seattle.rb 6.4deanhudson
 
Mcdm presentations
Mcdm presentationsMcdm presentations
Mcdm presentationsdeanhudson
 
Stupid Canvas Tricks
Stupid Canvas TricksStupid Canvas Tricks
Stupid Canvas Tricks
deanhudson
 
Pointer Events in Canvas
Pointer Events in CanvasPointer Events in Canvas
Pointer Events in Canvasdeanhudson
 
Com546 Final Pres
Com546 Final PresCom546 Final Pres
Com546 Final Pres
deanhudson
 
Reading Slides
Reading SlidesReading Slides
Reading Slides
deanhudson
 

More from deanhudson (6)

Seattle.rb 6.4
Seattle.rb 6.4Seattle.rb 6.4
Seattle.rb 6.4
 
Mcdm presentations
Mcdm presentationsMcdm presentations
Mcdm presentations
 
Stupid Canvas Tricks
Stupid Canvas TricksStupid Canvas Tricks
Stupid Canvas Tricks
 
Pointer Events in Canvas
Pointer Events in CanvasPointer Events in Canvas
Pointer Events in Canvas
 
Com546 Final Pres
Com546 Final PresCom546 Final Pres
Com546 Final Pres
 
Reading Slides
Reading SlidesReading Slides
Reading Slides
 

Recently uploaded

How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...
Product School
 
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdfFIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance
 
"Impact of front-end architecture on development cost", Viktor Turskyi
"Impact of front-end architecture on development cost", Viktor Turskyi"Impact of front-end architecture on development cost", Viktor Turskyi
"Impact of front-end architecture on development cost", Viktor Turskyi
Fwdays
 
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
Product School
 
Key Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdfKey Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdf
Cheryl Hung
 
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
Product School
 
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptxIOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
Abida Shariff
 
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdfFIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance
 
Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........
Alison B. Lowndes
 
Leading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdfLeading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdf
OnBoard
 
ODC, Data Fabric and Architecture User Group
ODC, Data Fabric and Architecture User GroupODC, Data Fabric and Architecture User Group
ODC, Data Fabric and Architecture User Group
CatarinaPereira64715
 
Assuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyesAssuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyes
ThousandEyes
 
When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...
Elena Simperl
 
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
Product School
 
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered QualitySoftware Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Inflectra
 
The Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and SalesThe Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and Sales
Laura Byrne
 
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
Sri Ambati
 
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
Product School
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
Prayukth K V
 
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Product School
 

Recently uploaded (20)

How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...How world-class product teams are winning in the AI era by CEO and Founder, P...
How world-class product teams are winning in the AI era by CEO and Founder, P...
 
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdfFIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
FIDO Alliance Osaka Seminar: Passkeys and the Road Ahead.pdf
 
"Impact of front-end architecture on development cost", Viktor Turskyi
"Impact of front-end architecture on development cost", Viktor Turskyi"Impact of front-end architecture on development cost", Viktor Turskyi
"Impact of front-end architecture on development cost", Viktor Turskyi
 
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
From Siloed Products to Connected Ecosystem: Building a Sustainable and Scala...
 
Key Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdfKey Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdf
 
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
AI for Every Business: Unlocking Your Product's Universal Potential by VP of ...
 
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptxIOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
IOS-PENTESTING-BEGINNERS-PRACTICAL-GUIDE-.pptx
 
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdfFIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
FIDO Alliance Osaka Seminar: The WebAuthn API and Discoverable Credentials.pdf
 
Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........Bits & Pixels using AI for Good.........
Bits & Pixels using AI for Good.........
 
Leading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdfLeading Change strategies and insights for effective change management pdf 1.pdf
Leading Change strategies and insights for effective change management pdf 1.pdf
 
ODC, Data Fabric and Architecture User Group
ODC, Data Fabric and Architecture User GroupODC, Data Fabric and Architecture User Group
ODC, Data Fabric and Architecture User Group
 
Assuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyesAssuring Contact Center Experiences for Your Customers With ThousandEyes
Assuring Contact Center Experiences for Your Customers With ThousandEyes
 
When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...When stars align: studies in data quality, knowledge graphs, and machine lear...
When stars align: studies in data quality, knowledge graphs, and machine lear...
 
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
De-mystifying Zero to One: Design Informed Techniques for Greenfield Innovati...
 
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered QualitySoftware Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
Software Delivery At the Speed of AI: Inflectra Invests In AI-Powered Quality
 
The Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and SalesThe Art of the Pitch: WordPress Relationships and Sales
The Art of the Pitch: WordPress Relationships and Sales
 
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
GenAISummit 2024 May 28 Sri Ambati Keynote: AGI Belongs to The Community in O...
 
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
From Daily Decisions to Bottom Line: Connecting Product Work to Revenue by VP...
 
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 previewState of ICS and IoT Cyber Threat Landscape Report 2024 preview
State of ICS and IoT Cyber Threat Landscape Report 2024 preview
 
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
Unsubscribed: Combat Subscription Fatigue With a Membership Mentality by Head...
 

The Canvas API for Rubyists

  • 1. The Canvas API for Rubyists Cascadia Ruby, August 3rd, 2012 Harry Dean Hudson
  • 2. Who Am I? Dean Hudson, @deanero Bit Twiddler at Massively Fun
  • 3. We make games with open web technology. (And are hiring). http://massivelyfun.com @massivelyfun
  • 5. What is this talk? • Actually less about Canvas API and more about patterns in which it can be used... • No Ruby! (Sorry Rubyists). • Play along! https://github.com/massivelyfun/ canvas-playground • ...in 5 / 7 / 5.
  • 6. Canvas API: 2D graphics in browser. It is quite simple.
  • 7. Canvas is • 2D, immediate mode, graphics API • Well supported in modern browsers • A collection of drawing calls • Not Flash!
  • 8. (function () { var ImageData = function (width, height) { this.width = width || 10; this.height = height || 10; numPixels = this.width * this.height; this.data = new Uint8Array(numPixels * 4); }; var CanvasGradient = function () {}; CanvasGradient.prototype.addColorStop = function (offset, color) { return undefined; }; var CanvasPattern = function (image, repetitionStyle) { this.image = image; this.repetitionStyle = repetitionStyle; }; var TextMetrics = function (ctx, text) { this... // quick and dirty style var fontSize = parseInt(ctx.font), chars = text.split().length; this.width = fontSize * chars; } var CanvasRenderingContext2D = function (canvas) { this.canvas = canvas; this.fillStyle = "rgb(0,0,0)"; this.font = "10px sans-serif"; this.globalAlpha = 1.0; this.globalCompositionOperation = "source-over"; this.lineCap = "butt"; this.lineJoin = "miter"; this.lineWidth = 1.0; this.miterLimit = 10; this.textAlign = "start"; this.textBaseLine = "alphabetic"; this.shadowBlur = 0; this.shadowColor = "rgba(0,0,0,0)"; this.shadowOffsetX = 0; this.shadowOffsetY = 0; this.strokeStyle = "rgb(0,0,0)"; this.__width = this.canvas.width; this.__height = this.canvas.height; this.__imageData = null; // don't do this until we need it, it's a memory hog. // new ImageData(this.__width, this.__height); this.__curX = 0; this.__curY = 0;
  • 9. this.__openSubpath = true; this.__initTime = new Date(); this.__lastUpdateTime = null; this.__lastFillTime = null; this.__updateCount = 0; this.__fillCount = 0; }; CanvasRenderingContext2D.prototype.__update = function () { var args = Array.prototype.slice.call(arguments); this.__lastUpdateTime = new Date(); this.__updateCount++; } CanvasRenderingContext2D.prototype.__fill = function () { var args = Array.prototype.slice.call(arguments); this.__lastFillTime = new Date(); this.__fillCount++; } // Stub out the real methods. I'm explicitly returning undefined // in cases where the API calls for void return, so as to be clear // about the intent. This is a simple sub-set of the API focused // on operations for images. TODO: implement transforms and state. CanvasRenderingContext2D.prototype.arc = function (x, y, radius, startAngle, endAngle, counterClockwise) { this.__openSubpath = true; ...plus this... return undefined; }; CanvasRenderingContext2D.prototype.arcTo = function (x1, y1, x2, y2, radius) { this.__openSubpath = true; this.__curX = x2; this.__curY = y2; return undefined; }; CanvasRenderingContext2D.prototype.beginPath = function () { this.__openSubpath = true; return undefined; }; CanvasRenderingContext2D.prototype.bezierCurveTo = function (cpX1, cpY1, cpX2, cpY2, x, y) { this.__openSubpath = true; this.__curX = x; this.__curY = y; return undefined; }; CanvasRenderingContext2D.prototype.clearRect = function () { return undefined; }; CanvasRenderingContext2D.prototype.clip = function () { return undefined; }; CanvasRenderingContext2D.prototype.closePath = function () { this.__openSubpath = false; return undefined; }; CanvasRenderingContext2D.prototype.createImageData = function () { var args = Array.prototype.slice.call(arguments);
  • 10. CanvasRenderingContext2D.prototype.clip = function () { return undefined; }; CanvasRenderingContext2D.prototype.closePath = function () { this.__openSubpath = false; return undefined; }; CanvasRenderingContext2D.prototype.createImageData = function () { var args = Array.prototype.slice.call(arguments); if (args[0].hasOwnProperty('data') && typeof args[0].data !== 'undefined') { return new ImageData(args[0].data.length, 1); } else if (typeof args[0] === 'number' && typeof args[1] === 'number') { return new ImageData(args[0], args[1]); } else { throw new Error("Invalid arguments. createImageData() takes 1 or 2 args."); } }; CanvasRenderingContext2D.prototype.createLinearGradient = function (xStart, yStart, xEnd, yEnd) { return new CanvasGradient(); }; CanvasRenderingContext2D.prototype.createPattern = function (image, repetitionStyle) { return new CanvasPattern(image, repetitionStyle); }; CanvasRenderingContext2D.prototype.createRadialGradient = function (xStart, yStart, radiusStart, xEnd, yEnd, radiusEnd) { ...plus this... return new CanvasGradient(); }; CanvasRenderingContext2D.prototype.drawImage = function () { switch(arguments.length) { case 3: return CanvasRenderingContext2D.prototype.__drawImage3.apply(this, arguments); case 5: return CanvasRenderingContext2D.prototype.__drawImage5.apply(this, arguments); case 9: return CanvasRenderingContext2D.prototype.__drawImage9.apply(this, arguments); default: throw new Error("Invalid number of arguments. drawImage() takes, 3, 5, or 9 args."); } }; // All that contortion and I don't do anything with it. I'm stubbing // this out in case I want to at some point. CanvasRenderingContext2D.prototype.__drawImage3 = function (image, dx, dy) { return undefined; }; CanvasRenderingContext2D.prototype.__drawImage5 = function (image, dx, dy, dw, dh) { return undefined; }; CanvasRenderingContext2D.prototype.__drawImage9 = function (image, sx, sy, sw, sh, dx, dy, dw, dh) { return undefined; }; CanvasRenderingContext2D.prototype.fill = function () { return undefined; }; CanvasRenderingContext2D.prototype.fillRect = function (x, y, width, height) {
  • 11. return retImageData; }; CanvasRenderingContext2D.prototype.isPointPath = function (x, y) { return true; }; CanvasRenderingContext2D.prototype.lineTo = function (x, y) { this.__openSubpath = true; this.__curX = x; this.__curY = y; return undefined; }; CanvasRenderingContext2D.prototype.measureText = function (text) { return new TextMetrics(this, text); }; CanvasRenderingContext2D.prototype.moveTo = function (x, y) { this.__curX = x; this.__curY = y; return undefined; }; CanvasRenderingContext2D.prototype.putImageData = function (insertData, dx, dy, sx, sy, sw, sh) { if (arguments.length !== 7) throw new Error("putImageData requires 7 arguments") var imageData = this.__imageData || new ImageData(this.__width, this.__height), ...plus this... startAt = dx * dy * 4 + dx + 4, fromData, fromOffset = sx * sy * 4 + sx * 4, fromNumPixels = sw * sh * 4 + sw * 4, endAt = imageData.length - 1, howMany; if (typeof fromOffset === 'number' && typeof fromNumPixels === 'number') { fromData = insertData.data.slice(fromOffset, fromOffset + fromNumPixels); } else { fromData = insertData.data; } startAt + fromData.length > endAt ? howMany = endAt - startAt : howMany = startAt + fromData.length; imageData.data.splice(startAt, howMany, fromData); return undefined; }; CanvasRenderingContext2D.prototype.quadraticCurveTo = function (cpX, cpY, x, y) { this.__curX = x; this.__curY = y; return undefined; }; CanvasRenderingContext2D.prototype.rect = function (x, y, width, height) { this.__curX = x; this.__curY = y; return undefined; }; CanvasRenderingContext2D.prototype.restore = function () { return undefined;
  • 12. return undefined; }; CanvasRenderingContext2D.prototype.restore = function () { return undefined; }; CanvasRenderingContext2D.prototype.rotate = function (angle) { return undefined; }; CanvasRenderingContext2D.prototype.save = function () { return undefined; }; CanvasRenderingContext2D.prototype.scale = function (sx, sy) { return undefined; }; CanvasRenderingContext2D.prototype.setTransform = function (a, b, c, d, e, f) { return undefined; }; CanvasRenderingContext2D.prototype.stroke = function () { return undefined; }; CanvasRenderingContext2D.prototype.strokeRect = function (x, y, width, height) { return undefined; }; CanvasRenderingContext2D.prototype.strokeText = function (text, x, y, max) { return undefined; ...and this. }; CanvasRenderingContext2D.prototype.transform = function (a, b, c, d, e, f) { return undefined; }; CanvasRenderingContext2D.prototype.translate = function (dx, dy) { return undefined; }; var Canvas = function () { this.width = 10; // API default is 300 x 150, but that makes this.height = 10; // our ImageData a memory hog. }; Canvas.prototype.getContext = function (cxtType) { return new CanvasRenderingContext2D(this); }; Canvas.prototype.toDataURL = function () { var buf = new Buffer("Say hello to my little friend."); return "data:text/plain;base64," + buf.toString('base64'); }; module.exports = Canvas; })();
  • 13. The CanvasContext: It has all the calls to draw Pixels to the screen.
  • 14. Simple, no? cvs = document.getElementById("canvas") ctx = cvs.getContext("2D") # ctx has all your draw methods... ctx.fillText("Hello!", 100, 100, 200)
  • 15. Simple, no? cvs = document.getElementById("canvas") ctx = cvs.getContext("2D") # ctx has all your draw methods... ctx.fillText("Hello!", 100, 100, 200) You’ll only see this from here out.
  • 16. dean@dean:~$ grep prototype canvas.js | wc -l 38 You can learn the draw API in a weekend!
  • 17. dean@dean:~$ grep prototype canvas.js | wc -l 38 You can learn the draw API in a weekend! ...so, on to bigger things!
  • 18. Browser event loops Are not well suited for games You must roll your own
  • 19. Simple Game Loop • Handle queued UI/server events • Update state of “things” on screen. • Re-draw!
  • 20. # a fake game loop gameLoop = run: -> @handleQueuedEvents() @update() @draw() # loop somehow?
  • 22. NO!
  • 23. 16 ms != 16.666...ms Text (60 FPS)
  • 24. For accurate loops requestAnimationFrame() Will bring you great joy
  • 25. requestAnimationFrame allows the browser to manage screen updates efficiently...
  • 26. requestAnimationFrame allows the browser to manage screen updates efficiently... ...but is not uniformly supported.
  • 27. buildRAFPolyfill = -> requestAnimationFrame = requestAnimationFrame || webkitRequestAnimationFrame || mozRequestAnimationFrame || oRequestAnimationFrame || msRequestAnimationFrame || -> (cb, elt) setTimeout (cb) -> cb(+new Date()) , 1000 / 60
  • 28. class GameLoop constructor: (cvs) -> @ctx = cvs.getContext("2D") @entities = [] addEntity: (entity) -> @entities.push(entity) run: () => tick = @frameId = requestAnimationFrame(@run) # Update entities entity.update(tick) for entity in @entities # Draw! entity.draw(@ctx) for entity in @entities stop: -> root.cancelAnimationFrame(@frameId)
  • 29. class GameLoop constructor: (cvs) -> @ctx = cvs.getContext("2D") @entities = [] addEntity: (entity) -> @entities.push(entity) run: () => tick = @frameId = requestAnimationFrame(@run) # Update entities entity.update(tick) for entity in @entities # Draw! entity.draw(@ctx) for entity in @entities stop: -> root.cancelAnimationFrame(@frameId) requestAnimationFrame returns a int “id”
  • 30. Entities and Rects: The largest things are built From just these pieces.
  • 31. Simple Game Entity • Respond to update call • Manage position, height, width • Delegate draw calls
  • 32. # Game level object class Entity # Rect is a drawing primitive constructor: (options = {}) -> @rect = options.rect ? null {x, y} = options @position = {x: x, y: y} update: (tick) -> # override and do something interesting here. draw: (ctx) -> # delegate to your drawing primitive! @rect.draw(ctx) module.exports = Entity
  • 33. # Game level object class Entity # Rect is a drawing primitive constructor: (options = {}) -> @rect = options.rect ? null {x, y} = options @position = {x: x, y: y} update: (tick) -> # override and do something interesting here. draw: (ctx) -> # delegate to your drawing primitive! @rect.draw(ctx) module.exports = Entity Game level logic, once per tick
  • 34. # Game level object class Entity # Rect is a drawing primitive constructor: (options = {}) -> @rect = options.rect ? null {x, y} = options @position = {x: x, y: y} update: (tick) -> # override and do something interesting here. draw: (ctx) -> # delegate to your drawing primitive! @rect.draw(ctx) module.exports = Entity DELEGATE!!!!!
  • 35. Simple Draw Primitive • Associated with Entity • Responds to draw() call • Has actual CanvasContext2D drawing calls • You can determine containment here (image hit detection, for instance)
  • 36. # # Rect: Drawing primitive for canvas. class Rect constructor: (options = {}) -> @width = options?.width ? 1 @height = options?.height ? 1 {x, y} = options @position = {x: x, y: y} draw: (canvasCtx) -> throw new Error("Implement draw()") module.exports = Rect
  • 37. # # Rect: Drawing primitive for canvas. class Rect constructor: (options = {}) -> @width = options?.width ? 1 @height = options?.height ? 1 {x, y} = options @position = {x: x, y: y} draw: (canvasCtx) -> throw new Error("Implement draw()") module.exports = Rect Your *simple* interface
  • 38. Rect = require "rect" # Sometimes we just need the simple things. # Make a simple box subclass. class Box extends Rect constructor: (options = {}) -> super(options) draw: (ctx) -> {x, y} = @position.get() ctx.fillRect(x, y, @width, @height) module.exports = Box
  • 39. Rect = require "rect" # Sometimes we just need the simple things. # Make a simple box subclass. class Box extends Rect constructor: (options = {}) -> super(options) draw: (ctx) -> {x, y} = @position.get() ctx.fillRect(x, y, @width, @height) module.exports = Box Implement draw
  • 40. Rect = require "rect" # Sometimes we just need the simple things. # Make a simple box subclass. class Box extends Rect constructor: (options = {}) -> super(options) draw: (ctx) -> {x, y} = @position.get() ctx.fillRect(x, y, @width, @height) module.exports = Box Do work on CanvasContext2D
  • 41. Canvas tests are hard; The one true way is to cheat. Stub, Stub, stub the world.
  • 42. CanvasRenderingContext2D.prototype.__update = function () { var args = Array.prototype.slice.call(arguments); this.__lastUpdateTime = new Date(); this.__updateCount++; } CanvasRenderingContext2D.prototype.__fill = function () { var args = Array.prototype.slice.call(arguments); this.__lastFillTime = new Date(); this.__fillCount++; } // Stub out the real methods. I'm explicitly returning undefined // in cases where the API calls for void return, so as to be clear // about the intent. This is a simple sub-set of the API focused // on operations for images. TODO: implement transforms and state. CanvasRenderingContext2D.prototype.arcTo = function (x1, y1, x2, y2, radius) { this.__openSubpath = true; this.__curX = x2; this.__curY = y2; return undefined; ...etc. };
  • 44. Thanks! dean@massivelyfun.com, @deanero http://massivelyfun.com

Editor's Notes

  1. \n
  2. \n
  3. \n
  4. \n
  5. \n
  6. \n
  7. \n
  8. \n
  9. \n
  10. \n
  11. \n
  12. \n
  13. \n
  14. \n
  15. \n
  16. \n
  17. \n
  18. \n
  19. \n
  20. \n
  21. \n
  22. \n
  23. \n
  24. \n
  25. \n
  26. \n
  27. \n
  28. \n
  29. \n
  30. \n
  31. \n
  32. \n
  33. \n
  34. \n
  35. \n
  36. \n
  37. \n
  38. \n
  39. \n
  40. \n
  41. \n
  42. \n
  43. \n
  44. \n