Styling Canvas Drawings With CSS

Today I implemented an idea to style canvas elements with CSS, I’ve never personally seen this done, and it’s not always practical, but in some cases it would be very handy. For example a text selection api may be comprised of several “elements”, the text, the caret, the selection range rect etc.

Let’s say by default, our drawing will look the image below:

default text selection

great, looks fine, but what if designers, or even developers for organizational purposes, could tweak this with CSS. Well you can! Suppose we want to support the following css:

  #editor .text {
    font: 20px helvetica, arial, sans-serif;
  }

  #editor .text .selection {
    background: #DFF3FC;
  }

First, the API to access such data will look very similar to how we wrote our css, however we provide the css property as well:

style('#editor .text .selection', 'font-family')

Now for the implementation details, keeping in mind this is not targeting cross-browser support, it’s simply a quick naive implementation, and I should note this does not cover the case of style changes, only initial styles.

So, firstly we check our cache, to prevent several expensive lookups, here style.cache is a property of our style function, if it’s available, we simply return the result.

  function style(selector, prop) {
    var cache = style.cache = style.cache || {}
      , cid = selector + ':' + prop;

    if (cache[cid]) return cache[cid];

    ...

Next up we will naively split the selector into an array so that we can work with it.

    ...
    var parts = selector.split(/ +/)
      , len = parts.length
      , parent = root = document.createElement('div')
      , child
      , part;

   ...

We loop through the “parts”, creating one div per “part”. This will allow us to see what the computed style is for an element, that otherwise, does not exist! because it’s a canvas. The naive implementation I have here, supports ids and classes only, so we check the first character, and assign the id and class respectively.

    ...
    for (var i = 0; i < len; ++i) {
      part = parts[i];
      child = document.createElement('div');
      parent.appendChild(child);
      parent = child;
      if ('#' == part[0]) {
        child.setAttribute('id', part.substr(1));
      } else if ('.' == part[0]) {
        child.setAttribute('class', part.substr(1));
      }
    }
    ...

Finally we append the root element to the document.body so that the styles are applied, get the computed style for the given prop, remove the element, and cache/return our value.

  ...
    document.body.appendChild(root);
    var ret = getComputedStyle(child)[prop];
    document.body.removeChild(root);
    return cache[cid] = ret;
  }

Example Usage

Now let’s try it out, first, the css, providing a light yellow background:

  #chalk .text .selection {
    background: #FAFFDF;
  }

In our JavaScript text selection logic, we should provide a JavaScript API for altering styling as well, however for this example, let’s add it directly:

 ctx.fillStyle = style('#editor .text .selection', 'background-color');
 ctx.fill(...)

Once applied, we get the following result:

resulting canvas style

That’s it! you can apply this trick to assign font sizes and families, foreground and background coloring, border colors, anything you can come up with! if anyone comes up with a robust implementation I would love to see it!

Full Source

Here’s the full implementation:

  function style(selector, prop) {
    var cache = style.cache = style.cache || {}
      , cid = selector + ':' + prop;

    if (cache[cid]) return cache[cid];

    var parts = selector.split(/ +/)
      , len = parts.length
      , parent = root = document.createElement('div')
      , child
      , part;

    for (var i = 0; i < len; ++i) {
      part = parts[i];
      child = document.createElement('div');
      parent.appendChild(child);
      parent = child;
      if ('#' == part[0]) {
        child.setAttribute('id', part.substr(1));
      } else if ('.' == part[0]) {
        child.setAttribute('class', part.substr(1));
      }
    }

    document.body.appendChild(root);
    var ret = getComputedStyle(child)[prop];
    document.body.removeChild(root);
    return cache[cid] = ret;
  }

node recap #2

JSONSelect

This first library is JSONSelect. This library allows you to query JSON using css selectors! this seems like an obvious one, but I haven’t personally seen any other implementations.

It’s not node-specific, however it is JavaScript, so naturally it works with nodejs as well, and over all it’s just a neat concept. Suppose we have the following JSON:

JSONSelect

  {
      "name": {
          "first": "Lloyd",
          "last": "Hilaiel"
      },
      "favoriteColor": "yellow",
      "languagesSpoken": [
          {
              "language": "Bulgarian",
              "level": "advanced"
          },
          {
              "language": "English",
              "level": "native"
          },
          {
              "language": "Spanish",
              "level": "beginner"
          }
      ],
      "seatingPreference": [
          "window",
          "aisle"
      ],
      "drinkPreference": [
          "whiskey",
          "beer",
          "wine"
      ],
      "weight": 172
  }

To grab the languages “Bulgarian”, “English” and “Spanish” we could issue the following query:

.languagesSpoken .language

cool!

node-canvas Image#src=Buffer

A few days ago I added support to node-canvas for Image#src= to accept Buffer objects, not only path strings. This needs to be polished, and to increase format support but we can finally apply image data from arbitrary sources:

 var buf = fs.readFileSync(__dirname + '/textures/jpeg');
 var img = new Image;
 img.src = buf;
 ctx.drawImage(img, 0, 0, 400, 400);

