Trello Confetti Effect

Trello just created a page to celebrate their user number reaches 5 million. In their celebration page, they introduce an interesting effect, a number of blue squares rotating and falling from the sky.

By inspecting their source code, I extracted the effect implementation from the page.

As you can see, the effect is implemented with Canvas animation. Trello guys created a light weight particle system.

  • ConfettiMachine is the particle system controller, which takes the responsibility to create new particles and call draw method on them one by one.
  • Particle is the representation of the blue square, it takes the rotation, fading out and rendering.

The source code is extracted for study purpose, all code and credits belongs to Trello guys.

Favicon is not as simple as you think

Favicon is the little icon that displayed on the title bar or tab bar when you browsing a web site. At very beginning, it is used to be the little icon displayed in favoriate bar when user add the website to favorites. Then later, it becomes the the standard way to specify custom icon for a website.

Most web site provides favico, developer add one line description in the <head>:

1
<link rel="shortcut icon" href="/favico.ico" type="image/x-icon">

This simple piece of code works, but there are a lot of issues with this declaration. Actually specify the favico isn’t as simple as you might expect!

For this piece of code, according to W3C document how to favicon, there are 2 issues with it:

First, shortcut isn’t a standard value for rel, it is only for IE.

Second, ico format is a Microsoft oriented file type, not all platform likes it. Linux, Mac, iOS, Android, do not really appreciate this format.

Beides the 2 issues described above, the size of the favicon also matters.

Someone says it should be 16x16. Yes, 16x16 icon is used in tab or tree view. Some other says it should be 32x32. Well, this is also true. 32x32 icon is displayed in toolbar.

16x16 and 32x32 are the most used sizes for favico, but that’s not all. The reality is a lot complicated, I’ll explain this issue later. Let focus on this 2 size first.

To provide the image with 2 different sizes. For ico it is not an issue, since ico is a image container file format, which can encapsulate several images with different sizes and color in a single file. It is convenient for developer, but not for users. Because it means the users need to download a big file, most of the data in which is not used at all.

For the recommended png, it is no way to provide multiple images in a single file. So we have to provide 2 different separate files, and specify them with 2 different <link> tags with sizes attribute. This is a more efficient way, but unfortunately, you’re living in the world has something called Internet Explorer. The favico in png is not supported by IE until IE 11. What a hell!

Actually, there is a lot of issue with IE in this case. There is an great article by Mathias Bynen that discussed this issue in detail, which provides a lot of interesting information related to favico.

Microsoft Windows 8

Besides typical browser usage, Favicon is also used to create Metro Tile by IE 10 and IE 11 on Windows 8. It requires something quite different. Here is a MSDN document that described how to create custom tiles for IE11 websites. For these tile icon, Windows 8 also asks for a background color other than the icon itself.

Android

In the age of mobile internet, favicon is not just used by the desktop browser, but also mobile devices. On mobile devices, there are some more specific requirements.

On android, the screen size and resolution varies between devices. So the visual elements on Android are measured in dp. According to the screen resolution, there are different conversion ratios between dp and px. And to have pixel perfect image on Android, developer should provide several images for different dp-px-ratios.

Google have a well written document that described how to create icon for the web app that added to homescreen.

iOS

For iOS, it is simliar to Android, but seems to be much more complicated. On iOS, it is also possible to create a shortcut for the web apps. Apple named such icon as apple touch icon, which is used by Safari and other browsers on iOS.

For the iOS devices, iPhone and iPad have different screen sizes, so they have different size requirements for touch icon.

Furthermore, there are device with retina display and with normal one. To have pixel perfect image on retina display, it requires the resolution of the image to be doubled.

And since iOS 7, iOS changed its UI style, the icon size used by iOS 7 is also slightly changed. So you should provide new icons for iOS 7 devices!

To make the icon fit iOS visual style best, Apple recommend web application to provide precomposed icon, which is a icon that added rounded corner and background by itself.

To have the pixel perfect icon on iOS, you might need to provide around 10 different images files as apple touch icon.

What a hell!!!! The touch icon on iOS is totally a mess!!!!

Here is a document from Apple that describes how configure web application icon.

Other

Besides all cases that describe above, favicon is also used in some special cases, such as Google TV, Opera Speed Dial, Chrome Web App. They all requires different size of favico.

Conclusion

