带有按钮和渐变的水平滚动区域

Horizontal scroll areas with buttons and gradients

到目前为止,这是我的代码:

// Show and hide gradients

$(document).ready(function() {
  $(".scroll-area").each(function(index) {
    if ($(this)[0].scrollWidth <= $(this)[0].clientWidth) {
      $(this).closest(".container").find(".left").css("display", "none");
      $(this).closest(".container").find(".right").css("display", "none");
    } else {
      $(this).scroll(function() {
        if ($(this)[0].scrollWidth > $(this)[0].clientWidth) {
          if ($(this).scrollLeft() > 0) {
            $(this).closest(".container").find(".left").css("display", "block");
          }

          if ($(this).scrollLeft() == 0) {
            $(this).closest(".container").find(".left").css("display", "none");
          }

          var fullWidth = $(this)[0].scrollWidth - $(this)[0].offsetWidth - 1;

          if ($(this).scrollLeft() >= fullWidth) {
            $(this).closest(".container").find(".right").css("display", "none");
          }

          if ($(this).scrollLeft() < fullWidth) {
            $(this).closest(".container").find(".right").css("display", "block");
          }
        }
      });
    }
  });
});


// Scroll buttons

let interval;

$('.scroll-btn').on('mousedown', ({
  target
}) => {
  const type = $(target).attr('id');

  interval = setInterval(() => {
    var x = $('#a').scrollLeft();
    $('#a').scrollLeft(type === 'left-arrow' ? x - 10 : x + 10);
  }, 50);
});

$('.scroll-btn').on('mouseup', () => clearInterval(interval));
* {
  margin: 0;
  padding: 0;
  font-family: sans-serif;
  font-size: 16px;
}

.container {
  width: 550px;
  height: 80px;
  background-color: grey;
  position: relative;
  margin-bottom: 20px;
}

.scroll-area {
  white-space: nowrap;
  overflow-x: auto;
  height: 100%;
}

.left,
.right {
  width: 50px;
  height: 100%;
  position: absolute;
  pointer-events: none;
  top: 0;
}

.left {
  background: linear-gradient(90deg, orange 0%, rgba(0, 0, 0, 0) 100%);
  left: 0;
  display: none;
}

.right {
  background: linear-gradient(-90deg, orange 0%, rgba(0, 0, 0, 0) 100%);
  right: 0;
}

.arrow {
  display: block;
  position: absolute;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  width: 15px;
  cursor: pointer;
}

.left-arrow {
  left: 0;
}

.right-arrow {
  right: 0;
}

.left-arrow div,
.right-arrow div {
  font-size: 40px;
}
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>

<div class="container">

  <div id="x" class="left"></div>
  <div class="right"></div>

  <div class="arrow left-arrow">
    <div class="scroll-btn" id="left-arrow">
      <</div>
    </div>
    <div class="arrow right-arrow">
      <div class="scroll-btn" id="right-arrow">></div>
    </div>

    <div id="a" class="scroll-area">
      <div class="text">Scroll to right. The gradients and arrows should appear and disappear based on the scroll position. It should work with more than one container. Lorem ipsum.</div>
    </div>
  </div>

需求是:

  1. 箭头应该以与渐变相同的方式出现和消失。
  2. 如果没有足够的文本导致可滚动区域,则不应有渐变和现在的箭头。
  3. 最后应该不止一个容器。

有人可以帮我做吗?非常感谢!

您可以将箭头放在 left/right 渐变 div 中。这样他们将 show/hide 与渐变相同。

编辑

我稍微清理了一下代码,因为原来的答案有点乱。 (或如 mstephen19 所说的 'weird' :))。

// Show gradient and left/right arrows only if scrollable
$(".scroll-area").each((i, el) => {
  $(el).parent().find(".right")[el.scrollWidth > el.clientWidth ? "show" : "hide"]();
});

// Show/hide gradient and arrows on scroll
$('.scroll-area').scroll((e) => {
  const fullWidth = $(e.target)[0].scrollWidth - $(e.target)[0].offsetWidth - 1;
  const left = $(e.target).scrollLeft()

  $(e.target).parent().find(".left, .left-arrow")[left > 0 ? "show" : "hide"]();
  $(e.target).parent().find(".right, .right-arrow")[left < fullWidth ? "show" : "hide"]();
});

// Scroll on left/right arrow mouse down
let intervalId;
$(".left-arrow, .right-arrow").on("mousedown", (e) => {
  const scroll = $(e.target).closest(".container").find(".scroll-area");
  intervalId = setInterval(() => {
    const left = scroll.scrollLeft();
    scroll.scrollLeft(e.target.classList.contains("left-arrow") ? left - 10 : left + 10);
  }, 50);
}).on("mouseup mouseleave", () => {
  clearInterval(intervalId);
});
* {
  margin: 0;
  padding: 0;
  font-family: sans-serif;
  font-size: 16px;
}

.container {
  width: 550px;
  height: 80px;
  background-color: grey;
  position: relative;
  margin-bottom: 20px;
  margin-left: 20px;
}

