Пытаюсь научится пользоватся AnimeJS
Не понимаю, как сделать таймлайн, в котором будет повторятся только отдельный промежуток.
Код:
leave: function (el, done) {
var showLeave = anime.timeline({
targets: el,
easing: 'easeInOutCubic',
duration: 600,
complete: function() {
done();
}
});
showLeave
.add({
// targets: el,
translateX: '15px',
rotateZ: '50deg'
})
.add({
rotateZ: '100deg',
loop: 2,
direction: 'reverse'
})
.add({
rotateZ: '45deg',
translateY: '30px',
translateX: '30px',
opacity: 0
});
}
Во втором add
необходимо, чтобы 2 раза покрутилось, но срабатывает только один раз. Повторение получается только повесить на весь таймлайн, но мне нужна только отдельная его часть.
Знаю, что это можно сделать более простым способом, например, через Animate.css
. Но я во второй раз сталкиваюсь с подобной проблемой, и хотелось бы понять, как это делается.
Во втором add необходимо, чтобы 2 раза покрутилось, но срабатывает только один раз.
На самом деле, если использовать хуки loopBegin
и loopComplete
, которые вызываются при каждом цикле (loop) и вывести данные об изменениях (хотя бы в консоль), то можно убедиться, что анимация выполняется дважды.
// Отключим ненужные для примера
// сообщения в консоли.
Vue.config.productionTip = false;
Vue.config.devtools = false;
new Vue({
el: '#app',
data: {
show: true
},
methods: {
leave: function(el, done) {
console.clear();
var loopBegan = 0;
var loopCompleted = 0;
var showLeave = anime.timeline({
targets: el,
easing: 'easeInOutCubic',
duration: 600,
complete(anim) {
done();
console.log('Все анимации выполнены:', el.style.transform);
}
});
showLeave
.add({
translateX: '15px',
rotateZ: '50deg',
complete: (anim) => console.log('Анимация 1 выполнена:', el.style.transform)
})
.add({
rotateZ: '100deg',
loop: 2,
direction: 'reverse',
loopBegin: (anim) => console.log('loop began:', ++loopBegan, anim.loop),
loopComplete: (anim) => console.log('loop completed:', ++loopCompleted, anim.loop),
complete: (anim) => console.log('Анимация 2 выполнена:', el.style.transform)
})
.add({
rotateZ: '45deg',
translateY: '30px',
translateX: '30px',
opacity: 0,
complete: (anim) => console.log('Анимация 3 выполнена:', el.style.transform)
});
}
}
});
*,
*:before,
*:after {
box-sizing: border-box;
}
.stamp {
display: inline-block;
max-width: 288px;
padding: 8px 12px;
box-sizing: border-box;
border: 1px solid #08c;
border-radius: 2px;
color: #80c;
background-color: #fff;
font-family: monospace;
font-size: 15px;
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, .2);
}
.stamp a {
color: #08c;
}
<div id="app">
<button @click="show = !show">{{ show ? 'Скрыть ...' : 'Показать ...' }}</button>
<hr>
<transition @leave="leave">
<div v-if="show" class="stamp">
Временные шкалы позволяют синхронизировать несколько анимаций вместе. По умолчанию каждая анимация, добавленная к временной шкале, начинается после окончания предыдущей анимации. <a href="https://github.com/juliangarnier/anime/issues/522" target="_blank">Loop behaviour in timeline</a>
</div>
</transition>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/animejs@3.0.1/lib/anime.min.js"></script>
Параметры loop
и direction
не находятся в списке наследуемых свойств от родительского экземпляра временной шкалы timeline
, в отличии от: targets, duration, delay, endDelay, round
. Да и сама по себе временная шкала больше предназначена для манипуляции несколькими объектами, как мне кажется.
Но значение rotateZ
второй анимацию можно разбить на несколько, и тем самым добиться нужного вам результата:
.add({
// Массив значений: три поворота.
rotateZ: [100, 50, 100],
// На каждый поворот по 600 миллисекунд.
duration: 1800,
})
// Отключим ненужные для примера
// сообщения в консоли.
Vue.config.productionTip = false;
Vue.config.devtools = false;
new Vue({
el: '#app',
data: {
show: true
},
methods: {
leave(el, done) {
const timeline = anime.timeline({
targets: el,
easing: 'easeInOutCubic',
duration: 600,
complete(anim) {
done();
}
});
timeline.add({
translateX: 15,
rotateZ: 50,
})
.add({
rotateZ: [100, 50, 100],
duration: 1800,
})
.add({
rotateZ: 45,
translateY: 30,
translateX: 30,
opacity: 0,
});
}
}
});
*,
*:before,
*:after {
box-sizing: border-box;
}
.stamp {
display: inline-block;
max-width: 288px;
padding: 8px 12px;
box-sizing: border-box;
border: 1px solid #08c;
border-radius: 2px;
color: #80c;
background-color: #fff;
font-family: monospace;
font-size: 15px;
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, .2);
}
.stamp a {
color: #08c;
}
<div id="app">
<button @click="show = !show">{{ show ? 'Скрыть ...' : 'Показать ...' }}</button>
<hr>
<transition @leave="leave">
<div v-if="show" class="stamp">
Параметры, установленные в родительском экземпляре временной шкалы, будут наследоваться всеми дочерними элементами: targets, duration, delay, endDelay, round.
</div>
</transition>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/animejs@3.0.1/lib/anime.min.js"></script>
Ниже расположены еще два примера реализации анимации.
Первый основан на ключевых кадрах.
// Отключим ненужные для примера
// сообщения в консоли.
Vue.config.productionTip = false;
Vue.config.devtools = false;
new Vue({
el: '#app',
data: {
show: true
},
methods: {
leave(el, done) {
// Массив ключевых кадров.
const keyframes = [{
// переместили вправо на 15 пикселей
translateX: 15,
// повернули на 50 градусов
rotateZ: 50,
}, {
// повернули от 50 до 100
rotateZ: 100,
}, {
// повернули от 100 до 50
rotateZ: 50,
}, {
// повернули от 50 до 100
rotateZ: 100,
}, {
// повернули от 100 до 45
rotateZ: 45,
// переместили вниз на 30
translateY: 30,
// переместили вправо от 15 до 30
translateX: 30,
// сделали прозрачным
opacity: 0,
}];
// Время выполнения анимации.
// Каждому кадру отводим по 600 миллисекунд.
const duration = keyframes.length * 600;
anime({
targets: el,
duration: duration,
keyframes: keyframes,
easing: 'easeInOutCubic',
complete(anim) {
done();
}
})
const defaultTweenSettings = {
duration: 1000,
delay: 0,
endDelay: 0,
easing: 'easeOutElastic(1, .5)',
round: 0
}
}
}
});
*,
*:before,
*:after {
box-sizing: border-box;
}
.stamp {
display: inline-block;
max-width: 288px;
padding: 8px 12px;
box-sizing: border-box;
border: 1px solid #08c;
border-radius: 2px;
color: #80c;
background-color: #fff;
font-family: monospace;
font-size: 15px;
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, .2);
}
.stamp a {
color: #08c;
}
<div id="app">
<button @click="show = !show">{{ show ? 'Скрыть ...' : 'Показать ...' }}</button>
<hr>
<!--
Лучше явным образом указывать v-bind:css="false" для переходов,
основанных только на JavaScript.
Это позволит Vue не тратить время на определение параметров CSS.
Кроме того, это убережёт нас от случайного взаимовлияния CSS-правил и JS-перехода.
-->
<transition @leave="leave" :css="false">
<div v-if="show" class="stamp">
Ключевые кадры анимации определяются с использованием массива в свойстве keyframes.
</div>
</transition>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/animejs@3.0.1/lib/anime.min.js"></script>
Второй - тоже на ключевых кадрах, но без применения библиотеки Anime.js.
// Отключим ненужные для примера
// сообщения в консоли.
Vue.config.productionTip = false;
Vue.config.devtools = false;
new Vue({
el: '#app',
data: {
show: true
}
});
*,
*:before,
*:after {
box-sizing: border-box;
}
.stamp {
display: inline-block;
max-width: 288px;
padding: 8px 12px;
box-sizing: border-box;
border: 1px solid #08c;
border-radius: 2px;
color: #80c;
background-color: #fff;
font-family: monospace;
font-size: 15px;
box-shadow: 0 2px 6px 0 rgba(0, 0, 0, .2);
}
.stamp a {
color: #08c;
}
.avulsion-leave {}
.avulsion-leave-active {
animation-name: avulsion-in;
animation-delay: 0s;
animation-duration: 3s;
animation-timing-function: ease;
}
@keyframes avulsion-in {
from {
/**
* Задаем начальное состояние.
* Его можно также задать в .avulsion-leave {}
*/
transform: translateX(0) rotateZ(0);
}
20% {
/**
* переместили вправо на 15 пикселей;
* повернули на 50 градусов.
*/
transform: translateX(15px) rotateZ(50deg);
}
40% {
/**
* повернули от 50 до 100.
*/
transform: rotateZ(100deg);
}
60% {
/**
* повернули от 100 до 50.
*/
transform: rotateZ(50deg);
}
80% {
/**
* повернули от 50 до 100;
* задали кадру свойство `opacity`,
* т.к. на следующем это свойство изменится.
*/
transform: rotateZ(100deg);
opacity: 1;
}
to {
/**
* повернули от 100 до 45;
* переместили вправо от 15 до 30;
* переместили вниз на 30;
* сделали прозрачным.
*/
transform: rotateZ(45deg) translateX(30px) translateY(30px);
opacity: 0;
}
}
/**
* Анимация появления элемента.
*/
.avulsion-enter-active {
animation-name: slidein;
animation-duration: .4s;
animation-delay: 0s;
animation-timing-function: ease-in-out;
}
@keyframes slidein {
from {
margin-left: -288px;
}
to {
margin-left: 0;
}
}
<div id="app">
<button @click="show = !show">{{ show ? 'Скрыть ...' : 'Показать ...' }}</button>
<hr>
<transition name="avulsion">
<div v-if="show" class="stamp">
v-enter-active и v-leave-active дают возможность указать различные анимационные эффекты для переходов появления и исчезновения элемента. CSS-анимации v-enter удаляется только при наступлении события animationend.
</div>
</transition>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<script src="https://cdn.jsdelivr.net/npm/animejs@3.0.1/lib/anime.min.js"></script>
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Я уже задавал вопрос по этому проекту (В какой-то момент, отскакивающий от краёв канваса мяч, попадает в баг и ведет себя аномально), код тот...
Есть форма для регистрации, но про нажатии на кнопку ничего не происходитКод формы:
Я пытаюсь создать экран ожидания c цифрами обратного отсчета, который показывает глаз вместе с веком, и глазное яблоко с эффектом радужной...