view index.html @ 5:d2f7b69ac319

animation for letter sliding out of palette on spawn
author Atul Varma <avarma@mozilla.com>
date Sun, 02 May 2010 15:21:48 -0700
parents 9c7ae0ae7240
children efb34c9b7c18
line wrap: on
line source

<html>
<head>
<style type="text/css">
body {
  font-family: Palatino;
  margin: 0;
}

div.letter {
  -webkit-transition-property: -webkit-transform, color;
  -webkit-transition-duration: 0.1s, 0.2s;
  -webkit-transition-timing-function: linear;
  position: absolute;
  cursor: pointer;
  font-size: 144pt;
}

div.letter .info {
  font-family: Futura, Helvetica;
  white-space: pre;
  font-size: 9pt;
}

div.letter.active {
  color: red;
}

div.palette {
  float: right;
  width: 1em;
  max-height: 1em;
  overflow: hidden;
  font-size: 144pt;
}

.letter-prototype {
  cursor: pointer;
  color: gray;
  padding: 1px;
  border-bottom: 1px solid gray;
}
</style>
</head>
<body>
<div id="letters" class="palette"></div>
<script>
if (typeof Array.isArray !== 'function') {
  Array.isArray = function (value) {
    return Object.prototype.toString.call(value) === '[object Array]';
  };
}

function $(selector) {
  return new $.fn.init(selector);
}

$.fn = $.prototype = {
  _qsa: function qsa(selector, element) {
    if (element === undefined)
      element = document;
    var nodeList = element.querySelectorAll(selector);
    var array = [];
    for (var i = 0; i < nodeList.length; i++) {
      array.push(nodeList[i]);
    }
    return array;
  },
  length: 0,
  init: function init(selector) {
    if (typeof(selector) == "string") {
      this.elems = this._qsa(selector);
    } else {
      if (Array.isArray(selector)) {
        this.elems = selector;
      } else
        this.elems = [selector];
    }
    this.length = this.elems.length;
  },
  get: function(i) {
    if (i !== undefined)
      return this.elems[i];
    return this.elems;
  },
  bind: function(name, handler) {
    this.elems.forEach(function(elem) {
     elem.addEventListener(name, handler, false);
    });
  },
  css: function(options) {
    this.elems.forEach(function(elem) {
      for (name in options)
        elem.style.setProperty(name, options[name], null);
    });
  },
  removeClass: function(name) {
    this.elems.forEach(function(elem) {
      var classes = elem.className.split(" ");
      var index = classes.indexOf(name);
      if (index != -1) {
        classes.splice(index, 1);
        elem.className = classes.join(" ");
      }
    });    
  },
  addClass: function(name) {
    this.elems.forEach(function(elem) {
      var classes = elem.className.split(" ");
      var index = classes.indexOf(name);
      if (index == -1) {
        classes.push(name);
        elem.className = classes.join(" ");
      }
    });
  },
  toggleClass: function(name) {
    this.elems.forEach(function(elem) {
      var classes = elem.className.split(" ");
      var index = classes.indexOf(name);
      if (index == -1)
        classes.push(name);
      else
        classes.splice(index, 1);
      elem.className = classes.join(" ");
    });
  },
  text: function(text) {
    if (text === undefined)
      return this.elems[0].textContent;
    this.elems.forEach(function(elem) {
      elem.textContent = text;
    });
  },
  find: function(selector) {
    var self = this;
    var array = [];
    this.elems.forEach(function(elem) {
      array = array.concat(self._qsa(selector, elem));
    });
    return new this.init(array);
  },
  each: function(cb) {
    this.elems.forEach(function(elem) { cb.call(elem); });
  }
};

$.fn.init.prototype = $.fn;

