Direction Aware Hover Effects

Avatar of Chris Coyier
Chris Coyier on

This is a particular design trick that never fails to catch people’s eye! I don’t know the exact history of who-thought-of-what first and all that, but I know I have seen a number of implementations of it over the years. I figured I’d round a few of them up here.

Noel Delagado

See the Pen Direction-aware 3D hover effect (Concept) by Noel Delgado (@noeldelgado) on CodePen.

The detection here is done by tracking the mouse position on mouseover and mouseout and calculating which side was crossed. It’s a small amount of clever JavaScript, the meat of which is figuring out that direction:

var getDirection = function (ev, obj) {
    var w = obj.offsetWidth,
        h = obj.offsetHeight,
        x = (ev.pageX - obj.offsetLeft - (w / 2) * (w > h ? (h / w) : 1)),
        y = (ev.pageY - obj.offsetTop - (h / 2) * (h > w ? (w / h) : 1)),
        d = Math.round( Math.atan2(y, x) / 1.57079633 + 5 ) % 4;
  
    return d;
};

Then class names are applied depending on that direction to trigger the directional CSS animations.

Fabrice Weinberg

See the Pen Direction aware hover pure CSS by Fabrice Weinberg (@FWeinb) on CodePen.

Fabrice uses just pure CSS here. They don’t detect the outgoing direction, but they do detect the incoming direction by way of four hidden hoverable boxes, each rotated to cover a triangle. Like this:

Codrops

Demo

In an article by Mary Lou on Codrops from 2012, Direction-Aware Hover Effect with CSS3 and jQuery, the detection is also done in JavaScript. Here’s that part of the plugin:

_getDir: function (coordinates) {
    // the width and height of the current div
    var w = this.$el.width(),
        h = this.$el.height(),
        // calculate the x and y to get an angle to the center of the div from that x and y.
        // gets the x value relative to the center of the DIV and "normalize" it
        x = (coordinates.x - this.$el.offset().left - (w / 2)) * (w > h ? (h / w) : 1),
        y = (coordinates.y - this.$el.offset().top - (h / 2)) * (h > w ? (w / h) : 1),
        // the angle and the direction from where the mouse came in/went out clockwise (TRBL=0123);
        // first calculate the angle of the point,
        // add 180 deg to get rid of the negative values
        // divide by 90 to get the quadrant
        // add 3 and do a modulo by 4 to shift the quadrants to a proper clockwise TRBL (top/right/bottom/left) **/
        direction = Math.round((((Math.atan2(y, x) * (180 / Math.PI)) + 180) / 90) + 3) % 4;
    return direction;
},

It’s technically CSS doing the animation though, as inline styles are applied as needed to the elements.

John Stewart

See the Pen Direction Aware Hover Goodness by John Stewart (@johnstew) on CodePen.

John leaned on Greensock to do all the detection and animation work here. Like all the examples, it has its own homegrown geometric math to calculate the direction in which the elements were hovered.

// Detect Closest Edge
function closestEdge(x,y,w,h) {
    var topEdgeDist = distMetric(x,y,w/2,0);
    var bottomEdgeDist = distMetric(x,y,w/2,h);
    var leftEdgeDist = distMetric(x,y,0,h/2);
    var rightEdgeDist = distMetric(x,y,w,h/2);
    var min = Math.min(topEdgeDist,bottomEdgeDist,leftEdgeDist,rightEdgeDist);
    switch (min) {
        case leftEdgeDist:
            return "left";
        case rightEdgeDist:
            return "right";
        case topEdgeDist:
            return "top";
        case bottomEdgeDist:
            return "bottom";
    }
}

// Distance Formula
function distMetric(x,y,x2,y2) {
    var xDiff = x - x2;
    var yDiff = y - y2;
    return (xDiff * xDiff) + (yDiff * yDiff);
}

Gabrielle Wee

See the Pen CSS-Only Direction-Aware Cube Links by Gabrielle Wee ✨ (@gabriellewee) on CodePen.

Gabrielle gets it done entirely in CSS by positioning four hoverable child elements which trigger the animation on a sibling element (the cube) depending on which one was hovered. There is some tricky stuff here involving clip-path and transforms that I admit I don’t fully understand. The hoverable areas don’t appear to be triangular like you might expect, but rectangles covering half the area. It seems like they would overlap ineffectively, but they don’t seem to. I think it might be that they hang off the edges slightly giving a hover area that allows each edge full edge coverage.

Elmer Balbin

See the Pen Direction Aware Tiles using clip-path Pure CSS by Elmer Balbin (@elmzarnsi) on CodePen.

Elmer is also using clip-path here, but the four hoverable elements are clipped into triangles. You can see how each of them has a point at 50% 50%, the center of the square, and has two other corner points.

clip-path: polygon(0 0, 100% 0, 50% 50%)
clip-path: polygon(100% 0, 100% 100%, 50% 50%);
clip-path: polygon(0 100%, 50% 50%, 100% 100%);
clip-path: polygon(0 0, 50% 50%, 0 100%);

Nigel O Toole

Demo

Raw JavaScript powers Nigel’s demo here, which is all modernized to work with npm and modules and all that. It’s familiar calculations though:

const _getDirection = function (e, item) {
  // Width and height of current item
  let w = item.offsetWidth;
  let h = item.offsetHeight;
  let position = _getPosition(item);

  // Calculate the x/y value of the pointer entering/exiting, relative to the center of the item.
  let x = (e.pageX - position.x - (w / 2) * (w > h ? (h / w) : 1));
  let y = (e.pageY - position.y - (h / 2) * (h > w ? (w / h) : 1));

  // Calculate the angle the pointer entered/exited and convert to clockwise format (top/right/bottom/left = 0/1/2/3).  See https://stackoverflow.com/a/3647634 for a full explanation.
  let d = Math.round(Math.atan2(y, x) / 1.57079633 + 5) % 4;

  // console.table([x, y, w, h, e.pageX, e.pageY, item.offsetLeft, item.offsetTop, position.x, position.y]);

  return d;
};

The JavaScript ultimately applies classes, which are animated in CSS based on some fancy Sass-generated animations.

Giana

A CSS-only take that handles the outgoing direction nicely!

See the Pen CSS-only directionally aware hover by Giana (@giana) on CodePen.


Seen any others out there? Ever used this on something you’ve built?