In a short description, favico isn’t as simple as it looks. And it actually used wrongly by most websites.
To provide proper favico for all platforms and devices is not a simple work to do.
Here is the favico declaration that used this blog:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<link rel="apple-touch-icon" sizes="57x57" href="/apple-touch-icon-57x57.png">
<link rel="apple-touch-icon" sizes="114x114" href="/apple-touch-icon-114x114.png">
<link rel="apple-touch-icon" sizes="72x72" href="/apple-touch-icon-72x72.png">
<link rel="apple-touch-icon" sizes="144x144" href="/apple-touch-icon-144x144.png">
<link rel="apple-touch-icon" sizes="60x60" href="/apple-touch-icon-60x60.png">
<link rel="apple-touch-icon" sizes="120x120" href="/apple-touch-icon-120x120.png">
<link rel="apple-touch-icon" sizes="76x76" href="/apple-touch-icon-76x76.png">
<link rel="apple-touch-icon" sizes="152x152" href="/apple-touch-icon-152x152.png">
<link rel="icon" type="image/png" href="/favicon-196x196.png" sizes="196x196">
<link rel="icon" type="image/png" href="/favicon-160x160.png" sizes="160x160">
<link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96">
<link rel="icon" type="image/png" href="/favicon-16x16.png" sizes="16x16">
<link rel="icon" type="image/png" href="/favicon-32x32.png" sizes="32x32">
<meta name="msapplication-TileColor" content="#00a300">
<meta name="msapplication-TileImage" content="/mstile-144x144.png">

And these files are served on this blog as favico:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
favicon.ico
favicon-16x16.png
favicon-32x32.png
favicon-96x96.png
favicon-160x160.png
favicon-196x196.png
apple-touch-icon.png
apple-touch-icon-precomposed.png
apple-touch-icon-57x57.png
apple-touch-icon-60x60.png
apple-touch-icon-72x72.png
apple-touch-icon-76x76.png
apple-touch-icon-114x114.png
apple-touch-icon-120x120.png
apple-touch-icon-144x144.png
apple-touch-icon-152x152.png
browserconfig.xml
mstile-70x70.png
mstile-144x144.png
mstile-150x150.png
mstile-310x150.png
mstile-310x310.png

As you can see, to prepare all these configurations and files is not an easy job to do. It really consumes you a lot effort.

Fortunately, we have the awesome Real Favicon Generator brought by Philippe Bernard, which will save your tons of time to have the proper fav icon configuration.

Real Favicon Generator also comes with a favicon checker, which check the favorite configuration for your website, and generates beautiful report.

At last the FAQ of the site also provides a good explanation of the issue described above in details. Hope it helps.

Complex Value Array in Stylus

Stylus is an awesome CSS pre-processor, which provides much more concise syntax and more powerful feature than its competitors, such as LESS or SCSS.

But now, with more and more features added into Stylus, it seems its syntax become over-weighted. Pitfall come up.

I wish to declare an array of values for box-shadow property. And I can reference them with index:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
drop-shadows = [
0 2px 10px 0 rgba(0, 0, 0, 0.16),
0 6px 20px 0 rgba(0, 0, 0, 0.19),
0 17px 50px 0 rgba(0, 0, 0, 0.19),
0 25px 55px 0 rgba(0, 0, 0, 0.21),
0 40px 77px 0 rgba(0, 0, 0, 0.22)
]
drop-shadow(n)
box-shadow shadows[n]
for i in (1..5)
.drop-shadow-{i}
drop-shadow(i)

