如何从滚动中阻止DIV大于视口?

时间:2017-03-07 12:23:13

标签: javascript jquery html css

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

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

请参阅嵌入式代码段以进行演示。

问题

注意演示中的黑色区域;我希望在褪色的同时将它保持在顶部(通过滚动一段距离)。

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

更新11-03-2017

我已经使用一个似乎在Chrome中运行正常的示例更新了我的代码(使用负marginTop来提升div(向上滚动时)和正top用于按下div(当向下滚动时)。但是在例如Edge或Firefox中运行非常“糟糕”(scroll - 滚动后触发的事件使其滞后)。

我尝试了什么

  • 我尝试过使用position: sticky。这首先使用position: relative,然后使用position: fixed,因此它不能完全按我的意愿使用。
  • 我尝试过使用'position:fixed'。当然,父容器DIV确实粘在视口上。但是,因为它的高度大于视口的高度,所以它不能按我的意图工作,例如如果用户滚动到大容器高度的50%,则使用“position:fixed”将此scrollTop丢失。
  • 我尝试将'position:fixed'与'scroll'事件和marginTop CSS-property(以及'top'CSS属性)结合使用。我得到了奇怪的结果。此外,在用户滚动一定量的像素之后总是执行“滚动”事件。因此,如果它可行,那么可能会获得“滞后”的体验。
  • 我搜索了jQuery插件等,但他们也依赖于“position:fixed”,因此也受到限制。
  • 我尝试过ScrollMagic并使用GreenSock的TimelineMax同时执行补间。例如淡入淡出和淡出的一个补间和一个补间利用marginTop(再次)或顶部的补间以补偿'position:absolute'和'position:relative'的滚动距离。
  • 我尝试过捕捉鼠标滚轮事件然后滚动programmaticaly(所以我可以选择不滚动)。这当然是一种选择。但我真的很喜欢滚动条。移动体验滞后,因为当编程滚动时,人们不能使用例如'双击'手势。
  • 我尝试过很多东西和很多变化。

代码(嵌入代码段) - 已更新11-03-2017

在非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>

1 个答案:

答案 0 :(得分:0)

可能没有办法真正 - 以更自然的方式 - 阻止div使用position: fixed - 甚至position: sticky(其中稍后使用相对定位以后它固定定位时 - 并且当你希望它在某个点时滚动时,它的大小比视口大。

仍然是我的问题的目标(停止滚动以淡化一些div元素)我可以用一些代码行回答自己。总结:使用marginTop的{​​{1}}来提取使用固定定位的div的一些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;
}