zoukankan      html  css  js  c++  java
  • jQuerymenuaim.js

    /**
    * menu-aim is a jQuery plugin for dropdown menus that can differentiate
    * between a user trying hover over a dropdown item vs trying to navigate into
    * a submenu's contents.
    *
    * menu-aim assumes that you have are using a menu with submenus that expand
    * to the menu's right. It will fire events when the user's mouse enters a new
    * dropdown item *and* when that item is being intentionally hovered over.
    *
    * __________________________
    * | Monkeys  >|   Gorilla  |
    * | Gorillas >|   Content  |
    * | Chimps   >|   Here     |
    * |___________|____________|
    *
    * In the above example, "Gorillas" is selected and its submenu content is
    * being shown on the right. Imagine that the user's cursor is hovering over
    * "Gorillas." When they move their mouse into the "Gorilla Content" area, they
    * may briefly hover over "Chimps." This shouldn't close the "Gorilla Content"
    * area.
    *
    * This problem is normally solved using timeouts and delays. menu-aim tries to
    * solve this by detecting the direction of the user's mouse movement. This can
    * make for quicker transitions when navigating up and down the menu. The
    * experience is hopefully similar to amazon.com/'s "Shop by Department"
    * dropdown.
    *
    * Use like so:
    *
    *      $("#menu").menuAim({
    *          activate: $.noop,  // fired on row activation
    *          deactivate: $.noop,  // fired on row deactivation
    *      });
    *
    *  ...to receive events when a menu's row has been purposefully (de)activated.
    *
    * The following options can be passed to menuAim. All functions execute with
    * the relevant row's HTML element as the execution context ('this'):
    *
    *      .menuAim({
    *          // Function to call when a row is purposefully activated. Use this
    *          // to show a submenu's content for the activated row.
    *          activate: function() {},
    *
    *          // Function to call when a row is deactivated.
    *          deactivate: function() {},
    *
    *          // Function to call when mouse enters a menu row. Entering a row
    *          // does not mean the row has been activated, as the user may be
    *          // mousing over to a submenu.
    *          enter: function() {},
    *
    *          // Function to call when mouse exits a menu row.
    *          exit: function() {},
    *
    *          // Selector for identifying which elements in the menu are rows
    *          // that can trigger the above events. Defaults to "> li".
    *          rowSelector: "> li",
    *
    *          // You may have some menu rows that aren't submenus and therefore
    *          // shouldn't ever need to "activate." If so, filter submenu rows w/
    *          // this selector. Defaults to "*" (all elements).
    *          submenuSelector: "*"
    *      });
    *
    * https://github.com/kamens/jQuery-menu-aim
    */
    (function($) {
        $.fn.menuAim = function(opts) {
     
            var $menu = $(this),
                activeRow = null,
                mouseLocs = [],
                lastDelayLoc = null,
                timeoutId = null,
                options = $.extend({
                    rowSelector: "> li",
                    submenuSelector: "*",
                    tolerance: 75,  // bigger = more forgivey when entering submenu
                    enter: $.noop,
                    exit: $.noop,
                    activate: $.noop,
                    deactivate: $.noop
                }, opts);
     
            var MOUSE_LOCS_TRACKED = 3,  // number of past mouse locations to track
                DELAY = 300;  // ms delay when user appears to be entering submenu
     
            /**
             * Keep track of the last few locations of the mouse.
             */
            var mousemoveDocument = function(e) {
                    mouseLocs.push({x: e.pageX, y: e.pageY});
     
                    if (mouseLocs.length > MOUSE_LOCS_TRACKED) {
                        mouseLocs.shift();
                    }
                };
     
            /**
             * Cancel possible row activations when leaving the menu entirely
             */
            var mouseleaveMenu = function() {
                    if (timeoutId) {
                        clearTimeout(timeoutId);
                    }
                };
     
            /**
             * Trigger a possible row activation whenever entering a new row.
             */
            var mouseenterRow = function() {
                    if (timeoutId) {
                        // Cancel any previous activation delays
                        clearTimeout(timeoutId);
                    }
     
                    options.enter(this);
                    possiblyActivate(this);
                },
                mouseleaveRow = function() {
                    options.exit(this);
                };
     
            /**
             * Activate a menu row.
             */
            var activate = function(row) {
                    if (row == activeRow) {
                        return;
                    }
     
                    if (activeRow) {
                        options.deactivate(activeRow);
                    }
     
                    options.activate(row);
                    activeRow = row;
                };
     
            /**
             * Possibly activate a menu row. If mouse movement indicates that we
             * shouldn't activate yet because user may be trying to enter
             * a submenu's content, then delay and check again later.
             */
            var possiblyActivate = function(row) {
                    var delay = activationDelay();
     
                    if (delay) {
                        timeoutId = setTimeout(function() {
                            possiblyActivate(row);
                        }, delay);
                    } else {
                        activate(row);
                    }
                };
     
            /**
             * Return the amount of time that should be used as a delay before the
             * currently hovered row is activated.
             *
             * Returns 0 if the activation should happen immediately. Otherwise,
             * returns the number of milliseconds that should be delayed before
             * checking again to see if the row should be activated.
             */
            var activationDelay = function() {
                    if (!activeRow || !$(activeRow).is(options.submenuSelector)) {
                        // If there is no other submenu row already active, then
                        // go ahead and activate immediately.
                        return 0;
                    }
     
                    var offset = $menu.offset(),
                        upperRight = {
                            x: offset.left + $menu.outerWidth(),
                            y: offset.top - options.tolerance
                        },
                        lowerRight = {
                            x: offset.left + $menu.outerWidth(),
                            y: offset.top + $menu.outerHeight() + options.tolerance
                        },
                        loc = mouseLocs[mouseLocs.length - 1],
                        prevLoc = mouseLocs[0];
     
                    if (!loc) {
                        return 0;
                    }
     
                    if (!prevLoc) {
                        prevLoc = loc;
                    }
     
                    if (prevLoc.x < offset.left || prevLoc.x > lowerRight.x ||
                        prevLoc.y < offset.top || prevLoc.y > lowerRight.y) {
                        // If the previous mouse location was outside of the entire
                        // menu's bounds, immediately activate.
                        return 0;
                    }
     
                    if (lastDelayLoc &&
                            loc.x == lastDelayLoc.x && loc.y == lastDelayLoc.y) {
                        // If the mouse hasn't moved since the last time we checked
                        // for activation status, immediately activate.
                        return 0;
                    }
     
                    // Detect if the user is moving towards the currently activated
                    // submenu.
                    //
                    // If the mouse is heading relatively clearly towards
                    // the submenu's content, we should wait and give the user more
                    // time before activating a new row. If the mouse is heading
                    // elsewhere, we can immediately activate a new row.
                    //
                    // We detect this by calculating the slope formed between the
                    // current mouse location and the upper/lower right points of
                    // the menu. We do the same for the previous mouse location.
                    // If the current mouse location's slopes are
                    // increasing/decreasing appropriately compared to the
                    // previous's, we know the user is moving toward the submenu.
                    //
                    // Note that since the y-axis increases as the cursor moves
                    // down the screen, we are looking for the slope between the
                    // cursor and the upper left corner to decrease over time, not
                    // increase (somewhat counterintuitively).
                    function slope(a, b) {
                        return (b.y - a.y) / (b.x - a.x);
                    };
     
                    var upperSlope = slope(loc, upperRight),
                        lowerSlope = slope(loc, lowerRight),
                        prevUpperSlope = slope(prevLoc, upperRight),
                        prevLowerSlope = slope(prevLoc, lowerRight);
     
                    if (upperSlope < prevUpperSlope &&
                            lowerSlope > prevLowerSlope) {
                        // Mouse is moving from previous location towards the
                        // currently activated submenu. Delay before activating a
                        // new menu row, because user may be moving into submenu.
                        lastDelayLoc = loc;
                        return DELAY;
                    }
     
                    lastDelayLoc = null;
                    return 0;
                };
     
            /**
             * Hook up initial menu events
             */
            var init = function() {
                $menu
                    .mouseleave(mouseleaveMenu)
                    .find(options.rowSelector)
                        .mouseenter(mouseenterRow)
                        .mouseleave(mouseleaveRow);
     
                $(document).mousemove(mousemoveDocument);
            };
     
            init();
            return this;
        };
    })(jQuery);
  • 相关阅读:
    MySQL Lock--gap before rec insert intention waiting
    MySQL Index--InnoDB引擎的主键索引
    MySQL Binlog--事务日志和BINLOG落盘参数对磁盘IO的影响
    MySQL Partition Table--分区表优缺点
    MySQL DataType--当整数列遇到小数
    MySQL DataType--日期格式
    MySQL DataType--隐式类型转换
    MySQL Hardware--RAID卡常用信息查看
    DBA常用SQL之表空间与数据文件
    DBA常用SQL之数据库基础信息
  • 原文地址:https://www.cnblogs.com/chorrysky/p/2950074.html
Copyright © 2011-2022 走看看