Saturday, December 20, 2014

Lollipop CSS effects.

CSS transitions based on Android 5.0

(If you simply want to see and read the finished code, just see the fiddle:
Android 5.0 introduced a pretty slick looking effect when you click on certain controls. If you haven't seen it yet, the effect basically is a growing circle that appears while holding down a control. Here's an example: Image

After seeing it, I figured it would be pretty simple to translate that to the web. It's a refreshing change from simply changing an element's background color or similar.

The Idea

As basic implementation goes, the process is simple enough. We'll have some element, and that element will have a child. The child will be transparent, but the same width and height. We will then apply a background to the child, starting at 0px x 0px, and will increase it substantially.
  <div id="parent"> 
    <div id="circle"> </div> 
  #parent {
    height: 200px;
    width: 400px;
    background-color: lightgray;

  #circle {
    background-image: url("
    background-position: center center;
    background-repeat: no-repeat;
    background-size: 0 0;
    transition: .3s ease-in;

  #parent:hover #circle {
    background-size: 600px 600px;

That gives us the following result:
Which does look quite nice. However, I then desired a way to make this even cooler. What if the effect started where you clicked?

Improving the Effect

Using that as the basis for the future, I figured the first thing I needed to do is first find a way to detect the position of the mouse when we need to mousedown. Turns out one of the easier solutions is to simply set up a mouse movement listener:
  document.onmousemove = getMouseXY; 
  var tempX = 0; 
  var tempY = 0; 

  function getMouseXY(e) { 
    tempX = e.clientX + document.body.scrollLeft; 
    tempY = e.clientY + document.body.scrollTop; 
This is when things get slightly messy. We want to move the #circle's background position relative to itself, we can't simply set the position equal to the mouse coordinates. Therefore, I did what any reasonable engineer would do; Make a sketch:
So basically we want the vertical and horizontal components from the center of #circle to offset your background-image. To do this, we require the distance from the top of #circle to the top of the document, as well as the distance from the left of #circle to the left of the document.

Unfortunately that's not super easy to do (assuming we want this to work even if responsive). We basically need to loop through all the parent elements and obtain offset distances so we can get the total. In code:
  function getPos(el) {
    for (var lx=0, ly=0;
             el != null;
             lx += el.offsetLeft, ly += el.offsetTop, el = el.offsetParent);
      return {
        x: lx,
        y: ly
We now have everything we need to actually implement the touch code. The implementation is fairly simple, we obtain most of the data that was in the graph, which we can now do using our getPos() function. After that we apply a background-position style of 50% (the center of #circle) - x/y. Here's that:
  function itTouched() {
    var someElement = document.getElementById("parent")
    var circle = document.getElementById("circle");
    var xValue = getPos(someElement).x + (someElement.offsetWidth / 2) - tempX;
    var yValue = getPos(someElement).y + (someElement.offsetHeight / 2) - tempY;
    var theString = "background-position: calc(50% - " + xValue + "px)" + " calc(50% - " + yValue + "px);";
    circle.setAttribute("style", theString);
Yay! Now all we have to do is call that onmousedown and onmouseup and change the transition to happen on background-size only. The finished product is shown in this JSFiddle: (Blogger apparently is doing stupid hacks that break this, so I can't integrate into the post.) I also have an example version on my website here:

Note about the circles

I strongly suggest using an .svg graphic like I did for two reasons. A) Scaling works flawlessly, so the edges don't look horrific. B) You can easily edit the .svg file to change the color. That allows it to fit into whatever theme you want.

1 comment: