Written on 8/8/2014 in Web development

Colourful blocks


Design Javascript Web workers

To be fair, I found my design in the previous blog post top-notch, clean and simple, but sweet. But there were ... other opinions. That's the worst thing about design. You can never ever design something that everyone will enjoy or like. Granted, it's the same for everything and it's probably why the human race thrives beyond every other species10. So, I showed it to my SO, who is a very sweet girl. Her eyes just went 'Really? That's the best you can do?' and her explanation was that it was very depressing and colourless, so not at all original or interesting. I do have to mention that the colours black, white or grey are extremely rare in her clothing.

Design, scratch, reiterate

In retrospect, I always start out thinking about an epic layout in my mind. With exactly the right colours, amazing animations and everything perfect and sweet. But then I start design by setting up the structure in HTML. I get some elements in the wrong place1, move them around a bit and struggle for a while. Soon I start scratching points off the board.

An inner monologue occurs very often: Hm, that's not supposed to be there, but I like it like that, so its ok ... Well, animations aren't all that important2, let's get the basic design right first and I'll do that later3... I wanted red, but that's very agressive so I'll go with blue ... Let's make that a little bit lighter ... Wow, that's actually worse, perhaps darker ... That's not any good. Perhaps I'll just use black or white here ... Well, there go all the colours ... I'll just get a great font then. Oh right, I don't know anything about fonts, how about Comic Sans, everyone likes that don't they??

The result is ... NOT AT ALL WHAT I HAD IN MIND. And this, boys and girls is why you don't design by code, but you design by image first. Even after imaging, you'll change stuff when coding but it won't nearly be that much, because you designed something that - without any other considerations - looks good. And you should not alter the main idea behind that design4. But sadly, for this website, it was too late. I still wanted to add some colour though and had some space left that I wanted to use for something special. The Sidebars were unused and actually a very good place to put stuff without messing with any structure or content. In the future I might add some functionality to the blocks, but that remains to be seen.

Colorwall [6]

My inspiration was the Windows Modern UI. I found the tiles idea to look just amazing, with all the colours. It was like a mozaïc rainbow. I could have just filled it up with a background image or some fancy canvas stuff but I already played around with that. I decided to give HTML5's Scalable Vector Graphics a go. For those among you that don't know what SVG is, look it up. (hah ;)). SVG is easy to understand, easy to create and is supported by most5 modern browsers. It's also very easy to dynamically add content to it and even CSS animations and effects. The contents of the SVG is XML and you can even assign classes, ids and selectors to them to animate or change specific pieces in your image.

I played around with it a bit in JSFiddle and soon created it on the fly, with variable colours, the whole nine yards.

I did have one issue though, I didn't want this pure design functionality to interfere with the other functionality. F.e. browser freeze, unsupported codes etc. Anything happening here should have no impact whatsoever on the rest of the website. Since Javascript runs single threaded, I was thinking about implementing async javascript. You know, with all the callbacks and stuff7. The kind of code you return to a week later, and have to find a Spaghetti to Dutch translator for. Luckily for me, HTML5 supports a feature named 'Web Workers'. Its a very nifty piece of functionality that makes it possible to get some work done in the background. I have not tested how it performs when errors occur in the worker whatsoever, but I trust the browser - and w3c people.11

Web worker

My honest opinion when first starting out was: 'What the H is this?'. Messages, events, ... you create the worker by telling it what file to use?? I soon changed my mind because it was amazing. The 'synchronised' code for providing the background was already finished and at first thought I felt I would have to rewrite everything to make this work. But I was wrong. It took a mere 5 minutes to alter the code to work with this background worker. My previous experiences with multi-threading were more Hellish. The only good experience I have had with multi-threading was with the C# Task api but this, this was just amazingly simple.

Granted, it's not a lot of code and functionality is more limited, but I could think of a hundred ways8 to make this bit of functionality more interesting. Without all the limitations I felt when using other language's multi-threading API's. I could render the background block by block, or change colours without really impacting the rest of the website's functionality. The only important thing that was left to do was check whether the browser supports workers and / or svg and just do nothing when it doesn't. White looks good too, right?

I have a small piece of code that creates and starts the worker. This however, indicates that the worker closes itself. When you want to be in control of that, you can just return the worker object that was created and call the close function on that.

function StartNewWorker(filename, data, execFunction) {
    var worker;
    if (Modernizr.webworkers) {
        worker = new Worker(filename);
        worker.onmessage = execFunction;
        worker.postMessage(data);
    }
    return worker;
}

