JavaScript

Swapyでカンバンボードやタスク管理に実装できるUI実装【JavaScript】

この記事では、JavaScriptでドラッグアンドドロップ機能を簡単に実装できる「Swapy」について詳しく解説します。Swapyは、HTML5のドラッグアンドドロップAPIを活用して、軽量かつ直感的な操作を可能にする便利なツールです。視覚的な並び替えが求められるUIの構築に役立ち、特別なライブラリに依存せずに実装できるため、パフォーマンスにも優れています。

この記事では、まずSwapyの基本的な使い方を紹介し、さらにサーバー連携や複数ゾーンのドラッグアンドドロップ、アニメーションの追加など、より高度な応用例もコード付きで解説します。タスク管理ツールやカンバンボードを作成したい方、視覚的にリッチなユーザーインターフェースを目指している方にぴったりの内容です。

Swapyとは?

Swapyは、JavaScriptを使ってカードやパネルといった要素をドラッグアンドドロップで自由に並び替えるための機能です。HTML5のドラッグアンドドロップAPIを使用して、特別なライブラリに依存せず、軽量かつシンプルに実装できるのが特徴です。Swapyを使うことで、視覚的に要素を操作できるインターフェースを簡単に作成することが可能です。

例えば、タスク管理ツールやカンバンボードのような、ユーザーが自由に要素を移動・並び替えできるシステムを簡単に構築することができます。

 

Swapyの主な機能

Swapyが提供するドラッグアンドドロップ機能は次のようなシンプルかつ効果的な機能です。

  1. 直感的なドラッグアンドドロップ操作
    ユーザーは要素をドラッグして好きな位置にドロップするだけで、順番を変えたり、異なるゾーンに移動させたりできます。これにより、UIの操作性が大幅に向上します。
  2. HTML5のドラッグアンドドロップAPIを活用
    Swapyは、HTML5のネイティブ機能を使用しているため、外部ライブラリに依存せずに、軽量で効率的なコードを実装できます。
  3. 簡単にカスタマイズ可能
    イベントリスナーを利用することで、ドラッグやドロップ時の動作をカスタマイズでき、ユーザーインターフェースに合わせた柔軟な動きを実現できます。

 

Swapyの利点

Swapyを活用することで、次のような利点があります。

  1. ユーザーエクスペリエンス(UX)の向上
    要素の並び替えを視覚的に行えるため、使いやすく、インタラクティブなユーザー体験を提供できます。特に、動的なレイアウトを持つアプリケーションに最適です。
  2. 軽量かつ効率的なコードベース
    Swapyはシンプルで、パフォーマンスに優れています。HTML5 APIを使用することで、冗長なコードや不要なライブラリの追加を避けることができ、コードベース全体を軽く保てます。
  3. 拡張性の高い設計
    Swapyは、基本的な並び替え機能にとどまらず、サーバーとのデータ同期や、複数ゾーンへのドロップなど、柔軟な機能を実装できます。また、アニメーションやカスタムイベントを追加して、よりリッチなインターフェースを作成することも可能です。

 

Swapyの使用例、コードの解説

See the Pen
Untitled
by Rin (@rinblog0408)
on CodePen.

まずは、Swapyの基本的な実装例を紹介します。以下は、カードをドラッグアンドドロップで並び替える簡単なサンプルコードです。

HTML

<div class="container">
  <div class="card" draggable="true">Card 1</div>
  <div class="card" draggable="true">Card 2</div>
  <div class="card" draggable="true">Card 3</div>
</div>

CSS

.container {
  display: flex;
  gap: 10px;
}

.card {
  width: 100px;
  height: 100px;
  background-color: #f2f2f2;
  text-align: center;
  line-height: 100px;
  border: 1px solid #ccc;
  cursor: grab;
}

JavaScript

const cards = document.querySelectorAll('.card');
let draggedCard = null;

