当用户手动向上或向下滚动时,重置当前节计数器

时间:2019-02-19 05:26:32

标签: javascript vue.js scroll vuejs2

我正在实现一个按钮,单击该按钮可以垂直向下滚动一位团队成员。到目前为止,我已经设法一键向下滚动一位团队成员,但是当用户手动滚动回到顶部时代码会中断。

这里在工作JSFiddle

我的代码

<main class="team container">
    <div v-for='(element, index) in members' :id="element.specialId" class="team__member" v-show="activeIndex === index || widthSmall">
        <div class="team__member__bio">
            <div class="team__member__name">{{element.name}}</div>
        </div>
    </div>
    <a class="scroll-more scroll-team" v-show="widthSmall" @click="move($event)" style="cursor: pointer;">
        <span></span>{{ $t('scroll') }}
    </a>
</main>

export default {
    name: "Team",
    data() {
        return {
            members: [
                {
                    name: "Bojan Dovrtel",
                    specialId: 'bojan-div'
                },
                {
                    name: "Klemen Razinger",
                    specialId: 'kelemen-div'
                },
                {
                    name: "Davor Pečnik",
                    specialId: 'davor-div'
                },
                {
                    name: "Maja Katalinič",
                    specialId: 'maja-div'
                },
                {
                    name: "Petra Vovk",
                    specialId: 'petra-div'
                }
            ],
            secs: document.getElementsByClassName('team__member'),
            currentSection: 0,
        }
    },
    methods: {
        move(e) {
            if (this.currentSection < this.secs.length) {
                if (this.currentSection === 3) {
                    this.widthSmall = false;
                }
                window.scroll({
                    top: this.secs[++this.currentSection].offsetTop,
                    left: 0,
                    behavior: 'smooth'
                });
            } else if (this.currentSection > 0) {
                window.scroll({
                    top: this.secs[--this.currentSection].offsetTop,
                    left: 0,
                    behavior: 'smooth'
                });

            }
        }
    }
};

如何检测到用户已向上滚动并更改了当前部分的值?如果您有其他任何信息,请让我知道,我会提供。谢谢

2 个答案:

答案 0 :(得分:1)

要检测到用户已滚动,可以侦听正在滚动的容器上的scroll事件。在这种情况下,这将是根元素,因此您可以使用window添加事件侦听器。

实现这一目标的一种方法是在scrollcreated生命周期挂钩中添加和删除destroyed侦听器,如in this answer所述。

请注意,当您使用window.scroll({...})触发滚动时,不仅用户滚动,还会触发滚动事件。因此,您需要注意这一点。

我建议向scroll事件侦听器中添加某种限制,然后通过更改scroll值来响应所有currentSection事件,并在限制之后进行响应。

例如,您的scroll事件处理程序可以是:

...,
onScroll(e) {
  if(this.throttle) {
    clearTimeout(this.throttle);
  }

  this.throttle = setTimeout(() => (this.currentSection = this.findCurrentSection(e)), 300);
},
...

throttle只是用于保存超时值的数据成员。仅在最后一个currentSection事件后 后300ms触发寻找scroll值的逻辑。您还可以使用requestAnimationFrame来执行此操作,如here所述。

findCurrentSection只是一种基本方法,它遍历secs数组以根据当前滚动值查找当前节。

...,
findCurrentSection(e) {
  const curTop = document.documentElement.scrollTop;
  for(let i=0, len=this.secs.length; i < len; ++i) {
    const secTop = this.secs[i].offsetTop;
    if(curTop === secTop) {
        return i;
    } else if(curTop < secTop) {
        return Math.max(i-1, 0);
    }
  }
},
...

请注意,由于在这种特殊情况下滚动容器是根元素,因此我使用的是document.documentElement.scrollTop,但是根据上下文,您可以从相应的ScrollEvent({ {1}})。

根据您的问题,这里是working fiddle。另外请注意,我已经根据引入的更改修改了e函数。

