Behind the scenes of an award-winning web page

Elendev
Swissquote Tech Blog
7 min readJan 29, 2020
Le Meilleur Du Web, Categorie Technologie, 27.11.2019

Mid-September. It’s beautiful outside. I have a meeting with my superiors about a critical project. We have a new marketing video whose purpose is to re-shape the way everyone thinks about Swissquote.

“Now that we have this video, we need a web page as innovative and as dynamic as this video. Something like what we see on Apple’s product pages, with everything moving everywhere when we scroll. You and Kilian will work together, he will provide you layouts, graphic elements, and animation ideas. You have three weeks.”.

In the end, the resulting webpage has received two awards. It has won the Technology category of “Le Meilleur du Web” and a Honors title of awwwards.

You can visit it here: https://en.swissquote.com/challenge-the-code

In this article, I will explain how some of the animations were technically made and why they have been made in such ways.

Working principle / Libraries used

To develop this page, two libraries have been used.

The first one is ScrollMagic. It is used to handle the scroll of the web page. ScrollMagic allows creating “scenes” and to pin some elements during a given scroll duration (in pixels, percents, em, …). It provides some callback options too to do some custom actions during the scroll.

The second one is GreenSock. GreenSock is an animation library that can handle animations on elements scale, position, opacity, and so on. It works really well with ScrollMagic as it can base all animations on scroll progression. And most of the animations on the page are scroll-dependent, thus using pure CSS (transitions and animations) was out of the question..

Working principle

When the web page is loaded, ScrollMagic is initialized with every “scenes” that compose the whole experience. When scrolling, ScrollMagic handles the progress in every scene and move from one to another based on the absolute scroll position. GreenSock is then leveraged to do every simple animation (scaling, movements). For advanced animations, custom code handles it.

The three custom animations that will be described here are:

  • Scroll-based video progression
  • Hybrid animation between a video and a SVG
  • Dive-in typography

Scroll-based video progression

The challenge of this animation is having the video progression linked to the scroll of the user, in a smooth way.

Scroll-based video playback

The naïve approach is to create a <video> element and to change the currentTime property of the video element according to the scroll progression. Unfortunately, due to the way videos are encoded, there is a delay between the change in the progression (setting currentTime) and the appearance of the targeted frame (between 250ms and 500ms). The resulting effect is by far not fluid enough.

Videos are encoded with some keyframes, which are full pictures, and the images in between encode the difference with the previous image. Thus moving within a video (seeking) is a costly operation because videos are optimized to read the file sequentially.

The solution here is to split the video in multiple frames and to take advantage of the <canvas> element to draw every frame one after the other. Since the <canvas> element is well optimized on browsers, the result is really smooth.

Splitting the video in multiple frames could be done directly in JavaScript after the video has been downloaded, or it can be done offline, and the browser has to download a set of images.
In the case of splitting the video in the browser; the video is approximately 1.5Mo, it takes approximately 1 second to download and the splitting takes more than 15 seconds in the browser. On the other hand, downloading the 60 images composing the sequence takes less than 2 seconds with a standard connection and nothing more needs to be done after that.
Because of this, the solution implemented is the offline split of the video.

Time to handle the video or the images

The code is divided in three parts. A method is provided to draw in a given <canvas> handling miscellaneous optimizations.

Draw in a canvas using some optimizations.

The ScrollMagic scene is initialized and a callback listen to the progress event. The image to display is calculated in imageId and is then drawn into the canvas.

Outsiders scene creation in ScrollMagic

The drawToCanvasWithSquare method is used to “square” the image depending on the scroll progress, widening the image as the user scrolls.

“Square” an image based on the scroll progression

Making content pop out of a video

The challenge of this animation is having both a video progression based on the browser’s scroll, mixed with a SVG animation also based on the scroll.

The reason behind the choice of having both a video and an SVG animation put together is performance.

A SVG curve is really light (only a few lines of code) and since the video part doesn’t move anymore when the SVG animation starts, having it integrated directly in the video meant having twice the frames, thus twice the time to download the animation.

For this animation, the video is done the same way ​as described above.
The SVG curve is placed in the same container as the canvas. It has a position: absolute and the animation is done by taking advantage of the CSS properties stroke-dasharray and stroke-dashoffset. The stroke-dashoffset​ property is dynamically changed based on the scroll position.

The following code is able to mix both animations:

Swiss stability scene with ScrollMagic

By doubling the scroll progress and drawing only existing images, half the scroll displays the full video. The other half scroll changes the strokeDashoffset property to 0, making the curve appear.

Dive-in typography

The challenge of this animation is to create an effect of dive in the typography to access the rest of the page.

The first approach was to display the text in a container on top of the background and grow the font-size until one letter covers all the viewport. Unfortunately, two issues were encountered: it’s not possible to have a font-size bigger than approximately 30'000px, and it’s not possible to “remove” the font from the background.

The current animation is based on a <canvas> element containing the background. To avoid quality issues with screens with high pixel ratios, the canvas is adapted to the screen pixel ratio.
The text “challenge the code” is added on top of the background. Every time a change in the scroll position occurs, the background is re-drawn.

Re-drawn of the background

Multiple thresholds are set based on the scroll position (from 0.0 to 1.0, 0 being the beginning of the scroll position of the beginning of the animation, 1.0 the end).

  • A threshold is used to make the text appear: from 0 to 0.25 the globalAlpha progress from 0 to 1. After that, the globalAlpha is over 1 (fully opaque).
  • Another threshold is used to make the text grow: from 0.4 to 1.0 the text is scaled in an exponential way from 1 to 250.
  • The last threshold is used to make the letter g disappear: from 0.65 to 0.8 the g is “removed” from the current canvas content (using the globalAlpha and the globalCompositeOperation of the canvas). After that, the g is entirely removed until the end.

A translation is linearly applied during the whole scale operation to ensure that the zoom end into a letter and not in the middle of the background (the diveAdjustment variable). Since the page is in multiple languages, this translation has to be tweaked for every translation.

The position of the text is calculated according to the scale (since the scale applies to the coordinate axis too). The position of the transparent letter is calculated using the function measureText of the canvas.

Full writeText method to display the whole text and handle the G transparency

As soon as the canvas is displayed in full screen the next animation (the timeline) is displayed in the background.

When the G is transparent enough, the CSS property pointer-events is set to none to enable the interaction with the background. The canvas stays on top of the timeline the whole time but is not visible since it’s fully transparent and it’s not possible to interact with it.

Conclusion

“Challenge The Code” represent 1301 lines of TypeScript, 1368 lines of CSS, 549 lines of Twig/HTML and 153 lines of PHP. It only took two highly motivated people, great freedom and three weeks of uninterrupted work to be able to do all this work.

One week to familiarize myself with the technologies, align ourselves on what was wanted and what was possible, and starting the skeleton.

One week to finish the skeleton, putting all the animations together with a majority of graphical elements, handling pre-loading of all the assets.

One last week to make the page fully translatable, fix the issues, add the missing graphical elements and do some fine-tuning.

Le Meilleur Du Web award the way the page “flattens” (or “projects”) a video on a web page and manage to keep the dynamism and the spirit of the original media. Having some fellow developers visiting the page during the ceremony, congratulating us for the job done and having them asking “how did you manage to do such animation?” was worth all the work we’ve done.

--

--