And expect it generates

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
.drop-shadow-1 {
box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.16);
}
.drop-shadow-2 {
box-shadow: 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}
.drop-shadow-3 {
box-shadow: 0 17px 50px 0 rgba(0, 0, 0, 0.19);
}
.drop-shadow-4 {
box-shadow: 0 25px 55px 0 rgba(0, 0, 0, 0.21);
}
.drop-shadow-5 {
box-shadow: 0 40px 77px 0 rgba(0, 0, 0, 0.22;
}

But I found there is not such thing called Array in Stylus!!!!
There is only Hash, and Hash doesn’t accept number as key!
So finally, I come up something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
drop-shadows = {
'1': 0 2px 10px 0 rgba(0, 0, 0, 0.16),
'2': 0 6px 20px 0 rgba(0, 0, 0, 0.19),
'3': 0 17px 50px 0 rgba(0, 0, 0, 0.19),
'4': 0 25px 55px 0 rgba(0, 0, 0, 0.21),
'5': 0 40px 77px 0 rgba(0, 0, 0, 0.22)
}
drop-shadow(n)
box-shadow shadows[n+'']
for i in (1..5)
.drop-shadow-{i}
drop-shadow(i)

In this piece of code, there are a bunch of pitfalls:

  1. Hash doesn’t accept number as key. So 1: 0 2px 10px 0 rgba(0, 0, 0, 0.16) cause compile error.
  2. '1' != 1, so drop-shadows[1] returns null
  3. There is no type conversion function in Stylus, use the same trick as JavaScript. ''+n convert n into string.

Just found Stylus provides something called List, which is pretty much similar to what array in other languages, except the syntax.

1
2
3
4
5
6
7
8
9
10
11
12
13
drop-shadows = 0 2px 10px 0 rgba(0, 0, 0, 0.16),
0 6px 20px 0 rgba(0, 0, 0, 0.19),
0 17px 50px 0 rgba(0, 0, 0, 0.19),
0 25px 55px 0 rgba(0, 0, 0, 0.21),
0 40px 77px 0 rgba(0, 0, 0, 0.22)
drop-shadow(n)
box-shadow shadows[n]
for i in (1..5)
.drop-shadow-{i}
drop-shadow(i)

So no brackets or parentesis needed.

Page renders improperly in IE before developer tool has opened

Today I found a super annoying issue about IE. Our website works perfectly in any browser except IE. The page isn’t rendered properly in IE 9. Well, this is common, this is the nature of IE. The mysterious issue I found is that once you opened or ever opened the developer tool, open the page or refresh the page, the problem is gone magically!!!!

As a conclusion, opening the developer tool changes the browser behavior!!!!! What a hell! So you know there is something wrong, but once you try to figure out the error message, you have to open developer tool. Once you open the developer tool, the bug is gone! DEAD END!!!

Because I cannot open developer tool, so I have to debug with alert. It is really a horrible experience to me, and feels like inspecting nuclear reaction with a plain optical magnifier or fixing a high-tech spacecraft with stones and clubs.

Since it is client-rich page, a lot of javascript is introduced. So I cannot go through the scripts line by line, instead I have to make an assumption to explain the phenomena spotted, then validate it with experiments, finally correct or extend the assumption according the validation result.

During the process I invalidated a couple of assumptions, some of them are seems very close to the “right answer”, such as “some script is loaded and executed before its dependencies, and developer tool load all the scripts first because it displays all the scripts”.

After spending a couple of hours on it, I put on eye on a line of code that is really out of my expectation: console.warn.

Code breaks the page rendering
1
2
3
console.warn('__proto__ is not supported by current browser, fallback to hard-copy approach');

I displays a warning message to console when a workaround is applied. But a tricky fact about IE 9 is that console isn’t available until developer tool is opened (MSDN reference here))!!!

The fact that console is not available until developer tools is opened really blows my mind away! (Maybe it is because I have little experience work with IE). As a chrome user, I take console as the universal log system for javascript. But in IE, according to the document), the code should check the existence of console every time print a log.

There is another pitfall here, and I saw someone really post it as answer on StackOverflow:

Bad polyfill implemntation
1
2
3
4
5
if (typeof console == "undefined") {
this.console = { log: function (msg) { alert(msg); } };
}

We usually access console as console.log, feels like console is a global instance to access. But actually console is an member of window, its full name should be window.console. When console exists, we can definitely reference to it via console. But if it doesn’t exist, the statement console cause script error! So the following code doesn’t work:

Pitfalls in console existence check
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if (typeof(console) === 'undefined'){ // Break the script execution
console.log('Never got executed');
}
if (console != null) { // Break the script execution
console.log('Never got executed');
}
if (console) { // Break the script execution
console.log('Never got executed');
}
if (window.console) {
console.log('this works!');
}

To avoid console issue, a ployfill could be very useful. Here is a great implementation available as bower package: console-polyfill

converting between HTML 5 data-attribute style hyphen name and javascript camcel-case name

I found a bug in widget.coffee today. To fix the issue, I need the conversion between HTML 5 data-attribute name and javascript function name, e.g. conversion between data-action-handler and actionHandler.

