最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - When element enters viewport, scroll its entire contents horizontally before it leaves the viewport - Stack Overflo

programmeradmin2浏览0评论

I have a horizontally scrolling element containing images that is situated out of view down the page.

To start with, I have the following markup and styles. I've used overflow:hidden because I don't want scrollbars. For the sake of simplicity, I have also removed some less-important styles:

<ul id="players-horizontal">
    <li class="player-box"><img src="..."></li>
    <li class="player-box"><img src="..."></li>
    ...
</ul>

#players-horizontal {
    overflow: hidden;
    height: 340px;
    width: 100%;
}

#players-horizontal .player-box {
    display: inline-block;
    width: 300px;
    height: 340px;
}

#players-horizontal .player-box:first-child {
    margin-left: 90%;
}

Which gives me the following view:

When this element scrolls into view, I want to scroll its entire contents horizontally until it's about to go out of view, so the user gets to see its entire contents whilst scrolling down.

The desired look when the element is about to leave the viewport would be:

And vice-versa, the reverse action should happen when the user scroll upwards.

To know when the element has entered the viewport I have use the Waypoints plugin:

var waypoints = $('#players-horizontal').waypoint(
    function(direction) {
        if (direction === 'down') {
            //console.log("Into view");
            $window.scroll(function () {
                var s = $(this).scrollTop();

            });
        } else {
            //console.log("Out of view");
            $window.scroll(function () {
                var s = $(this).scrollTop();

            });
        }
    }, { offset: '90%' }
);

But I'm stuck understanding what to do next, more specifically, how to work out how much to scroll (alter the margin of) the element in the short timeframe it has before leaves the viewport, ensuring all elements inside of it are shown before it does.

I would appreciate a help over the finishing line. I don't expect working code, although I'd be grateful, I really just need an easy to understand explanation of what is required.

UPDATE:

I have just tried using an alternative method which doesn't use the Waypoints plugin but instead uses ScrollMagic. This has definitely lifted a lot of the work required.

var scene = new ScrollMagic.Scene({triggerElement: "#players-horizontal", duration: 200})
.addTo(controller)
.on("progress", function (e) {
    console.log((e.progress * 100));
});

The above snippet detects when the element #players-horizontal enters and leaves the viewport.

When the element enters the bottom of viewport when scrolling down, the value returned starts at 0 and stops at 100 just before leaving the top of the viewport. When I scroll back up, the value starts at 100 and stops at 0 when the element is about to leave the bottom of the viewport. So, I'm definitely a lot closer.

I have a horizontally scrolling element containing images that is situated out of view down the page.

To start with, I have the following markup and styles. I've used overflow:hidden because I don't want scrollbars. For the sake of simplicity, I have also removed some less-important styles:

<ul id="players-horizontal">
    <li class="player-box"><img src="..."></li>
    <li class="player-box"><img src="..."></li>
    ...
</ul>

#players-horizontal {
    overflow: hidden;
    height: 340px;
    width: 100%;
}

#players-horizontal .player-box {
    display: inline-block;
    width: 300px;
    height: 340px;
}

#players-horizontal .player-box:first-child {
    margin-left: 90%;
}

Which gives me the following view:

When this element scrolls into view, I want to scroll its entire contents horizontally until it's about to go out of view, so the user gets to see its entire contents whilst scrolling down.

The desired look when the element is about to leave the viewport would be:

And vice-versa, the reverse action should happen when the user scroll upwards.

To know when the element has entered the viewport I have use the Waypoints plugin:

var waypoints = $('#players-horizontal').waypoint(
    function(direction) {
        if (direction === 'down') {
            //console.log("Into view");
            $window.scroll(function () {
                var s = $(this).scrollTop();

            });
        } else {
            //console.log("Out of view");
            $window.scroll(function () {
                var s = $(this).scrollTop();

            });
        }
    }, { offset: '90%' }
);

But I'm stuck understanding what to do next, more specifically, how to work out how much to scroll (alter the margin of) the element in the short timeframe it has before leaves the viewport, ensuring all elements inside of it are shown before it does.

I would appreciate a help over the finishing line. I don't expect working code, although I'd be grateful, I really just need an easy to understand explanation of what is required.

UPDATE:

I have just tried using an alternative method which doesn't use the Waypoints plugin but instead uses ScrollMagic. This has definitely lifted a lot of the work required.

var scene = new ScrollMagic.Scene({triggerElement: "#players-horizontal", duration: 200})
.addTo(controller)
.on("progress", function (e) {
    console.log((e.progress * 100));
});

The above snippet detects when the element #players-horizontal enters and leaves the viewport.

When the element enters the bottom of viewport when scrolling down, the value returned starts at 0 and stops at 100 just before leaving the top of the viewport. When I scroll back up, the value starts at 100 and stops at 0 when the element is about to leave the bottom of the viewport. So, I'm definitely a lot closer.

Share Improve this question edited Jan 29, 2019 at 12:03 Martin James asked Jan 29, 2019 at 11:20 Martin JamesMartin James 9301 gold badge11 silver badges26 bronze badges 1
  • Thank you so much for your update. I've been looking for a solution like this for some time. Works great! – TechyDude Commented Jun 17, 2021 at 15:53
