RPeraltaJr
10/19/2017 - 2:43 PM

Direction-Aware Hover Effect

/**
 * 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;
        }
    }

}