SlideShare ist ein Scribd-Unternehmen logo
1 von 44
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

Weitere ähnliche Inhalte

Was ist angesagt?

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 LabsAlfonso Peletier
 
Hidden Gems in Swift
Hidden Gems in SwiftHidden Gems in Swift
Hidden Gems in SwiftNetguru
 
JavaScript Design Patterns
JavaScript Design PatternsJavaScript Design Patterns
JavaScript Design PatternsDerek Brown
 
C# v8 new features - raimundas banevicius
C# v8 new features - raimundas baneviciusC# v8 new features - raimundas banevicius
C# v8 new features - raimundas baneviciusRaimundas Banevičius
 
Exploring Canvas
Exploring CanvasExploring Canvas
Exploring CanvasKevin Hoyt
 
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 resourcescorehard_by
 
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
 
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
 

Was ist angesagt? (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
 

Ähnlich wie 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 JavaScriptkvangork
 
Object-Oriented Javascript
Object-Oriented JavascriptObject-Oriented Javascript
Object-Oriented Javascriptkvangork
 
TypeScript Introduction
TypeScript IntroductionTypeScript Introduction
TypeScript IntroductionDmitry Sheiko
 
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 developerAndrea 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.docxmattjtoni51554
 
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 WillVincenzo 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.pdfarcotstarsports
 
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 VisualisationAlex Hardman
 
Sencha Touch
Sencha TouchSencha Touch
Sencha TouchCraig Walker
 
Virtual Madness @ Etsy
Virtual Madness @ EtsyVirtual Madness @ Etsy
Virtual Madness @ EtsyNishan 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 2013Laurent_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, 2015pixelass
 

Ähnlich wie 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
 

Mehr von 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 Tricksdeanhudson
 
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 Presdeanhudson
 
Reading Slides
Reading SlidesReading Slides
Reading Slidesdeanhudson
 

Mehr von 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
 

KĂźrzlich hochgeladen

How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerThousandEyes
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slidespraypatel2
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationSafe Software
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdfhans926745
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfEnterprise Knowledge
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...Neo4j
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonetsnaman860154
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationMichael W. Hawkins
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsEnterprise Knowledge
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024The Digital Insurer
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024The Digital Insurer
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityPrincipled Technologies
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...Martijn de Jong
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘RTylerCroy
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024Rafal Los
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Igalia
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Servicegiselly40
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Scriptwesley chun
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreternaman860154
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Drew Madelung
 

KĂźrzlich hochgeladen (20)

How to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected WorkerHow to Troubleshoot Apps for the Modern Connected Worker
How to Troubleshoot Apps for the Modern Connected Worker
 
Slack Application Development 101 Slides
Slack Application Development 101 SlidesSlack Application Development 101 Slides
Slack Application Development 101 Slides
 
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time AutomationFrom Event to Action: Accelerate Your Decision Making with Real-Time Automation
From Event to Action: Accelerate Your Decision Making with Real-Time Automation
 
[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf[2024]Digital Global Overview Report 2024 Meltwater.pdf
[2024]Digital Global Overview Report 2024 Meltwater.pdf
 
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdfThe Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
The Role of Taxonomy and Ontology in Semantic Layers - Heather Hedden.pdf
 
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...Workshop - Best of Both Worlds_ Combine  KG and Vector search for  enhanced R...
Workshop - Best of Both Worlds_ Combine KG and Vector search for enhanced R...
 
How to convert PDF to text with Nanonets
How to convert PDF to text with NanonetsHow to convert PDF to text with Nanonets
How to convert PDF to text with Nanonets
 
GenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day PresentationGenCyber Cyber Security Day Presentation
GenCyber Cyber Security Day Presentation
 
IAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI SolutionsIAC 2024 - IA Fast Track to Search Focused AI Solutions
IAC 2024 - IA Fast Track to Search Focused AI Solutions
 
Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024Tata AIG General Insurance Company - Insurer Innovation Award 2024
Tata AIG General Insurance Company - Insurer Innovation Award 2024
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
Boost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivityBoost PC performance: How more available memory can improve productivity
Boost PC performance: How more available memory can improve productivity
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024The 7 Things I Know About Cyber Security After 25 Years | April 2024
The 7 Things I Know About Cyber Security After 25 Years | April 2024
 
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
Raspberry Pi 5: Challenges and Solutions in Bringing up an OpenGL/Vulkan Driv...
 
CNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of ServiceCNv6 Instructor Chapter 6 Quality of Service
CNv6 Instructor Chapter 6 Quality of Service
 
Automating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps ScriptAutomating Google Workspace (GWS) & more with Apps Script
Automating Google Workspace (GWS) & more with Apps Script
 
Presentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreterPresentation on how to chat with PDF using ChatGPT code interpreter
Presentation on how to chat with PDF using ChatGPT code interpreter
 
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
Strategies for Unlocking Knowledge Management in Microsoft 365 in the Copilot...
 

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

Hinweis der Redaktion

  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