SlideShare uma empresa Scribd logo
1 de 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

Mais conteúdo relacionado

Mais procurados

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
 

Mais procurados (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
 

Semelhante a 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
 
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
 
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
 

Semelhante a 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
 

Mais de 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
 

Mais de 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
 

Último

Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesSinan KOZAK
 
🐬 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
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j
 
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
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Miguel Araújo
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024Results
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptxHampshireHUG
 
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
 
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
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxKatpro Technologies
 
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
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationRadu Cotescu
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsMaria Levchenko
 
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
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking MenDelhi Call girls
 
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
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking MenDelhi Call girls
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Enterprise Knowledge
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Paola De la Torre
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxMalak Abu Hammad
 

Último (20)

Unblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen FramesUnblocking The Main Thread Solving ANRs and Frozen Frames
Unblocking The Main Thread Solving ANRs and Frozen Frames
 
🐬 The future of MySQL is Postgres 🐘
🐬  The future of MySQL is Postgres   🐘🐬  The future of MySQL is Postgres   🐘
🐬 The future of MySQL is Postgres 🐘
 
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
Neo4j - How KGs are shaping the future of Generative AI at AWS Summit London ...
 
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
 
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
Mastering MySQL Database Architecture: Deep Dive into MySQL Shell and MySQL R...
 
A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024A Call to Action for Generative AI in 2024
A Call to Action for Generative AI in 2024
 
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
04-2024-HHUG-Sales-and-Marketing-Alignment.pptx
 
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...
 
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
 
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptxFactors to Consider When Choosing Accounts Payable Services Providers.pptx
Factors to Consider When Choosing Accounts Payable Services Providers.pptx
 
Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024Finology Group – Insurtech Innovation Award 2024
Finology Group – Insurtech Innovation Award 2024
 
Scaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organizationScaling API-first – The story of a global engineering organization
Scaling API-first – The story of a global engineering organization
 
Handwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed textsHandwritten Text Recognition for manuscripts and early printed texts
Handwritten Text Recognition for manuscripts and early printed texts
 
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
 
08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men08448380779 Call Girls In Friends Colony Women Seeking Men
08448380779 Call Girls In Friends Colony Women Seeking Men
 
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...
 
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
08448380779 Call Girls In Diplomatic Enclave Women Seeking Men
 
Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...Driving Behavioral Change for Information Management through Data-Driven Gree...
Driving Behavioral Change for Information Management through Data-Driven Gree...
 
Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101Salesforce Community Group Quito, Salesforce 101
Salesforce Community Group Quito, Salesforce 101
 
The Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptxThe Codex of Business Writing Software for Real-World Solutions 2.pptx
The Codex of Business Writing Software for Real-World Solutions 2.pptx
 

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

Notas do Editor

  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