Trippy 3D hover animations with CSS3 perspective

I recently updated my minimal personal site with some links to my online existence and decided to try and spice up the design with a full-screen 3D animation. I started by randomly selecting some of my favorite looking unicode characters – € ç ∅ ξ φ ð ⊗ ∴ ∉ ♣ ¤ ‰ ◊ and stuffing them into layers of nested <span> structures.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<div id="beejeez">
  <div id="bg1">
    <span>
      &phi;
      <span>
        &phi;
        <span>
          &phi;
          ...etc
        </span>
      </span>
    </span>
  </div>
</div>

Then with Less CSS I blew up the characters huge, put them on z-index: 0, declared some CSS3 transitions and 3D-related properties, and made it so each nested <span> is slightly more transparent. 3d translated, and rotated from the previous layer.

beejeez.less
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#beejeez {
  position: fixed;
  font-size: 35em;
  z-index: 0;
  top: -.5em;
  left: 0;
  #bg1 {
    .transition(all .8s ease-in-out);
    .transform(translate3d(0,0,0));
    .transform-style(preserve-3d);
    .backface-visibility(visible);
    span {
      .transform-style(preserve-3d);
      position: absolute;
      top: 0;
      left: 0;
      .opacity(77);
      .perspective-origin(50% 50%);
      .transform(~"translate3d(-.004em, -0.002em, 0.01em) rotateX(2deg) rotateY(3deg)");
    }
  }
}

Finally, I wanted the background characters to react dynamically and randomly when hovering around the page. The way I did this is by animating the perspective property based on the mouse’s X and Y coordinates in the window. The visual effect is the same principle as changing the focal length of a camera lens – the higher the perspective number is, the flatter the background layers appear; the lower the perspective, the “wider the lens is” and more exaggerated each layer’s changed properties appears.

beejeez.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# prefix a css attribute declaration with vendor prefixes
vendorPrefix = (attr, v) ->
  o = {}
  o["-webkit-#{attr}"]=v
  o["-moz-#{attr}"]=v
  o["-ms-#{attr}"]=v
  o["#{attr}"]=v
  o

# set each bg layer's perspective based on an event positioned within the window
setPerspective = (e) ->
  x = e.pageX / window.innerWidth
  y = e.pageY / window.innerHeight
  $("#bg1").css(vendorPrefix("perspective",(x*500)))

# upon the mouse moving in the window, calculate the perspective values every 200ms
$(window).mousemove (e) -> _.debounce setPerspective(e), 200

The final result is pretty trippy. Although it is probably too hardware intensive to be practical and really only looks good in Webkit browsers like Chrome and Safari, it was an interesting experiment. I put together a JSFiddle of the general components if anyone wants to hack around with it.

Comments