【问题标题】:How to stop DIV larger than viewport from scrolling?如何阻止大于视口的DIV滚动?
【发布时间】:2017-03-18 03:43:20
【问题描述】:

我有一个 DIV(一个容器元素)。

其中有一些我想淡入和淡出的元素(取决于用户滚动的方向)。这没问题。

查看嵌入式代码 sn-p 进行演示。

问题

注意演示中逐渐消失的黑色区域;我想在淡入淡出时将其保持在顶部(通过滚动一段距离)。

在此类元素的淡入和淡出期间,我希望保持父容器 (DIV) 不会(垂直)移动,直到淡入或淡出完成。这部分对我来说是个问题。

2017 年 11 月 3 日更新

我已经用一个在 Chrome 中运行良好的示例更新了我的代码(使它坚持使用负数 marginTop 拉起 div(向上滚动时)和正数 top 向下推 @987654324 @(向下滚动时)。但在 Edge 或 Firefox 中运行非常“糟糕”(滚动后触发的scroll-事件使其落后)。

我尝试过的

  • 我尝试过使用position: sticky。这首先使用position: relative,然后使用position: fixed,所以它并不能完全满足我的要求。
  • 我已尝试使用“位置:固定”。当然,父容器 DIV 确实会粘在视口上。但是,因为它的高度大于视口的高度,所以它不能按照我的预期工作,例如如果用户滚动到大容器高度的 50%,则此 scrollTop 会因使用“位置:固定”而丢失。
  • 我已尝试将“位置:固定”与“滚动”事件和 marginTop CSS 属性(以及“顶部”CSS 属性)结合使用。我得到了奇怪的结果。此外,“滚动”事件总是在用户滚动一定数量的像素后执行。因此,如果它能够奏效,您可能会得到一种“滞后”的体验。
  • 我搜索过 jQuery 插件等,但它们也依赖于“位置:固定”,因此也受到限制。
  • 我已尝试使用 ScrollMagic 并同时使用 GreenSock 的 TimelineMax 执行补间。例如,一个补间用于淡入和淡出,一个补间用于对 marginTop(再次)或 top 进行动画处理,以使用“位置:绝对”和“位置:相对”来补偿滚动距离。
  • 我已经尝试捕获鼠标滚轮事件,然后以编程方式滚动(因此我可以选择不滚动)。这当然是一种选择。但我真的很想要一个滚动条。移动体验落后,因为当一个人进行程序化滚动时,一个人不能使用例如“双击”手势。
  • 我已经尝试了很多东西以及这些东西的很多变体。

代码(嵌入式 sn-p)- 2017 年 11 月 3 日更新

代码在非 Chrome (!) 的浏览器中运行糟糕

var $window = $(window);
// Element which needs to fade in and out.
var $fadingblack = $("#fadingblack");
// Parent element of fading element.
var $scrollcontainer = $("#scrollcontainer");

// Last scrollTop-value.
var lastScrollTop = $window.scrollTop();
var lockForFade = false;

$window.on('scroll',
  // Function which is to be called after user scrolls.
  function() {
    // Current scrollTop value.
    var currentScrollTop = $window.scrollTop();
    // Y-coordinate of element which needs to fade.  
    var scrollTopStart = $fadingblack.position().top;
    // Y-coordinate of end of element which needs to fade.
    var scrollTopEndDown = scrollTopStart + $fadingblack.height();
    var scrollTopEndUp = scrollTopStart - $fadingblack.height();
    // Has element which needs to fade scrolled into view.

    // Does the fading itself.
    function doFade($el, $parent, lastScrollTop, currentScrollTop, scrollTopStart, scrollTopEnd) {
      // Curent opacity for fade; determined by scroll position.
      //var currentOpacity = (currentScrollTop - scrollTopStart) / (scrollTopEnd - scrollTopStart);
      var currentOpacity;


      // Temporary variables for scrollTop.
      var theTop;
      var fadeCompleted;

      function undoPushAndScroll() {
        // Save the amount of pixels the parent element has been pushed down.
        var savedTop = $parent.position().top;
        // Then reset this 'push amount'.
        $parent.css("top", 0);
        // And scroll the pushed down amount of pixels back upwards.
        $window.scrollTop(currentScrollTop - savedTop);
        currentScrollTop -= savedTop;
      }

      function undoPullAndScroll() {
        // Save the amount of pixels the parent element has been pulled up.
        var savedTop = parseFloat($parent.css('marginTop'));
        // Then reset this 'pull amount'.
        $parent.css("marginTop", 0);
        // And scroll the pulled up amount of pixels back downwards.
        $window.scrollTop(currentScrollTop - savedTop);
        currentScrollTop -= savedTop;
      }

      function undoPullAndDoPush() {
        var savedMarginTop = parseFloat($parent.css('marginTop'));

        $parent.css('marginTop', 0);
        $window.scrollTop(currentScrollTop - savedMarginTop);
        currentScrollTop -= savedMarginTop;

        // Determine difference between start of fade (Y-value) and current scrollTop (Y-value).
        var theTop = Math.abs(scrollTopStart - currentScrollTop); // + savedMarginTop;
        // Push the parent element down that same difference.
        $parent.css("top", theTop);
      }

      function undoPushAndDoPull() {
        // Save the amount of pixels the parent element has been pushed down.
        var savedTop = $parent.position().top;

        $parent.css('top', 0);
        $window.scrollTop(currentScrollTop - savedTop);
        currentScrollTop -= savedTop;
        // User has scrolled up.
        // Determine difference between start of fade (Y-value) and current scrollTop (Y-value).
        var theTop = Math.abs(scrollTopStart - currentScrollTop);
        // Pull the parent element up that same difference.
        $parent.css("marginTop", -theTop);
      }



      if (lastScrollTop < currentScrollTop) {
        // User has scrolled down.
        undoPullAndDoPush();

        //currentOpacity = Math.abs(currentScrollTop - scrollTopStart) / $el.height();

        fadePercent = Math.abs(currentScrollTop + $parent.position().top - scrollTopStart) / $el.height();
        currentOpacity = fadePercent;

        // Fade to current opacity immediately.
        $el.fadeTo(0, currentOpacity);


        // Determine if fade has completed (must scroll at least the height of the fading element).
        fadeCompleted = ($parent.position().top >= $el.height());
        if (fadeCompleted) {
          // Then immediately set opacity to 1.
          $el.fadeTo(0, 1);
          // Fade has completed.
          undoPushAndScroll();
          lockForFade = false;
        }
      } else if (lastScrollTop > currentScrollTop) {
        // User has scrolled up.
        undoPushAndDoPull();


        fadePercent = Math.abs(currentScrollTop + parseFloat($parent.css('marginTop')) - scrollTopStart) / $el.height();
        currentOpacity = 1 - fadePercent;
        // Fade to current opacity immediately.
        $el.fadeTo(0, currentOpacity);

        // Determine if fade has completed (must scroll at least the height of the fading element).
        fadeCompleted = (-parseFloat($parent.css('marginTop')) >= $el.height());
        if (fadeCompleted) {
          // Then immediately set opacity to 0.
          $el.fadeTo(0, 0);
          // Fade has completed.
          undoPullAndScroll();
          lockForFade = false;
        }
      }
    }

    if (lastScrollTop < currentScrollTop) {
      // Scrolling down in fade area.
      if (!lockForFade && currentScrollTop >= scrollTopStart && lastScrollTop < scrollTopStart) {
        if (parseFloat($fadingblack.css('opacity')) < 1) {
          lockForFade = true;
        }
      }
      if (lockForFade) {
        doFade(
          $fadingblack,
          $scrollcontainer,
          lastScrollTop,
          currentScrollTop,
          scrollTopStart,
          scrollTopEndDown);
      }
    } else if (lastScrollTop > currentScrollTop) {
      // Scrolling up in fade area.
      if (!lockForFade && currentScrollTop <= scrollTopStart && lastScrollTop > scrollTopStart) {
        if (parseFloat($fadingblack.css('opacity')) > 0) {
          lockForFade = true;
        }
      }
      if (lockForFade) {
        console.log('dofade up');
        doFade(
          $fadingblack,
          $scrollcontainer,
          lastScrollTop,
          currentScrollTop,
          scrollTopStart,
          scrollTopEndUp);
      }
    }


    // Save last scrollTop-value for next scroll-event-call.
    lastScrollTop = $window.scrollTop();
  });
body {
  background-color: whitesmoke;
}

#scrollcontainer {
  position: absolute;
  left: 0px;
  top: 0px;
}

