自定义滚动条拇指超出范围

Custom scrollbars thumb goes out of bounds

本文关键字:范围 滚动条 自定义      更新时间:2023-09-26

我试图创建一个拇指滚动与默认滚动(动画)一起滚动。我希望最终能够实现它,唯一的问题是,自定义滚动条不能在精确位置滚动。如果你一直向下滚动,拇指滚动条就会越过它的父元素。

同时,拇指滚动条比默认滚动条大。计算方法如下:

scrollBarThumb.style.height = (innerWrapper.parentElement.offsetHeight * innerWrapper.parentElement.offsetHeight / innerWrapper.scrollHeight) + 'px';
scrollBarPosition = clamp(scrollBarPosition, 0, scrolledToBottom)
scrollBarPosition = scrollBar.offsetHeight * scrollBarPosition / innerWrapper.scrollHeight;

我使用的公式是可接受的自定义滚动条公式。

这两个问题是:(它们可能是一个问题,因为我认为如果高度固定,那么滚动位置也会自动固定。)首先,滚动条的拇指比默认值大。其次,当你向下滚动时,滚动条的拇指会越过它的父节点。

我做错了什么,我该如何修复它?

JSFiddle

console.clear();
var innerWrapper = document.getElementById('innerWrapper');
var scrollBar = document.getElementById('scrollbar');
var scrollBarThumb = scrollBar.firstElementChild
scrollBarThumb.style.height = (innerWrapper.parentElement.offsetHeight * innerWrapper.parentElement.offsetHeight / innerWrapper.scrollHeight) + 'px';
innerWrapper.addEventListener('mousewheel', handleScroll);
innerWrapper.addEventListener('DOMMouseScroll', handleScroll);
var duration = 35,
  scrollSpeed = 2,
  animateAmount = 30;
var scrolledToBottom = 0,
  scrollDirection = 0, // 1 = scroll down, -1 = scroll up
  animationID;

