Canvas Query

  • Extended canvas for gamedevelopers
  • Methods chaining
  • Easy setup for a logic loop, rendering, mouse, touch, gamepad and keyboard
cq(640, 480)
  .drawImage(image, 0, 0)
  .fillStyle("#ff0000")
  .fillRect(64, 64, 32, 32)
  .appendTo("body");

Important changes

These are changes that can impact your current code:

  • Effects has been removed as a useles feature
  • Framework events has been lowercased in order to keep up with js naming conventions
  • Framework has been moved to a plugin canvasquery.framework.js

Overviews

0.9 0.8 0.7

Download

Current version 0.9

Getting started

You can try the code using button playground

Creating wrapper

From existing canvas

var canvas = document.create("canvas");
var canvas = document.getElementById("something");

cq(canvas);

From image

var image = new Image;
var image = document.getElementById("something");

cq(image);

From CSS Selector

cq("#canvas");
cq(".image");

Empty

cq(320, 240);

Fullscreen

cq();

CocoonJS

This will create CocoonJS specific ScreenCanvas - should be used once to create first canvas that will suit as a screen.

cq.cocoon()

Antialias/Smoothing

If you want sharp oldschool experience you can set this value before creating your canvases. This will work for both browser and CocoonJS.

cq.smoothing = false;

Additional info

  • Wrapper will have all original Canvas2DContext methods and properties.
  • For reference print this and learn this

You can still access original context and canvas element

cq("#something").canvas;
cq("#something").context;

Chaining

CanvasQuery provides methods chaining similar to jQuery:

cq().fillStyle("#ff0000").fillRect(0, 0, 640, 480).drawImage(image, 0, 0).blur();

All setters/getters has been transformed to functions:

cq().fillStyle("#ff0000").globalAlpha(0.2).lineWidth(6);

However sometimes it is more convenient to use .set() method

cq().set({
  fillStyle: "#ff0000",
  globalAlpha: 0.2,
  lineWidth: 6
});

Clone

Any change done to the wrapper will be applied to the original provided (or created) canvas element. Whenever you want to break the chain reaction and get a fresh copy use .clone() method:

cq().clone().setHsl(...);

Appending

If you want to insert your canvas to document body use .appendTo() method:

cq(320, 240).fillStyle("#00ff00").fill(0, 0).appendTo("body");

You can use either css selector or provide a DOM element.

Help

If you have any question ask it here or on stackoverlow

Extensions

borderImage

.borderImage (image, x, y, top, right, bottom, left, fill)

Create expandable widgets (buttons, frames, e.t.c) - fill can be set to false, true, or color as a string.

circle

.circle (x, y, radius)

Creates a circle path. You will have to call fill() or stroke() afterwards. Calls beginPath() internally

Additional methods are fillCircle() and strokeCircle()

clear

.clear (color = null)

Clears the canvas using clearRect or fillRect if the color is specified.

crop

.crop (x, y, width, height)

Crops the canvas to specified rect.

drawSprite

It is just a shorthand for copying a rect out of image using an array instead of arguments list.

.drawSprite(image, sprite, x, y, scale)

  • image - source image
  • sprite - [x, y, w, h] - region to be coppied
  • x, y - where to draw it
  • scale - scale WITHOUT using the scale() method

imageLine

Draws a line using provided image

.imageLine (image, region, x, y, ex, ey)

  • image - image to be used as line graphics
  • region - [x, y, w, h] region of image to be used as sprite
  • x, y, ex, ey - line start and end

trim

.trim (color = false, data)

  • color transparent color
  • data if an object is provided it will contain trim boundaries

Automatically trims the image. If no color is provided transparent pixels will be used to determine boundaries.

var boundaries = { };

/* trim transparent pixels and save boundaries */
cq(someImage).trim(null, boundaries);

resize

.resize (width, height)

Provide false as width or height to keep aspect ratio

.resize (scale)

Scales both width and height

roundRect

.roundRect (x, y, width, height, radius)

Creates a rounded rectangle. You will have to call fill() or stroke() afterwards.

Caching

Extra useful for recoloring sprites or creating GUI and then caching expensive results as an Image

temp

Instead of creating a new Canvas object each time you want to make some temporary operations you can use .temp() method to get an intermmediate workspace.

