Last week in our festive coding challenge, we explored how to code a sliding puzzle toy with some of the latest front-end techniques to grace the developer’s tool kit. But with Christmas drawing ever nearer, we thought it was time to up the ante with a ‘build your own emoji tree’ creation from front-end developer Kate. You can have a play below (or on CodePen). But do tweet us with your tree designs, @simpleweb, we’d love to see them!
If you want to build one yourself, stick around for our second festive tutorial from Kate below.
See the Pen Tree Decorator by Kate Howard (@KateH2) on CodePen.
Here is the seasonal story of how the tree came to be:
The Original Treeđ˛
First of all, I created a basic tree in Sketch, exported it as an SVG and used the code from that. As you can see itâs comprised of four overlapping, mathematically beautiful right-angled triangles (polygon
s), and a rect
for the trunk. Later, I added a few shadows to each triangle to make it look marginally more realistic, so this code did get a bit more complex.
Tree Topper Selector â
I started off the customisation by controlling something simple – the decoration to sit at the top of the tree. Traditionally, this is either a star or an angel, although the Apple angel emoji is a quite creepy baby with wings but no body, so that’s why a star is the default. When a different option is selected, the handleChange
function sets that as the textContent
of the topSelector
div. Easy!
Add Present đ
In a similar vein, I added an event listener to a button labelled “Add present”, which adds a present emoji to the textContent
of the present container (making sure to set its width to match that of the tree, or it all ends up a mess).
Baublesâ¨
Time to make things a bit more interesting. I went back to Sketch to add baubles on each branch, then set about letting the user change their colour. This was done with custom CSS properties, which are set on the root element with the prefix --
…
…referenced in the fill attribute for each bauble using the var()
function…
…and controlled via the values of a couple of colour pickers with ‘change’ event listeners.
Decorationsđ
Once it was 3D the tree called out for more decorations. I added a ‘click’ event listener to the tree itself that adds a div to the DOM, setting its top and left values according to the coordinates of the MouseEvent (e.clientX
and e.clientY
). Its textContent
is set to the value of a select
element, filled with an increasingly wide variety of emojis that vaguely resemble Christmas ornaments.
Being absolutely positioned, the decorations didnât really work when the browser got resized. I sneakily solved this problem by moving the tree into the top left, after an embarrassingly long time trying to add new SVG text elements to the tree instead.
When I came to add lights (i.e. yellow circle
s) I worked out the trick behind this, which wasnât all that difficult:
const light = document.createElementNS("http://www.w3.org/2000/svg", 'circle');
rather than const light = document.createElement('circle');
Twinkly lightsđĄ
Next, I wanted to make the lights twinkly. This was done with a function that changes the radius of each one every 500 milliseconds, selecting a random number of pixels between 1 and 2.
Let it snow âď¸
Next, for some reason, I decided it was a good idea to conjure up JavaScript snow, using a “Let it snow! â︔ button to call letItSnow()
, which includes two nested functions, createFlake()
and snowfall()
.
createFlake()
creates an absolutely positioned <div>
 which starts off with its top
property set to 0px, and left
set to a random number within the width of the screen. snowfall()
increases the top
value by 1px at a random interval, so each one falls down the screen at its own speed.
This was lovely but as the flakes kept falling forever it made my laptop whir quite a lot, so I made each flake remove itself from the DOM when its top exceeded the height of the tree. This led to a few simple styling changes to add some snowy ground (a white div with a width of 100%), and a limit on the number of presents in the present container, so they wouldn’t look silly with snow disappearing halfway down them.
As the processing power usage still sounded excessive I set about making it possible to stop new snow being created. Originally this was with the same button, which toggled its textContent
from “Let it snow! â︔ to “Stop, it’s too snowy đ¨ď¸” when clicked, and used this textContent
value to determine what function to perform. This all got horribly confusing and led me to look into various solutions, such as a simple return;
to exit the letItSnow()
function, and custom errors with a throw statement.
None of this worked, and what I had missed was that the two buttons have to do two separate, but quite simple, things – letItSnow()
(set an interval on createFlake()
) and stopSnow()
(clear that interval). This was also much easier once they were actually two different buttons (which each toggle the other to display: none;
, so they appear to be the same button). Crucially, they do both need to refer to snowing
, the value returned by setting the interval, so I had to set snowing = null
in global scope.
Cat Button đ
At the request of a colleague I added a cat into the mix, which knocks decorations off. The suggestion was actually to throw a cat into the tree, but I thought that was a bit too ambitious and cruel.
Tinselđ
To make tinsel I used SVG path
elements, created by mousedown
and mouseup
events set on the tree while tinsel is selected in the decoration selector. The d
attribute for each of them is set to four coordinates: M (the start of the tinsel) has the coordinates of the mousedown event (with some maths to account for margins), and C (Bezier curve) has the coordinates of a spot 18px below the start point, followed by 18px below the end point, then the end point itself, which is where the mouseup event occurred.
The two lower points make the path curve downwards slightly. A better explanation can be found here.
It could look a bit more like tinsel, and also doesnât really work if someone tries to break it by swiping vertically rather than straight across the tree, but it was quite interesting to work out.
This was a fun festive challenge, and great practice for working with CSS and vanilla JavaScript (especially as Iâve got so used to React (/Native) that I nearly forgot how to do event listeners). I also learnt a lot about SVGs, which have always kind of scared me, and hopefully Iâll stay motivated to continue exploring these whenever I run out of good films to watch over the holidays.
If youâd like to discuss your startup or project, get in touch with Simpleweb today.