scrollingData .vue
3.21 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
<template>
<div class="scrolling-container" @mouseenter="pauseScroll" @mouseleave="resumeScroll">
<div :style="{ transform: `translateY(${offset}px)` }" class="scrolling-content">
<div v-for="(item, index) in dataList" :key="index" class="scrolling-item">
<span/> {{ item }}
</div>
<!-- 复制一份数据实现无缝滚动 -->
<div v-for="(item, index) in dataList" :key="`copy-${index}`" class="scrolling-item">
<span/> {{ item }}
</div>
</div>
</div>
</template>
<script setup>
import {ref, onMounted, onUnmounted} from 'vue';
const props = defineProps({
data: {
type: Array,
required: true
},
speed: {
type: Number,
default: 1 // 滚动速度,数值越大越快
},
delay: {
type: Number,
default: 1000 // 初始延迟时间(毫秒)
}
});
const dataList = ref([...props.data]);
const offset = ref(0);
const scrollInterval = ref(null);
const isPaused = ref(false);
const containerHeight = ref(0);
const contentHeight = ref(0);
const itemHeight = ref(0);
// 初始化滚动
const initScroll = () => {
// 清除之前的定时器
if (scrollInterval.value) {
clearInterval(scrollInterval.value);
}
// 重置位置到第一条数据
offset.value = 0;
// 设置定时器
scrollInterval.value = setInterval(() => {
if (!isPaused.value) {
offset.value -= props.speed;
// 当滚动到内容的一半时,重置位置实现无缝滚动
if (Math.abs(offset.value) >= contentHeight.value / 2) {
offset.value += contentHeight.value / 2;
}
}
}, 20);
};
// 暂停滚动
const pauseScroll = () => {
isPaused.value = true;
};
// 恢复滚动
const resumeScroll = () => {
isPaused.value = false;
};
onMounted(() => {
// 延迟启动滚动,确保DOM已渲染
setTimeout(() => {
const container = document.querySelector('.scrolling-container');
const content = document.querySelector('.scrolling-content');
const firstItem = document.querySelector('.scrolling-item');
if (container && content && firstItem) {
containerHeight.value = container.clientHeight;
contentHeight.value = content.clientHeight;
itemHeight.value = firstItem.clientHeight;
// 如果内容高度小于容器高度,不需要滚动
if (contentHeight.value > containerHeight.value) {
// 初始位置设置为显示第一条数据
// offset.value = 0;
initScroll();
}
}
}, props.delay);
});
onUnmounted(() => {
if (scrollInterval.value) {
clearInterval(scrollInterval.value);
}
});
</script>
<style scoped>
.scrolling-container {
width: 100%;
height: 100%;
overflow: hidden;
position: relative;
}
.scrolling-content {
transition: transform 10ms ease;
will-change: transform;
}
.scrolling-item {
font-family: PingFang SC, serif;
font-weight: 400;
font-size: calc(17 * 100vw / 1920);
color: #FFFFFF;
height: calc(40 * 100vh / 1920);
margin: calc(8 * 100vw / 1920) 0;
span {
display: inline-block;
width: calc(12 * 100vw / 1920);
height: calc(12 * 100vw / 1920);
background-color: #01D7F0;
transform: rotate(45deg);
margin-left: calc(3 * 100vw / 1920);
border-radius: calc(3 * 100vw / 1920);
}
}
</style>