質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

2回答

953閲覧

マウスドラッグでアニメーション付きリスト入れ替えを実装したい

vnsa7221

総合スコア348

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2021/12/22 03:54

編集2021/12/22 04:08

前提・実現したいこと

JSにてマウスドラッグでリストの入れ替えを実装したい

発生している問題・エラーメッセージ

アニメーション付きでリスト入れ替えを実装したいがうまくいかない
アニメーションなしのリスト入れ替えは実装できている

該当のソースコード

html

1 <ul class="box"> 2 <li class="box__item" data-num="0"> 3 <input type="checkbox"> 4 <span>リスト1</span> 5 </li> 6 <li class="box__item" data-num="1"> 7 <input type="checkbox"> 8 <span>リスト2</span> 9 </li> 10 <li class="box__item" data-num="2"> 11 <input type="checkbox"> 12 <span>リスト3</span> 13 </li> 14 <li class="box__item" data-num="3"> 15 <input type="checkbox"> 16 <span>リスト4</span> 17 </li> 18 <li class="box__item" data-num="4"> 19 <input type="checkbox"> 20 <span>リスト5</span> 21 </li> 22 </ul>

scss

1* { 2 margin: 0; 3 padding: 0; 4} 5 6.box { 7 list-style: none; 8 padding: 10px; 9 10 &__item { 11 user-select: none; 12 background: #fff; 13 box-shadow: 0 0 2px 1px rgba(0, 0, 0, .2); 14 cursor: grab; 15 max-width: 500px; 16 padding: 10px; 17 margin-bottom: 10px; 18 transition: box-shadow .3s; 19 20 &.is-grab { 21 cursor: grabbing; 22 box-shadow: 0 0 10px 2px rgba(0, 0, 0, .15); 23 position: absolute; 24 z-index: 1; 25 } 26 27 &:last-of-type { 28 margin-bottom: 0; 29 } 30 } 31} 32 33.is-hidden { 34 // visibility: hidden; 35 color: red; 36}

js

1'use strict'; 2 3// 変数 4const box = document.querySelector('.box'); 5const item = document.querySelectorAll('.box__item'); 6let status = false; 7 8// 対象要素の初期情報格納obj 9let data = { 10 target: null, 11 diffX: 0, 12 diffY: 0, 13 cloneName: "" 14}; 15 16// 要素まとめ 17const util = { 18 // 対象要素のindex番号を取得 19 index(e) { 20 const parent = e.parentElement; // 対象要素の親要素 21 const siblings = parent.children; // li要素全て 22 const siblingsArr = [].slice.call(siblings); // li要素の配列 23 const idx = siblingsArr.indexOf(e); // 対象要素のindex 24 25 return idx; 26 }, 27 28 // クローン要素作成および差し込み 29 insertClone(target, idx) { 30 const cloneName = `itemClone_${Math.trunc(Math.random() * 10000)}`; // クローン要素のクラス名 31 const clone = target.cloneNode(true); // 対象要素のクローン要素を作成 32 const parent = target.parentElement; // 対象要素の親要素 33 const siblings = parent.children; // 子要素(li) 34 35 // クローン要素にクラス付与 36 clone.classList.add('is-hidden'); 37 clone.classList.add(cloneName); 38 39 // 対象要素の後ろにクローン要素を追加 40 siblings[idx].insertAdjacentElement('afterend', clone); 41 42 // クローンに付与するクラス名を返却 43 return cloneName; 44 }, 45 46 // 要素入れ替え 47 swap(target) { 48 const selfIdx = util.index(target); // ドラッグ要素のindex 49 const cloneIdx = selfIdx + 1; // ドラッグ要素のclone要素 50 const parent = target.parentElement; // ドラッグ要素の親要素 51 const items = parent.querySelectorAll('.box__item'); 52 const siblings = parent.querySelectorAll(`:scope > *:not(.is-grab):not(.${data.cloneName})`); // ドラッグ要素およびクローン要素以外のli 53 54 // ドラッグおよびクローン要素以外にて入れ替え処理を実施 55 for (let thatIdx = 0; thatIdx < siblings.length; thatIdx++) { 56 const targetW = target.offsetWidth; // ドラッグ要素の幅 57 const targetH = target.offsetHeight; // ドラッグ要素の高さ 58 const targetRect = target.getBoundingClientRect(); // ドラッグ要素の位置を画面左上を(0, 0)とした上での相対位置 59 const targetRectX = targetRect.left; // ドラッグ要素のX座標 60 const targetRectY = targetRect.top; // ドラッグ要素のY座標 61 const that = siblings[thatIdx]; // 入れ替え対象要素 62 const thatW = that.offsetWidth; // 入れ替え対象要素の幅 63 const thatH = that.offsetHeight; // 入れ替え対象要素の高さ 64 const thatRect = that.getBoundingClientRect(); // 入れ替え対象要素の位置を画面左上を(0, 0)とした上での相対位置 65 const thatRectX = thatRect.left; // 入れ替え対象要素のX座標 66 const thatRectY = thatRect.top; // 入れ替え対象要素のY座標 67 68 const thatRectYHalf = thatRectY + (thatH / 2); // 入れ替え対象要素の高さ中央 69 const hitX = thatRectX <= (targetRectX + targetW) && thatRectX + thatW >= targetRectX; // 水平方向にてドラッグ要素が入れ替え対象要素に当たっているかの判定 70 const hitY = targetRectY <= thatRectYHalf && (targetRectY + targetH) >= thatRectYHalf; // 垂直方向にてドラッグ要素が入れ替え対象要素に当たっているかの判定 71 const isHit = hitX && hitY; // ドラッグ要素が入れ替え対象要素に当たっているか判定 72 73 if (isHit) { 74 const siblingsAll = parent.children; // 子要素(li) 75 const clone = siblingsAll[cloneIdx]; // クローン要素 76 const time = 1000; 77 const move = targetH + 10; 78 79 console.log("idx", selfIdx, thatIdx); 80 81 // == ここで入れ替えアニメーションを実施する? == 82 // clone.style.transition = `transform ${time}ms`; 83 // that.style.transition = `transform ${time}ms`; 84 85 // 上にスライドした場合 86 // if(selfIdx > thatIdx) { 87 // clone.style.transform = `translateY(-${move * (selfIdx - thatIdx)}px)`; 88 // that.style.transform = `translateY(${move}px)`; 89 // } 90 // 下にスライドした場合 91 // else { 92 // clone.style.transform = `translateY(${move * (thatIdx - selfIdx + 1)}px)`; 93 // that.style.transform = `translateY(-${move}px)`; 94 // } 95 96 // ===== 終わったタイミングで入れ替えしないといけない ===== 97// document.addEventListener('transitionend', (e) => { 98// if(e.target === clone) { 99// // parent.insertBefore(e.target, selfIdx > thatIdx ? that : that.nextSibling); 100// } 101// }) 102 103 // クローン及びドラッグ要素の入れ替え 104 parent.insertBefore(clone, selfIdx > thatIdx ? that : that.nextSibling); 105 parent.insertBefore(target, clone); 106 107 break; 108 } 109 } 110 } 111} 112 113// マウスイベント 114const mouseEvent = { 115 // クリック時 116 down(e) { 117 // 並び替え不可の場合はreturn 118 if(!status) return; 119 120 const target = e.currentTarget; // クリック要素 121 const pageX = e.pageX; // クリック時の水平位置 122 const pageY = e.pageY; // クリック時の垂直位置 123 const targetW = target.offsetWidth; // クリック要素の幅 124 const targetRect = target.getBoundingClientRect(); // クリック要素の位置を画面左上を(0, 0)とした上での相対位置を取得 125 const targetRectX = targetRect.left; // クリック要素左端のX座標 126 const targetRectY = targetRect.top; // クリック要素上端のY座標 127 128 // クリック要素の初期情報をobjに格納 129 data.target = target; 130 data.diffX = pageX - targetRectX; 131 data.diffY = pageY - targetRectY; 132 data.cloneName = util.insertClone(target, util.index(target)); 133 134 // クリック要素のstyle設定, class付与 135 target.style.width = `${targetW}px`; 136 target.classList.add('is-grab'); 137 138 // マウスイベントを設定 139 window.addEventListener('mousemove', mouseEvent.move); 140 window.addEventListener('mouseup', mouseEvent.up); 141 }, 142 143 // ドラッグ時(要素を移動させる処理) 144 move(e) { 145 const target = data.target; // クリック時の対象要素 146 const pageX = e.pageX; // ドラッグ時の水平位置 147 const pageY = e.pageY; // ドラッグ時の垂直位置 148 const targetPosL = pageX - data.diffX; // 初期位置から水平方向への移動量 149 const targetPosT = pageY - data.diffY; // 初期位置から垂直方向への移動量 150 151 // 対象要素のstyleを変更(ドラッグについてくるようにさせる) 152 target.style.left = `${targetPosL}px`; 153 target.style.top = `${targetPosT}px`; 154 155 // 要素入れ替え処理 156 util.swap(target); 157 }, 158 159 // クリック終了時(要素、データの初期化) 160 up() { 161 const target = data.target; // クリック時の要素 162 const cloneSelector = `.${data.cloneName}`; // クローン要素のセレクタ 163 const clone = document.querySelector(cloneSelector); // クローン要素 164 165 // 要素、データ、イベントの初期化 166 data.cloneName = ''; 167 clone.remove(); 168 target.removeAttribute('style'); 169 target.classList.remove('is-grab'); 170 window.removeEventListener('mousemove', mouseEvent.move); 171 window.removeEventListener('mouseup', mouseEvent.up); 172 } 173} 174 175// li要素にイベント設定 176item.forEach((e) => { 177 e.addEventListener('mousedown', mouseEvent.down); 178});

試したこと

swap()内で入れ替え処理を実装しているので、transitionendでtransitionが終了後にinsertbeforeによる入れ替えを実施しようとしたが、入れ替え処理が複数回発火する関係でうまくいかない
Promiseを使うかも考えたが、どうもピンとこない

補足情報(FW/ツールのバージョンなど)

実現したいアニメーションのイメージとしては「https://php-archive.net/demo-swap-html-elements/」のようなもの
こちらはボタンクリックで入れ替えアニメーションが実行されるが、これをマウスドラッグで実現したい

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yambejp

2021/12/22 03:58

「アニメーション付き」とはなんでしょう?
guest

回答2

0

自己解決

https://www.youtube.com/watch?v=PJYFQYyzRgg&ab_channel=QixotlLFC
こちらのサイトを元に実装ができました。

投稿2022/03/18 02:04

vnsa7221

総合スコア348

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ライブラリを使ってはどうでしょうか。

SortableJS

投稿2021/12/24 06:33

Lhankor_Mhy

総合スコア36960

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問