(追記ここから)
Enter/Leave とトランジション一覧 — Vue.js
Vue.jsで同じようなやつあるしこれ使ったらええやんという話を聞いた。よさそう。
結果同じような見た目だけど、translateで動かして、そのあとDOMの並びを調整しているっぽい。
データの並び = DOMの並びに完全にリンクしているので、Vueの中でも調整しやすいよね的な感じなのかな。
(追記ここまで)
DOM ががーーーーっとならんだものを並び替えるときにアニメーションできないかなあ、というもの。 うにょーんって動いたら面白いのでは?と思って試してみた。
このページで動く様子が確認できるようにした(動いてるときに再度押すとバグる)
そのまま動かそうとすると、移動先の座標がわからず動かせないので、いくつかのステップを通してみた。
- 今の表示されている座標を保存
- 今の表示されている座標の場所に固定した DOM を生成(この時点ではメモリに乗っているだけで表示されない)
- コンテンツを並び替えて新しい座標を計算
- 今の表示されている DOM を非表示 2 で作った DOM を表示する(見え方は変わらないが表示されているものを切り変える)
- 3 の座標に向けて 2 の DOM を CSS アニメーションで動かす
- 終わったころあいで 2 と 3 の DOM を入れ替えて 2 の DOM を捨てる。
要は動かす用の DOM を生成して、動かすだけ動かして、前後は元の DOM のまま、というもの。 パッと思いついたやり方はこれ。
ちゃんとやろうとすると、再描画が無茶苦茶大変になって遅くなるとおもうので、何か対策考えないといけなさそう。 透過色まずやめよう、とか。
コードはこんな感じ。 ちょっと座標のところが面倒になって jQuery を使っている。が、そんなに難しいことはやっていないので生 DOM でもわりかし簡単にいけるはず。
連打対策は入れてないので、連打するとブラウザ固まる。
const DomSortAnimation = ($target, AnimationTimeSec, AnimationCompleteDelaySec, SortFunction) => {
// fixed now position
const targetPositions = $target.map((_, e) => {
const $t = $(e);
return $t.position()
});
const $newTarget = $target.clone()
$newTarget.each((i, e) => {
$(e).css({
top: targetPositions[i].top,
left: targetPositions[i].left,
position: 'absolute',
display: 'block',
transition: AnimationTimeSec + 's all ease',
});
});
// calculate new position
const targetContents = $target.toArray()
.sort(SortFunction)
.map((a) => {
return {
html: a.innerHTML,
href: a.href
}
});
$target.each((i, e) => {
const $e = $(e);
$e.html(targetContents[i].html);
$e.attr({href:targetContents[i].href});
});
const newPositions = {};
$target.each((_, e) => {
const $e = $(e)
newPositions[$e.attr('href')] = $e.position();
})
// swap DOM
$newTarget.appendTo($target.parent());
$target.hide();
// animation start
setTimeout(() => {
$newTarget.each((i, e) => {
const $e = $(e);
const pos = newPositions[$e.attr('href')];
$e.css({
top: pos.top,
left: pos.left,
});
});
}, 0);
// complete animation restore DOM
setTimeout(() => {
$newTarget.remove();
$target.show();
}, (AnimationTimeSec + AnimationCompleteDelaySec) * 1000);
};
DomSortAnimation(
$(".c-article-content .c-badge"),
2,
0.2,
(a, b) => {
const aContent = a.textContent.trim();
const bContent = b.textContent.trim();
if(aContent < bContent){
return -1;
}else if(aContent > bContent){
return 1;
}
return 0;
}
);