.red,
.blue,
.black {
  position: relative;
  width: 900px;
}

.red,
.blue {
  height: 300px;
}

.black {
  height: 600px;
}

.red {
  background-color: red;
}

.blue {
  background-color: blue;
}

.black {
  background-color: black;
}

#fadingblack {
  opacity: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="scrollcontainer">
  <div class="red">BEGIN</div>
  <div class="blue">Fading black area is ahead...</div>
  <div id="fadingblack" class="black">&nbsp;</div>
  <div class="blue">&nbsp;</div>
  <div class="red">&nbsp;</div>
  <div class="blue">END</div>
</div>

【问题讨论】:

  • 所以你想让'fadingblack' div在它出现后就粘住,然后滚动淡入淡出,一旦淡入淡出,继续让其他div进入视野?
  • @AmericanSlime 是的,这正是我想要实现的目标。

标签: javascript jquery html css


【解决方案1】:

可能没有办法真的 - 以一种更自然的方式 - 阻止使用 position: fixeddiv - 甚至是 position: sticky(它首先使用相对定位 和稍后当它保持固定定位时) - 并且当您希望它在某个点滚动时,它的尺寸大于视口。

不过,我的问题的目标(停止滚动以淡化一些 div 元素)我可以用一些代码行来回答自己。总而言之:使用divmarginTop 拉起使用固定定位的div 的一些div 子元素。

