Changing the canvas; Redefining Nails Inc’s polish-builder

As part of Tangent Snowball’s recent re-brand of Nails Inc’s website, I undertook the challenge of making a nail polish customiser in HTML canvas and JavaScript. It’s been something completely new to me, providing more than a few new challenges. I’m here to share some of those.

Nails Inc is a highly popular nail polish brand based in London who have in the last year extended their reach overseas with a US online store and a space in SAKS, Fifth avenue. In terms of their online presence, they receive millions of page views per year, with an average of 750,000 visits a month.

We discussed the best way to bring this idea to life, and canvas was chosen for it’s speed on mobile devices as opposed to building it all with HTML & jQuery, and the ability to easily take a snapshot of the canvas and allow users to share their creation was a real clincher. In terms of Tangent’s (and my) development this was a big deal, as it was the first time they had ever used canvas on a live client site, and my first time ever using it.

Now that the project is live (and you can view it here), I thought I would share some of the main challenges, beyond the regular learning of canvas, on the off chance anyone is suffering from the same issues (or is just interested!):

nails_shot

Responsiveness

The first main challenge was that it needed to scale appropriately with the rest of the site, as well as be optimised for tablet and mobile. With most HTML this is not a problem. However, the elements painted to a canvas do not respond to regular media queries, and therefore an alternative approach was required.

This involved manipulating the width attribute of each elements object (e.g. the box, the bottle, the cap) when the canvas changed size. So on resize, a check would see if the page was below the threshold for the site’s mobile view, and adjusted the width, height, posX, and posY attributes accordingly.

This made my approach to responsiveness fairly straightforward. However if anyone is looking for a more fluid change, I would recommend setting up the width of each of your elements to be a certain percentage of the canvas width; thereby ensuring it will react each time the canvas size changes. For example, if your standard canvas size is 500px and your regular element is 100px, you can continuously set the element’s width to 20% of the canvas, and this will maintain your object’s scale in relation to the canvas, however large or small it gets.

Object Oriented JavaScript

This was quite the challenge for me, but something I can now look back on and say that I learnt the most about from this project. Due to the sheer amount of JavaScript required to implement all of the behaviour and manipulate all of the individual elements, the main JavaScript file ended up over 1100 lines long; without a form of structure, this would be impossible to maintain now.

My architecture is far from perfect, and changed slightly as the project went on based on things I learnt, but this is the current crux of it:

• There is one overarching object
• Each piece of the customiser has it’s own object inside this object (bottle, cap, polish)
• Each object has two consistent functions to make it easy to understand

1. handleClick() – This passes various data from the front end to the JavaScript, and in each circumstance is responsible for updating the object’s information when an appropriate interaction is carried out by the user.

As an example, let’s take filling the bottle with polish. The handleClick() function here is responsible for:
– Checking the selection to see if it’s a pattern or a block colour polish
– Finding the appropriate image or hex code in the array
– Setting this as the pending colour
– Updating the front end to reflect the users selection
– Calling the draw() function

This last part is key to the layout; there is one function that is called each time the canvas needs updating. This makes it clear when a drawing action is taking place, and creates one point of reference responsible for it. This function largely consists of each of the object’s second function:

2. drawX() – e.g. drawPolish() – This is responsible for actually painting to the canvas and showing the user what their next action should be. So to continue our polish example, drawPolish() will:

– Set the pending polish to current
– Check the filltype of the current colour
– Set the fillStyle as either a pattern(image) or as the colour itself
– Draw the polish using the objects current attributes (polish.width, polish.posX…)
– Remove the disabled state from the next button to show the user they can move on

There are of course many more aspects to this layout, but this simple core has enabled me to more forward with development knowing that the base is cleanly set out and, perhaps most importantly, that it wouldn’t be too tasking to explain the layout to another developer.

Click vs Touch for co-ordinates

This issue is fairly specific, but definitely worth raising. As part of the customiser, the user is able to adorn their polish box with stickers of their choosing. The way they do this is:

– Click a sticker from a list
– The canvas presents them with two areas that they can add the sticker to
– The user clicks within that area
– The sticker is added

The issue I had here was that the canvas wasn’t interpreting clicks in the same way as taps when it came to working out the X and Y co-ordinate of the click. The way I got around this was:

1) Create two functions, one that grabs the position using event.pageX/Y, and another that does so using event.targetTouches[0].pageX/Y

2) Check for touch device capabilities (I did this using Modernizr) and then assign the correct one under a prototype with the same name:

if(Modernizr.touch) {
    HTMLCanvasElement.prototype. Coords = touchCoords;
} else {
    HTMLCanvasElement.prototype. Coords = clickCoords;
}

This meant that the rest of the code could operate as normal (by referencing.Coords), and this was the only check required. However, this solution falls short when you have a device with both touch and click capabilities (there are a few of these about now). I am still looking at the best solution for this, so if you’re reading this and have been through the same issue, please drop me a line!

Pre-loading

There was an interesting issue I came across whilst trying to change the content of the canvas based on the change of a dropdown. One particular cap in the customiser was capable of having a monogrammed image engraved onto it. A user would choose from this dropdown, and on change the canvas should update to show that image. However I found that only the first image was visible; changing the option wasn’t showing the image.

After some investigation, I discovered that this was because canvas wasn’t actually capable of loading the image in time to display it. The solution for this came from my colleague Andy Sellick who created a nifty couple of functions that would:

– Iterate through an array of images
– Push each item through a function
– This function then created a new image and loaded the array item as the source
– Then added this completed image to the array in place of the original string

The result of this was that you could preload all images on window.load, and then just reference the image from the array rather than creating it again. I ended up using this for most assets within the application, the result of which is a bit of a loading screen when a user arrives, and then an experience free of half-loaded images and waiting for assets to appear. Fair trade in my eyes!

In all honesty there is a long, long list of issues and roadblocks that have been overcome, as with any project. The fact is we’d be here all day. Nevertheless, all of these, plus the pressure associated with getting it completed and to a high standard for release, really helped to keep me focused and shorten the learning curve.

Overall I’m hugely pleased with the outcome. I was able to go from concept, to prototype, to live in 4 weeks, and integrate with the backend so the client can control the options users can choose when customising. With a happy client and a stable product, I’m looking forward to more ‘outside-the-norm’ projects with Tangent Snowball.