背景:web网页,实现列表项无缝横向滚动,hover时,列表项可以上移并且滚动暂停
正常滚动
hover时
列表项子组件scss
.excellent-cases__item {
flex: 0 0 auto; // 不会放大也不缩小
margin: 0 10px; // 左右平分间距
width: 338px;
height: 433px;
border-radius: 12px;
transition-property: all !important;
transition-duration: 0.3s !important;
transition-timing-function: ease !important;
/* 平滑过渡效果 */
box-shadow: -4px 0px 7.9px 0px rgba(0, 0, 0, 0.05);
&:hover {
/* 上移 */
transform: translateY(-20px);
}
}
父组件html,ts
<template>
<div class="excellent-cases">
<div class="excellent-cases__content" style="--t: 60s" v-if="dataList.length">
<div class="excellent-cases__content-list">
<ExcellentCasesItem :sourceData="dataList" />
</div>
<div class="excellent-cases__content-list">
<ExcellentCasesItem :sourceData="dataList" />
</div>
</div>
</div>
</template>
<script lang="ts">
export default {
name: 'ExcellentCases',
}
</script>
<script setup lang="ts">
import { ref, watch } from 'vue'
import ExcellentCasesItem from './excellent-cases-item/index.vue'
const props = defineProps({
sourceData: {
type: Array<{ [key: string]: any }>,
default: () => [],
},
})
const dataList = ref([]) // 数据源
watch(
() => props.sourceData,
newVal => {
dataList.value = initData(newVal)
},
{
deep: true,
immediate: true,
}
)
// 初始化数据
const initData = (list: []) => {
const listLen = list.length
if (!listLen) {
return []
}
// 若小于8个,需要补充到长度为8
if (listLen < 8) {
return fillList(list, 8)
}
// 超出8个,但为奇数 => 补充到偶数
if (listLen % 2 !== 0) {
return fillList(list, listLen + 1)
}
return list
}
// 填充数组
const fillList = (list: [], minCount = 8) => {
// 计算需要复制的次数
const timesToCopy = minCount / list.length
// 创建新数组并填充内容
const newArray = []
for (let i = 0; i < timesToCopy; i++) {
newArray.push(...list)
}
// 如果新数组的长度超过了minCount,截取前minCount个元素
if (newArray.length > minCount) {
newArray.length = minCount // 截取前minCount个元素
}
return newArray
}
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>
父组件scss
.excellent-cases {
margin-bottom: 100px;
width: 100%;
display: flex;
flex-direction: column;
align-items: center;
.excellent-cases__content {
padding-top: 60px;
display: flex;
width: 100%;
overflow: hidden;
.excellent-cases__content-list {
animation: animate var(--t) linear infinite;
animation-delay: calc(var(--t) * -1);
display: flex;
@keyframes animate {
0% {
transform: translateX(100%);
}
100% {
transform: translateX(-100%);
}
}
&:nth-child(2) {
animation: animate2 var(--t) linear infinite;
animation-delay: calc(var(--t) / -2);
}
@keyframes animate2 {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-200%);
}
}
}
}
// 暂停
.excellent-cases__content:hover > .excellent-cases__content-list {
animation-play-state: paused !important;
}
}