使用Jquery构建动画菜单,响应页面滚动

时间:2014-11-30 12:29:10

标签: javascript jquery animation navigation scroll

对于可怕的头衔感到抱歉,但我不确定如何描述我正在努力建立的内容。我正在使用我在这个网站上找到的一些代码,基本上我正在尝试做的是构建一个左手导航菜单,当用户滚动到它时突出显示相应的部分。

$(document).ready(function() {
  var topRange = 200, // measure from the top of the viewport to X pixels down
    edgeMargin = 20, // margin above the top or margin from the end of the page
    animationTime = 600, // time in milliseconds
    contentTop = []; //array of sidebar links
  $('nav ul').append('<div id="slider"></div>');

  var sliderTop = $("nav ul li a").eq(0).parent().position().top;
  var sliderLeft = $("nav ul li a").eq(0).parent().position().left;
  var sliderHeight = $("nav ul li a").eq(0).parent().outerHeight();

  $('#slider').css({
    'height': sliderHeight,
    'left': sliderLeft,
    'top': sliderTop,
    'width': '100%'
  });


  // Stop animated scroll if the user does something
  $('html,body').bind('scroll mousedown DOMMouseScroll mousewheel keyup', function(e) {
    if (e.which > 0 || e.type == 'mousedown' || e.type == 'mousewheel') {
      $('html,body').stop();
    }
  })

  // Set up content an array of locations
  $('#sidebar').find('a').each(function() {
    contentTop.push($($(this).attr('href')).offset().top);
  })

  // Animate menu scroll to content
  $('#sidebar').find('a').click(function() {
    var sel = this,
      newTop = Math.min(contentTop[$('#sidebar a').index($(this))], $(document).height() - $(window).height()); // get content top or top position if at the document bottom
    $('html,body').stop().animate({
      'scrollTop': newTop
    }, animationTime, function() {
      window.location.hash = $(sel).attr('href');
    });
    return false;
  })

  //scroll function
  function scroller() {
      var winTop = $(window).scrollTop(),
        bodyHt = $(document).height(),
        vpHt = $(window).height() + edgeMargin; // viewport height + margin
      $.each(contentTop, function(i, loc) {
        if ((loc > winTop - edgeMargin && (loc < winTop + topRange || (winTop + vpHt) >= bodyHt))) {

          //animate slider
          x = $("#sidebar li").eq(i).position();
          $("#slider").animate({
            top: (x.top)
          }, 100);
        }
      })
    }
    //scroll event handler
  $(window).scroll(scroller)
})

我的大部分都在工作,但是当您实际点击菜单上的链接时,动画很难赶上实际的滚动。我理解为什么会这样,因为它在每个部分到达后一次更新一个位置,但我想知道是否有办法让这个动画更快,更流畅。我附上了我的代码,请事先感谢你的帮助!

http://jsfiddle.net/jamesmyers/6mbmq1pe/

1 个答案:

答案 0 :(得分:0)

通过暂时分离scroll处理程序并直接滚动滑块,使用与主动画相同的animationTime,您将获得更好的滑块动画效果。

为此,您还需要:

  • 命名滚动事件.nav,以便安全使用.off()
  • stop()“如果用户做某事”阻止
  • 中的滑块动画

我还在设置contentTop#slider的方式中节省了一些效率,但这些实际上并不是必需的。

$(document).ready(function() {
    var topRange = 200, // measure from the top of the viewport to X pixels down
        edgeMargin = 20, // margin above the top or margin from the end of the page
        animationTime = 600, // time in milliseconds
        contentTop, //array of sidebar links
        navLinkWrapper = $("nav ul li a").eq(0).parent();

    var $slider = $("<div id=\"slider\" />").css({
        'height': navLinkWrapper.outerHeight(),
        'left': navLinkWrapper.position().left,
        'top': navLinkWrapper.position().top,
        'width': '100%'
    }).appendTo($('nav ul'));

    // Stop animated scroll if the user does something
    $('html,body').on('scroll mousedown DOMMouseScroll mousewheel keyup', function(e) {
        if (e.which > 0 || e.type == 'mousedown' || e.type == 'mousewheel') {
            $('html,body').stop();
            $slider.stop(); //<<<<<<<
        }
    });

    // Set up content an array of locations
    contentTop = $('#sidebar a').map(function() {
        return $($(this).attr('href')).offset().top;
    });

    // Animate menu scroll to content
    $('#sidebar a').on('click', function(e) {
        e.preventDefault();
        $(window).off('scroll.nav', scroller); //<<<<<<<
        $slider.stop().animate({ //<<<<<<<
            top: ($(this).closest("li").position().top) //<<<<<<<
        }, animationTime); //<<<<<<<
        var sel = this,
            newTop = Math.min(contentTop[$('#sidebar a').index($(this))], $(document).height() - $(window).height()); // get content top or top position if at the document bottom
        $('html,body').stop().animate({
            'scrollTop': newTop
        }, animationTime, function() {
            window.location.hash = $(sel).attr('href');
            $(window).on('scroll.nav', scroller); //<<<<<<<
        });
    });

    //scroll function
    function scroller() {
        var winTop = $(window).scrollTop(),
            bodyHt = $(document).height(),
            vpHt = $(window).height() + edgeMargin; // viewport height + margin
        $.each(contentTop, function(i, loc) {
            if ((loc > winTop - edgeMargin && (loc < winTop + topRange || (winTop + vpHt) >= bodyHt))) {
                //animate slider
                $slider.animate({
                    top: ($("#sidebar li").eq(i).position().top)
                }, 100);
            }
        });
    }
    //scroll event handler
    $(window).on('scroll.nav', scroller); //<<<<<<<
});

<强> Updated fiddle