Add a ment  | 

1 Answer 1

Reset to default 6

I would think about this differently. First I would consider using translation to make it easier to handle. You also need a little JS code to handle this

I will have the main container that I will translate by 90% to have your margin effect, then I will translate the content inside it from 0% to 100%. The translation will consider the scroll and screen height. Basically when the element enter the sreen I will start translating consider the difference between the offset top and the screen height.

Here is a first example.

var h =document.querySelector('#players-horizontal');

document.body.onscroll = function(e) {
  
  var offset = h.getBoundingClientRect()
  var top = offset.top;
  if(top < window.innerHeight) {
     h.style.setProperty('--x',(top - window.innerHeight)+'%');
  }
}
body {
 margin:0;
 overflow-x:hidden;
}
.top,.bottom {
  min-height:150vh;
  background:yellow;
}
.container {
    transform:translateX(90%);
}
#players-horizontal {
    white-space:nowrap;
    margin:0;
    padding:0;
    display:inline-block; /*This is important !!*/
    transform:translateX(var(--x,0%));
    border:5px solid;
}

#players-horizontal .player-box {
    display: inline-block;
    width: 200px;
    height: 240px;
    margin:0 10px;
    background:red;
}

#players-horizontal .player-box:first-child {
    background:green;
    margin-left:0;
}
#players-horizontal .player-box:last-child {
    background:blue;
    margin-right:0;
}
<div class="top"></div>
<div class="container">
<ul id="players-horizontal">
    <li class="player-box"></li>
    <li class="player-box"></li>
    <li class="player-box"></li>
    <li class="player-box"></li>
    <li class="player-box"></li>
</ul>
</div>
<div class="bottom"></div>

This above will create the scrolling effect but now we need to define the correct values.

It's clear that we need the scroll to start at top = window.innerHeight (translate(0%)) and when top = 0 we want our element to be fully shown and the last item at the right edge which means that we need to have translate(-100% + width of screen) but since the width of screen is also the same as the container width and we already translated the container by 90% then we can only reach translate(-100%) (we can add negative margin to last element to rectify the position).

We simply need to convert the difference (top - window.innerHeight) to a percentage value considering window.innerHeight as 100% (when top=0).

var h =document.querySelector('#players-horizontal');

document.body.onscroll = function(e) {
  
  var offset = h.getBoundingClientRect()
  var top = offset.top;
  if(top < window.innerHeight && top >=0) {
     h.style.setProperty('--x',(top - window.innerHeight)*(100/window.innerHeight)+'%');
  }
}
body {
 margin:0;
 overflow-x:hidden;
}
.top,.bottom {
  min-height:150vh;
  background:yellow;
}
.container {
    transform:translateX(90%);
}
#players-horizontal {
    white-space:nowrap;
    margin:0;
    padding:0;
    display:inline-block; /*This is important !!*/
    transform:translateX(var(--x,0%));
    border:5px solid;
}

#players-horizontal .player-box {
    display: inline-block;
    width: 200px;
    height: 240px;
    margin:0 10px;
    background:red;
    vertical-align:top;
}

#players-horizontal .player-box:first-child {
    background:green;
    margin-left:0;
}
#players-horizontal .player-box:last-child {
    background:blue;
    margin-right:-10vw; /*the missing 10%*/
}
<div class="top"></div>
<div class="container">
<ul id="players-horizontal">
    <li class="player-box"></li>
    <li class="player-box"></li>
    <li class="player-box"></li>
    <li class="player-box"></li>
    <li class="player-box"></li>
    <li class="player-box"></li>
    <li class="player-box"></li>
    <li class="player-box"></li>
</ul>
</div>
<div class="bottom"></div>

I have used CSS variable (a personnal preference) but it's not really mandatory and you can simply set transform with the JS code:

var h =document.querySelector('#players-horizontal');

document.body.onscroll = function(e) {
  
  var offset = h.getBoundingClientRect()
  var top = offset.top;
  if(top < window.innerHeight && top >=0) {
     h.style.transform="translateX("+(top - window.innerHeight)*(100/window.innerHeight)+'%)';
  }
}
body {
 margin:0;
 overflow-x:hidden;
}
.top,.bottom {
  min-height:150vh;
  background:yellow;
}
.container {
    transform:translateX(90%);
}
#players-horizontal {
    white-space:nowrap;
    margin:0;
    padding:0;
    display:inline-block; /*This is important !!*/
    transform:translateX(0%);
    border:5px solid;
}

#players-horizontal .player-box {
    display: inline-block;
    width: 200px;
    height: 240px;
    margin:0 10px;
    background:red;
    vertical-align:top;
}

#players-horizontal .player-box:first-child {
    background:green;
    margin-left:0;
}
#players-horizontal .player-box:last-child {
    background:blue;
    margin-right:-10vw;
}
<div class="top"></div>
<div class="container">
<ul id="players-horizontal">
    <li class="player-box"></li>
    <li class="player-box"></li>
    <li class="player-box"></li>
    <li class="player-box"></li>
    <li class="player-box"></li>
    <li class="player-box"></li>
    <li class="player-box"></li>
    <li class="player-box"></li>
</ul>
</div>
<div class="bottom"></div>

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论