1 juin 2014

Velocity.js and Meteor

Introduction

Velocity.js is a new animation library in JS. It acts as a replacement of jQuery's animation library. Internally, it uses the CSS3 transformation and animation properties leveraging their hardware acceleration capabilities where possible and falling back on basic jQuery's animation when they are not available. Neat. Here is a small video of a simple animation developed by its creator:
Doing front end development with Meteor is so easy that I've recreated this demo and deployed it in less than one hour. Here is the result: http://velocity3ddemo.meteor.com/

Step-by-step recreation

Create a basic Meteor application:
mrt create Velocity3dDemo
cd Velocity3dDemo
rm -rf Velocity3dDemo*
Import packages:
mrt add coffeescript
mrt add jade
mrt add stylus
mrt add velocityjs
Create a basic web app structure:
mkdir -p client/stylesheets
Create the Stylus file client/stylesheets/app.styl:
@import 'nib'

*
  padding: 0
  margin: 0

body
  background-color: #060b14
  overflow: hidden
  color: #ffffff
  font-family: Helvetica Neue, Open Sans, sans-serif
  font-weight: 100

a
  color: #4bc2f1
  text-decoration: none
  &:hover
    text-decoration: underline

#container
  perspective: 50px
  transform-origin: 50% 100%
  pointer-events: none
  opacity: 0.55

#welcome
  position: fixed
  width: 22rem
  left: 50%
  top: 45%
  margin-top: -1rem
  margin-left: -11rem
  font-weight: 200
  opacity: 0.65
  text-align: center
  font-size: 0.775rem
  line-height: 1.05rem
  letter-spacing: 0.135em
  word-spacing: -0.075rem

@media screen and (max-width: 400px)
  #welcome
    font-size: 0.45rem !important

#logo
  position: fixed
  right: 0.75rem
  bottom: 0.65rem
  cursor: pointer
  text-decoration: none
  color: #d6d6d6
  font-size: 2rem
  letter-spacing: 0.025em

#logoDot
  color: #d74580

.dot
  position: fixed
  width: 30px
  height: 30px
  border-radius: 30px
  background-color: #4bc2f1
Create the Jade file client/index.jade:
head
  title Velocity 3D demo
body
  +index

template(name='index')
  #welcome No WebGL. No Canvas. Just pure DOM.
  a#logo(href='http://julian.com/research/velocity/') Velocity.js
  #container
Create the CoffeeScript file client/index.coffee:
Template.index.rendered = ->
  # Device detection
  isWebkit = /Webkit/i.test navigator.userAgent
  isChrome = /Chrome/i.test navigator.userAgent
  isMobile = window.ontouchstart isnt undefined
  isAndroid = /Android/i.test navigator.userAgent
  isIE = document.documentMode

  # Redirection
  if isMobile and isAndroid and not isChrome
    alert 'Use Chrome on Android'

  # Helpers
  # Randomly generate an integer between 2 numbers.
  r = (min, max) ->
    Math.floor(Math.random() * (1 + max - min)) + min

  # Dot creation
  # Differentiate dot counts on based on device and browser capabilities
  dotsCount = if isMobile then (if isAndroid then 40 else 60) else ( if isChrome then 175 else 125)
  dotsHtml = ''
  for i in [0..dotsCount]
    dotsHtml += '<div class="dot"></div>'
  $dots = $ dotsHtml

  # Setup
  $container = $ '#container'
  $welcome = $ '#welcome'

  screenWidth = window.screen.availWidth
  screenHeight = window.screen.availHeight
  chromeHeight = screenHeight - (document.documentElement.clientHeight or screenHeight)

  translateZMin = - 725
  translateZMax = 600

  containerAnimationMap =
    perspective: [215, 50]
    opacity: [0.90, 0.55]

  # IE10+ produce odd glitching issues when you rotateZ on a parent element subjected to 3D transforms.
  containerAnimationMap.rotateZ = [5, 0] if not(isIE)

  # Animation
  # Fade out the welcome message.
  $welcome.velocity
      opacity: [0, 0.65]
    ,
      display: 'none'
      delay: 3500
      duration: 1100
  # Animate the dot's container.
  $container
    .css 'perspective-origin', "#{screenWidth/2}px #{(screenHeight*0.45)-chromeHeight}px"
    .velocity containerAnimationMap, {duration: 800, loop: 1, delay: 3250}

  # Special visual enhancement for WebKit browsers which are faster at box-shadow manipulation
  ($dots.css 'boxShadow', '0px 0px 4px 0px #4bc2f1') if isWebkit

  $dots
    .velocity
        translateX: [
          -> '+=' + r -screenWidth/2.5, screenWidth/2.5
          -> r 0, screenWidth
        ]
        translateY: [
          -> '+=' + r -screenHeight/2.75, screenHeight/2.75
          -> r 0, screenHeight
        ]
        translateZ: [
          -> '+=' + r translateZMin, translateZMax
          -> r translateZMin, translateZMax
        ]
        opacity: [
          -> Math.random()
          -> Math.random() + 0.1
        ]
      ,
        duration: 6000
        easing: 'easeInOutsine'
    .velocity 'reverse', {easing: 'easeOutQuad'}
    .velocity
        opacity: 0
      ,
        duration: 2000
        complete: ->
          $welcome
            .html "<a href='https://www.youtube.com/watch?v=MDLiVB6g2NY&hd=1'>Watch the making of this demo.</a><br /><br />Go create something amazing.<br />Sincerely, <a href='http://twitter.com/shapiro'>@Shapiro</a>"
            .velocity
                opacity: 0.75
              ,
                duration: 3500
                display: 'block'
    .appendTo $container
Launch it:
mrt
Publish it:
mrt deploy velocity3ddemo.meteor.com
Side note: I've just redeploy this little web app with a link to Meteor. My browser was on the page. Automatically, my browser has been informed that a new release of the code was available. It has only reloaded the modified assets. This is the incredible power of the live reload even when your apps are deployed. Amazing.

The original tutorial

Julian Shapiro, the author of Velocity.js, has provided a very nice Youtube channel which describes how he achieves his demo. A must watch.