nathann282
9/16/2017 - 11:35 AM

jquery.hoverdir.js

/**
 *  @name hoverdir
 *  @description description
 *  @version 1.0
 *  @options
 *    speed
 *    easing
 *    hoverDelay
 *    inverse
 *    hoverEle
 *  @methods
 *    init
 *    supportTransition
 *    loadEvents
 *    getDir
 *    getStyle
 *    showHover
 *    hideHover
 *    applyAnimation
 *    destroy
 */
;(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';

  var pluginName = 'hoverdir';

  function HoverDir(element, options) {
    this.element = $(element);
    this.options = $.extend({}, $.fn[pluginName].defaults, this.element.data(), options);
    this.init();
  }

  HoverDir.prototype = {
    init: function() {
      // initialize visibility to false for show and hide method
      this.isVisible = false;
      // transition properties
      this.transitionProp = 'all ' + this.options.speed + 'ms ' + this.options.easing;
      // get the hover element
      this.$hoverEle = this.element.find(this.options.hoverEle);
      // check CSS transition support
      this.support = this.supportTransition;
      // load the events
      this.loadEvents();
    },
    /**
     * Detect if CSS transition is supported
     *
     * @return {Boolean}
     */
    supportTransition: function() {
      var b = document.body || document.documentElement,
          s = b.style,
          p = 'transition';

      if (typeof s[p] === 'string') {
        return true;
      }

      var pfx = ['Moz', 'webkit', 'Webkit', 'Khtml', 'O', 'ms'];

      p = p.replace(/\b\w/g, function(l) {
        return l.toUpperCase();
      });

      for (var i in pfx) {
        if (typeof s[pfx[i] + p] === 'string') {
          console.log(s[pfx[i] + p]);
          return true;
        }
      }

      return false;
    },
    /**
     * Bind the events to the element
     */
    loadEvents: function() {
      var that = this;
      var $el = this.element;

      $el.on('mouseenter.' + pluginName + ' mouseleave.' + pluginName, function(event) {
        that.direction  = that.getDir( { x : event.pageX, y : event.pageY } );
        that.cssStyle = that.getStyle(that.direction);

        if (event.type === 'mouseenter') {
          that.showHover();
        } else {
          that.hideHover();
        }
      });
    },
    /**
     * 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 element
      var $el = this.element;
      var w = $el.width(),
          h = $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 - $el.offset().left - (w / 2)) * (w > h ? (h / w) : 1),
          y = (coordinates.y - $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} direction
     * @returns {Object}
     */
    getStyle: function(direction) {
      var from,
          to,
          fromTop = {'left': '0', 'top': '-100%'},
          fromRight = {'left': '100%', 'top': '0'},
          fromBottom = {'left': '0', 'top': '100%'},
          fromLeft = {'left': '-100%', 'top': '0'},
          toTop = {'top': '0'},
          toLeft = {'left': '0'};
      
      if (this.options.inverse) {
        fromTop = [fromBottom, fromBottom = fromTop][0];
        fromRight = [fromLeft, fromLeft = fromRight][0];
      }

      switch( direction ) {
        case 0:
          // from top
          from = fromTop;
          to = toTop;
          break;
        case 1:
          // from right
          from = fromRight;
          to = toLeft;
          break;
        case 2:
          // from bottom
          from = fromBottom;
          to = toTop;
          break;
        case 3:
          // from left
          from = fromLeft;
          to = toLeft;
          break;
      }

      return {from: from, to: to};
    },
    /**
     * Show the hover of the element
     */
    showHover: function() {
      var that = this;

      if (this.support) {
        that.$hoverEle.css('transition', '');
      }
      
      this.$hoverEle.hide().css(this.cssStyle.from);
      clearTimeout(this.tmhover);
      
      this.tmhover = setTimeout(function() {
        that.$hoverEle.show(0, function() {
          if (this.support) {
            that.$hoverEle.css('transition', that.transitionProp);
          }
          that.applyAnimation(that.cssStyle.to);
        });
      }, this.options.hoverDelay);
      this.isVisible = true;
    },
    /**
     * Hide the hover to the element
     */
    hideHover: function() {
      var that = this;
      if (this.support) {
        that.$hoverEle.css('transition', that.transitionProp);
      }
      clearTimeout(this.tmhover);
      that.applyAnimation(that.cssStyle.from);
      this.isVisible = false;
    },
    /**
     * Apply a transition or fallback to jquery animate based on css transition supported
     *
     * @param {Object} styleCSS
     */
    applyAnimation: function(style) {
      $.fn.applyStyle = this.support ? $.fn.css : $.fn.animate;
      this.$hoverEle.stop().applyStyle(style, $.extend(true, [], {duration: this.options.speed}));
    },
    /**
     * Unbinds the plugin.
     */
    destroy: function() {
      $.removeData(this.element[0], pluginName);
    }
  };

  $.fn[pluginName] = function(options, params) {
    return this.each(function() {
      var instance = $.data(this, pluginName);
      if (!instance) {
        $.data(this, pluginName, new HoverDir(this, options));
      } else if (instance[options]) {
        instance[options](params);
      }
    });
  };

  $.fn[pluginName].defaults = {
    speed: 300,
    easing: 'ease',
    hoverDelay: 0,
    inverse: false,
    hoverEle: 'div'
  };

  $(function() {
    $('[data-' + pluginName + ']').on('customEvent', function() {
    });

    $('[data-' + pluginName + ']')[pluginName]({
    });
  });
});