我确实有至少一个与此代码相关的其他问题(可能是两个)(但不是防止滚动)。但这是我可以作为新问题提出的其他问题。

我现在会接受我自己的答案。如果有人对这个问题有更好或更“自然”的想法——我有点怀疑——我会很高兴地改变我对这个答案的接受。

var $window = $(window);
var $document = $(document);
// Element which needs to fade in and out.
var $fadingblack = $("#fadingblack");
var $scrolldistract = $("#scrolldistract");
var $scrollsviascrolldistract = $("#scrollsviascrolldistract");
// Pulls up the child divs of #scrollsviascrolldistract, under it.
var $puller = $("#puller");


// Start of fading area (Y-value).
var scrollTopStart = $fadingblack.position().top;
// And of course the Y-value of the end of the fading area.
var scrollTopEnd = scrollTopStart + $fadingblack.height();

// Maximum scrollTop-value (when scrollbar is at 100%).
var lastScrollTop = $document.height() - $window.height();

// Amount of scrolled pixels (vertically) including amount scrolled while
// the fading element is fading.
var scrollAmountWithFadeAmount = $document.height + $fadingblack.height();
// Setting height does not quite work for an empty div,
// so we are using some padding.
$scrolldistract.css("paddingTop", scrollAmountWithFadeAmount);
// Percentage of which we have scrolled (1 = 100%).
var currentScrollTopP;
// Current scrollTop value.
var realCurY;

$(function() {
  // Off you go code...

  function doScrollOrFade() {
    currentScrollTopP = Math.ceil($window.scrollTop() / lastScrollTop * 100) / 100;
    realCurY = currentScrollTopP * lastScrollTop;

    if (realCurY >= scrollTopStart && realCurY <= scrollTopEnd) {
      // Current realCurY dictates we are in fade area.
      // So scroll the fading area into view at top of browser viewport.
      $puller.css("marginTop", -scrollTopStart);
      // Determine opacity percentage.
      var fadePercent = (realCurY - scrollTopStart) / (scrollTopEnd - scrollTopStart);
      // Fade to current opacity immediately.
      $fadingblack.fadeTo(0, fadePercent);
    } else {
      // We are outside of the fading area and in scroll-mode.
      if (realCurY < scrollTopStart) {
        // We are somewhere before the fading area, so set opacity to 0.
        $fadingblack.fadeTo(0, 0);
      } else {
        // We are somewhere after the fading area, so set opacity to 1.
        $fadingblack.fadeTo(0, 1);
      }

      if (realCurY > scrollTopEnd) {
        // We have passed the fading area. So we have an amount
        // of pixels we wasted on the opacity changes.
        // Correct it here.
        $puller.css("marginTop", -realCurY + $fadingblack.height());
      } else {
        $puller.css("marginTop", -realCurY);
      }
    }
    window.requestAnimationFrame(doScrollOrFade);
  }

  window.requestAnimationFrame(doScrollOrFade);

  $window.on('resize orientationchange', function(e) {
    // On resize or orientation change recalculate some stuff.
    lastScrollTop = $document.height() - $window.height();
    scrollAmountWithFadeAmount = $document.height + $fadingblack.height();
    $scrolldistract.css("paddingTop", scrollAmountWithFadeAmount);
    window.requestAnimationFrame(doScrollOrFade);
  });
});
body {
  background-color: whitesmoke;
}

#scrollsviascrolldistract {
  position: fixed;
  left: 0px;
  top: 0px;
  width: 100%;
}

#scrolldistract {
  position: absolute;
  left: 0px;
  top: 0px;
  width: 100%;
  padding-top: 2100px;
  height: 0px;
}

#puller {
  position: relative;
  margin-top: 0px;
  left: 0px;
  top: 0px;
}

img {
  display: block;
}

.black,
.red,
.blue {
  border: solid 1px yellow;
  font-size: 32pt;
  position: relative;
  width: 100%;
  height: 300px;
}

.red {
  background-color: red;
}

.blue {
  background-color: blue;
}

.black {
  background-color: black;
}
<!--
For mobile support use viewport meta-tag inside <head>:
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=yes">
-->

<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="scrolldistract"></div>

<div id="scrollsviascrolldistract">
  <!-- For pulling up the red, blue and fading area -->
  <div id="puller"></div>
  <div class="red">BEGIN</div>
  <div class="blue">Fading black area is ahead...</div>
  <div id="fadingblack" class="black">&nbsp;</div>
  <div class="blue">&nbsp;</div>
  <div class="red">&nbsp;</div>
  <div class="blue">END</div>
</div>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-10-14
    • 2014-01-19
    • 2018-01-12
    • 2012-04-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多