如何将滑块指示器位置与滑块滚动位置同步
How to sync slider indicator position with slider scroll position
我创建了这个滑块和自定义指示器来显示你在哪张幻灯片上,但我无法将指示器同步到滑块的滚动位置。
在下面的示例中,指标的 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>
感谢 @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>
我创建了这个滑块和自定义指示器来显示你在哪张幻灯片上,但我无法将指示器同步到滑块的滚动位置。
在下面的示例中,指标的 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>
感谢 @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>