If you haven’t had the chance yet, give the application a try! Since the last post, I’ve added the ability to change background colors. Click here or on the screencap below to open up the application. After you make a snowflake, you can share the image to your computer or share it on Facebook. (It will work on mobile devices, but it’s still not optimized for touch events. That will have to wait for a future version.)
You can also see this flickr set for some of the snowflakes I created with this app.
Using multiple canvases
Although the application is rather simple looking on the surface, there are actually several canvases in use, some on-screen and some off-screen. These canvases are placed on top of each other, or used to cut out shapes from other canvases by changing the globalCompositeOperation. In total there are ten different canvas instances used for various purposes, plus an array of six canvases containing the differently colored gradient backgrounds.
The interactive board
The interactive board (on the right side of the application) containing the folded paper triangle shape is made up of three canvases of equal size layered on top of each other. Layering the canvases is easy using CSS by using absolute positioning and different z-index values, and placing these canvases in one div that can be positioned as desired.
boardBackgroundCanvas: A colored background canvas, which only changes when the user selects a different background color.
polygonLayerCanvas: This canvas contains the polygon outlines as they are drawn by the user. The graphics consist of vertices (dots) and lines only. The filled in color is drawn into the boardCanvas below.
boardCanvas: A canvas containing the cut out triangle shape, which is white with a transparent background.
Here is an illustration of these three canvases:
When the user draws new polygons on the board (or edits existing polygons by dragging vertices), the cut-out shape is accomplished by drawing an opaque polygon onto the
boardCanvas after setting the globalCompositeOperation to “destination-out”, which preserves existing content in the canvas where it doesn’t overlap drawn image.
When the user presses the “make snowflake!” button, several things happen to draw the snowflake on the left. In total, six canvases are used: two on-screen and four off-screen. These canvases are used together to create the snowflake with a folded-paper shading effect and colored background.
displayBackgroundCanvas holds a gradient-colored background (explained below), and on top of this is a
displayCanvas holding the snowflake image and its shadow with a transparent background.
Off-screen, a snowflake shape is created by copying the cut-out triangle shape from the interactive board twelve times into a
snowflakeShapeCanvas, using appropriate translations, rotations, and scaling transformations (the geometry of these transformations will be the subject of a future blog post). Once this off-screen snowflake shape is created, this shape is used to “cut out” the same shape from a textured and shaded paper image which is stored in a separate off-screen
paperShadingCanvas (this shading is created programmatically – see below). This time we use the “destination-in”
globalCompositeOperation, which preserves existing content in the canvas only wherever it overlaps the newly drawn image. We first draw the
paperShadingCanvas into an off-screen
bufferCanvas, set the
globalCompositeOperation to “destination-in”, then draw the
snowflakeShapeCanvas onto the
bufferCanvas. See the illustration below:
Once we have this properly shaded snowflake stored in the buffer canvas off-screen, we assemble the on-screen snowflake image by drawing a shadow in another off-screen canvas first (explained below), then drawing this shadow and the shaded snowflake from the buffer into the on-screen
displayCanvas. Behind this
displayCanvas is a
displayBackgroundCanvas, which contains the gradient colored background.
Although the HTML5 canvas has built-in methods for drawing shadows, at the time of this writing it doesn’t work with bitmaps with transparency in current version of Chrome due to a bug (see here). So for cross-browser consistency I decided to draw my own shadows. Here is where the StackBlur for Canvas by Mario Klingemann (a.k.a. quasimondo) came to the rescue! (I changed the code slightly so it would take a canvas context as an argument rather than a canvas object). I wrote a
makeShadow() method which copies the
snowflakeShapeCanvas to a new canvas, while changing the color to a semi-transparent gray shadow color. This canvas is then blurred using quasimodo’s StackBlur. This shadow canvas can then be copied into the on-screen display canvas with an offset, creating the shadow effect behind the snowflake.
The paper shading
This web app uses no images loaded at runtime. The paper shading is drawn programmatically at the beginning and stored in an off-screen
paperShadingCanvas, using the following method that I came upon after some experimentation, and which again uses quasimondo’s StackBlur:
- Gray wedges drawn of various shadings to suggest folded paper,
- noise is added to this image by randomly shifting pixel colors
- The image is blurred with StackBlur by Quasimodo
- more noise (subtler this time) is added.
The gradient backgrounds
The background colors behind the snowflake may look solid, but they are actually subtle gradients, which I found to look nicer. But the gradients are subtle enough that a “banding” effect could be noticeable. So I used my dithered gradients for the HTML5 canvas that I posted at this blog here. (See the radial version here, and dithered gradients for Processing here).
Since the code for drawing these gradients is computationally intensive (although still rather fast), the gradient backgrounds are drawn once at the beginning of the application and stored in an array of canvases, then drawn into the
displayBackgroundCanvas as needed when the user selects a different color.
More to come!
There is a lot more going on in this application, and I plan on blogging about more of the code in the future. In particular, the use of transformations to create the snowflake symmetries, plus the code used for image saving and Facebook sharing are worthy of further discussion. Stay tuned!