📑

x column表示を x +-n column表示にアニメーション付きで切り替えたい

2022/06/30に公開約3,000字1件のコメント

つまりどういう(動きの)ことだってばよ

ソース

概要

desplayプロパティにtransitionはかけられないので、gridっぽいカードレイアウトなのにgridが使えないのが絶妙に面倒でした。他に良い方法ありそうな気もします
列を増やしたい場合は
<button class="switch-button" data-columnnum="☆">☆列</button>
↑☆の部分に増やしたい列数を入れるだけでOKです

<button class="switch-button" data-columnnum="5">5列</button>

見やすいver

HTML

<div class="switch-control">
  <button class="switch-button" data-columnnum="1">1列</button>
  <button class="switch-button" data-columnnum="2">2列</button>
  <button class="switch-button" data-columnnum="3">3列</button>
  <button class="switch-button" data-columnnum="4">4列</button>
</div>
<div class="switch-box">
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
  <div class="block"></div>
</div>

SCSS

.switch-box {
  position: relative;
  .block {
    border: 1px solid #333;
    position: absolute;
    transition: 0.4s;
  }
}

JavaScript

let deviceWidth = window.innerWidth; // 画面幅を取得
const switchBox = document.querySelector(".switch-box"); // 個々の要素をwrapしている要素
const switchBlock = document.querySelectorAll(".block"); // 個々の要素
const switchBlockLength = switchBlock.length; // 個々の要素の合計数
let switchBlockWidth; // 列数によって可変させる個々の要素にwidthを格納するため一旦宣言
let switchBlockHeight; // 列数によって可変させる個々の要素にheightを格納するため一旦宣言
const switchButton = document.querySelectorAll(".switch-button"); // 列数を変更するトリガーとなるボタン群
let setSwitchBoxHeight; // 個々の要素をwrapしている要素に指定するheight幅を算出して格納するため一旦宣言
let columnNum; // 列数を変更するトリガーとなるボタンに設定されているdata属性の数値(文字列)を取得
const heightRatio = 1; // 個々の要素の縦横比の縦の部分を横の何倍にするか指定
 
switchButton.forEach(function (elem, index) {
  elem.addEventListener("click", function () {
    columnNum = Number(elem.dataset.columnnum); //data属性の文字列を数値型に変更(計算に使用できるように)
    switchBlockWidth = deviceWidth / columnNum; // 画面幅から個々の要素の横幅を算出
    switchBlockHeight = switchBlockWidth / heightRatio; // 算出した個々の要素の横幅から個々の要素の縦幅を算出
    setItem(columnNum); // 個々の要素の横幅・縦幅・改行を実現する関数を実行
  });
});
 
function setItem(value) {
  const column = value; // 列数変更のトリガーとなったボタンのdata属性を取得
  switchBlock.forEach(function (elem2, index2) {
    elem2.style.width = switchBlockWidth + "px"; // 個々の要素に画面幅から算出したwidthを指定
    elem2.style.height = switchBlockHeight + "px"; // 個々の要素に画面幅と個々の要素の横幅から算出した縦幅を指定
    elem2.style.left = `${switchBlockWidth * (index2 % value)}px`; // 個々の要素のleftの値を算出して指定(個々の要素の横幅 * (列数 * n番目の余))
    const lineIndex = Math.floor(index2 / column); // topにスタイルを指定することで改行させるための基準値を取得(2列目の群 = 1 3列目の群 = 2 ...)
    elem2.style.top = switchBlockHeight * lineIndex + "px"; // 個々の要素にtopの数値を算出して指定
    switchBox.style.height = `${switchBlockHeight * (lineIndex + 1)}px`; // 個々の要素をwrapしている要素のheightを指定(小数点以下を切り捨てた数値に+1で最大行数算出)
  });
}
 
// リサイズ時に要素幅を調整
window.addEventListener("resize", function () {
  deviceWidth = window.innerWidth;
  switchBlockWidth = deviceWidth / columnNum;
  switchBlockHeight = switchBlockWidth / heightRatio;
  setItem(columnNum);
});
 
// 初期状態を何列表示にするか 1で2列、2で3列
switchButton[1].click();

Discussion

desplayプロパティにtransitionはかけられないので、gridっぽいカードレイアウトなのにgridが使えないのが絶妙に面倒でした。他に良い方法ありそうな気もします

"FLIP animation"と呼ばれるものを使うと本記事で達成したいことは実現しやすそうと考えました。
他にもreact-springgsapのFLIPプラグインなどでも実現できそうだと思います。

今回のデモではreact-flip-toolkitを使用しています。
このライブラリではreactに束縛されず、バニラでもライブラリが提供されています。

また、この"FLIP animation"関連でgridレイアウトに特化したライブラリanimate-css-gridもあります。おそらく本記事で実現したいことへの最短距離かもしれません。上記同様にバニラで使えます。

demo code

demo site

以上です。

ログインするとコメントできます