【问题标题】:CSS Scroll Snap with Animation Effect带有动画效果的 CSS Scroll Snap
【发布时间】:2019-10-14 18:59:39
【问题描述】:

前提条件

我正在尝试制作一个用户可以在其上滚动的数字列表选择器。 所选数字应该与容器的center 对齐。 一旦选择了一个数字,它应该被放大并改变颜色。


问题

  • 如何将第一个或最新的项目捕捉到中心 容器?
  • 是否有 CSS 或 JavaScript 方法来检测数字是 捕捉到中心然后在上面分配selected 类?
  • 不要依赖任何插件。

* {
  box-sizing: border-box;
  font-family: Roboto, sans-serif;
}

.container {
  display: flex;
  flex-direction: row;
  width: 10rem;
  height: 22rem;
  border-radius: 3rem;
  border: solid 0.2rem #b2b2c2;
  background-color: #000000;
  display: flex;
  align-items: center;
  justify-content: center;
}

.scrollport {
  display: flex;
  flex-direction: column;
  flex-wrap: nowrap;
  width: 9.4rem;
  height: 22rem;
  overflow: auto;
  scroll-snap-type: y mandatory;
}

.cell {
  display: block;
  scroll-snap-align: center;
  flex-grow: 1;
  flex-shrink: 0;
  flex-basis: 33.3%;
  display: flex;
  justify-content: center;
  align-items: center;
  color: #e9e9f2;
  font-size: 2.4rem;
}

.selected {
  font-size: 3rem;
  font-weight: bold;
  color: #0073e6;
}
<div class="container">
  <div class="scrollport">
    <div class="cell">09</div>
    <div class="cell selected">10</div>
    <div class="cell">11</div>
    <div class="cell">12</div>
    <div class="cell">13</div>
    <div class="cell">14</div>
    <div class="cell">15</div>
    <div class="cell">16</div>
  </div>
</div>

预期结果

【问题讨论】:

  • 我不清楚:您是否希望仅在滚动时选择您的号码,而无需单击它?如果是这样,您将如何选择列表中的第一项和最后一项?您还应该更改您的 sn-p IMO,因为在当前状态下,它准确显示了您的期望,因此人们可能会混淆要修复什么
  • 感谢您的 cmets。给我几分钟。我会很快更新我的帖子。
  • 这是两个问题。让我们坚持一个。
  • 我对我的回答进行了编辑以回答您的两个问题

标签: javascript html css animation scroll


【解决方案1】:

你可以在这里试试这个代码:

在这个答案中使用了 jquery ma​​lihu-custom-scrollbar-plugin link 并在此处更新一些代码

(function($){
    $(window).on("load",function(){
        $(".scrollport").mCustomScrollbar({autoHideScrollbar:true});
    });
})(jQuery);
* {
  box-sizing: border-box;
  font-family: Roboto, sans-serif;
}

