I’ve been using Turbolinks (with jquery.turbolinks) in a lot of my projects recently in an attempt to keep things fast while still doing most all of the rendering on the server. I like that Turbolinks more or less works out of the box without a ton of dependencies or set up and, IMO, speeds up page renders. The problem I ran up against was to add page transitions (like a swipe effect) to mobile pages. Since Turbolinks just swaps the body of the document without any incremental steps, it’s a little tricky to effectively animate that transition. The way I ended up overcoming it was to use Turbolinks’ event emissions to add classes to the HTML node and animate a child node with CSS transforms.
The CoffeeScript:
1# starting to fetch a new target page
2$(document).on 'page:fetch', ->
3 $(document.documentElement)
4 .removeClass('loaded')
5
6 $(document.documentElement)
7 .addClass('loading')
8
9# the page has been parsed and changed to the new version and on DOMContentLoaded
10$(document).on 'page:change', ->
11 $(document.documentElement)
12 .removeClass('loading')
13
14
15# is fired at the end of the loading process
16$(document).on 'page:load', ->
17 $(document.documentElement)
18 .addClass('loaded')
The SCSS (the @media
rule keeps this on mobile-only):
1@media (max-width: $grid-float-breakpoint) {
2 $duration: 0.15s;
3 $swing: 150%;
4
5 #page-content {
6 @include transform(translate3d($swing, 0, 0));
7
8 .loaded & {
9 @include transform(none);
10 @include transition(transform $duration ease-in-out);
11 }
12
13 .loading & {
14 @include transform(translate3d(-#{ $swing }, 0, 0));
15 @include transition(transform $duration ease-in-out);
16
17 &:before {
18 @include transform(translate3d($swing, 0, 0));
19
20 -webkit-animation: pulse 3s infinite ease-in-out;
21 -moz-animation: pulse 3s infinite ease-in-out;
22 content: 'Loading...';
23 display: block;
24 font-size: 28px;
25 left: 0;
26 position: fixed;
27 text-align: center;
28 top: 100px;
29 width: 100%;
30 }
31 }
32 }
33}
34
35@-webkit-keyframes pulse {
36 0% { opacity: 0; }
37 50% { opacity: 0.3; }
38 100% { opacity: 0; }
39}
40@-moz-keyframes pulse {
41 0% { opacity: 0; }
42 50% { opacity: 0.3; }
43 100% { opacity: 0; }
44}
So, we:
- On
page:fetch
add the ’loading’ class to the HTML, causing the#page-content
node to swing hard left (the negative X translation) and appear to ‘swipe away’ off-screen (that’s thetransition
). - On
page:change
we remove ’loading’ from the HTML, causing#page-content
to swing hard right (the positive X translation) so it resides in the wings of the page, invisible until… - We add the ’loaded’ class to the HTML, removing any translation on the
#page-content
node, animating it into center position where it looks just fine and normal.
A note on Bootstrap: I’d used translate3d(0, 0, 0)
for the ’loaded’ state
before but it totally breaks any instances of the Bootstrap modal whereas the
transform(none)
doesn’t. The modals use a translate3d effect to slide them
into view and for whatever reason the stacking of these effects doesn’t play
nice in Chrome.