By taking jQuery implementation as reference, I come up 2 utility functions for the conversion:

NameConversion
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Utils =
hyphenToCamelCase: (hyphen) -> # Convert 'action-handler' to 'actionHandler'
hyphen.replace /-([a-z])/g, (match) ->
match[1].toUppercase()
camelCaseToHyphen: (camelCase) -> # Convert 'actionHandler' to 'action-handler'
camelCase.replace(/[A-Z]/g, '-$1').toLowerCase()
attributeToCamelCase: (attribute) -> # Convert 'data-action-handler' or 'action-handler' to 'actionHandler'
Utils.hyphenToCamelCase dataAttribute.replace(/^(data-)?(.*)/, '$2')
camelCaseToAttribute: (camelCase) -> # Convert 'actionHanlder' to 'data-action-handler'
'data-' + Utils.camelCaseToHyphen(camelCase)

Here is a more solid implementation based on previous one.

a sloid javascript version
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
var Utils = (function() {
function hyphenToCamelCase(hyphen) {
return hyphen.replace(/-([a-z])/g, function(match) {
return match[1].toUppercase();
});
}
function camelCaseToHyphen(camelCase) {
return camelCase.replace(/[A-Z]/g, '-$1').toLowerCase();
}
function attributeToCamelCase(attribute) {
return hyphenToCamelCase(dataAttribute.replace(/^(data-)?(.*)/, '$2'));
}
function camelCaseToAttribute(camelCase) {
return 'data-' + camelCaseToHyphen(camelCase);
}
return {
hyphenToCamelCase: hyphenToCamelCase,
camelCaseToHyphen: camelCaseToHyphen,
attributeToCamelCase: attributeToCamelCase,
camelCaseToAttribute: camelCaseToAttribute
};
})();

Process.nextTick Implementation in Browser

Recursion is a common trick that is often used in JavaScript programming. So infinite recursion will cause stack overflow errors.
Some languages resolves this issue by introduce automatically tail call optimization, but in JavaScript we need to take care it on our own.

To solve the issue, Node.js has the utility functions nextTick to ensure specific code is invoked after the current function returned.
In Browser there is no standard approach to solve this issue, so workarounds are needed.

Thanks to Roman Shtylman(@defunctzombie), who created the node-process for Browserify, which simulate the Node.js API in browser environment.
Here is his implementation:

node-process

Infinite Recursion
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
process.nextTick = (function () {
var canSetImmediate = typeof window !== 'undefined'
&& window.setImmediate;
var canPost = typeof window !== 'undefined'
&& window.postMessage && window.addEventListener;
if (canSetImmediate) {
return function (f) { return window.setImmediate(f) };
}
if (canPost) {
var queue = [];
window.addEventListener('message', function (ev) {
var source = ev.source;
if ((source === window || source === null) && ev.data === 'process-tick') {
ev.stopPropagation();
if (queue.length > 0) {
var fn = queue.shift();
fn();
}
}
}, true);
return function nextTick(fn) {
queue.push(fn);
window.postMessage('process-tick', '*');
};
}
return function nextTick(fn) {
setTimeout(fn, 0);
};
})();

Here is some comments on the implementation.

setTimeout

To simulate the nextTick behavior, setTimeout(fn, 0) is a well-known and easy to adopt approach. The issue of this method is that setTimeout function does heavy operations, call it in loop causes significant performance issue. So we should try to use cheaper approach when possible.

setImmidate

There is a function called setImmediate, which behaves quite similar to nextTick but with a few differences when dealing with IO stuff. But in browser environment, there is no IO issue, so we can definitely replace the nextTick with it.

Immediates are queued in the order created, and are popped off the queue once per loop iteration. This is different from process.nextTick which will execute process.maxTickDepth queued callbacks per iteration. setImmediate will yield to the event loop after firing a queued callback to make sure I/O is not being starved. While order is preserved for execution, other I/O events may fire between any two scheduled immediate callbacks.

setImmediate(callback, [arg], [...])Node.js

The setImmediate function is perfect replacement for nextTick, but it is not supported by all the browsers. Only IE 10 and Node.js 0.10.+ supports it. Chrome, Firefox, Opera and all mobile browsers don’t.

Note: This method is not expected to become standard, and is only implemented by recent builds of Internet Explorer and Node.js 0.10+. It meets resistance both from Gecko (Firefox) and Webkit (Google/Apple).