function handleScroll(e) {
  // Cancel previous animation
  cancelAnimationFrame(animationID);
  // Scroll faster
  scrollSpeed += 2;
  // Reason for negative `-.wheelDelta` because Firefox
  // return oposite value. See http://phrogz.net/js/wheeldelta.html
  // Get 1 or -1
  var delta = Math.max(-1, Math.min(1, (-e.wheelDelta || e.detail)));
  // Check if scroll direction changed
  if (scrollDirection != delta) {
    scrollSpeed = 2; // Start slowly - restart speed
    scrollDirection = delta;
  }
  var start = innerWrapper.parentElement.scrollTop,
    end = start + animateAmount * scrollSpeed * delta, // Where to end the scroll
    change = end - start, // base change in one scroll
    step = 0, // current step in animation
    tempScrollPosition; // Cannot assign any number yet (i.e. 0), because `scrollPosition` may be 0.
  // Get amount of scrolled to bottom
  scrolledToBottom = innerWrapper.scrollHeight - innerWrapper.parentElement.offsetHeight;
  animationID = requestAnimationFrame(smoothScrollAnim); // Start animation
  function smoothScrollAnim() {
    animationID = requestAnimationFrame(smoothScrollAnim); // Restart animation
    // Get scroll position
    var scrollBarPosition = easeOut(step++, start, change, duration);
    scrollBarPosition = clamp(scrollBarPosition, 0, scrolledToBottom)
    scrollBarPosition = scrollBar.offsetHeight * scrollBarPosition / innerWrapper.scrollHeight;
    // Apply scroll movement
    scrollBarThumb.style.top = scrollBarPosition + 'px';
    // Check if scroll finished (either animation finished, or bumped to top or bottom)
    if (step >= duration || tempScrollPosition === scrollBarPosition) {
      // Clean up
      tempScrollPosition = null;
      scrollSpeed = 2;
      cancelAnimationFrame(animationID);
    } else {
      tempScrollPosition = scrollBarPosition;
    }
  }
}
function easeOut(time, begin, change, duration) {
  time /= duration;
  return -change * time * (time - 2) + begin;
}
function clamp(val, min, max) {
  if (typeof min !== 'number') min = 0;
  if (typeof max !== 'number') max = 1;
  return Math.min(Math.max(val, min), max);
}
html {
  height: 100%;
  overflow-y: hidden;
}
body {
  height: 100%;
  overflow-y: hidden;
  display: flex;
}
#outerWrapper {
  height: 400px;
  overflow: auto;
  background-color: black;
}
#content {
  background-image: url("http://images.freeimages.com/images/premium/previews/3037/30376024-beautiful-flower-portrait.jpg");
  width: 400px;
}
#scrollbar {
  height: 400px;
  width: 50px;
  background-color: orange;
  border: 2px solid green;
}
#scrollbar_thumb {
  background-color: yellow;
  border: 2px solid blue;
  position: relative;
}
<div id="outerWrapper">
  <div id="innerWrapper">
    <div id="content">
      Lorem ipsum dolor sit amet consectetuer laoreet faucibus id ut et. Consequat Ut tellus enim ante nulla molestie vitae sem interdum turpis. Fames ridiculus cursus pellentesque Vestibulum justo sem lorem neque accumsan nulla. Lacinia Suspendisse vitae libero
      sem et laoreet risus Sed condimentum Cras. Nunc massa mauris tempor dolor pulvinar justo neque dui ipsum vitae. Lacinia dui cursus pellentesque Vestibulum justo sem lorem neque accumsan nulla. Lacinia Suspendisse vitae libero sem et laoreet risus
      Sed condimentum Cras. Nunc massa mauris tempor dolor pulvinar justo neque dui ipsum vitae. Lacinia dui scelerisque Sed convallis nonummy orci Vestibulum orci tempusLorem ipsum dolor sit amet consectetuer laoreet faucibus id ut et. Consequat Ut tellus
      enim ante nulla molestie vitae sem interdum turpis. Fames ridiculus cursus pellentesque Vestibulum justo sem lorem neque accumsan nulla. Lacinia Suspendisse vitae libero sem et laoreet risus Sed condimentum Cras. Nunc massa mauris tempor dolor pulvinar
      justo neque dui ipsum vitae. Lacinia dui scelerisque Sed convallis nonummy orci Vestibulum orci tempusLorem ipsum dolor sit amet consectetuer laoreet faucibus id ut et. Consequat Ut tellus enim ante nulla molestie vitae sem interdum turpis. Fames
      ridiculus cursus pellentesque Vestibulum justo sem lorem neque accumsan nulla. Lacinia Suspendisse vitae libero sem et laoreet risus Sed condimentum Cras. Nunc massa mauris tempor dolor pulvinar justo neque dui ipsum vitae. Lacinia dui scelerisque
      Sed convallis nonummy orci Vestibulum orci tempusLorem ipsum dolor sit amet consectetuer laoreet faucibus id ut et. Consequat Ut tellus enim ante nulla molestie vitae sem interdum turpis. Fames ridiculus cursus pellentesque Vestibulum justo sem
      lorem neque accumsan nulla. Lacinia Suspendisse vitae libero sem et laoreet risus Sed condimentum Cras. Nunc massa mauris tempor dolor Lorem ipsum dolor sit amet consectetuer laoreet faucibus id ut et. Consequat Ut tellus enim ante nulla molestie
      vitae sem interdum turpis. Fames ridiculus cursus pellentesque Vestibulum justo sem lorem neque accumsan nulla. Lacinia Suspendisse vitae libero sem et laoreet risus Sed condimentum Cras. Nunc massa mauris tempor dolor pulvinar justo neque dui ipsum
      vitae. Lacinia dui scelerisque Sed convallis nonummy orci Vestibulum orci tempusLorem ipsum dolor sit amet consectetuer laoreet faucibus id ut et. Consequat Ut tellus enim ante nulla molestie vitae sem interdum turpis. Fames ridiculus cursus pellentesque
      Vestibulum justo sem lorem neque accumsan nulla. Lacinia Suspendisse vitae libero sem et laoreet risus Sed condimentum Cras. Nunc massa mauris tempor dolor pulvinar justo neque dui ipsum vitae. Lacinia dui scelerisque Sed convallis nonummy orci
      Vestibulum orci tempusLorem ipsum dolor sit amet consectetuer laoreet faucibus id ut et. Consequat Ut tellus enim ante nulla molestie vitae sem interdum turpis. Fames ridiculus cursus pellentesque Vestibulum justo sem lorem neque accumsan nulla.
      Lacinia Suspendisse vitae libero sem et laoreet risus Sed condimentum Cras. Nunc massa mauris tempor dolor pulvinar justo neque dui ipsum vitae. Lacinia dui scelerisque Sed convallis nonummy orci Vestibulum orci tempusLorem ipsum dolor sit amet
      consectetuer laoreet faucibus id ut et. Consequat Ut tellus enim ante nulla molestie vitae sem interdum turpis. Fames ridiculus cursus pellentesque Vestibulum justo sem lorem neque accumsan nulla. Lacinia Suspendisse vitae libero sem et laoreet
      risus Sed condimentum Cras. Nunc massa mauris tempor dolor pulvinar justo neque dui ipsum vitae. Lacinia dui scelerisque Sed convallis nonummy orci Vestibulum orci tempusLorem ipsum dolor sit amet consectetuer laoreet faucibus id ut et. Consequat
      Ut tellus enim ante nulla molestie vitae sem interdum turpis. Fames ridiculus cursus pellentesque Vestibulum justo sem lorem neque accumsan nulla. Lacinia Suspendisse vitae libero sem et laoreet risus Sed condimentum Cras. Nunc massa mauris tempor
      dolor
    </div>
  </div>