cards.forEach(card => {
  card.addEventListener('dragstart', function (e) {
    draggedCard = card;
    setTimeout(() => {
      card.style.display = 'none';
    }, 0);
  });

  card.addEventListener('dragend', function () {
    setTimeout(() => {
      draggedCard.style.display = 'block';
      draggedCard = null;
    }, 0);
  });

  card.addEventListener('dragover', function (e) {
    e.preventDefault();
  });

  card.addEventListener('drop', function (e) {
    e.preventDefault();
    if (draggedCard !== this) {
      const allCards = Array.from(document.querySelectorAll('.card'));
      const droppedIndex = allCards.indexOf(this);
      const draggedIndex = allCards.indexOf(draggedCard);
      
      if (droppedIndex > draggedIndex) {
        this.parentNode.insertBefore(draggedCard, this.nextSibling);
      } else {
        this.parentNode.insertBefore(draggedCard, this);
      }
    }
  });
});

 

このコードでは、カードをドラッグして別のカードの前後に移動できるように設定しています。簡単なイベントリスナーを使い、ドラッグ開始、ドロップ位置の特定、そして挿入処理を行っています。

 

応用例

Swapyを使ったドラッグアンドドロップ機能は、さまざまな形で応用できます。ここでは、3つの具体的な応用例を紹介します。

1. 複数のドロップゾーンの実装

See the Pen
Swapy02
by Rin (@rinblog0408)
on CodePen.

タスク管理アプリのように、複数の列に要素をドラッグして移動できるシステムを実装します。下記のコードは、「To Do」「In Progress」「Done」の列にカードを自由に移動させる例です。

HTML

<div class="container">
  <div class="column" id="todo">
    <h3>To Do</h3>
    <div class="card" draggable="true">Task 1</div>
    <div class="card" draggable="true">Task 2</div>
  </div>

  <div class="column" id="inprogress">
    <h3>In Progress</h3>
  </div>

  <div class="column" id="done">
    <h3>Done</h3>
  </div>
</div>
  • .container: すべてのカラムを横並びに配置するための親要素です。
  • .column: 各カラム(To Do、In Progress、Done)を表します。カラム内には、ドラッグ可能なタスクカードが含まれています。

CSS

body {
  font-family: Arial, sans-serif;
  display: flex;
  flex-direction: column;
  align-items: center;
  margin: 20px;
}

.container {
  display: flex;
}

.column {
  width: 200px;
  min-height: 300px;
  background-color: #f0f0f0;
  border: 2px solid #ccc;
  padding: 10px;
  margin: 0 10px;
}

.card {
  background-color: #ffffff;
  border: 1px solid #ccc;
  padding: 10px;
  margin: 10px 0;
  text-align: center;
  cursor: grab;
  transition: transform 0.2s ease;
}

.card.dragging {
  opacity: 0.5;
  transform: scale(1.1);
}

JavaScript

const cards = document.querySelectorAll('.card');
const columns = document.querySelectorAll('.column');

let draggedCard = null;

cards.forEach(card => {
  card.addEventListener('dragstart', function () {
    draggedCard = card; // ドラッグが開始されたカードを設定
    setTimeout(() => {
      card.classList.add('dragging'); // ドラッグ中のカードにクラスを追加
    }, 0);
  });

  card.addEventListener('dragend', function () {
    setTimeout(() => {
      card.classList.remove('dragging'); // ドラッグが終了したらクラスを削除
      draggedCard = null; // ドラッグしたカードをnullに設定
    }, 0);
  });
});

columns.forEach(column => {
  column.addEventListener('dragover', function (e) {
    e.preventDefault(); // ドラッグオーバーを許可
  });

  column.addEventListener('drop', function (e) {
    e.preventDefault(); // ドロップイベントのデフォルト動作を防止
    if (draggedCard) {
      this.appendChild(draggedCard); // ドロップゾーンにカードを追加
      draggedCard = null; // ドラッグしたカードをnullに設定
    }
  });
});
  • ドラッグ開始 (dragstart): カードがドラッグされると、そのカードをdraggedCard変数に設定し、ドラッグ中のスタイルを適用します。
  • ドラッグ終了 (dragend): ドラッグが終了すると、ドラッグ中のスタイルを削除し、draggedCardnullに設定します。
  • ドラッグオーバー (dragover): カラムにドラッグオーバーされるとき、デフォルトの動作を防ぐことで、ドロップを許可します。
  • ドロップ (drop): ドロップイベントが発生すると、draggedCardが存在する場合に、そのカードを現在のカラムに追加します。

 

2. アニメーションの追加

See the Pen
Swapy03
by Rin (@rinblog0408)
on CodePen.