.scroll-area {
  white-space: nowrap;
  overflow-x: auto;
  height: 100%;
}

.left,
.right {
  width: 50px;
  height: 100%;
  position: absolute;
  top: 0;
}

.left {
  background: linear-gradient(90deg, orange 0%, rgba(0, 0, 0, 0) 100%);
  left: 0;
  display: none;
}

.right {
  background: linear-gradient(-90deg, orange 0%, rgba(0, 0, 0, 0) 100%);
  right: 0;
  text-align: right;
}

.left-arrow,
.right-arrow {
  margin: 0 10px;
  position: absolute;
  top: 50%;
  -ms-transform: translateY(-50%);
  transform: translateY(-50%);
  cursor: pointer;
  user-select: none;
  font-size: 40px
}

.left-arrow {
  display: none;
  left: -25px;
}

.right-arrow {
  right: -25px;
}
<html>

<head>
  <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
</head>

<body>
  <div class="container">
    <div class="left"></div>
    <div class="right"></div>
    
    <div class="left-arrow">&lt;</div>
    <div class="right-arrow">&gt;</div>

    <div class="scroll-area">
      <div class="text">Scroll to right. The gradients and arrows should appear and disappear based on the scroll position. It should work with more than one container. Lorem ipsum.</div>
    </div>
  </div>
  
  <div class="container">
    <div class="left"><span class="left-arrow">&lt;</span></div>
    <div class="right"><span class="right-arrow">&gt;</span></div>

    <div class="scroll-area">
      <div class="text">No scroll.</div>
    </div>
  </div>

</body>

</html>

关于您的代码的一些事情:

  1. 您的原始代码不适用于多个容器,因为您在间隔代码中有一个硬编码的 #a ID。无论如何,理想情况下您实际上应该只在一个元素上设置 ID(它们是唯一标识符,而 classes 可以放在多个元素上)。 .scroll-area 元素应该根据点击的目标找到。
  2. 您应该将渐变和箭头元素合并为一个元素。也就是说,我的意思是使箭头所在的 div 应该是渐变 div 的子级。为什么要分别管理它们?
  3. 使用class adding/removing/toggling 而不是直接设置CSS。请记住 - 当您发现自己多次编写相同的代码时,通常意味着有一种方法可以将其压缩并使您的代码更干,更易于理解和阅读。
  4. 不要使用文字 <> 符号,因为它会混淆某些浏览器。请改用 &lt;&gt;
  5. 与其将 display 切换为 noneblock,不如在这种特定情况下使用 visibility。在我的示例中,我们使用 opacity 来获得有趣的淡化效果。
  6. 不要忘记监听两个 mouseup mouseout 事件 :)

这是可行的解决方案。我稍微重构了代码:

let interval;

$('.arrow').on('mousedown', ({ target }) => {
    const type = target.classList[1];

    const scrollArea = $(target).parent().find('.scroll-area');
    interval = setInterval(() => {
        const prev = scrollArea.scrollLeft();
        scrollArea.scrollLeft(type === 'left-arrow' ? prev - 10 : prev + 10);
    }, 50);
});

$('.arrow').on('mouseup mouseout', () => clearInterval(interval));

$('.scroll-area').on('scroll', ({ target }) => {
    const left = $(target).parent().find('.left-arrow');
    const right = $(target).parent().find('.right-arrow');

    const scroll = $(target).scrollLeft();

    const fullWidth = $(target)[0].scrollWidth - $(target)[0].offsetWidth;

    if (scroll === 0) left.addClass('hide');
    else left.removeClass('hide');

    if (scroll > fullWidth) right.addClass('hide');
    else right.removeClass('hide');
});
.container {
    width: 550px;
    height: 80px;
    background: grey;
    position: relative;
}

.right-arrow,
.left-arrow {
    height: 100%;
    width: 50px;
    position: absolute;
    display: flex;
    justify-content: center;
    align-items: center;
    font-size: 2rem;
    cursor: pointer;
    transition: all 0.2s linear;
}

.scroll-area {
    white-space: nowrap;
    overflow-x: scroll;
    height: 100%;
}

.right-arrow {
    background: linear-gradient(-90deg, orange 0%, rgba(0, 0, 0, 0) 100%);
    left: 500px;
}

.left-arrow {
    background: linear-gradient(90deg, orange 0%, rgba(0, 0, 0, 0) 100%);
    left: 0px;
}

.scroll-btn {
    pointer-events: none;
}

.hide {
    opacity: 0;
}
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>

<div class="container">
    <div class="arrow left-arrow">
        <div class="scroll-btn" id="left-arrow">&lt;</div>
    </div>

    <div class="arrow right-arrow">
        <div class="scroll-btn" id="right-arrow">&gt;</div>
    </div>

    <div class="scroll-area">
        <div class="text">
            Scroll to right. The gradients and arrows should appear and disappear based on the scroll position. It should work with more than one
            container. Lorem ipsum.
        </div>
    </div>
</div>

PS:如果不喜欢淡入淡出的效果,去掉CSS的transition: all 0.2s linear;部分,换成.hideopacity: 0visibility: hidden.