程式碼解釋整理
if (!isCurrentAnswerCorrect)
: 如果答案不正確,則延遲 500 毫秒後觸發重新排序。handleAnimationEnd()
: 在動畫結束後,關閉動畫並排序資料。isAnimation
: 用於判斷是否正在進行過渡效果,避免干擾。@transitionend="handleAnimationEnd"
: 在過渡效果結束後,執行 handleAnimationEnd()。如果資料不正確觸發重新排序
if (!isCurrentAnswerCorrect) {
setTimeout(() => {
handleUpdateTimelineTargetPosition(gameStatus.currentStep);
isAnimation.value = true;
}, 500);
}
為什麼要使用 setTimeout?我也嘗試使用 Vue 提供的 nextTick 方法,但不會產生過度效果他就更新了。所以為了先讓他畫一次畫面,我使用 setTimeout 處理。
動畫進行中避免再有其他觸發事件干擾過度,使用 isAnimation 判斷是否再進行過度效果,並在 transition 結束時 isAnimation 改為 false 關閉過度效果。
Vue.js 模板
<div
v-for="(timelineEvent, index) in timelineEvents"
...
:class="isAnimation ? 'transition-transform duration-500' : ''"
...
@transitionend="handleAnimationEnd"
>
//其他 略
</div>
動畫效果結束資料排序,並關閉 isAnimation 過度效果
const handleAnimationEnd = () => {
isAnimation.value = false;
handleSortedTimelineEvents();
};
因為如果我直接針對 timelineEvents 也就是作答的卡片進行排序,就不會產生過度的效果。所以我是這樣處理這個效果。1. 整理出接下來每張卡片要排序的目標定位,為每一張卡片在 style 上綁定目標訂位,此刻資料本身還是錯誤的排序,但畫面上因為 style 的改變會產生移動的效果。2. 移動到正確位置,瞬間重新排序資料。
更新目標位置的方法
const handleUpdateTimelineTargetPosition = () => {
const sortedTargetLists = [
...timelineEvents.value.map((timelineEvent, index) => {
return {
...timelineEvent,
transform: timelineEventsStyleRaw.value[index].transform,
zIndex: index + 10,
};
}),
].sort((a, b) => a.year - b.year);
sortedTargetLists.forEach((sortedTargetList, index) => {
timelineEventsStyleRawAnimationTarget.value[index].transform = sortedTargetList.transform;
timelineEventsStyleRawAnimationTarget.value[index].zIndex = sortedTargetList.zIndex;
});
};
目前這就是我使用的方法,但為了控制卡片的位置,我使用了 timelineEventsStyleRaw (原本預設每張卡片的排序位置)和 timelineEventsStyleRawAnimationTarget (暫時性用在動畫效果的排序位置),變得程式可讀性很低,我也正在測試不同改進的可能。
另外也試著使用 gsap 或 Anime.js, Velocity.js 搭配 vue transition hooks,但遇到渲染打架的問題,無法很流暢的執行卡片移動的效果。這個目前還在研究中。也期待之後能讓動畫更順暢。