滚动播放组件

This commit is contained in:
JenniferW 2025-09-03 11:01:02 +08:00
parent 0f1bb5320f
commit 87139ecdc4
2 changed files with 244 additions and 91 deletions

View File

@ -0,0 +1,233 @@
<template>
<div class="auto-scroll">
<!-- 滚动区域 -->
<div
class="scroll-wrapper"
ref="scrollWrapper"
@mouseenter="pauseScroll"
@mouseleave="resumeScroll"
>
<div class="scroll-content" ref="scrollContent">
<!-- 滚动的项目元素 -->
<div
class="scroll-item"
v-for="(item, index) in items"
:key="index"
:style="itemStyle(item)"
@click="$emit('item-click', item, index)"
>
<!-- 自定义项目内容插槽 -->
<slot name="item" :item="item">
<!-- 默认内容 -->
<span class="default-item-content">{{ item }}</span>
</slot>
</div>
<!-- 复制一份用于无缝循环 -->
<div
class="scroll-item"
v-for="(item, index) in items"
:key="'copy-' + index"
:style="itemStyle(item)"
@click="$emit('item-click', item, index)"
>
<slot name="item" :item="item">
<span class="default-item-content">{{ item }}</span>
</slot>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "AutoScroll",
props: {
//
items: {
type: Array,
required: true,
default: () => [],
},
//
scrollSpeed: {
type: Number,
default: 20,
},
//
scrollStep: {
type: Number,
default: 1,
},
// 1-1
direction: {
type: Number,
default: 1,
validator: (value) => value === 1 || value === -1,
},
//
itemWidth: {
type: Number,
default: 150,
},
//
itemHeight: {
type: Number,
default: 100,
},
//
itemMargin: {
type: Number,
default: 10,
},
},
data() {
return {
scrollInterval: null,
originalContentWidth: 0, //
};
},
mounted() {
//
this.$nextTick(() => {
//
this.originalContentWidth = this.$refs.scrollContent.scrollWidth / 2;
this.startAutoScroll();
this.$emit("ready", {
start: this.startAutoScroll,
pause: this.pauseScroll,
resume: this.resumeScroll,
});
});
//
window.addEventListener("resize", this.handleResize);
},
beforeDestroy() {
//
this.clearScrollInterval();
//
window.removeEventListener("resize", this.handleResize);
},
methods: {
//
itemStyle(item) {
return {
width: `${this.itemWidth}px`,
height: `${this.itemHeight}px`,
margin: `0 ${this.itemMargin}px`,
background: item.color || "#ccc",
};
},
//
startAutoScroll() {
this.clearScrollInterval();
this.scrollInterval = setInterval(() => {
this.autoScroll();
}, this.scrollSpeed);
this.$emit("scroll-started");
},
//
clearScrollInterval() {
if (this.scrollInterval) {
clearInterval(this.scrollInterval);
this.scrollInterval = null;
}
},
// -
autoScroll() {
if (!this.$refs.scrollWrapper || !this.$refs.scrollContent) return;
const wrapper = this.$refs.scrollWrapper;
//
wrapper.scrollLeft += this.scrollStep * this.direction;
//
if (
this.direction === 1 &&
wrapper.scrollLeft >= this.originalContentWidth
) {
wrapper.scrollLeft = 0;
}
//
else if (this.direction === -1 && wrapper.scrollLeft <= 0) {
wrapper.scrollLeft = this.originalContentWidth;
}
},
//
pauseScroll() {
this.clearScrollInterval();
this.$emit("scroll-paused");
},
//
resumeScroll() {
this.startAutoScroll();
this.$emit("scroll-resumed");
},
//
handleResize() {
this.$nextTick(() => {
//
this.originalContentWidth = this.$refs.scrollContent.scrollWidth / 2;
});
},
},
};
</script>
<style lang="scss" scoped>
.auto-scroll {
position: relative;
width: 100%;
max-width: 1200px;
margin: 20px auto;
padding: 0;
box-sizing: border-box;
.scroll-wrapper {
overflow: hidden;
white-space: nowrap;
padding: 20px 0;
scroll-behavior: smooth;
.scroll-content {
display: inline-block;
}
.scroll-item {
display: inline-block;
border-radius: 8px;
color: white;
font-weight: bold;
text-align: center;
line-height: 100%;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease;
cursor: pointer;
padding: 10px;
box-sizing: border-box;
&:hover {
transform: scale(1.05);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
}
.default-item-content {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
}
}
}
</style>

View File

@ -1,8 +1,6 @@
<template>
<div class="business-system">
<!-- 左右结构容器标题区域 + 内容区域 -->
<div class="business-container">
<!-- 左侧标题区域 - 单独分离出来 -->
<div class="title-section">
<div class="title-content">
<div class="main-title">
@ -18,45 +16,31 @@
<!-- 右侧内容区域 -->
<div class="content-section">
<div class="system-items" ref="systemList">
<div
class="system-card"
v-for="(item, index) in systems"
:key="index"
ref="systemCards"
>
<div class="card-icon"></div>
<div class="card-name">{{ item }}</div>
</div>
</div>
<auto-scroll :items="systems" @item-click="handleItemClick" />
</div>
</div>
</div>
</template>
<script>
import AutoScroll from "@/components/AutoScroll.vue";
export default {
data() {
return {
systems: [
"招生系统",
"学工系统",
"就业指导网管理系统",
"大数据应用平台",
"评教系统",
"健康评估系统",
"辅助助手工具",
"迎新系统(验证)",
"科研系统(验证)",
"教务管理系统",
"财务管理平台",
"图书管理系统",
"资产管理系统",
"校园一卡通系统",
"网络教学平台",
"智慧校园系统",
"教务系统",
"一卡通系统",
"人力资源管理系统",
"科研创新服务平台",
"文献管理系统",
],
};
},
components: {
AutoScroll,
},
computed: {
systemCount() {
return this.systems.length;
@ -112,70 +96,6 @@ export default {
box-sizing: border-box;
}
.system-items {
display: inline-flex;
gap: 15px;
will-change: scroll-position; //
padding: 5px 0; //
}
.system-card {
display: flex;
flex-direction: column;
align-items: center;
min-width: 120px;
background: rgba(74, 144, 226, 0.1);
border-radius: 8px;
padding: 15px 10px;
margin: 0; // margin
transition: all 0.3s ease;
box-sizing: border-box;
&:hover {
transform: translateY(-3px);
background: rgba(74, 144, 226, 0.2);
box-shadow: 0 0 10px rgba(74, 144, 226, 0.3);
}
.card-icon {
width: 50px;
height: 50px;
background: #4a90e2;
border-radius: 8px;
margin-bottom: 10px;
position: relative;
overflow: hidden;
&::before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: linear-gradient(
45deg,
rgba(255, 255, 255, 0.1) 0%,
rgba(255, 255, 255, 0) 50%,
rgba(255, 255, 255, 0.1) 100%
);
transform: skewX(-20deg) translateX(-120%);
animation: shine 2s infinite;
}
}
.card-name {
font-size: 14px;
color: #fff;
text-align: center;
text-shadow: 0 0 3px rgba(74, 144, 226, 0.5);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
max-width: 100px;
}
}
//
.title-content {
text-align: center;