.temp() .temp(width, height) .temp(image) .temp(canvas)

Remember that there is only one temp canvas instance in project. Meaning .temp() always returns the same temporary canvas wrapper.

cache

Flush contents of your canvas to an Image for laters. I use this especially for refreshing GUI elements, storing text and sprites color variations.


  var temp = cq.temp(someImage);

  temp.shiftHsl(0.5, null, null);

  sprites[color] = temp.cache();

  /* ... */

  this.layer.drawImage(sprites["red"], 32, 32);

Blending

Blending two layers (as in gimp/photoshop) using globalCompositeOperation - performance benchmark

use .blend (above, mode, mix)

Allows you to blend two layers using a blend mode like in gimp/photoshop. Wrapper will be used as a bottom layer.

  • above [canvas, image, cq, color] - a layer that will be blend on wrapped canvas
  • mode - list of available blend modes
  • mix [0 - 1] - blending ammount
cq(someImage).blend(anotherImage, "hard-light", 0.6);
cq(someImage).blend("#ff0000", "hue", 1.0);

Colors

cq.color

Creates Canvas Query color object which can be used to convert color throught variety of formats. It is CQ's internal property - not a context extension

var color = cq.color(arguments);

cq.color(128, 64, 32, 0.5);
cq.color("#ff00aa");
cq.color("rgb(32, 64, 128)");
cq.color("rgba(32, 64, 128, 0.5)");
cq.color("hsl(0.5, 1.0, 0.2)");
cq.color("hsv(0.1, 0.4, 0.5)");

conversions

var color = cq.color(128, 64, 32, 0.5);

color.toArray() // [128, 64, 32, 0.5]
color.toRgb()   // "rgb(128, 64, 32)"
color.toRgba()  // "rgba(128, 64, 32, 0.5)"
color.toHex()   // "#804020"
color.toHsl()   // [0.05, 0.6, 0.31]
color.toHsv()   // [0.05, 0.75, 0.5]

setHsl

Very useful for coloring units and bullets

.setHsl (hue, saturation, lighting)

Values are between 0 and 1. If you don't want to change the value provide null as an argument.

cq(image).setHsl(0.5, null, null); // changes Hue only

shiftHsl

.shiftHsl (hue, saturation, lighting)

Same as .setHsl but instead of being set each component is shifted in positive or negative direction between -1 and 1;

cq(image).adjustHsl(null, -1.0, null); // completly desaturate image

grayscaleToAlpha

.grayscaleToAlpha ()

Convert grayscale of an image to its transparency. Light pixels become opaque. Dark - transparent.

getPalette

.getPalette ()

Returns an array with all colors of an image in hex represantation.

matchPalette

.matchPalette (palette)

Reduces colors to a certain palette. Palette is an array of hex colors ["#ffaa00", "#844223"]

example

Text

textBoundaries

.textBoundaries (text, x, y, maxWidth)

Gets boundaries { width, height } for a wrapped text.

wrappedText

.wrappedText (text, x, y, maxWidth)

Uses fillText to create a wrapped text. You can use \n for newline

gradientText

.gradientText (text, x, y, maxWidth, gradient)

Fills text with a gradient. For best crossbrowser experience text baseline should be set to top before using this method.

Gradient is an array of color stops and values:

cq()
  .textBaseline("top")
  .gradientText("some text", 32, 32, 160, [
    0.0, "#ff0000",
    0.5, "#ffff00",
    0.8, "#00aaaa"
  ]);

Masking

grayscaleToMask

.grayscaleToMask ()

Creates [float] mask from grayscale values. So if you have pixel [128, 128, 128] it is pushed to the array as [128 / 255 = 0.5] If subpixels are not uniform ex [212, 100, 54] they will get converted to grayscale by average

colorToMask

.colorToMask (transparentColor, inverted)

  • inverted [false/true] wether the transparent pixel should be pushed as false or true

Create a [boolean] mask. Selected color will be treated as transparent.

For example using following code on image with red background:

cq(image).colorToMask("#ff0000");

Will result in boolean array [true, false false, true, ... ] where all red pixels become false.

applyMask

.applyMask (mask)

Applies mask to a canvas - false (or 0) pixels become transparent.

