Неожиданное поведение группы переходов при использовании отфильтрованного массива
У меня проблема с переходной группой. Проблема в том, что я пытаюсь сделать список отфильтрованным по какому-то условию. В моем примере - в зависимости от мин / макс.
Как это можно исправить?
Первоначально я пытался использовать вычисленное значение для массива, отображаемого внутри переходной группы. Но очевидно, что он возвращал новый массив каждый раз, когда значение ползунка диапазона обновлялось. Итак, я пытался использовать @input
обработчик событий, который обновляет список, который должен отображаться только тогда, когда его нужно обновить. Что меня больше всего смущает, так это то, что я пытался настроить хуки переходной группы для @enter
@enter-to
@leave
@leave-to
события, и они запускаются правильно. Только раз. Я также пытался сделать его управляемым JS, но группа переходов устанавливает классы CSS, даже если я установил :css="false" :duration="500"
,
Я сделал упрощенный пример, который иллюстрирует проблему. codesandbox.io
Я ожидаю, что список (массив) будет обновлен, а затем инициирует повторное рендеринг содержимого группы переходов. И анимация FLIP выполняется. Но проблема в том, что если значение, которое используется в качестве условия фильтра, быстро изменяется (например, пользователь перетаскивает бегунок диапазона с помощью мыши), анимация либо вообще не выполняется, либо вызывает лаги. Внимательно следите за поведением, когда вы перетаскиваете ползунок диапазона слева направо (анимации запаздывают), и он отличается от того, что вы видите, когда перетаскиваете его справа налево (анимации для первого элемента не выполняются). Также обратите внимание, что если вы нажмете на ползунок, чтобы установить значение - анимация будет выполняться так, как ожидалось.
1 ответ
Ссылка: "И анимация FLIP выполняется"...
Учитывая, что вы хотите перевернуть предмет, я бы не стал использовать <transition-group>
совсем.
Я бы использовал эффект трехмерного вращения (который, как я предполагаю, вам нужен) и управлял бы углом поворота, используя вычисляемое свойство, основанное на соотношении между подпорками элемента и значением диапазона.
Вот:
Vue.config.devtools = false;
Vue.config.productionTip = false;
Vue.component('rotatingButton', {
template: `
<div>
<div class="flipper">
<div
class="items"
:style="{ transform: \`rotateX(\${rotation}deg)\` }">
<div v-for="item in items"
:key="item.ID"
:class="[item.value, 'item']"
v-text="item.value" />
</div>
</div>
<input type="range" v-model="range" min="0" max="1500">
<div class="color-range">
<div v-for="item in items"
:style="{width: \`\${(item.max - item.min)/15}%\`,
left: \`\${item.min/15}%\`,
backgroundColor: item.barColor }"
/>
</div>
</div>`,
data: () => ({
range: 100,
items: [{
ID: 0,
min: 0,
max: 500,
value: "first",
barColor: '#369'
}, {
ID: 1,
min: 500,
max: 700,
value: "second",
barColor: "#900"
}, {
ID: 2,
min: 700,
max: 1500,
value: "third",
barColor: "#393"
}
],
}),
computed: {
rotation() {
return this.range < this.items[0].max ? 0 : (this.range < this.items[1].max ? 90 : 180);
}
}
})
new Vue({
el: '#app'
});
#app {
text-align: center;
margin-top: 60px;
}
.flipper {
perspective: 210px;
height: 30px;
width: 150px;
line-height: 30px;
margin: 15px auto;
}
.items {
height: 100%;
perspective-origin: 150% 150%;
transform-style: preserve-3d;
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
}
.item {
display: block;
position: absolute;
width: 100%;
height: 100%;
border: 1px solid rgba(33,66,99,.33);
font: 25px monospace;
color: rgba(33,66,99,.33);
background-color: white;
}
.first { transform: translateZ(15px); }
.second { transform: rotateX(-90deg) translateZ(15px); }
.third { transform: rotateX(180deg) translateZ(15px); }
.fourth { transform: rotateX(90deg) translateZ(15px); }
.color-range {
padding: 0 1rem;
position: relative;
height: 4px;
}
input[type="range"] {width: 100%; margin: 0;}
.color-range > div {
position: absolute;
top: 0;
height: 100%;
opacity: .65;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<rotating-button />
</div>
Пример может быть расширен для обработки большего количества элементов, если это необходимо:
Vue.config.devtools = false;
Vue.config.productionTip = false;
Vue.component('rotatingButton', {
template: `
<div>
<div class="flipper">
<div
class="items"
:style="{ transform: \`rotateX(\${rotation}deg)\` }">
<div v-for="(item, key) in items"
v-show="range > key -1 && range < key + 3"
:key="key"
:class="['item', \`key-\${key}\`]"
v-text="item + 1" />
</div>
</div>
<div class="inputs">
<input type="range" v-model="range" min="1" :max="max" step=".01">
<input type="number" v-model="max">
</div>
<pre>{{logger}}</pre>
</div>`,
data: () => ({
range: 10,
max: 15,
}),
computed: {
items() {
return _.times(this.max);
},
rotation() {
return Math.floor(((Number(this.range) * 9) + 36) * 1e3) / 1e2;
},
logger() {
return JSON.stringify({
range: Number(this.range),
items: Number(this.max),
rotation: this.rotation
}, true, 2);
}
},
})
new Vue({
el: '#app'
});
#app {
text-align: center;
}
.flipper {
perspective: 210px;
height: 30px;
width: 150px;
line-height: 30px;
margin: 15px auto;
}
.items {
height: 100%;
perspective-origin: 150% 150%;
transform-style: preserve-3d;
transition: transform 0.1s ease-in-out;
}
.item {
display: block;
position: absolute;
width: 100%;
height: 100%;
border: 1px solid rgba(33, 66, 99, .42);
font: 25px monospace;
color: rgba(33, 66, 99, .42);
background-color: white;
transform-style: preserve-3d;
}
.item {
transform: translateZ(15px);
}
.inputs {
display: flex;
padding: 0 1rem;
}
input[type="range"] {
flex-grow: 1;
margin: 0 1rem 0 0;
}
input[type="number"] {
font-size: 1.5rem;
width: 100px;
}
.item:nth-child(4n + 1) {
transform: rotateX(-90deg) translateZ(15px);
}
.item:nth-child(4n + 2) {
transform: rotateX(180deg) translateZ(15px);
}
.item:nth-child(4n + 3) {
transform: rotateX(90deg) translateZ(15px);
}
.color-range>div {
position: absolute;
top: 0;
height: 100%;
opacity: .65;
}
pre {
text-align: left;
background-color: #def;
border: 1px solid #ccc;
border-radius: .25rem;
padding: 1rem;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.11/lodash.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<rotating-button />
</div>