.container {
  display: flex;
  flex-direction: row;
  width: 10rem;
  height: 22rem;
  border-radius: 3rem;
  border: solid 0.2rem #b2b2c2;
  background-color: #000000;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

.scrollport {
  display: flex;
  flex-direction: column;
  flex-wrap: nowrap;
  width: 9.4rem;
  height: 22rem;
  overflow: auto;
}

.cell {
  flex-basis: 120px;
  height: 120px;
  color: #e9e9f2;
  font-size: 2.4rem;
  display: flex; 
  align-items: center;
  justify-content: center;
  padding-left: 20px;
}

.selected {
  font-size: 3rem;
  font-weight: bold;
  color: #0073e6;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/malihu-custom-scrollbar-plugin/3.1.5/jquery.mCustomScrollbar.concat.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/malihu-custom-scrollbar-plugin/3.1.5/jquery.mCustomScrollbar.css" rel="stylesheet" />

<div class="container">
  <div class="scrollport">
    <div class="cell">09</div>
    <div class="cell selected">10</div>
    <div class="cell">11</div>
    <div class="cell">12</div>
    <div class="cell">13</div>
    <div class="cell">14</div>
    <div class="cell">15</div>
    <div class="cell">16</div>
  </div>
</div>

【讨论】:

    【解决方案2】:

    将伪元素添加到.scrollport 并将它们的样式设置为.cell

    .scrollport:before,
    .scrollport:after {
      content: '';
    }
    .scrollport:before,
    .scrollport:after,
    .cell {
      /* ... */
    }
    

    我还创建了一个 JS 函数,它利用 David Walshdebounce 函数来模拟滚动结束事件。然后我检查每个cell 的中心是否在scrollport 的中心附近,并相应地设置类

    "use strict";
    console.clear()
    
    {
      const selector = 'scrollport'
      const selected = 'selected'
      const scrollports = document.getElementsByClassName(selector)
    
      for (const scrollport of scrollports) {
        scrollport.addEventListener('scroll', debounce(check, 250) /* simulate scrollend event */ )
      }
    
      function check(e) {
        // uses native elementFromPoint for better performance
        const rect = e.target.getBoundingClientRect();
        const centerCell = document.elementFromPoint(rect.left + e.target.offsetWidth / 2, rect.top + e.target.offsetHeight / 2)
        for (const cell of e.target.getElementsByClassName(selected)) {
          cell.classList.remove(selected)
        }
        centerCell.classList.add(selected)
        
        // Old version for backward compatibility
        // const rect = e.target.getBoundingClientRect();
        // for (const cell of e.target.children) {
        //   const cellRect = cell.getBoundingClientRect();
        //   const bounds = [rect.height/2 - cellRect.height/2, rect.height/2 + cellRect.height/2];
        //   const centerScrollOffset = cellRect.top - rect.top + cell.offsetHeight / 2
        //   if (bounds[0] < centerScrollOffset && centerScrollOffset < bounds[1]) {
        //     cell.classList.add(selected)
        //   } else {
        //     cell.classList.remove(selected)
        //   }
        // }
      }
    }
    // From: https://davidwalsh.name/javascript-debounce-function
    // Returns a function, that, as long as it continues to be invoked, will not
    // be triggered. The function will be called after it stops being called for
    // N milliseconds. If `immediate` is passed, trigger the function on the
    // leading edge, instead of the trailing.
    function debounce(func, wait, immediate) {
      var timeout;
      return function() {
        var context = this,
          args = arguments;
        var later = function() {
          timeout = null;
          if (!immediate) func.apply(context, args);
        };
        var callNow = immediate && !timeout;
        clearTimeout(timeout);
        timeout = setTimeout(later, wait);
        if (callNow) func.apply(context, args);
      };
    };
    * {
      box-sizing: border-box;
      font-family: Roboto, sans-serif;
    }
    
    .container {
      display: flex;
      flex-direction: row;
      width: 10rem;
      height: 22rem;
      border-radius: 3rem;
      border: solid 0.2rem #b2b2c2;
      background-color: #000000;
      display: flex;
      align-items: center;
      justify-content: center;
    }
    
    .scrollport:before,
    .scrollport:after {
      content: '';
    }
    
    .scrollport {
      display: flex;
      flex-direction: column;
      flex-wrap: nowrap;
      width: 9.4rem;
      height: 22rem;
      overflow: auto;
      scroll-snap-type: y mandatory;
    }
    
    .scrollport:before,
    .scrollport:after,
    .cell {
      display: block;
      scroll-snap-align: center;
      flex-grow: 1;
      flex-shrink: 0;
      flex-basis: 33.3%;
      display: flex;
      justify-content: center;
      align-items: center;
      color: #e9e9f2;
      font-size: 2.4rem;
    }
    
    .selected {
      font-size: 3rem;
      font-weight: bold;
      color: #0073e6;
    }
    <div class="container">
      <div class="scrollport">
        <div class="cell">09</div>
        <div class="cell selected">10</div>
        <div class="cell">11</div>
        <div class="cell">12</div>
        <div class="cell">13</div>
        <div class="cell">14</div>
        <div class="cell">15</div>
        <div class="cell">16</div>
      </div>
    </div>

    【讨论】:

    • 如何在 debounce 中使用箭头功能而不是 var context = this,
    • @PennyLiu 我真的不知道,你想达到什么目的。您想在哪里使用箭头功能。注意:虽然箭头函数在很多地方都很有用,但它们并不适用于所有情况。
    • 谢谢,我明白了,这是我的参考。 medium.com/@griffinmichl/…
    • @PennyLiu 好像是箭头功能不行的情况之一
    • 取决于 elementFromPoint 方法,使用 offsetWidthoffsetHeight 会将值四舍五入为整数。使用getBoundingClientRect().widthgetBoundingClientRect().height 来提高准确性怎么样?
    猜你喜欢
    • 2021-06-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-26
    • 1970-01-01
    • 1970-01-01
    • 2016-04-24
    相关资源
    最近更新 更多