</div>
<div id="scrollbar">
  <div id="scrollbar_thumb"></div>
</div>

对于滚动,只需注册"scroll"事件,
对于捕获延迟效果,使用CSS3 transition
对于简单的数学,请参见下面的示例:

const el = (sel, par) => (par || document).querySelector(sel);
const
  elContent = el("#content"),
  elHandler = el("#handler");
// FROM ELEMENT SCROLL TO HANDLER POSITION
const moveScrollbar = () => {
  const
    height = elContent.clientHeight,
    scrollHeight = elContent.scrollHeight,
    handlerHeight = height ** 2 / scrollHeight,
    handlerTop = elContent.scrollTop / height * handlerHeight;
  Object.assign(elHandler.style, {
    height: `${handlerHeight}px`,
    top: `${handlerTop}px`
  });
}
moveScrollbar(); // At init
elContent.addEventListener("scroll", moveScrollbar); // and on scroll
* { margin: 0; box-sizing: border-box; }
#area {
  display: flex;
}
#content {
  height: 160px;
  width: 300px;
  font-size: 3rem;
  background: #eee;
  padding: 1rem;
}
#content {
  overflow-y: scroll;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
#content::-webkit-scrollbar {
  width: 0;
  height: 0;
}
#scrollbar {
  flex: none;
  position: relative;
  background: #333;
  width: 1rem;
}
#handler {
  position: absolute;
  top: 0;
  background: orange;
  width: 100%;
  transition: 0.2s; /* smooth move */
}
<div id="area">
  <div id="content">
    Scroll and see the custom scrollbar move.<br>Lorem ipsum dolor sit amet consectetuer laoreet faucibus id ut et. Consequat Ut tellus enim ante nulla molestie vitae sem interdum turpis.
  </div>
  <div id="scrollbar">
    <div id="handler"></div>
  </div>
</div>

然后,要使处理程序可拖动,公式如下:
// FROM HANDLER POSITION TO ELEMENT SCROLL:
const
    height = elContent.clientHeight,
    scrollHeight = elContent.scrollHeight,
    scrollPos = scrollHeight / height * elHandler.clientTop;