答案 1 :(得分:1)

您可以遍历元素,找到最接近的元素,其offsetTop(+ offsetHeight)在滚动时与当前window.scrollY的偏移量匹配(或在其范围内),然后决定是滚动到下一个元素还是“重新调整”偏移量:

new Vue({
  el: '#app',

  data() {
    return {
      members: [
        {
          name: "Bojan",
          specialId: 'bojan-div'
        }, 
        {
          name: "Klemen",
          specialId: 'kelemen-div'
        }, 
        {
          name: "Davor",
          specialId: 'davor-div'
        }, 
        {
          name: "Maja",
          specialId: 'maja-div'
        }, 
        {
          name: "Petra",
          specialId: 'petra-div'
        }
      ],
      secs: document.getElementsByClassName('height'),
      currentSection: 0
    }
  },

  mounted() {
    this.move();
  },

  methods: {
    move() {
      let y = window.scrollY;
      let totalSection = this.secs.length;
      
      for (let index = 0; index < totalSection; index++) {
        let sec = this.secs[index];

        if (sec.offsetTop === y) {
          // currentSection matches current window.scrollY, so we want to move to the next section/element
          // Math.min() to ensure it won't go out of range, capping at the length of the total elements.
          this.currentSection = Math.min(index + 1, totalSection - 1);

          // Or reset the index once it has scrolled all the way down
          // this.currentSection = (index + 1) % totalSection;
          break;
        }
        else if (sec.offsetTop >= y && y <= (sec.offsetTop + sec.offsetHeight)) {
          // window.scrollY is currently between the matched element's offsetTop and offsetHeight.
          // This is user-initiated scrolling, so let's just "readjust" the offset rather than scrolling to the next element.
          this.currentSection = index;
          break;
        }
      }

      window.scroll({
        top: this.secs[this.currentSection].offsetTop,
        left: 0,
        behavior: 'smooth'
      });
    }
  },
})
.height {
  background-color: grey;
  height: 300px;
  border: solid 2px black;
}

.scroll-team {
  position: fixed;
  top: calc(100vh - 6rem);
  left: 50%;
  z-index: 2;
  display: inline-block;
  -webkit-transform: translate(0, -50%);
  transform: translate(0, -50%);
  color: #fff;
  letter-spacing: 0.1em;
  text-decoration: none;
  transition: opacity 0.3s;
}

.scroll-team a:hover {
  opacity: 0.5;
}

.scroll-more {
  padding-top: 60px;
  font-size: 1.35rem;
}

.scroll-more span {
  position: absolute;
  top: 0;
  left: 50%;
  width: 46px;
  height: 46px;
  margin-left: -23px;
  border: 1px solid #fff;
  border-radius: 100%;
  box-sizing: border-box;
  background: rgba(255, 255, 255, 0.2);
}

.scroll-more span::after {
  position: absolute;
  top: 50%;
  left: 50%;
  content: "";
  width: 16px;
  height: 16px;
  margin: -12px 0 0 -8px;
  border-left: 1px solid #fff;
  border-bottom: 1px solid #fff;
  -webkit-transform: rotate(-45deg);
  transform: rotate(-45deg);
  box-sizing: border-box;
}

.scroll-more span::before {
  position: absolute;
  top: 0;
  left: 0;
  z-index: -1;
  content: "";
  width: 44px;
  height: 44px;
  box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.1);
  border-radius: 100%;
  opacity: 0;
  -webkit-animation: sdb03 3s infinite;
  animation: sdb03 3s infinite;
  box-sizing: border-box;
}
<script src="https://unpkg.com/vue"></script>

<div id="app">
  <div class="height" v-for="(element, index) in members" :key="index">
    {{ element.name }}
  </div>
  <a class="scroll-more scroll-team" @click="move" style="cursor: pointer;">
    <span></span>
  </a>
</div>