JavaScript實(shí)現(xiàn)web端鼠標(biāo)橫向滑動(dòng)&觸控板滑動(dòng)效果?
支持鼠標(biāo)拖動(dòng)滑動(dòng)&觸控板滑動(dòng)效果
web端實(shí)現(xiàn)滑動(dòng),就是對(duì)鼠標(biāo)按下、鼠標(biāo)松開(kāi)、鼠標(biāo)移動(dòng)事件進(jìn)行監(jiān)聽(tīng)
效果圖?
?代碼
結(jié)構(gòu)代碼
<template>
<div class="swiper">
<div class="container" ref="container">
<!-- 在這里添加要滑動(dòng)的橫向元素 -->
<div class="box" v-for="num in 40" :key="num">
<img src="@/assets/logo.png" style="width: 58px; height: 58px" />
<span>滑動(dòng)元素{{ num }}</span>
</div>
</div>
</div>
</template>
樣式代碼
<style lang="scss" scoped>
.swiper {
.container {
width: 90%;
margin: 0 auto;
display: flex;
/* white-space: nowrap; //避免子元素在父元素內(nèi)換行 */
overflow-x: auto; //顯示橫向滾動(dòng)條
/* scroll-snap-type: x mandatory; //自動(dòng)吸附 */
/* scroll-behavior: smooth; //平滑滾動(dòng)的效果 */
.box {
width: 100px;
min-height: 100px;
padding: 0 20px;
/* scroll-snap-align: start; //自動(dòng)吸附 */
}
}
::-webkit-scrollbar {
width: 0 !important;
}
::-webkit-scrollbar {
width: 0 !important;
height: 0;
}
}
</style>
業(yè)務(wù)邏輯代碼
<script setup>
import { ref, reactive, onMounted } from 'vue';
const container = ref(null);
// console.log(container);
const control = reactive({
isDown: false, // 是否按下鼠標(biāo)
startX: 0, // 鼠標(biāo)起始位置
scrollLeft: 0 // 滾動(dòng)條位置
});
onMounted(() => {
console.log('dom', container.value);
// 總結(jié)web端實(shí)現(xiàn)滑動(dòng),就是對(duì)鼠標(biāo)按下、鼠標(biāo)松開(kāi)、鼠標(biāo)移動(dòng)事件進(jìn)行監(jiān)聽(tīng)
container.value.addEventListener('mousedown', (e) => {
control.isDown = true;
control.startX = e.pageX - container.value.offsetLeft;
control.scrollLeft = container.value.scrollLeft;
});
container.value.addEventListener('mouseup', (e) => {
control.isDown = false;
// // 找到最接近的滑動(dòng)元素
// const children = container.value.getElementsByClassName('box');
// let closestElement;
// let closestDistance = Infinity;
// const containerCenterX = container.value.offsetWidth / 2;
// for (let i = 0; i < children.length; i++) {
// const box = children[i];
// const boxCenterX = box.offsetLeft + box.offsetWidth / 2;
// const distance = Math.abs(boxCenterX - containerCenterX);
// if (distance < closestDistance) {
// closestDistance = distance;
// closestElement = box;
// }
// }
// // 計(jì)算滾動(dòng)到最接近的滑動(dòng)元素的位置
// const scrollLeft =
// closestElement.offsetLeft -
// container.value.offsetWidth / 2 +
// closestElement.offsetWidth / 2;
// container.value.scrollTo({ left: scrollLeft, behavior: 'smooth' });
// 找到最接近的滑動(dòng)元素
// const children = container.value.getElementsByClassName('box');
// let closestElement;
// let closestDistance = Infinity;
// const containerCenterX = container.value.offsetWidth / 2;
// for (let i = 0; i < children.length; i++) {
// const box = children[i];
// const boxCenterX = box.offsetLeft + box.offsetWidth / 2;
// const distance = Math.abs(boxCenterX - containerCenterX);
// if (distance < closestDistance) {
// closestDistance = distance;
// closestElement = box;
// }
// }
// // 計(jì)算滾動(dòng)到最接近的滑動(dòng)元素的位置
// // const scrollLeft = closestElement.offsetLeft - container.value.offsetLeft;
// const scrollLeft =
// closestElement.offsetLeft -
// container.value.offsetLeft -
// closestElement.offsetWidth;
// container.value.scrollTo({ left: scrollLeft, behavior: 'smooth' });
});
container.value.addEventListener('mousemove', (e) => {
if (!control.isDown) return;
e.preventDefault();
const x = e.pageX - container.value.offsetLeft;
const walk = (x - control.startX) * 2; // 滑動(dòng)距離
container.value.scrollLeft = control.scrollLeft - walk;
});
// 元素的吸附效果
// container.value.addEventListener('scroll', () => {
// // 找到最接近的滑動(dòng)元素
// const children = container.value.getElementsByClassName('box');
// let closestElement;
// let closestDistance = Infinity;
// const containerCenterX = container.value.offsetWidth / 2;
// for (let i = 0; i < children.length; i++) {
// const box = children[i];
// const boxCenterX = box.offsetLeft + box.offsetWidth / 2;
// const distance = Math.abs(boxCenterX - containerCenterX);
// if (distance < closestDistance) {
// closestDistance = distance;
// closestElement = box;
// }
// }
// // 計(jì)算滾動(dòng)到最接近的滑動(dòng)元素的位置
// const scrollLeft = closestElement.offsetLeft - container.value.offsetLeft;
// container.value.scrollLeft = scrollLeft;
// });
});
</script>
在Vue中實(shí)現(xiàn)鼠標(biāo)橫向滑動(dòng)&觸控板滑動(dòng)效果可以通過(guò)以下步驟實(shí)現(xiàn):
-
首先在Vue中創(chuàng)建一個(gè)父組件,在該組件中引入子組件或者使用slot插入內(nèi)容。
-
在父組件中創(chuàng)建一個(gè)div容器,用來(lái)包裹滑動(dòng)內(nèi)容。
-
在該div容器中綁定一個(gè)事件監(jiān)聽(tīng)器,用來(lái)監(jiān)聽(tīng)用戶的鼠標(biāo)或觸摸板滑動(dòng)事件。
-
在事件監(jiān)聽(tīng)器中使用JavaScript獲取鼠標(biāo)或觸摸板的滑動(dòng)距離,并根據(jù)該距離計(jì)算出需要滑動(dòng)的內(nèi)容距離。
-
使用Vue的ref屬性獲取div容器,然后通過(guò)Vue的$refs屬性訪問(wèn)該容器,并修改其left屬性值,實(shí)現(xiàn)滑動(dòng)效果。
下面是一個(gè)簡(jiǎn)單的示例代碼:
<template>
<div class="scroll-container" @mousemove="handleMouseMove"
@touchmove="handleTouchMove">
<div class="scroll-content" ref="content">
<slot></slot>
</div>
</div>
</template>
<script>
export default {
methods: {
handleMouseMove(event) {
const scrollContent = this.$refs.content;
const scrollSpeed = 5; // 滑動(dòng)速度
scrollContent.style.left = `${-event.pageX / scrollSpeed}px`;
},
handleTouchMove(event) {
const scrollContent = this.$refs.content;
const scrollSpeed = 5; // 滑動(dòng)速度
scrollContent.style.left = `${-event.touches[0].pageX / scrollSpeed}px`;
},
},
};
</script>
<style>
.scroll-container {
width: 100%;
height: 200px;
overflow-x: scroll;
}
.scroll-content {
position: relative;
width: 2000px;
height: 200px;
white-space: nowrap;
font-size: 0;
}
</style>
在上述示例代碼中,我們創(chuàng)建了一個(gè)scroll-container容器并綁定了鼠標(biāo)和觸摸板的滑動(dòng)事件監(jiān)聽(tīng)器,然后使用JavaScript計(jì)算出需要滑動(dòng)的距離,并修改了scroll-content容器的left屬性值實(shí)現(xiàn)了滑動(dòng)效果。需要注意的是,由于父組件的高度限制了滑動(dòng)內(nèi)容的高度,因此我們?cè)趕croll-content中使用了white-space: nowrap和font-size: 0兩個(gè)CSS樣式屬性,防止滑動(dòng)內(nèi)容換行和出現(xiàn)空白間隙。
在Vue中實(shí)現(xiàn)橫向滑動(dòng)效果可以通過(guò)以下步驟實(shí)現(xiàn):
- 在Vue組件中定義一個(gè)容器div,并設(shè)置其樣式為橫向滾動(dòng)條,例如:
<div class="scroll-container" ref="scrollContainer">
<div class="scroll-content">
<!-- 滾動(dòng)內(nèi)容 -->
</div>
</div>
<style>
.scroll-container {
overflow-x: auto;
white-space: nowrap;
-webkit-overflow-scrolling: touch; // 兼容iOS平滑滾動(dòng)
}
.scroll-content {
display: inline-block;
}
</style>
在組件的created生命周期中,注冊(cè)滑動(dòng)事件監(jiān)聽(tīng)器,并記錄開(kāi)始滑動(dòng)時(shí)的鼠標(biāo)位置或觸摸位置:
created() {
this.$refs.scrollContainer.addEventListener('mousedown', this.handleMouseDown);
this.$refs.scrollContainer.addEventListener('touchstart', this.handleTouchStart, { passive: true });
},
methods: {
handleMouseDown(event) {
this.startX = event.clientX;
},
handleTouchStart(event) {
this.startX = event.touches[0].clientX;
this.touching = true;
}
}
在組件的mounted生命周期中,注冊(cè)滑動(dòng)事件監(jiān)聽(tīng)器,并根據(jù)滑動(dòng)方向和滑動(dòng)距離計(jì)算出滾動(dòng)距離,并通過(guò)JavaScript控制滾動(dòng)條的位置:
mounted() {
this.$refs.scrollContainer.addEventListener('mousemove', this.handleMouseMove);
this.$refs.scrollContainer.addEventListener('touchmove', this.handleTouchMove, { passive: false });
this.$refs.scrollContainer.addEventListener('mouseup', this.handleMouseUp);
this.$refs.scrollContainer.addEventListener('touchend', this.handleTouchEnd);
},
methods: {
handleMouseMove(event) {
if (this.startX) {
const distance = event.clientX - this.startX;
this.$refs.scrollContainer.scrollLeft -= distance;
this.startX = event.clientX;
}
},
handleTouchMove(event) {
if (this.startX) {
const distance = event.touches[0].clientX - this.startX;
this.$refs.scrollContainer.scrollLeft -= distance;
this.startX = event.touches[0].clientX;
}
},
handleMouseUp() {
this.startX = null;
},
handleTouchEnd() {
this.startX = null;
}
}
通過(guò)以上步驟,就可以在Vue中實(shí)現(xiàn)橫向滑動(dòng)效果,并且支持鼠標(biāo)和觸摸操作。如果需要支持慣性滑動(dòng)效果,可以在滑動(dòng)結(jié)束時(shí)根據(jù)滑動(dòng)速度和滑動(dòng)方向計(jì)算出滾動(dòng)距離,并通過(guò)requestAnimationFrame動(dòng)畫函數(shù)實(shí)現(xiàn)平滑滾動(dòng)效果。
可以通過(guò)以下步驟實(shí)現(xiàn)該功能:
綁定事件監(jiān)聽(tīng)器,在vue組件的mounted生命周期函數(shù)或者created生命周期函數(shù)中,通過(guò)addEventListener方法綁定document的mousemove事件和touchmove事件。
mounted() {
document.addEventListener('mousemove', this.handleMouseMove);
document.addEventListener('touchmove', this.handleMouseMove);
},
methods: {
handleMouseMove(event) {
// 處理鼠標(biāo)或者觸摸板的移動(dòng)事件
}
}
在處理移動(dòng)事件的函數(shù)中,根據(jù)元素的滑動(dòng)距離和滑動(dòng)方向,通過(guò)style屬性的left或者transform屬性改變?cè)氐奈恢谩F渲?,可以通過(guò)computed屬性或者ref獲取需要滑動(dòng)的元素的位置和寬度。
handleMouseMove(event) {
const delta = event.clientX - this.lastX;
const direction = delta > 0 ? -1 : 1;
const absDelta = Math.abs(delta);
const containerLeft = this.$refs.container.offsetLeft;
const containerWidth = this.$refs.container.offsetWidth;
const contentWidth = this.$refs.content.offsetWidth;
const maxDelta = contentWidth - containerWidth - containerLeft;
const currentDelta = containerLeft + delta;
if (absDelta > 5 && (currentDelta >= 0 || currentDelta <= maxDelta)) {
// 通過(guò)transform改變滑動(dòng)距離,可以有更好的性能表現(xiàn)
this.$refs.container.style.transform = `translateX(${currentDelta}px)`;
this.lastX = event.clientX;
}
}
在滑動(dòng)停止時(shí),判斷靠近開(kāi)始的元素,通過(guò)計(jì)算元素到左端的距離來(lái)確定元素的位置,通過(guò)定位或者transform改變?cè)氐奈恢谩?/p>
handleMouseMove(event) {
// ...
this.timer = setTimeout(() => {
const containerLeft = this.$refs.container.offsetLeft;
const containerWidth = this.$refs.container.offsetWidth;
const contentWidth = this.$refs.content.offsetWidth;
const maxDelta = contentWidth - containerWidth - containerLeft;
if (containerLeft < 0) {
this.$refs.container.style.transform = `translateX(0)`;
return;
}
if (maxDelta < 0) {
this.$refs.container.style.transform = `translateX(${-maxDelta}px)`;
return;
}
const items = this.$refs.content.children;
let minDelta = Infinity;
let index = 0;
for (let i = 0; i < items.length; i++) {
const itemLeft = items[i].offsetLeft;
const delta = Math.abs(itemLeft - containerLeft);
if (delta < minDelta) {
minDelta = delta;
index = i;
}
}
const itemLeft = items[index].offsetLeft;
const delta = itemLeft - containerLeft;
this.$refs.container.style.transform = `translateX(${-delta}px)`;
}, 100);
}
注意在組件銷毀時(shí),需要清除事件監(jiān)聽(tīng)器和定時(shí)器。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-613204.html
beforeDestroy() {
document.removeEventListener('mousemove', this.handleMouseMove);
document.removeEventListener('touchmove', this.handleMouseMove);
clearTimeout(this.timer);
}
完整的vue組件代碼如下:文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-613204.html
<template>
<div class="container" ref="container">
<div class="content" ref="content">
<div class="item" v-for="(item, index) in items" :key="index">{{item}}</div>
</div>
</div>
</template>
<script>
export default {
name: "HorizontalScroll",
data() {
return {
items: ["Item 1", "Item 2", "Item 3", "Item 4", "Item 5", "Item 6", "Item 7", "Item 8", "Item 9", "Item 10"],
lastX: 0,
timer: null,
};
},
mounted() {
document.addEventListener('mousemove', this.handleMouseMove);
document.addEventListener('touchmove', this.handleMouseMove);
},
beforeDestroy() {
document.removeEventListener('mousemove', this.handleMouseMove);
document.removeEventListener('touchmove', this.handleMouseMove);
clearTimeout(this.timer);
},
methods: {
handleMouseMove(event) {
const delta = event.clientX - this.lastX;
const direction = delta > 0 ? -1 : 1;
const absDelta = Math.abs(delta);
const containerLeft = this.$refs.container.offsetLeft;
const containerWidth = this.$refs.container.offsetWidth;
const contentWidth = this.$refs.content.offsetWidth;
const maxDelta = contentWidth - containerWidth - containerLeft;
const currentDelta = containerLeft + delta;
if (absDelta > 5 && (currentDelta >= 0 || currentDelta <= maxDelta)) {
this.$refs.container.style.transform = `translateX(${currentDelta}px)`;
this.lastX = event.clientX;
}
clearTimeout(this.timer);
this.timer = setTimeout(() => {
const containerLeft = this.$refs.container.offsetLeft;
const containerWidth = this.$refs.container.offsetWidth;
const contentWidth = this.$refs.content.offsetWidth;
const maxDelta = contentWidth - containerWidth - containerLeft;
if (containerLeft < 0) {
this.$refs.container.style.transform = `translateX(0)`;
return;
}
if (maxDelta < 0) {
this.$refs.container.style.transform = `translateX(${-maxDelta}px)`;
return;
}
const items = this.$refs.content.children;
let minDelta = Infinity;
let index = 0;
for (let i = 0; i < items.length; i++) {
const itemLeft = items[i].offsetLeft;
const delta = Math.abs(itemLeft - containerLeft);
if (delta < minDelta) {
minDelta = delta;
index = i;
}
}
const itemLeft = items[index].offsetLeft;
const delta = itemLeft - containerLeft;
this.$refs.container.style.transform = `translateX(${-delta}px)`;
}, 100);
},
},
};
</script>
<style scoped>
.container {
position: relative;
width: 100%;
height: 80px;
overflow: hidden;
margin: 0 auto;
padding: 0 20px;
}
.content {
position: absolute;
top: 0;
left: 0;
height: 100%;
white-space: nowrap;
}
.item {
display: inline-block;
height: 100%;
line-height: 80px;
padding: 0 10px;
font-size: 16px;
color: #333;
background-color: #ddd;
border-radius: 5px;
margin-right: 10px;
}
</style>
到了這里,關(guān)于vue中通過(guò)JavaScript實(shí)現(xiàn)web端鼠標(biāo)橫向滑動(dòng)&觸控板滑動(dòng)效果-demo的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!