我正在使用 Shopify 主题中的此 Web 组件,我需要向其添加拖放功能。目前,在移动设备和桌面上滑动以及按钮一样有效,但我不知道如何让拖动起作用。
控制台日志工作显示更新的位置,但滑块从未移动
class SliderComponent extends HTMLElement {
constructor() {
super();
this.slider = this.querySelector('[id^="Slider-"]');
this.sliderItems = this.querySelectorAll('[id^="Slide-"]');
this.enableSliderLooping = false;
this.currentPageElement = this.querySelector('.slider-counter--current');
this.pageTotalElement = this.querySelector('.slider-counter--total');
this.prevButton = this.querySelector('button[name="previous"]');
this.nextButton = this.querySelector('button[name="next"]');
if (!this.slider || !this.nextButton) return;
this.initPages();
const resizeObserver = new ResizeObserver((entries) => this.initPages());
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));
this.onDrag = this.onDrag.bind(this);
this.stopDragging = this.stopDragging.bind(this);
this.slider.addEventListener('mousedown', this.startDragging.bind(this));
}
initPages() {
this.sliderItemsToShow = Array.from(this.sliderItems).filter((element) => element.clientWidth > 0);
if (this.sliderItemsToShow.length < 2) return;
this.sliderItemOffset = this.sliderItemsToShow[1].offsetLeft - this.sliderItemsToShow[0].offsetLeft;
this.slidesPerPage = Math.floor(
(this.slider.clientWidth - this.sliderItemsToShow[0].offsetLeft) / this.sliderItemOffset
);
this.totalPages = this.sliderItemsToShow.length - this.slidesPerPage + 1;
this.update();
}
resetPages() {
this.sliderItems = this.querySelectorAll('[id^="Slide-"]');
this.initPages();
}
update() {
console.log('update');
// Temporarily prevents unneeded updates resulting from variant changes
// This should be refactored as part of https://github.com/Shopify/dawn/issues/2057
if (!this.slider || !this.nextButton) return;
const previousPage = this.currentPage;
this.currentPage = Math.round(this.slider.scrollLeft / this.sliderItemOffset) + 1;
if (this.currentPageElement && this.pageTotalElement) {
this.currentPageElement.textContent = this.currentPage;
this.pageTotalElement.textContent = this.totalPages;
}
if (this.currentPage != previousPage) {
this.dispatchEvent(
new CustomEvent('slideChanged', {
detail: {
currentPage: this.currentPage,
currentElement: this.sliderItemsToShow[this.currentPage - 1],
},
})
);
}
if (this.enableSliderLooping) return;
if (this.isSlideVisible(this.sliderItemsToShow[0]) && this.slider.scrollLeft === 0) {
this.prevButton.setAttribute('disabled', 'disabled');
} else {
this.prevButton.removeAttribute('disabled');
}
if (this.isSlideVisible(this.sliderItemsToShow[this.sliderItemsToShow.length - 1])) {
this.nextButton.setAttribute('disabled', 'disabled');
} else {
this.nextButton.removeAttribute('disabled');
}
}
isSlideVisible(element, offset = 0) {
const lastVisibleSlide = this.slider.clientWidth + this.slider.scrollLeft - offset;
return element.offsetLeft + element.clientWidth <= lastVisibleSlide && element.offsetLeft >= this.slider.scrollLeft;
}
onButtonClick(event) {
event.preventDefault();
const step = event.currentTarget.dataset.step || 1;
this.slideScrollPosition =
event.currentTarget.name === 'next'
? this.slider.scrollLeft + step * this.sliderItemOffset
: this.slider.scrollLeft - step * this.sliderItemOffset;
this.setSlidePosition(this.slideScrollPosition);
}
setSlidePosition(position) {
this.slider.scrollTo({
left: position,
});
}
startDragging(event) {
event.preventDefault();
this.startX = event.clientX;
this.slider.classList.add('dragging');
document.addEventListener('mousemove', this.onDrag);
document.addEventListener('mouseup', this.stopDragging);
console.log('Dragging started');
}
onDrag(event) {
event.preventDefault();
const dx = event.clientX - this.startX;
this.slider.scrollLeft -= dx;
this.startX = event.clientX;
console.log('Dragging:', dx);
}
stopDragging(event) {
this.slider.classList.remove('dragging');
document.removeEventListener('mousemove', this.onDrag);
document.removeEventListener('mouseup', this.stopDragging);
console.log('Dragging stopped');
}
}
根据评论,人们可能对答案不满意,但使用 Shopify 构建的内容就是解决方案。
在
constructor
我添加了
this.isDragging = false;
this.startX = 0;
this.scrollStartX = 0;
this.dragThreshold = 10;
this.isLinkClickAllowed = true;
this.addEventListener('mousedown', (e) => this.startDrag(e));
this.addEventListener('mouseup', (e) => this.stopDrag(e));
this.addEventListener('mouseleave', (e) => this.stopDrag(e));
this.addEventListener('mousemove', (e) => this.onDrag(e));
this.addEventListener('click', (e) => this.onClick(e), true);
然后将这些方法添加到comp:
/**
* Handles the start of a drag event.
* @param {MouseEvent} e
*/
startDrag(e) {
e.preventDefault();
this.isDragging = true;
this.startX = e.pageX;
this.scrollStartX = this.slider.scrollLeft;
this.isLinkClickAllowed = true;
}
/**
* Handles the drag event.
* @param {MouseEvent} e
*/
onDrag(e) {
if (!this.isDragging) return;
e.preventDefault();
const currentX = e.pageX;
const dx = currentX - this.startX;
// If we've dragged past a certain threshold, don't allow the click.
if (Math.abs(dx) > this.dragThreshold) {
this.isLinkClickAllowed = false;
}
this.slider.scrollLeft = this.scrollStartX - dx;
}
/**
* Resets the isDragging state.
* @param {MouseEvent} e
*/
stopDrag(e) {
this.isDragging = false;
}
/**
* Prevents the click event from firing if the interaction was a drag.
* @param {MouseEvent} e
*/
onClick(e) {
if (!this.isLinkClickAllowed) {
e.preventDefault();
e.stopPropagation();
this.isLinkClickAllowed = true;
}
}