Vue 中大型交错列表的速度动画
Velocity-animate for large staggered lists in Vue
我有一个很大的列表,我正在介绍velocity-animate into. At the moment, my test list has 59 individual items that a user can filter by search query (potentially could grow to hundreds of items). I am animating currently according to the way the Vue docs显示它已经完成,但当然他们的列表只有几个项目。
在我的应用程序中,动画需要几秒钟才能完成,这绝对不理想。有什么“批量”动画的方法吗?我认为理想情况下会发生的事情是我会对输入进行去抖动,这样只有一两秒钟后,搜索才会真正执行。然后在该方法中,过滤项目,所有不匹配的项目都会消失。我怎样才能做到这一点?
<template>
<v-text-field
background-color="#fff"
clearable
dense
flat
hide-details
label="Search"
placeholder="Search"
solo-inverted
:value="searchQuery"
@click:append="clearSearch"
@input="debounceSearchQuery" />
<v-list>
<v-list-item-group>
<draggable
class="drag-area list-group"
:group="{ name: 'items', pull: 'clone', put: false}"
handle=".gear-handle"
:list="filteredItems"
:sort="false"
@change="log">
<transition-group
:css="false"
name="staggered-fade"
@before-enter="beforeEnter"
@enter="enter"
@leave="leave">
<v-list-item
v-for="(item, i) in filteredItems"
:key="item.id"
:data-index="i">
<p class="mb-0 white--text">
{{ item.name }}
</p>
</v-list-item>
</transition-group>
</draggable>
</v-list-item-group>
</v-list>
</template>
export default {
data: () => ({
searchQuery: ''
}),
computed: {
filteredItems () {
const filteredItems = [];
this.categories.forEach(category => {
if (this.searchQuery) {
const text = this.searchQuery.toLowerCase();
filteredItems.push(category.items.filter(item => item.name && item.name.toLowerCase().includes(text)));
} else {
filteredItems.push(category.items);
}
});
return filteredItems.flat();
},
},
methods: {
// gsap methods //
beforeEnter (el) {
el.style.opacity = 0;
el.style.height = 0;
},
enter (el, done) {
const delay = el.dataset.index * 150;
setTimeout(() => {
this.$velocity(
el,
{ opacity: 1, height: '1.6em', duration: 50 },
[0.57, 0.06, 0, 1.06],
{ complete: done }
);
}, delay);
},
leave (el, done) {
const delay = el.dataset.index * 150;
setTimeout(() => {
this.$velocity(
el,
{ opacity: 0, height: 0 },
{ complete: done }
);
}, delay);
},
// end gsap methods
debounceSearchQuery: debounce(function (e) {
this.searchQuery = e;
}, 1000)
}
}
我认为“交错”动画和“批量”运行 过渡更像是两种不同的东西——这几乎就像你不能吃蛋糕一样。
尽管如此,如果您只是追求具有惊人效果的平滑滑动过渡,您实际上可以仅使用 <transition-group>
+ CSS 来实现(不需要过渡挂钩)。这是一个简单的示例(单击“运行”,然后单击“全页”以获得更好的视图):
new Vue({
vuetify: new Vuetify({
theme: {
dark: true
}
}),
data: () => ({
searchQuery: '',
categories: [
{ items: [{ name: 'vue.js', id: 1 }] },
{ items: [{ name: 'react.js', id: 2 }] },
{ items: [{ name: 'angular.js', id: 3 }] },
{ items: [{ name: 'velocity-animate', id: 4 }] },
{ items: [{ name: 'lodash', id: 5 }] },
{ items: [{ name: 'debounce', id: 6 }] },
]
}),
computed: {
filteredItems() {
const filteredItems = [];
const text = this.searchQuery.toLowerCase();
this.categories.forEach(category => {
if (this.searchQuery) {
filteredItems.push(category.items.filter(item =>
item.name && item.name && item.name.toLowerCase().includes(text)));
}
else {
filteredItems.push(category.items);
}
});
return filteredItems.flat();
},
},
methods: {
debounceSearchQuery: _.debounce(function(e) {
this.searchQuery = e;
}, 200),
clearSearch() {
this.searchQuery = '';
}
}
}).$mount('#app');
.staggered-fade-item {
transition-timing-function: cubic-bezier(0.57, 0.06, 0, 1.06);
transition-duration: 500ms;
transition-property: opacity, transform;
}
.staggered-fade-enter,
.staggered-fade-leave-to {
opacity: 0;
transform: translateY(-30px);
}
<script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@4.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
<div id="app">
<v-app>
<v-main>
<v-container>
<v-text-field
@click:append="clearSearch"
@input="debounceSearchQuery"
:value="searchQuery"
label="Search"
placeholder="Search"
autocomplete="off"
solo-inverted clearable dense flat hide-details autofocus dark>
</v-text-field>
<v-list>
<transition-group name="staggered-fade" tag="v-list-item-group">
<v-list-item
v-for="item in filteredItems" :key="item.id"
class="staggered-fade-item">
<p class="mb-0 white--text">
{{ item.name }}
</p>
</v-list-item>
</transition-group>
</v-list>
</v-container>
</v-main>
</v-app>
</div>
另一个建议是 Velocity UI pack。这个插件允许你做序列 运行 并且它带有一些预注册的效果,这对于惊人的效果非常有用。 (查看文档以获得更多过渡效果)。
new Vue({
vuetify: new Vuetify({
theme: {
dark: true
}
}),
data: () => ({
searchQuery: '',
categories: [
{ items: [{ name: 'vue.js', id: 1 }] },
{ items: [{ name: 'react.js', id: 2 }] },
{ items: [{ name: 'angular.js', id: 3 }] },
{ items: [{ name: 'velocity-animate', id: 4 }] },
{ items: [{ name: 'lodash', id: 5 }] },
{ items: [{ name: 'debounce', id: 6 }] },
]
}),
computed: {
filteredItems() {
const filteredItems = [];
const text = this.searchQuery.toLowerCase();
this.categories.forEach(category => {
if (this.searchQuery) {
filteredItems.push(category.items.filter(item =>
item.name && item.name && item.name.toLowerCase().includes(text)));
}
else {
filteredItems.push(category.items);
}
});
return filteredItems.flat();
},
},
methods: {
debounceSearchQuery: _.debounce(function(e) {
this.searchQuery = e;
}, 200),
clearSearch() {
this.searchQuery = '';
}
},
watch: {
filteredItems() {
Velocity(
this.$refs.items.map(vnode => vnode.$el),
'transition.flipYIn', {
stagger: 100,
drag: true
}
);
}
}
}).$mount('#app');
<script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>
<script src="https://cdn.jsdelivr.net/npm/velocity-animate@1.5.2/velocity.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/velocity-animate@1.5.2/velocity.ui.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@4.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
<div id="app">
<v-app>
<v-main>
<v-container>
<v-text-field
@click:append="clearSearch"
@input="debounceSearchQuery"
:value="searchQuery"
label="Search"
placeholder="Search"
autocomplete="off"
solo-inverted clearable dense flat hide-details autofocus dark>
</v-text-field>
<v-list>
<v-list-item-group>
<v-list-item
v-for="item in filteredItems" :key="item.id"
ref="items">
<p class="mb-0 white--text">
{{ item.name }}
</p>
</v-list-item>
</v-list-item-group>
</v-list>
</v-container>
</v-main>
</v-app>
</div>
我有一个很大的列表,我正在介绍velocity-animate into. At the moment, my test list has 59 individual items that a user can filter by search query (potentially could grow to hundreds of items). I am animating currently according to the way the Vue docs显示它已经完成,但当然他们的列表只有几个项目。
在我的应用程序中,动画需要几秒钟才能完成,这绝对不理想。有什么“批量”动画的方法吗?我认为理想情况下会发生的事情是我会对输入进行去抖动,这样只有一两秒钟后,搜索才会真正执行。然后在该方法中,过滤项目,所有不匹配的项目都会消失。我怎样才能做到这一点?
<template>
<v-text-field
background-color="#fff"
clearable
dense
flat
hide-details
label="Search"
placeholder="Search"
solo-inverted
:value="searchQuery"
@click:append="clearSearch"
@input="debounceSearchQuery" />
<v-list>
<v-list-item-group>
<draggable
class="drag-area list-group"
:group="{ name: 'items', pull: 'clone', put: false}"
handle=".gear-handle"
:list="filteredItems"
:sort="false"
@change="log">
<transition-group
:css="false"
name="staggered-fade"
@before-enter="beforeEnter"
@enter="enter"
@leave="leave">
<v-list-item
v-for="(item, i) in filteredItems"
:key="item.id"
:data-index="i">
<p class="mb-0 white--text">
{{ item.name }}
</p>
</v-list-item>
</transition-group>
</draggable>
</v-list-item-group>
</v-list>
</template>
export default {
data: () => ({
searchQuery: ''
}),
computed: {
filteredItems () {
const filteredItems = [];
this.categories.forEach(category => {
if (this.searchQuery) {
const text = this.searchQuery.toLowerCase();
filteredItems.push(category.items.filter(item => item.name && item.name.toLowerCase().includes(text)));
} else {
filteredItems.push(category.items);
}
});
return filteredItems.flat();
},
},
methods: {
// gsap methods //
beforeEnter (el) {
el.style.opacity = 0;
el.style.height = 0;
},
enter (el, done) {
const delay = el.dataset.index * 150;
setTimeout(() => {
this.$velocity(
el,
{ opacity: 1, height: '1.6em', duration: 50 },
[0.57, 0.06, 0, 1.06],
{ complete: done }
);
}, delay);
},
leave (el, done) {
const delay = el.dataset.index * 150;
setTimeout(() => {
this.$velocity(
el,
{ opacity: 0, height: 0 },
{ complete: done }
);
}, delay);
},
// end gsap methods
debounceSearchQuery: debounce(function (e) {
this.searchQuery = e;
}, 1000)
}
}
我认为“交错”动画和“批量”运行 过渡更像是两种不同的东西——这几乎就像你不能吃蛋糕一样。
尽管如此,如果您只是追求具有惊人效果的平滑滑动过渡,您实际上可以仅使用 <transition-group>
+ CSS 来实现(不需要过渡挂钩)。这是一个简单的示例(单击“运行”,然后单击“全页”以获得更好的视图):
new Vue({
vuetify: new Vuetify({
theme: {
dark: true
}
}),
data: () => ({
searchQuery: '',
categories: [
{ items: [{ name: 'vue.js', id: 1 }] },
{ items: [{ name: 'react.js', id: 2 }] },
{ items: [{ name: 'angular.js', id: 3 }] },
{ items: [{ name: 'velocity-animate', id: 4 }] },
{ items: [{ name: 'lodash', id: 5 }] },
{ items: [{ name: 'debounce', id: 6 }] },
]
}),
computed: {
filteredItems() {
const filteredItems = [];
const text = this.searchQuery.toLowerCase();
this.categories.forEach(category => {
if (this.searchQuery) {
filteredItems.push(category.items.filter(item =>
item.name && item.name && item.name.toLowerCase().includes(text)));
}
else {
filteredItems.push(category.items);
}
});
return filteredItems.flat();
},
},
methods: {
debounceSearchQuery: _.debounce(function(e) {
this.searchQuery = e;
}, 200),
clearSearch() {
this.searchQuery = '';
}
}
}).$mount('#app');
.staggered-fade-item {
transition-timing-function: cubic-bezier(0.57, 0.06, 0, 1.06);
transition-duration: 500ms;
transition-property: opacity, transform;
}
.staggered-fade-enter,
.staggered-fade-leave-to {
opacity: 0;
transform: translateY(-30px);
}
<script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@4.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
<div id="app">
<v-app>
<v-main>
<v-container>
<v-text-field
@click:append="clearSearch"
@input="debounceSearchQuery"
:value="searchQuery"
label="Search"
placeholder="Search"
autocomplete="off"
solo-inverted clearable dense flat hide-details autofocus dark>
</v-text-field>
<v-list>
<transition-group name="staggered-fade" tag="v-list-item-group">
<v-list-item
v-for="item in filteredItems" :key="item.id"
class="staggered-fade-item">
<p class="mb-0 white--text">
{{ item.name }}
</p>
</v-list-item>
</transition-group>
</v-list>
</v-container>
</v-main>
</v-app>
</div>
另一个建议是 Velocity UI pack。这个插件允许你做序列 运行 并且它带有一些预注册的效果,这对于惊人的效果非常有用。 (查看文档以获得更多过渡效果)。
new Vue({
vuetify: new Vuetify({
theme: {
dark: true
}
}),
data: () => ({
searchQuery: '',
categories: [
{ items: [{ name: 'vue.js', id: 1 }] },
{ items: [{ name: 'react.js', id: 2 }] },
{ items: [{ name: 'angular.js', id: 3 }] },
{ items: [{ name: 'velocity-animate', id: 4 }] },
{ items: [{ name: 'lodash', id: 5 }] },
{ items: [{ name: 'debounce', id: 6 }] },
]
}),
computed: {
filteredItems() {
const filteredItems = [];
const text = this.searchQuery.toLowerCase();
this.categories.forEach(category => {
if (this.searchQuery) {
filteredItems.push(category.items.filter(item =>
item.name && item.name && item.name.toLowerCase().includes(text)));
}
else {
filteredItems.push(category.items);
}
});
return filteredItems.flat();
},
},
methods: {
debounceSearchQuery: _.debounce(function(e) {
this.searchQuery = e;
}, 200),
clearSearch() {
this.searchQuery = '';
}
},
watch: {
filteredItems() {
Velocity(
this.$refs.items.map(vnode => vnode.$el),
'transition.flipYIn', {
stagger: 100,
drag: true
}
);
}
}
}).$mount('#app');
<script src="https://cdn.jsdelivr.net/npm/vue@2.x/dist/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.js"></script>
<script src="https://cdn.jsdelivr.net/npm/velocity-animate@1.5.2/velocity.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/velocity-animate@1.5.2/velocity.ui.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/2.4.1/lodash.min.js"></script>
<link href="https://cdn.jsdelivr.net/npm/@mdi/font@4.x/css/materialdesignicons.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/vuetify@2.x/dist/vuetify.min.css" rel="stylesheet">
<div id="app">
<v-app>
<v-main>
<v-container>
<v-text-field
@click:append="clearSearch"
@input="debounceSearchQuery"
:value="searchQuery"
label="Search"
placeholder="Search"
autocomplete="off"
solo-inverted clearable dense flat hide-details autofocus dark>
</v-text-field>
<v-list>
<v-list-item-group>
<v-list-item
v-for="item in filteredItems" :key="item.id"
ref="items">
<p class="mb-0 white--text">
{{ item.name }}
</p>
</v-list-item>
</v-list-item-group>
</v-list>
</v-container>
</v-main>
</v-app>
</div>