When the Buffer is assigned we can sniff the bytes to determine the format by looking for the “magic number”. For example a PNG starts with the following bytes “89 50 4e 47 0d 0a 1a 0a”, while a JPEG starts with “ff d8”.

Express 2.3.8

A recent Express refactor moved Connect’s router middleware into Express so that I could modify it further. This also means that Connect 2.0 will no longer have router. At first it seemed like a good idea, but routing to specific and it gives people the wrong idea, Connect is meant to be an abstraction layer for higher level frameworks, not to be used directly for application logic. Though this is not true for all cases, such as simply serving static files from a directory or two.

This commit consists of a general refactor of the router, while retaining the public API. What does this mean for Express? flexibility! For example now we may query Express to see which (if any) routes match a specific route path:

 app.get('/user/:id');
 // => [Route]

or routes that match any HTTP method:

 app.all('/user/:id');
 // => [Route, Route, Route]

this is functionally equivalent to:

 app.lookup.all('/user/:id');

Another alternative is “matching” rather than “lookup”. With app.match.VERB() we can query Express to see which routes would match the url passed, for example:

app.match.get('/user/12/edit');
// => [Route]

app.match.del('/user/12');
// => [Route]

app.match.all('/user/12?foo=bar');
// => [Route, Route]

In this commit I’ve introduced the ability to register logic for defining parameters. Within Express core we only support middleware-style param pre-conditions, for example auto-loading a user for any route that includes the :uid param:

app.param('uid', function(req, res, next, uid){
  User.find(uid, function(err, user){
    if (err) return next(err);
    req.user = user;
    next();
  });
});

app.get('/user/:uid, function(){
  // req.user
});

while this is great, some cases can be less verbose, while still expanding to middleware-style functions, this is where express-params comes in. This plugin extends Express with additional app.param() logic, for example via RegExp:

  app.param('uid', /^[0-9]+$/);

  app.get('/user/:uid', function(req, res, next){
    var uid = req.params.uid;
    res.send('user ' + uid);
  });

  app.get('/user/:name', function(req, res, next){
    var name = req.params.name;
    res.send('user ' + name);
  });

or via return value, useful for validations and coercion:

  app.param('id', Number);

  app.get('/user/:id', function(req, res, next){
    var id = req.params.id;
    res.send('typeof ' + typeof id + ' ' + id);
  });

Fabric.js

This one is not node related, but I wanted to sneak it in :) Fabric is a slick client-side library providing an object model on top of canvas. It has a built-in drawing mode and path smoothing, SVG parsing and some other really cool features. If you do a lot of canvas work it’s worth checking out, the author did a good job at keeping the source clean and commented, which is very important when utilizing open source.

fabricjs

node-migrate

node-migrate is a very small abstract migration framework. All it cares about is that you supply an up() and down() method, and invoke the callback when your migration is complete:

 exports.up = function(next){
   next();
 };

 exports.down = function(next){
   next();
 };

node-migrate comes with the migrate(1) executable, allowing you to run and create migrations, for example here we add two migrations, populating ./migrations/0-add-pets.js etc.

 $ migrate create add-pets
 $ migrate create add-owners

The contents of our badass pet adding migration might look something like this:

 var db = require('./db');

  exports.up = function(next){
    db.rpush('pets', 'tobi');
    db.rpush('pets', 'loki');
    db.rpush('pets', 'jane', next);
  };

  exports.down = function(next){
    db.rpop('pets');
    db.rpop('pets', next);
  };

Now all we have to do is run the migrations:

 $ migrate
 up : migrations/0-add-pets.js
 up : migrations/1-add-jane.js
 up : migrations/2-add-owners.js
 up : migrations/3-coolest-pet.js
 migration : complete

The next time we run migrate, we’ll see that they have already been completed:

 $ migrate
 migration : complete

For more information check out the GitHub repo.

Move.js

The other night I noticed that Apple’s new iMac page showcased some really slick CSS3 animations, and wanted a really simple and intuitive way to create similar effects so I came up with Move.js.

Below is an example of the API:

move('#example-13 .box2')
  .set('background-color', 'red')
  .x(500)
  .scale(.5)
  .rotate(60)
    .then()
      .rotate(30)
      .scale(1.5)
      .set('border-radius', 5)
      .set('background-color', 'white')
      .then()
        .set('opacity', 0)
        .pop()
      .pop()
  .end();

It’s still in it’s early stages, and I don’t have much in the way of documentation yet however feel free to check out the site as well as the examples in the repository.

node-canvas open sourced

Today we open sourced our latest LearnBoost project, node-canvas, a canvas implementation for nodejs. The project is certainly a work in progress but we implement a large portion of the api, as well as node-specific additions such as Canvas#toBuffer() and Canvas#createPNGStream().

Examples

Shown below is the test suite running side by side with the browser implementation. node-canvas renders the left, and the browser (chrome) renders the canvas on the right using the same code.

node canvas test suite

Below is an example of chrome rendering flot on the left, and node-canvas on the right.

flot running on nodejs