function makeDraggable(node, matrix) {
  var startX = 0;
  var startY = 0;
  var currX = 0;
  var currY = 0;

  function updateInfo() {
    var msg = "";
    if (isMouseDown)
      msg = "x: " + matrix.e + "\ny: " + matrix.f;
    $(node).find(".info").text(msg);
  }

  function move() {
    matrix = matrix.translate(currX - startX, currY - startY);
    node.style.webkitTransform = matrix;
  }

  updateInfo();

  if (!isTouchSupported) {
    var isMouseDown = false;

    $(node).bind("mousedown", function(event) {
      event.preventDefault();
      startX = event.clientX;
      startY = event.clientY;
      isMouseDown = true;
      $(node).addClass("active");
      updateInfo();
    });

    $(node.ownerDocument).bind("mousemove", function(event) {
      if (isMouseDown) {
        currX = event.clientX;
        currY = event.clientY;
        move(this);
        startX = currX;
        startY = currY;
        updateInfo();
      }
    });

    $(node.ownerDocument).bind("mouseup", function(event) {
      if (isMouseDown) {
        event.preventDefault();
        isMouseDown = false;
        $(node).removeClass("active");
        updateInfo();
      }
    });
  } else {
    $(node).bind("touchstart", function(event) {
      event.preventDefault();
      startX = event.targetTouches[0].pageX;
      startY = event.targetTouches[0].pageY;
      updateInfo();
      $(this).addClass("active");
    });

    $(node).bind("touchmove", function(event) {
      event.preventDefault();
      currX = event.targetTouches[0].pageX;
      currY = event.targetTouches[0].pageY;
      move(this);
      startX = currX;
      startY = currY;
      updateInfo();
    });

    $(node).bind("touchend", function(event) {
      event.preventDefault();
      $(this).removeClass("active");
    });
  }
}

function makeScrollable() {
  var node = this;
  var startY = 0;
  var currY = 0;
  var floatScrollTop = 0;
  var timerID;
  var veloc = 0;

  var maxHeight = node.scrollHeight - node.clientHeight;

  function timer() {
    floatScrollTop += (veloc * 0.05);
    if (floatScrollTop < 0)
      floatScrollTop = 0;
    else if (floatScrollTop > maxHeight)
      floatScrollTop = maxHeight;
    node.scrollTop = Math.floor(floatScrollTop);
    veloc = 0.95 * veloc;
    if (Math.abs(veloc) < 1) {
      veloc = 0;
      clearInterval(timerID);
      timerID = undefined;
    }
  }

  function accelerate(amount) {
    veloc += amount;
    if (timerID === undefined)
      timerID = setInterval(timer, 10);
  }

  if (!isTouchSupported) {
    $(node).bind("mousewheel", function(event) {
      accelerate(-event.wheelDelta);
    });
  } else {
    $(node).bind("touchstart", function(event) {
      event.preventDefault();
      startY = event.targetTouches[0].pageY;
    });

    $(node).bind("touchmove", function(event) {
      event.preventDefault();
      currY = event.targetTouches[0].pageY;
      accelerate(startY - currY);
      startY = currY;
    });
  }
}

function makeLetter() {
  var node = this;
  var doc = this.ownerDocument;

  var letter = doc.createElement("div");
  var char = doc.createTextNode(this.textContent);
  var info = doc.createElement("div");

  letter.className = "letter";
  info.className = "info";
  letter.appendChild(char);
  letter.appendChild(info);

  var matrix = new WebKitCSSMatrix();

  console.log(this.offsetLeft + " " + this.offsetTop);

  // TODO: This assumes things about the DOM structure that
  // we don't want to have to assume.
  matrix = matrix.translate(
    this.offsetLeft,
    this.offsetTop - this.parentNode.scrollTop
  );
  letter.style.webkitTransform = matrix;

  doc.body.appendChild(letter);

  // Apply a new transform to make the letter animate.
  matrix = matrix.translate(-this.offsetWidth, 0);
  letter.style.webkitTransform = matrix;

  makeDraggable(letter, matrix);
}

function addLetters() {
  var node = this;

  const ALPHABET = "abcdefghijklmnopqrstuvwxyz" +
                   "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

  for (var i = 0; i < ALPHABET.length; i++) {
    var div = document.createElement("div");
    div.textContent = ALPHABET[i];
    div.className = "letter-prototype";
    node.appendChild(div);
    $(div).bind("click", makeLetter);
  }
}

var isTouchSupported = (function isTouchSupported() {
  try {
    document.createEvent("TouchEvent");
    return true;
  } catch (e) {
    if (e.code != e.NOT_SUPPORTED_ERR)
      throw e;
    return false;
  }
})();

$(window).bind("DOMContentLoaded", function() {
  $("#letters").each(addLetters);
  $("#letters").css({"max-height": document.body.clientHeight});
  $(".palette").each(makeScrollable);
});
</script>
</body>
</html>