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

javascript - CSS Scroll Snap with Animation Effect - Stack Overflow

programmeradmin0浏览0评论

Precondition

I am trying to make a number list picker that user can scroll on it. The selected number was supposed to snap to the center of the container. Once a number was selected, it should be enlarged and changed the color.


Problems

  • How can I snap the first or latest items to the center of the container?
  • Is there a CSS or JavaScript way to detect a number is snapping to the center then assign selected class on it?
  • Do not rely on any plugins.

* {
  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>

Precondition

I am trying to make a number list picker that user can scroll on it. The selected number was supposed to snap to the center of the container. Once a number was selected, it should be enlarged and changed the color.


Problems

  • How can I snap the first or latest items to the center of the container?
  • Is there a CSS or JavaScript way to detect a number is snapping to the center then assign selected class on it?
  • Do not rely on any plugins.

* {
  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>


Expect Result

Share Improve this question edited Sep 6, 2021 at 14:49 Penny Liu asked May 29, 2019 at 8:13 Penny LiuPenny Liu 17.6k5 gold badges86 silver badges108 bronze badges 4
  • 2 Something is not clear to me: do you want your number to be selected only when scrolling, without having to click on it? If so, how would you be able to select the first and last item in the list? You should also change your snippet IMO, because in current state, it shows exactly what you expect, so that people can be confused what to fix – Kaddath Commented May 29, 2019 at 8:17
  • 1 Thanks for your ments. Give me a few minutes. I'll update my post very soon. – Penny Liu Commented May 29, 2019 at 8:26
  • These are two questions. Let's stick to one. – yunzen Commented May 29, 2019 at 9:03
  • 1 I made an edit to my answer to answer both your questions – yunzen Commented May 29, 2019 at 10:17
Add a ment  | 

2 Answers 2

Reset to default 5

Add pseudo elements to .scrollport and style them like .cell

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

Also I created a JS function that utilizes David Walsh's debounce function to simulate a scrollend event. I then check for every cell if it's center is around the scrollport's center and set the classes accordingly

"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 patibility
    // 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>

You can try this code here:

in this answer used jquery malihu-custom-scrollbar-plugin link and update some code here

(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./ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare./ajax/libs/malihu-custom-scrollbar-plugin/3.1.5/jquery.mCustomScrollbar.concat.min.js"></script>
<link href="https://cdnjs.cloudflare./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>

发布评论

评论列表(0)

  1. 暂无评论