カードを移動させる際に、スムーズなアニメーションを追加して、視覚的な効果を向上させます。

HTML

<div class="container">
  <div class="column" id="todo">
    <h3>To Do</h3>
    <div class="card" draggable="true">Task 1</div>
    <div class="card" draggable="true">Task 2</div>
  </div>

  <div class="column" id="inprogress">
    <h3>In Progress</h3>
  </div>

  <div class="column" id="done">
    <h3>Done</h3>
  </div>
</div>

CSS

body {
  font-family: Arial, sans-serif;
  display: flex;
  flex-direction: column;
  align-items: center;
  margin: 20px;
}

.container {
  display: flex;
}

.column {
  width: 200px;
  min-height: 300px;
  background-color: #f0f0f0;
  border: 2px solid #ccc;
  padding: 10px;
  margin: 0 10px;
  transition: background-color 0.3s ease; /* カラムの背景色のアニメーション */
}

.column:hover {
  background-color: #e0e0e0; /* ホバー時の背景色 */
}

.card {
  background-color: #ffffff;
  border: 1px solid #ccc;
  padding: 10px;
  margin: 10px 0;
  text-align: center;
  cursor: grab;
  transition: transform 0.2s ease; /* カードの変形に対するアニメーション */
}

.card.dragging {
  opacity: 0.5;
  transform: scale(1.1);
  transition: opacity 0.2s ease, transform 0.2s ease; /* ドラッグ中のアニメーション */
}

.card.move {
  transition: transform 0.5s ease; /* カードの移動に対するアニメーション */
}
  • transition: CSSのtransitionプロパティを使用して、カードが変形する際のアニメーション効果を設定しています。これにより、カードが移動する際に滑らかに見えるようになります。
  • .draggingクラス: カードがドラッグされている間に適用され、透明度を下げて拡大することで、視覚的なフィードバックを提供します。
  • .moveクラス: カードがドロップされたときに適用され、スムーズに移動するアニメーションを実現します。

JavaScript

const cards = document.querySelectorAll('.card');
const columns = document.querySelectorAll('.column');

let draggedCard = null;

cards.forEach(card => {
  card.addEventListener('dragstart', function () {
    draggedCard = card;
    setTimeout(() => {
      card.classList.add('dragging');
    }, 0);
  });

  card.addEventListener('dragend', function () {
    setTimeout(() => {
      card.classList.remove('dragging');
      draggedCard = null;
    }, 0);
  });
});

columns.forEach(column => {
  column.addEventListener('dragover', function (e) {
    e.preventDefault();
  });

  column.addEventListener('drop', function (e) {
    e.preventDefault();
    if (draggedCard) {
      this.appendChild(draggedCard);
      draggedCard.classList.add('move'); // 移動アニメーションを追加
      setTimeout(() => {
        draggedCard.classList.remove('move'); // アニメーションのクラスを削除
      }, 500); // アニメーションが終了するまで待つ
      draggedCard = null; // draggedCardをnullにして、次のドラッグ用に準備
    }
  });
});
  • draggedCard.classList.add('move'): カードがドロップされたときに、移動アニメーションを追加します。
  • setTimeout: アニメーションが終了するまで待ってから、moveクラスを削除します。これにより、次回のドラッグアンドドロップの際にアニメーションを再び適用できるようになります。

 

3. 並び替えた順番をサーバーに保存する処理

並び替え後にその順番をサーバーに保存する処理を追加する例です。fetch関数を使用して、並び替えた順序をデータベースに送信します。

JavaScript

function saveCardOrder() {
  const cardOrder = Array.from(document.querySelectorAll('.card')).map(card => card.textContent);
  console.log('Saving order:', cardOrder);

  fetch('/saveOrder', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ order: cardOrder })
  }).then(response => response.json())
    .then(data => console.log('Order saved successfully:', data))
    .catch(error => console.error('Error saving order:', error));
}

 

これらの応用例を使うことで、Swapyの基本的な機能をさらに強化し、サーバーとのデータ同期や複数ゾーン対応、アニメーションの追加など、より複雑でリッチなインターフェースを実現できます。

ABOUT ME
りん
沖縄から福岡に移住。QA/Webデザイナー/SE/フリーランス/SE人事など。趣味や好きなことをブログにまとめてます。