window.setImmediateMDN

window.postMessage

window.postMessage enable developer to access message queue in the browser. By adding some additional code, we can simulate nextTick behavior based on message queue. It works in most modern browser, except IE 8. In IE 8, the API is implemented in a synchronous way, which introduce an extra level of stack-push, so it cannot be used to simulate nextTick.

Overall, there is no perfect workaround to the nextTick issue for now. All the solutions have different limitations, we can only hope that this issue can be resolved in the future ECMAScript standard.

CSS trick: Place Scrollbar outside of the client area

Today, I found a interesting difference between padding and margin when I’m working on Metrics 2.0 Introduction page. There are several VideoThumbnail widget on the page, which contains a video snapshot and a paragraph text description.
Here is the Html DOM of the widget, written in Haml:

VideoThumbnail Widget Html
1
2
3
4
5
6
7
8
9
%li.span4
%a.thumbnail.new(data-widget="IntroductionPage.VideoThumbnail")
.snapshot-container
%img.snapshot{ src: snapshot }
%img.status(src="/assets/new.png" )
.caption
.title
%h3 #{index}. #{title}
%p.description #{description}

Since the description could be very short or very long, so I make the div that contains the description scrollable, so I wrote the following stylesheet for caption div:

VideoThumbnail Widget Stylesheet
1
2
3
4
5
.caption {
padding: 9px;
height: 150px;
overflow-y: auto;
}

The style looks fine, and here is how it looks:

Wiget

But very soon, I found the widget with scrollbar is taller than the one without it, it is because padding on 2 elements next to each other will not be merged: Red rect in following snapshot

Padding

It is caused because padding will not merged together as margin does, To solve the issue, I changed the padding to margin in the stylesheet:

VideoThumbnail Widget Stylesheet
1
2
3
4
5
6
.caption {
padding: 0;
margin: 9px;
height: 150px;
overflow-y: auto;
}

But bottom margin is corrected, but I found the scrollbar begin to occupy the space of content, which is not good! The center widget uses padding(Blue) and the right one uses margin(Red)

Margin

And I remember if I uses padding, the scrollbar takes the space of right padding; but if I use margin, it takes the space of the content. So I update the stylesheet in this way:

VideoThumbnail Widget Stylesheet
1
2
3
4
5
6
.caption {
padding: 0 9px 0 0;
margin: 9px 0 9px 9px;
height: 150px;
overflow-y: auto;
}
Padding Margin Mixing

I use padding on the right but uses margin on other side, so vertical scrollbar will take the right padding when necessary. It is a very interesting CSS trick, and it works fine under webkit based browser.

A pitfall in jQuery form serialization

Today, I was so surprised that I got an empty string when I call the serialize method on a jQuery wrapped form.
The html is written in Haml:

Html
1
2
3
4
5
6
7
%form#graph-option.horizontal-form
%fieldset
%label{ :for=>'start-date'} Start Date
%select#start-date
%label{ :for=>'end-date'} End Date
%select#end-date
%button#submit-option.btn.large-btn

And the script is written in coffee-script:

Script
1
2
3
4
5
6
7
$ ->
$('#submit-option').click ->
option = $('#graph-option').serialize()
$.post '/dashboard/graph', option, (data) ->
renderCharts data

When I execute the script, I got 500 error. And the reason is that the option is empty.
I believe this must be caused by a super silly mistake, so I try to call serialize methods on Twitter Bootstrap website, and I still got empty string!!!!

After half an hour debugging, I just realize that I forgot to assign the name to all the input elements. And according to html specification, the browser uses the name of the elements to identify whom the value belongs to.
So when the name is omitted, the serailizeArray method in jQuery returns an empty array, as a result, the serialize method returns empty string.

According to my experience, it is easy to identify this problem, if the html is in html-like format, such as erb. But it is really hard to identify this issue if the page is written in haml, because in haml, id is used much more often.
To fix this problem, we need to specify the name explicitly for each form element.

Here is the fixed haml code:

Html
1
2
3
4
5
6
7
%form#graph-option.horizontal-form
%fieldset
%label{ :for=>'start-date'} Start Date
%select#start-date{ :name=>'start-date' }
%label{ :for=>'end-date'} End Date
%select#end-date{ :name=>'end-date' }
%button#submit-option.btn.large-btn