The e2d Declarative Canvas View Layer
In this post, we will dive right in and compare using e2d vs direct canvas.
Getting started with e2d
Install e2d or use e2d.js at requirebin to try it!
npm install --save e2d
How do we compare?
Let's take a look at a few side by side comparisons and the benefits/downsides to using e2d vs pure canvas.
A Hello World Example
The hello world example shouldn't exactly be a deciding factor, but consider the following example:
e2d
requires a bit of setup too.
Getting the Mouse Position
In VanillaJS, set up an event listener and capture the mouse position using the getBoundingClientRect()
function, e.clientX
, and e.clientY
properties.
let canvas = document.querySelector('canvas');
let mousePosition = {x:0,y:0};
canvas.addEventListener('mousemove', function(e) {
let rect = canvas.getBoundingClientRect();
mousePosition.x = e.clientX - rect.left;
mousePosition.y = e.clientY - rect.top;
});
To get the mouse position in e2d
, use the renderer.mouseData.x
and renderer.mouseData.y
properties.
let { x, y } = renderer.mouseData;
Comparing the two, we can see that VanillaJS ends up being less code over all. This is because e2d
does many more things under the hood. For Example, e2d enables "custom transformed polygonal mouse regions." To see the code, take a look at this on line 937.
Hit regions are Path2d
objects in pure canvas. See here.. addHitRegion
has little support in today's browsers, but e2d
tries to fix the problem in a big way. User interaction with the mouse on canvas
is one of the main reasons to use it.
Event propagation is another thing to consider with mouse events. These problems are left as an exercise to the reader.
Following the Mouse
Now that we have mouse position, let's use these objects and make a circle follow the mouse.
This is a lot of code to get started, and most of it is boiler-plate, but it works.
Now we get the e2d comparison.
The biggest upside to this approach is default parameters. A full arc(0, 0, radius, 0, Math.PI * 2)
is actually fillArc(radius)
in e2d
instead of the difficult-to-remember VanillaJS approach.
Another upside to this approach is ditching the save()
and restore()
function calls. These functions copy the entire CanvasRenderingContext2D
object's properties and push them to a stack. Internally, e2d
only does a setTransform
vs a full context save()
or restore()
. When using VanillaJS, do not forget to use save
/restore
.
Move a Square with WASD
This is a bit more complicated. The code is messy, but each part is important.
Now we are responsible for each keycode value, each key state, event listeners, and doing some kind of action each frame based on the key state.
Instead, compare the code to the following e2d
example.
Nesting transforms is FUN. rotate()
is a child of translate()
, and fillRect()
is a child of both transforms. Visually, the indentation is html-like. We can certainly compare these function calls to React.createElement(type, props, ...children)
. The data flows to the component much like data flows to a React
component.
Conclusion
Fine grain control over code is valuable because it can teach a deep understanding of Javascript.
Take a look at the following list and make a mental note of the things your canvas app needs:
- Transformable Polygonal Mouse Regions
- Immutable canvas instructions (Make a function with static canvas instructions)
- Touch support (
e2d
has touch support, and HammerJS is an alternative) - Keyboard support (internally
e2d
uses keycode.js and it's only a few kb - A virtual canvas stack (
save
andrestore
are still performant for batch operations) - Implied
setTransform
(thetransform
,scale
, androtate
functions work well enough without custom matrix math) - Creating and mapping polygonal shapes to path instructions
If you need more than 3 of these, e2d
may be the wiser choice for your team!
let result = fillStyle('red',
translate(200, 200,
text("Thank you!")
)
);