fillMask

.fillMask (mask, color)

Fill mask with given color.

.fillMask (mask, colorA, colorB)

Fill mask using a gradient before colorA and colorB.

Check out using mask and game-icons

Framework

Micro framework which allows you to quickly deploy bare bones canvas application with mouse and keyboard. Especially useful for javascript playgrounds.

You need to include framework plugin first:

<script src="http://canvasquery.com/canvasquery.framework.js"></script>

Check out example usage

Setup

cq().framework({

  /* game loop using requestAnimationFrame */
  onstep: function(delta, time) { },

  /* same as above - provided if you want to separate logic from rendering */
  onrender: function(delta, time) { },

  /* window resize */
  onresize: function(width, height) { },

  /* mouse events */
  onousedown: function(x, y) { },
  onmouseup: function(x, y) { },
  onmousemove: function(x, y) { },
  onmousewheel: function(delta) { },

  /* touch events */
  ontouchstart: function(x, y, touches) { },
  ontouchend: function(x, y, touches) { },
  ontouchmove: function(x, y, touches) { },

  /* keyboard events */
  onkeydown: function(key) { },
  onkeyup: function(key) { },

  /* gamepad events (chrome only) */
  ongamepaddown: function(button, gamepad) { },
  ongamepadup: function(button, gamepad) { },
  ongamepadmove: function(xAxis, yAxis, gamepad) { },

  ondeviceorientation: function(alpha, beta, gamma) { },

  /* user drops image from disk */
  ondropimage: function(image) { }

}).appendTo("body");

Within the framework - if context is not specified this becomes a reference to the canvas wrapper which was used to call the framework method. It is explained later under advanced usage

Game loop

onstep is internally requestAnimationFrame

Provided delta argument is a difference of time between current and last frame in miliseconds. You should use it in your calculations to ensure same game speed on different machines:

onstep: function(delta) {

  player.x = player.speed * delta / 1000;

  player.render();
}

Keyboard

in keyboard events key is translated to string ex. "escape", "a", "f4", "pagedown"

Mouse

  • onmousemove (x, y)
  • onmousedown (x, y, button)
  • onmouseup (x, y, button)
  • onmousewheel (delta)

Mouse events are disabled on mobiles

Touch

  • ontouchstart (x, y, touches)
  • ontouchend (x, y, touches)
  • ontouchmove (x, y, touches)

x, y are coordinates of a touch triggering the event while touches are all your fingers currently touching the screen.

Touches are original DOM events so the x position is hidden under pageX property ex: touches[0].pageX

Gamepad

w3c / chrome implementation

This is rather experimental feature.

  • ongamepaddown (button, gamepad)
  • ongamepadup (button, gamepad)
  • ongamepadmove (x, y, gamepad) - left analog stick

Experiment with console.log(button) to get button names

Drop image

Provides a way to quickly deploy an application which expects image from users disk. Check the following example:

http://cssdeck.com/labs/html5-drag-and-drop-image-tool-with-canvasquery

Advanced usage

In fact framework method takes two arguments .framework(events, context) first one is set of events to be applied the other is context which defaults to the canvas wrapper.

Professional application could be built like this:

var game = {

  setup: function() {  

       /* 1st argument says that we should take bind events from `game` object */
    /* 2nd argument says that we want the `game` to be `this` within the events */

    this.layer = cq().framework(this, this);    
    this.layer.appendTo("body");
  },

  onstep: function(delta) {

    this.entities.step(delta);
  },

  onrender: function() {

      this.entities.render(this.layer);
  }

}

game.setup();

Plugins API

Similar to jQuery - however uses no shorthands.

Extending the wrapper

CanvasQuery.Wrapper.prototype.newMethod = function() { ... }

is equal to jQuery:

jQuery.fn.newMethod = function() { ... }

Extending CanvasQuery itself

CanvasQuery.newMethod = function() { ... }

Example plugin

CanvasQuery.Wrapper.prototype.fillWithColor = function(color) {
  this.context.fillStyle = color;
  this.context.fillRect(0, 0, this.canvas.width, this.canvas.height);
  return this;
}

Please let me know if you write any plugin.

Credits

Main contributors

Thanks to

New documentation is running on node.js