Source: https://tympanus.net/TipsTricks/DirectionAwareHoverEffect/
Documentation: https://github.com/codrops/DirectionAwareHoverEffect
/**
* jquery.hoverdir.js v1.1.2
* http://www.codrops.com
*
* Licensed under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
*
* Copyright 2012, Codrops
* http://www.codrops.com
*/
(function (factory) {
'use strict';
if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
} else if (typeof exports !== 'undefined') {
module.exports = factory(require('jquery'));
} else {
factory(jQuery);
}
})(function ($) {
'use strict';
function Hoverdir(element, options) {
this.$el = $(element);
// set options
this.options = $.extend(true, {}, this.defaults, options);
// initialize visibility to false for show and hide method
this.isVisible = false;
// get the hover for this element
this.$hoverElem = this.$el.find(this.options.hoverElem);
// transition properties
this.transitionProp = 'all ' + this.options.speed + 'ms ' + this.options.easing;
// support for CSS transitions
this.support = this._supportsTransitions();
// load the events
this._loadEvents();
}
Hoverdir.prototype = {
defaults: {
speed: 300,
easing: 'ease',
hoverDelay: 0,
inverse: false,
hoverElem: 'div'
},
constructor: Hoverdir,
/**
* Detect if CSS transitions are supported
*
* @return {Boolean}
*/
_supportsTransitions: function () {
if (typeof Modernizr !== 'undefined') {
return Modernizr.csstransitions;
} else {
var b = document.body || document.documentElement,
s = b.style,
p = 'transition';
if (typeof s[p] === 'string') {
return true;
}
// Tests for vendor specific prop
var v = ['Moz', 'webkit', 'Webkit', 'Khtml', 'O', 'ms'];
p = p.charAt(0).toUpperCase() + p.substr(1);
for (var i = 0; i < v.length; i++) {
if (typeof s[v[i] + p] === 'string') {
return true;
}
}
return false;
}
},
/**
* Bind the events to the element
*/
_loadEvents: function () {
this.$el.on('mouseenter.hoverdir mouseleave.hoverdir', $.proxy(function (event) {
this.direction = this._getDir({x: event.pageX, y: event.pageY});
if (event.type === 'mouseenter') {
this._showHover();
}
else {
this._hideHover();
}
}, this));
},
/**
* Show the hover of the element
*/
_showHover: function () {
var styleCSS = this._getStyle(this.direction);
if (this.support) {
this.$hoverElem.css('transition', '');
}
this.$hoverElem.hide().css(styleCSS.from);
clearTimeout(this.tmhover);
this.tmhover = setTimeout($.proxy(function () {
this.$hoverElem.show(0, $.proxy(function () {
if (this.support) {
this.$hoverElem.css('transition', this.transitionProp);
}
this._applyAnimation(styleCSS.to);
}, this));
}, this), this.options.hoverDelay);
this.isVisible = true;
},
/**
* Hide the hover to the element
*/
_hideHover: function () {
var styleCSS = this._getStyle(this.direction);
if (this.support) {
this.$hoverElem.css('transition', this.transitionProp);
}
clearTimeout(this.tmhover);
this._applyAnimation(styleCSS.from);
this.isVisible = false;
},
/**
* get the direction when the event is triggered
* credits : http://stackoverflow.com/a/3647634
*
* @param {Object} coordinates
* @returns {Interger}
*/
_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;
},
/**
* get the style when the event is triggered
*
* @param {(Interger|String)} direction
* @returns {Object}
*/
_getStyle: function (direction) {
var fromStyle, toStyle,
slideFromTop = {'left': '0', 'top': '-100%'},
slideFromBottom = {'left': '0', 'top': '100%'},
slideFromLeft = {'left': '-100%', 'top': '0'},
slideFromRight = {'left': '100%', 'top': '0'},
slideTop = {'top': '0'},
slideLeft = {'left': '0'};
switch (direction) {
case 0:
case 'top':
// from top
fromStyle = !this.options.inverse ? slideFromTop : slideFromBottom;
toStyle = slideTop;
break;
case 1:
case 'right':
// from right
fromStyle = !this.options.inverse ? slideFromRight : slideFromLeft;
toStyle = slideLeft;
break;
case 2:
case 'bottom':
// from bottom
fromStyle = !this.options.inverse ? slideFromBottom : slideFromTop;
toStyle = slideTop;
break;
case 3:
case 'left':
// from left
fromStyle = !this.options.inverse ? slideFromLeft : slideFromRight;
toStyle = slideLeft;
break;
}
return {from: fromStyle, to: toStyle};
},
/**
* Apply a transition or fallback to jquery animate based on Modernizr.csstransitions support
*
* @param {Object} styleCSS
*/
_applyAnimation: function (styleCSS) {
$.fn.applyStyle = this.support ? $.fn.css : $.fn.animate;
this.$hoverElem.stop().applyStyle(styleCSS, $.extend(true, [], {duration: this.options.speed}));
},
/**
* Show $hoverElem from the direction in argument
*
* @param {String} [direction=top] direction
*/
show: function (direction) {
this.$el.off('mouseenter.hoverdir mouseleave.hoverdir');
if (!this.isVisible) {
this.direction = direction || 'top';
this._showHover();
}
},
/**
* Hide $hoverElem from the direction in argument
*
* @param {String} [direction=bottom] direction
*/
hide: function (direction) {
this.rebuild();
if (this.isVisible) {
this.direction = direction || 'bottom';
this._hideHover();
}
},
setOptions: function (options) {
this.options = $.extend(true, {}, this.defaults, this.options, options);
},
/**
* Unbinds the plugin.
*/
destroy: function () {
this.$el.off('mouseenter.hoverdir mouseleave.hoverdir');
this.$el.data('hoverdir', null);
},
/**
* Bind the plugin.
*/
rebuild: function (options) {
if (typeof options === 'object') {
this.setOptions(options);
}
this._loadEvents();
}
};
$.fn.hoverdir = function (option, parameter) {
return this.each(function () {
var data = $(this).data('hoverdir');
var options = typeof option === 'object' && option;
// Initialize hoverdir.
if (!data) {
data = new Hoverdir(this, options);
$(this).data('hoverdir', data);
}
// Call hoverdir method.
if (typeof option === 'string') {
data[option](parameter);
if (option === 'destroy') {
$(this).data('hoverdir', false);
}
}
});
};
$.fn.hoverdir.Constructor = Hoverdir;
});
....
....
<!-- jQuery -->
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<!-- Modernizr -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/modernizr/2.8.3/modernizr.min.js"></script>
<!-- jQuery HoverDir -->
<script src="<?php echo get_template_directory_uri(); ?>/assets/build/js/jquery.hoverdir.js"></script>
<script>
$(function() {
$('#careers-list > .career').hoverdir({ hoverElem: '.career__back' });
});
</script>
</body>
</html>
<section class="careers">
<div class="careers__container container">
<div class="col col--main">
<div class="box text-center">
<h3 class="font-display-1 text-purple">Explore Our Wide Range of Career Areas</h3>
</div>
</div>
<div class="col col--careers" id="careers-list">
<?php
include "_data.php";
foreach($careers as $career): ?>
<a href="<?php echo $career->url; ?>" class="career" <?php if(isset($career->image)): echo "style='background-image: linear-gradient(rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0.6)), url({$img_path}/careers/{$career->image})'"; endif;?>>
<div class="career__front">
<p class="career__title body-copy"><?php echo $career->title; ?></p>
</div>
<div class="career__back">
<div><p class="body-copy"><strong>View Jobs</strong></p></div>
</div>
</a>
<?php endforeach; ?>
</div>
<div class="col col--cta">
<div class="box text-center">
<a href="#" class="button button--purple button--lg">See All Open Jobs</a>
</div>
</div>
</div>
</section>
<?php
$careers = [
(object) [
"title" => "Accounting & <br>Finance",
"image" => "accounting.jpg",
"url" => "",
],
(object) [
"title" => "Administrative",
"image" => "admin.jpg",
"url" => "",
],
(object) [
"title" => "Building <br>Services",
"image" => "building.jpg",
"url" => "",
],
(object) [
"title" => "Culinary <br>Services",
"image" => "culinary.jpg",
"url" => "",
],
(object) [
"title" => "Executive <br>Management",
"image" => "executive.jpg",
"url" => "",
],
(object) [
"title" => "Health & <br>Wellness",
"image" => "health.jpg",
"url" => "",
],
(object) [
"title" => "Human <br>Resources",
"image" => "hr.jpg",
"url" => "",
],
(object) [
"title" => "Information <br>Technology",
"image" => "it.jpg",
"url" => "",
],
(object) [
"title" => "Legal",
"image" => "legal.jpg",
"url" => "",
],
(object) [
"title" => "Lifestyle & <br>Memory",
"image" => "lifestyle.jpg",
"url" => "",
],
(object) [
"title" => "Operations",
"image" => "operations.jpg",
"url" => "",
],
(object) [
"title" => "Sales & <br>Marketing",
"image" => "sales.jpg",
"url" => "",
],
];
.careers {
// layout
&__container {
padding-top: 12rem;
padding-bottom: 12rem;
.col {
flex: 0 1 100%;
&:not(:last-child) {
margin-bottom: 6rem;
}
&--careers {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
}
}
// career block
.career {
position: relative;
flex: 0 1 24%;
min-height: 200px;
background-repeat: no-repeat;
background-position: center;
background-size: cover;
color: white;
overflow: hidden;
display: flex;
&__front,
&__back {
padding: 2rem;
display: flex;
}
&__back {
display: none; // * set to 'display: none' if using jquery.hoverdir.js
transition: all 0.4s ease;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(172,157,199,0.9);
& > div {
display: flex;
height: 100%;
width: 100%;
& > * { margin: auto; }
}
}
&:nth-child(n+5) {
margin-top: 1.3rem;
}
// font
&__title {
line-height: 1.1;
margin: auto auto 0 0;
color: white;
}
}
}