动效列表
大约 3 分钟
效果
源码
<template>
<h2>待办事项</h2>
<input v-model="inputValue" placeholder="试试添加事项" /><button
class="btn-add"
@click="add"
>
添加
</button>
<div class="list" ref="listRef">
<div
class="item"
v-for="(item, index) in list"
:key="item.id"
:class="{
leave: delIndex == index,
up: delIndex != -1 && delIndex < index,
down: topIndex > index,
}"
>
<div class="left">{{ item.content }}</div>
<div class="right">
<button @click="top(index)">置顶</button>
<button @click="del(index)">删除</button>
</div>
</div>
</div>
</template>
<script>
export default {
data: () => ({
list: [
{
id: "1",
content: "事项一",
},
{
id: "2",
content: "事项二",
},
{
id: "3",
content: "事项三",
},
],
animationTime: 500,
delIndex: -1,
topIndex: -1,
inputValue: "",
}),
computed: {
// 是否正在操作节点
isHandle() {
return this.delIndex == -1 && this.topIndex == -1 ? false : true;
},
// 当前执行操作节点
nowHandleNode() {
if (this.isHandle) {
if (this.delIndex !== -1) {
return this.$refs.listRef.children[this.delIndex];
} else if (this.topIndex !== -1) {
return this.$refs.listRef.children[this.topIndex];
}
} else {
return {};
}
},
},
methods: {
del(index) {
// 当前已有元素在执行操作,退出
if (this.isHandle) return;
this.delIndex = index;
setTimeout(() => {
this.list.splice(index, 1);
this.delIndex = -1;
}, this.animationTime);
},
top(index) {
// 当前已有元素在执行操作,退出
if (this.isHandle) return;
this.topIndex = index;
this.nowHandleNode.style.transition = "all 0.5s";
this.nowHandleNode.style.transform = `translateY(-${30 * index}px)`;
setTimeout(() => {
this.nowHandleNode.style.transition = "";
this.nowHandleNode.style.transform = "";
const item = this.list[index];
this.list.splice(index, 1);
this.list.unshift(item);
this.topIndex = -1;
}, this.animationTime);
},
add() {
if (!this.inputValue.trim()) return;
this.list.push({
id: (this.list.length + 1).toString(),
content: this.inputValue.trim(),
});
this.inputValue = "";
this.$nextTick(() => {
this.$refs.listRef.children[this.list.length - 1].classList.add("from");
setTimeout(() => {
this.$refs.listRef.children[this.list.length - 1].classList.remove(
"from",
);
}, this.animationTime);
});
},
},
};
</script>
<style>
.list {
display: flex;
flex-direction: column;
}
.list .item {
width: 200px;
height: 30px;
display: flex;
justify-content: space-between;
}
.btn-add {
margin-left: 10px;
margin-bottom: 10px;
}
.up {
transition: all 0.5s;
transform: translateY(-30px);
}
.leave {
transition: all 0.5s;
transform: translateX(200px);
opacity: 0;
}
.down {
transition: all 0.5s;
transform: translateY(30px);
}
.from {
animation: 0.5s linear 1 from;
}
@keyframes from {
0% {
transform: translateX(50px);
opacity: 0;
}
100% {
transform: none;
opacity: 1;
}
}
</style>
实现思路
删除操作
删除元素右移消失,下方元素往上移动一格
- 定义删除动画和上移动画
/* 往右渐变消失 */
.leave {
transition: all 0.5s;
transform: translateX(200px);
opacity: 0;
}
/* 往上移动 */
.up {
transition: all 0.5s;
transform: translateY(-30px);
}
- 模板根据删除元素索引绑定删除动画和上移动画
<div
class="item"
v-for="(item, index) in list"
:key="item.id"
:class="{
leave: delIndex == index,
up: delIndex != -1 && delIndex < index,
}"
></div>
- 点击删除按钮指定删除元素索引,删除动画播放完毕后,删除元素
del(index) {
// 当前已有元素在执行操作,退出
if (this.isHandle) return;
this.delIndex = index;
setTimeout(() => {
this.list.splice(index, 1);
this.delIndex = -1;
}, this.animationTime);
}
置顶操作
置顶元素移到最上方,上方元素往下移动一格
- 定义下移动画(置顶动画需要手动计算移动距离)
/* 往下移动 */
.down {
transition: all 0.5s;
transform: translateY(30px);
}
- 模板根置顶元素索引绑定下移动画
<div
class="item"
v-for="(item, index) in list"
:key="item.id"
:class="{
down: topIndex > index,
}"
></div>
- 点击置顶按钮指定置顶元素索引,计算置顶需要移动的距离(索引 * 元素高度),绑定置顶动画,动画播放完毕后,移动元素
top(index) {
// 当前已有元素在执行操作,退出
if (this.isHandle) return;
this.topIndex = index;
// 置顶动画
this.nowHandleNode.style.transition = "all 0.5s";
this.nowHandleNode.style.transform = `translateY(-${30 * index}px)`;
setTimeout(() => {
// 移除置顶动画
this.nowHandleNode.style.transition = "";
this.nowHandleNode.style.transform = "";
// 移动元素
const item = this.list[index];
this.list.splice(index, 1);
this.list.unshift(item);
this.topIndex = -1;
}, this.animationTime);
}
添加操作
- 定义添加动画
/* 从右方渐变进入 */
.from {
animation: 0.5s linear 1 from;
}
@keyframes from {
0% {
transform: translateX(50px);
opacity: 0;
}
100% {
transform: none;
opacity: 1;
}
}
- 点击添加按钮,添加元素,DOM 渲染后绑定添加动画,动画播放完毕后,移除样式
add() {
if (!this.inputValue.trim()) return;
// 添加元素
this.list.push({
id: (this.list.length + 1).toString(),
content: this.inputValue.trim(),
});
this.inputValue = "";
this.$nextTick(() => {
// this.$refs.listRef.children[this.list.length - 1] 最后一个元素
this.$refs.listRef.children[this.list.length - 1].
classList.add("from");
setTimeout(() => {
this.$refs.listRef.children[this.list.length - 1].
classList.remove("from");
}, this.animationTime);
});
}