
实现一个如上的效果
li 元素的 Y 轴位移由 Vue 自动算出来(位置变化时平移)。
<template>
<div id="app">
<h2>实时排行榜</h2>
<!-- 列表展示 -->
<transition-group name="flip-list" tag="ul" class="list">
<li v-for="item in sortedItems" :key="item.id" class="list-item">
<!-- 外层 li 负责位置移动 -->
<!-- 内层 wrapper 负责旋转 -->
<div
class="item-wrapper"
:class="{
rotatedUp: item.animate && item.trend === 'up',
rotatedDown: item.animate && item.trend === 'down',
}"
@animationend="item.animate = false"
>
<span>{{ item.name }}</span>
<span>{{ item.score }}</span>
<span>{{ item.date }}</span>
<span v-if="item.trend === 'up'" class="up">↑</span>
<span v-else-if="item.trend === 'down'" class="down">↓</span>
<span v-else>不变</span>
</div>
</li>
</transition-group>
</div>
</template>
<script>
export default {
name: 'App',
data() {
return {
items: [],
oldItems: [],
sortKey: 'score',
timer: null,
};
},
computed: {
sortedItems() {
return [...this.items].sort((a, b) => {
if (this.sortKey === 'score') return b.score - a.score;
});
},
},
methods: {
mockApi() {
const baseNames = ['项目A', '项目B', '项目C', '项目D', '项目E', '项目F', '项目G', '项目H', '项目I', '项目J'];
return baseNames.map((name, index) => ({
id: index + 1,
name,
score: Math.floor(Math.random() * 100),
date: new Date(Date.now() - Math.random() * 100000000).toISOString().slice(0, 10),
}));
},
fetchData() {
const newData = this.mockApi();
// 新数据 + 趋势标记
let tempItems = newData.map(newItem => {
const oldItem = this.oldItems.find(i => i.id === newItem.id);
if (oldItem) {
if (newItem.score > oldItem.score) newItem.trend = 'up';
else if (newItem.score < oldItem.score) newItem.trend = 'down';
else newItem.trend = 'same';
} else {
newItem.trend = 'same';
}
newItem.animate = false; // 默认不旋转
return newItem;
});
// 排序后的新顺序
const sortedNew = [...tempItems].sort((a, b) => {
if (this.sortKey === 'score') return b.score - a.score;
});
// 对比旧顺序,判断位置是否变化
if (this.oldItems.length) {
sortedNew.forEach((item, idx) => {
const oldIdx = this.oldItems.findIndex(i => i.id === item.id);
if (oldIdx !== -1 && oldIdx !== idx) {
item.animate = true; // 位置变化才旋转
}
});
}
this.items = sortedNew;
this.oldItems = JSON.parse(JSON.stringify(sortedNew));
},
},
mounted() {
this.fetchData();
this.timer = setInterval(this.fetchData, 3000);
},
beforeDestroy() {
clearInterval(this.timer);
},
};
</script>
<style>
.list {
list-style: none;
padding: 0;
}
.list-item {
display: flex;
justify-content: space-between;
padding: 8px;
background: #f2f2f2;
margin-bottom: 4px;
border-radius: 4px;
}
.up {
color: green;
font-weight: bold;
}
.down {
color: red;
font-weight: bold;
}
/* Vue transition-group 自动处理位移动画 */
.flip-list-move {
transition: transform 0.8s;
}
/* 内层 wrapper 控制旋转 */
.item-wrapper {
display: flex;
justify-content: space-between;
width: 100%;
}
/* 上升时绿色翻转 */
.rotatedUp {
animation: rotateXUp 0.8s linear;
}
@keyframes rotateXUp {
0% {
transform: rotateX(0deg);
}
100% {
transform: rotateX(360deg);
}
}
/* 下降时红色翻转 */
.rotatedDown {
animation: rotateXDown 0.8s linear;
}
@keyframes rotateXDown {
0% {
transform: rotateX(0deg);
}
100% {
transform: rotateX(-360deg);
}
}
</style>
Vue平移反转动画实现
728

被折叠的 条评论
为什么被折叠?