This bit of code9 will start the worker and uses an anonymous function to put the svg into the "Sides" container.

var InitBlockBackground = function InitBlockBackground() {
// Initialize the colorful block background
var worker;
function createBlockBackground() { // This extra function was implemented for a workaround when resizing the window..
    var container = document.getElementById("Sides");
    if (Modernizr.inlinesvg && window.detections.windowWidth() > 1200 && !window.detections.mobile()) { // Only when SVG is supported, the window width is wide enough and its not a mobile device
        if (typeof worker === "undefined") { // Web  worker support on browser?
            worker = StartNewWorker("/Scripts/Blocks.js", { width: container.offsetWidth, height: container.offsetHeight }, function (event) {
                var svgContainer = document.getElementById("Sides")

                svgContainer.innerHTML = event.data;
            });
        } else {
            worker.postMessage({ width: container.offsetWidth, height: container.offsetHeight });
        }
    } else {
        var background = document.getElementById("BackgroundBlocks");
        if (background != null && background.parentNode !== "undefined") {
            background.parentNode.removeChild(background);
        }
    }
}

Finally, the code for building the svg. I did have to work around a big, but logical annoyance with the web worker. Obviously it does not support direct DOM manipulation. But I was hoping to be able to build the tags and attributes using the "document.createElementNS(..)" or an alternative method. I did not yet succeed on a way to do this but for the time being, this will work fine.

function drawRectangle(element, x, y, width, height, style) {
    element += '<rect x="' + x + '" y="' + y + '" width="' + width + '" height="' + height + '" style="' + style + '" />';

    return element;
}

function getRandomColour() {
    var colour1 = Math.floor((Math.random() * 255));
    var colour2 = Math.floor((Math.random() * 255));
    var colour3 = Math.floor((Math.random() * 255));

    return "fill:rgb(" + colour1 + "," + colour2 + "," + colour3 + ")";
}

// Create a row of blocks
function fillRow(element, startX, drawWidth, drawHeight, elementWidth, row, padding, height) {
    var y = height * (row - 1) + padding * row;
    var nextX = startX + padding;

    while (nextX < startX + drawWidth - padding && nextX < elementWidth - padding) {
        var possibleWidth = drawWidth - (nextX - startX);
        possibleWidth = (possibleWidth / drawWidth) * 150;

        var nextWidth = Math.floor((Math.random() * possibleWidth) + 1);

        if (nextWidth < 25) {
            nextWidth = nextWidth + 25;
        }

        if (nextWidth + (nextX - startX) + padding > drawWidth - padding) {
            nextWidth = drawWidth;
        }

        if (nextWidth + (nextX - startX) > elementWidth - padding || nextWidth + (nextX - startX) > drawWidth - padding) {
            nextWidth = drawWidth - (nextX - startX);
        }
        element = drawRectangle(element, nextX, y, nextWidth, height, getRandomColour());
        nextX = nextX + nextWidth + padding;
    }

    return element;
}

// Fill a rectangle with blocks
function fillSpace(element, startX, drawWidth, drawHeight, elementWidth, elementHeight) {
    var padding = 10;
    var height = 60;
    var row = 1;
    var y = (height * (row - 1) + padding * row) + height;

    while (y <= drawHeight - 10 && y <= elementHeight - 10) {
        element = fillRow(element, startX, drawWidth, drawHeight, elementWidth, row, padding, height);
        row++;
        y = (height * (row - 1) + padding * row) + height;
    }

    return element;
}

// This will create the string for filling up the background
function fillBackground(height, width) {
    var svg = '<svg xmlns="http://www.w3.org/2000/svg" width="' + width + 'px" height="' + height + 'px" id="BackgroundBlocks">';

    svg = fillSpace(svg, 0, width / 2 - 490 - 10, height, width, height); // Left sidebar
    svg = fillSpace(svg, width / 2 + 490, width / 2 - 490 - 10, height, width, height); // Right sidebar

    return svg + "</svg>";
}

// This function is the event handler, whenever called from the main thread, it will start the blocks generation and return the generated result
self.onmessage = function (oEvent) {
    var width = oEvent.data.width;
    var height = oEvent.data.height;

    self.postMessage(fillBackground(height, width));
};

I'll leave you to dive into the deeper end of the web workers, try it out, you will be amazed! More reads about it:

MSDN
MDN
Html 5 Rocks
WHATWG

Newer Older Top
blog comments powered by Disqus