【问题标题】:How to sync slider indicator position with slider scroll position如何将滑块指示器位置与滑块滚动位置同步
【发布时间】:2021-10-01 23:20:14
【问题描述】:

我创建了这个滑块和自定义指示器来显示您在哪张幻灯片上,但我无法将指示器同步到滑块的滚动位置。

在下面的示例中,指标的left 属性被设置为主滑块已滚动的任何百分比。但是,它没有考虑指标本身的宽度,因此它会突破包含它的灰色框。

有什么想法可以修改moveIndicator 函数以使指标正确显示吗?同样在此示例中,屏幕上有两张幻灯片,但这需要在 1 或 3 张幻灯片的情况下工作(为了测试这一点,请将 css 中的 <li> 元素宽度更改为 100%33.3%

class SliderComponent extends HTMLElement {
  constructor() {
    super();
    this.slider = this.querySelector('ul');
    this.slides = this.querySelectorAll('li');
    this.sliderTray = this.querySelector('.indicator-tray');
    this.sliderIndicator = this.querySelector('.indicator');
    this.prevButton = this.querySelector('button[name="previous"]');
    this.nextButton = this.querySelector('button[name="next"]');

    const resizeObserver = new ResizeObserver(entries => this.initialise());
    resizeObserver.observe(this.slider);

    this.slider.addEventListener('scroll', this.update.bind(this));
    this.prevButton.addEventListener('click', this.onButtonClick.bind(this));
    this.nextButton.addEventListener('click', this.onButtonClick.bind(this));
  }

  initialise() {
    const slidesToShow = Array.from(this.slides).filter(element => element.clientWidth > 0);

    this.sliderLastItem = slidesToShow[slidesToShow.length - 1];

    if (slidesToShow.length === 0) return;

    this.slidesPerPage = Math.floor(this.slider.clientWidth / slidesToShow[0].clientWidth);
    this.totalPages = slidesToShow.length - this.slidesPerPage + 1;
    this.update();
  }

  moveIndicator() {
    const indicatorWidth = 100 / this.totalPages;
    const scrollPercentage = Math.floor(100 * (this.slider.scrollLeft / (this.slider.scrollWidth - this.slider.clientWidth)));

    this.sliderIndicator.style.width = `${indicatorWidth}%`;
    this.sliderIndicator.style.left = `${scrollPercentage}%`;
  }

  update() {
    this.currentPage = Math.round(this.slider.scrollLeft / this.sliderLastItem.clientWidth) + 1;
    requestAnimationFrame(() => this.moveIndicator());
  }

  onButtonClick(event) {
    event.preventDefault();
    const slideScrollPosition = event.currentTarget.name === 'next' ?
      this.slider.scrollLeft + this.sliderLastItem.clientWidth :
      this.slider.scrollLeft - this.sliderLastItem.clientWidth;

    this.slider.scrollTo({
      left: slideScrollPosition
    });
  }
}

customElements.define('slider-component', SliderComponent);
ul {
  position: relative;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scroll-padding-left: 1rem;
  -webkit-overflow-scrolling: touch;
  display: flex;
  width: 100%;
  margin: 0;
  padding: 0;
  list-style: none;
}

li {
  width: 50%;
  min-width: auto;
  height: 100px;
  flex-shrink: 0;
  scroll-snap-align: start;
  background-color: lightblue;
  border: 2px dashed white;
}

.indicator-tray {
  position: relative;
  display: flex;
  height: 5px;
  width: 50%;
  background-color: grey;
  margin: 20px auto;
}

.indicator {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  background-color: lightblue;
}
<slider-component>
  <ul>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
  </ul>

  <div class="indicator-tray">
    <span class="indicator"></span>
  </div>

  <div class="arrows">
    <button type="button" name="previous">Prev</button>
    <button type="button" name="next">Next</button>
  </div>
</slider-component>

【问题讨论】:

  • 问题在这里 this.sliderIndicator.style.left = ${scrollPercentage}%;.把左边 = "${(scrollPercentage / 100) * (width_of_gray - width_of_blue_ind)}px";。将 " 替换为 `(反引号)

标签: javascript html css slider


【解决方案1】:

感谢 @DraganS 的评论 - 为任何想要构建类似东西的人提供解决方案:

class SliderComponent extends HTMLElement {
  constructor() {
    super();
    this.slider = this.querySelector('ul');
    this.slides = this.querySelectorAll('li');
    this.sliderTray = this.querySelector('.indicator-tray');
    this.sliderIndicator = this.querySelector('.indicator');
    this.prevButton = this.querySelector('button[name="previous"]');
    this.nextButton = this.querySelector('button[name="next"]');

    const resizeObserver = new ResizeObserver(entries => this.initialise());
    resizeObserver.observe(this.slider);

    this.slider.addEventListener('scroll', this.update.bind(this));
    this.prevButton.addEventListener('click', this.onButtonClick.bind(this));
    this.nextButton.addEventListener('click', this.onButtonClick.bind(this));
  }

  initialise() {
    const slidesToShow = Array.from(this.slides).filter(element => element.clientWidth > 0);

    this.sliderLastItem = slidesToShow[slidesToShow.length - 1];

    if (slidesToShow.length === 0) return;

    this.slidesPerPage = Math.floor(this.slider.clientWidth / slidesToShow[0].clientWidth);
    this.totalPages = slidesToShow.length - this.slidesPerPage + 1;
    this.update();
  }

  moveIndicator() {
    const indicatorWidth = 100 / this.totalPages;
    const scrollPercentage = Math.floor(100 * (this.slider.scrollLeft / (this.slider.scrollWidth - this.slider.clientWidth)));
    const left = (scrollPercentage / 100) * (this.sliderTray.clientWidth - this.sliderIndicator.clientWidth);

    this.sliderIndicator.style.width = `${indicatorWidth}%`;
    this.sliderIndicator.style.left = `${left}px`;
  }

  update() {
    this.currentPage = Math.round(this.slider.scrollLeft / this.sliderLastItem.clientWidth) + 1;
    requestAnimationFrame(() => this.moveIndicator());
  }

  onButtonClick(event) {
    event.preventDefault();
    const slideScrollPosition = event.currentTarget.name === 'next' ?
      this.slider.scrollLeft + this.sliderLastItem.clientWidth :
      this.slider.scrollLeft - this.sliderLastItem.clientWidth;

    this.slider.scrollTo({
      left: slideScrollPosition
    });
  }
}

customElements.define('slider-component', SliderComponent);
ul {
  position: relative;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  scroll-padding-left: 1rem;
  -webkit-overflow-scrolling: touch;
  display: flex;
  width: 100%;
  margin: 0;
  padding: 0;
  list-style: none;
}

li {
  width: 50%;
  min-width: auto;
  height: 100px;
  flex-shrink: 0;
  scroll-snap-align: start;
  background-color: lightblue;
}

.indicator-tray {
  position: relative;
  display: flex;
  height: 5px;
  width: 50%;
  background-color: grey;
  margin: 20px auto;
}

.indicator {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  background-color: lightblue;
}
<slider-component>
  <ul>
    <li></li>
    <li></li>
    <li></li>
    <li></li>
  </ul>

  <div class="indicator-tray">
    <span class="indicator"></span>
  </div>

  <div class="arrows">
    <button type="button" name="previous">Prev</button>
    <button type="button" name="next">Next</button>
  </div>
</slider-component>

【讨论】:

    猜你喜欢
    • 2020-08-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-06-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多