uniapp小程序中实现无缝衔接滚动效果
组件滚动通知只能实现简单的滚动效果,不能实现滚动内容中的字进行不同颜色的更改,下面实现一个无缝衔接的滚动动画,可以根据自己的需要进行艺术化的更改需要滚动的内容,也可以自定义更改滚动速度。
<template><view class="container"><!-- 文字滚动条 --><view v-for="(item, index) in scrollItems" :key="'text-' + index"class="scroll" :style="{'--t': item.duration + 's'}"><view class="scroll-row"><text v-for="(skill, i) in skills" :key="i"class="skill-tag"@click="handleSkillClick(skill)">{{ skill }}</text></view><view class="scroll-row"><text v-for="(skill, i) in skills" :key="i + 'copy'"class="skill-tag"@click="handleSkillClick(skill)">{{ skill }}</text></view></view><!-- 彩色方块滚动条 --><view class="scroll img-box" style="--t: 25s"><view class="scroll-row"><view v-for="n in 9" :key="n"class="color-box" :style="{'--r': (n-1)*40}"@click="changeBoxColor(n)">{{ n }}</view></view><view class="scroll-row"><view v-for="n in 9" :key="n + 'copy'"class="color-box" :style="{'--r': (n-1)*40}"@click="changeBoxColor(n)">{{ n }}-1</view></view></view></view>
</template>
<script setup>
import { ref } from 'vue'const skills = ref(['HTML', 'CSS', 'JavaScript', 'Vue', 'React', 'Figma', 'Photoshop'
])const scrollItems = ref([{ duration: 20 },{ duration: 30 },{ duration: 10 },{ duration: 35 }
])const handleSkillClick = (skill) => {uni.showToast({title: `点击了: ${skill}`,icon: 'none'})
}const changeBoxColor = (n) => {// 在实际应用中,你可能需要找到对应的DOM元素来修改样式// 这里只是演示点击事件uni.showToast({title: `点击了方块 ${n}`,icon: 'none'})
}
</script>
<style>
/* 基础样式 */
.container {min-height: 100vh;background-color: #222;color: #fff;display: flex;flex-direction: column;justify-content: center;align-items: center;padding: 20px;
}/* 滚动容器 */
.scroll {position: relative;display: flex;width: 700px;overflow: hidden;-webkit-mask-image: linear-gradient(90deg, transparent, #fff 20%, #fff 80%, transparent);mask-image: linear-gradient(90deg, transparent, #fff 20%, #fff 80%, transparent);margin: 15px 0;
}/* 滚动行 */
.scroll-row {white-space: nowrap;will-change: transform;
}/* 技能标签 */
.skill-tag {display: inline-block;margin: 10px;padding: 8px 15px;background-color: #333;border-radius: 5px;letter-spacing: 0.2em;text-transform: uppercase;transition: all 0.3s ease;box-shadow: 0 4px 8px rgba(0,0,0,0.2);
}.skill-tag:active {background-color: #4caf50;transform: scale(1.05);box-shadow: 0 6px 12px rgba(0,0,0,0.3);
}/* 彩色方块 */
.img-box {display: flex;column-gap: 10px;
}.color-box {width: 150px;height: 150px;background-color: #ff3e3e;filter: hue-rotate(calc(var(--r) * 1deg));display: flex;justify-content: center;align-items: center;font-weight: bold;font-size: 1.5em;border-radius: 8px;margin: 0 10px;transition: all 0.5s ease;
}.color-box:active {transform: rotate(15deg) scale(1.1);box-shadow: 0 8px 16px rgba(0,0,0,0.3);
}/* 动画效果 */
.scroll-row:first-child {animation: animate var(--t) linear infinite;animation-delay: calc(var(--t) * -1);
}.scroll-row:nth-child(2) {animation: animate2 var(--t) linear infinite;animation-delay: calc(var(--t) / -2);
}@keyframes animate {0% { transform: translateX(100%); }100% { transform: translateX(-100%); }
}@keyframes animate2 {0% { transform: translateX(0); }100% { transform: translateX(-200%); }
}/* 响应式设计 */
@media screen and (max-width: 768px) {.scroll {width: 95vw;}.skill-tag {padding: 5px 10px;font-size: 0.8em;}.color-box {width: 20vw;height: 20vw;font-size: 1em;}
}
</style>