#javascript #animation #vue.js
#javascript #Анимация #vue.js
Вопрос:
Я создал простое приложение для выполнения задач, используя VueJS. Я также добавил vue2-animate (A Vue.js порт Animate.css 2.0. Для использования со встроенными переходами Vue.) Анимация добавления одного элемента работает корректно.
Но были две проблемы, которые я хотел бы решить без ненужного кодирования:
- Отображение анимации для списка загруженных из локального хранилища работает для всех элементов одновременно. Мне нужно, чтобы анимация работала последовательно для каждого элемента в отдельности.
- Анимация удаления элемента работает некорректно — всегда удаляется последний элемент, а затем следует сдвиг.
PS: Посмотрите демонстрацию в JSFiddle, потому что localstorage не работает во фрагментах SO.
Vue.component("adder", {
data: function() {
return {
task: ""
};
},
template: `
<div class="input-group mb-3">
<input type="text" class="form-control" placeholder="New task..." aria-label="New task..." aria-describedby="" v-model="task" v-on:keyup.enter="add">
<div class="input-group-append">
<button class="btn btn-primary" id="" v-on:click="add" > </button>
</div>
</div>
`,
methods: {
add: function() {
this.$emit("addtodo", {
title: this.task,
done: false
});
this.task = "";
}
}
});
Vue.component("todo", {
props: ["item"],
template: `
<a href="#" class="list-group-item list-group-item-action task" v-bind:class="{'disabled done' : item.done==true}">
<label class="form-check-label">
<input class="form-check-input" type="checkbox" name="" id="" value="checkedValue" v-model="item.done"> {{item.title}}
</label>
<button type="button" class="close" aria-label="Close" v-on:click="del">
<span aria-hidden="true">amp;times;</span>
</button>
</a>
`,
methods: {
del: function() {
this.$emit("deletetodo");
}
}
});
Vue.component("todos", {
props: ["items"],
template: `
<div class="list-group">
<transition-group name="bounceLeft" tag="a">
<todo v-for="(item, index) in items" :key="index" :item.sync="item" v-on:deletetodo="delTodo(item)"></todo>
</transition-group>
</div>
`,
methods: {
delTodo: function(i) {
this.$emit("deletetodo", i);
}
}
});
Vue.config.devtools = true;
let app = new Vue({
el: ".todoapp",
data: {
title: "Todo App",
items: []
},
methods: {
addTodo: function(e) {
this.items.push(e);
},
delTodo: function(i) {
this.items = this.items.filter(e => e != i);
}
},
mounted() {
if (localStorage.items) {
this.items = JSON.parse(localStorage.getItem("items"));
}
},
watch: {
items: {
handler(val) {
localStorage.setItem("items", JSON.stringify(this.items));
},
deep: true
}
}
});
.done>label {
text-decoration: line-through;
}
.task {
padding-left: 36px;
}
<!DOCTYPE html>
<html lang="en">
<head>
<title>Todo App</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous" />
<link rel="stylesheet" href="https://unpkg.com/vue2-animate/dist/vue2-animate.min.css" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<div class="container todoapp">
<div class="row">
<br />
</div>
<div class="card">
<div class="card-header">
{{ title }}
</div>
<div class="card-body">
<adder v-on:addtodo="addTodo"></adder>
<todos :items.sync="items" v-on:deletetodo="delTodo"></todos>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X 965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH 8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV 2 9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3 MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>
<script src="script.js"></script>
</body>
</html>
Ответ №1:
Хорошо, беру это по одному за раз:
Удаление задачи
Причина, по которой всегда кажется, что удаляемая задача удаляется последней, заключается в том, что вы вводите элементы своего списка с помощью index
. Когда вы заменяете весь items
массив в вашем delTodo
методе, который, в свою очередь, выдает вам новый массив с новыми ключами для каждого элемента в списке. Нажимайте item
, и вы получите правильный результат:
<todo v-for="(item, index) in items" :key="item" :item.sync="item" v-on:deletetodo="delTodo(item)"></todo>
Отображение задач по одной при загрузке
Мой совет состоял бы в том, чтобы подходить к показу / скрытию задач с вычисляемым свойством:
computed: {
tasks: function(){
return this.items.filter(item => item.isVisible);
}
}
Здесь мы будем показывать / скрывать, переключая isVisible
для каждой задачи.
Это означает, что при первоначальной загрузке задач из локального хранилища вы могли бы установить для них всех значение isVisible: false
, а затем использовать setTimeout
в for
цикле для отображения их всех по одному за раз:
mounted() {
// Get your items and set all to hidden
if (localStorage.items) {
this.items = JSON.parse(localStorage.getItem("items"))
.map(item => item.isVisible = false);
}
// Loop through and show the tasks
for(let i=1; i<=this.items.length; i ){
// Where 300 is milliseconds to delay
let delay = i * 300;
setTimeout(function(){
this.items[i].isVisible = true;
}.bind(this), delay);
}
},
Ответ №2:
Лучше всего сработало поэтапное добавление в массив items:
mounted() {
let items = [];
if (localStorage.items) {
items = JSON.parse(localStorage.getItem("items"))
}
for (let i = 0; i < items.length; i ) {
let delay = i * 1000;
setTimeout(
function() {
this.items.push(items[i])
}.bind(this),
delay
)
}
}
Комментарии:
1. Вы можете использовать обозначения со стрелками для этой функции вместо того, чтобы привязывать это к вашей функции. () => this.items.push(элементы[i]) Это обозначение сохраняет ‘this’ (или предполагается, что это. У меня было несколько странных сценариев, когда этого не происходило)
Ответ №3:
Просто чтобы добавить к разговору, следующее обеспечивает ошеломляющее действие в Vuex и использует синтаксис жирной стрелки:
async fetchRepositories( {commit} ){
const response = await gitHubApi.get<Repository[]>('/users/rodolphocastro/repos') // Calling API with Axios
const staggered: Repository[] = []
response.data.forEach((r, i) => {
const delay = i * 300 // 300m -> Time to wait for each item in the array
setTimeout(() => {
staggered.push(r)
commit('setRepositories', staggered)
}, delay)
})
}