このサイトは、只今WEB業界で活躍中のデザイナー、プログラマーの方々の情報を集めたweb統合情報サイトです。

Archives Details

JavaScriptで、DOMを放り投げる処理

JavaScript

2024.07.27

どもです。

今回は、JavaScriptで、DOM(HTML)を放り投げる処理についてです。

先にお見せすると、この様な処理になります。

特段難しい処理などではないのですが、ある日メンバーに実装をお願いしたところ「できない。」と返ってきたので、え。なんでと思った次第ではありました。

過去にもこの処理について、大分昔に書いた覚えがありますが、「できない」と言われたのが結構ずっとひっかかっていて今回改めて記事化してみました。

自分が仕事で「できない」と言ってこなかったのもあるか(「技術的には可能です」)、そういってしまうとエンジニアの価値を自分で下げていることと同じだぞ。と思いましたが、老害と思われたくないので(笑)それは告げず。「じゃぁいいよ」と言ってしまった。

 

そもそも、お願いした処理としては、上記の円形の移動よりももっと簡易なもので、タブを持ち上げる処理なので、上下の移動しかないです。

※ ツマミを持ち上げると全体が持ち上がる形。つまむのと放り上げで移動。

クリックでの上下の移動は実装できたみたいなのですが、掴んで投げるがどうやら難しかったみたいです。

処理実装

それでは実装していきたいと思いますが、まず考え方として、

質量は存在しないものとします。なので大きさによって運動量の変化もしない形とするので、運動の法則よりより簡易なものとなります。

しかしながら摩擦力などがないとDOM(円形)が止まらないので、空気抵抗が存在するものとします。

また、重力も存在しないものとします。なので、開始位置、終了位置、時間、距離が算出できれば簡潔するような簡易な内容となっております。

何はともあれ、操作に扱う円形を生成します。(CSSは省略)

<body>
  <div id="circle"></div>
  <script> const circle = document.getElementById('circle'); </script>
</body>

それぞれの変数と空気抵抗の係数(任意の値)を用意します。

let isDragging = false;
let startX, startY, startLeft, startTop, startTime;
let endX, endY, endTime;
let velocityX, velocityY;
const airResistance = 0.01;

まずは、円形を掴む処理。円形のDOMに対してマウスダウンイベントをアタッチします。

circle.addEventListener('mousedown', (e) => {
  isDragging = true;
  startX = e.clientX;
  startY = e.clientY;
  startLeft = circle.offsetLeft;
  startTop = circle.offsetTop;
  startTime = Date.now();
  document.addEventListener('mousemove', onMouseMove);
  document.addEventListener('mouseup', onMouseUp);
});

続いて、ドラッグ(円形移動)の処理。

function onMouseMove(e) {
  if (isDragging) {
    const currentX = e.clientX;
    const currentY = e.clientY;
    circle.style.left = `${startLeft + currentX - startX}px`;
    circle.style.top = `${startTop + currentY - startY}px`;
  }
}

円形のどの位置を掴んでもそのまま移動できるように処理。

続いてマウスアップの処理

function onMouseUp(e) {
  if (isDragging) {
    isDragging = false;
    endX = e.clientX;
    endY = e.clientY;
    endTime = Date.now();
    const elapsedTime = (endTime - startTime) / 1000;
    velocityX = (endX - startX) / elapsedTime;
    velocityY = (endY - startY) / elapsedTime;
    document.removeEventListener('mousemove', onMouseMove);
    document.removeEventListener('mouseup', onMouseUp);
    applyPhysics();
  }
}

elapsedTimeは経過時間となり、/ 1000を行い秒に変換。

x, yと各それぞれマウスアップされた位置を最初にクリックした位置で引き、経過時間で割って速度を求めます。

最後に、applyPhysicsで円形の移動を行います。

function applyPhysics() {
  const move = () => {
    const currentLeft = parseFloat(circle.style.left);
    const currentTop = parseFloat(circle.style.top);
    if (Math.abs(velocityX) > 0.1 || Math.abs(velocityY) > 0.1) {
      velocityX *= (1 - airResistance);
      velocityY *= (1 - airResistance);
      circle.style.left = `${currentLeft + velocityX * 0.01}px`;
      circle.style.top = `${currentTop + velocityY * 0.01}px`;
      requestAnimationFrame(move);
    }
  };
  move();
}

x, yと各それぞれ速度を加算し、移動させています。空気抵抗である、airResistanceで速度を減速し、加速度がなくなるまでrequestAnimationFrameでmoveの再帰処理を行います。

と、言うことで出来上がり。

しかしながら現状だと、直線上の移動などは良いが、複雑な動きや長時間掴んで動かすとすぐに変な方向へと行く。

これは、最初に円形を掴んだ(クリック)した場所を元にしてしまっているので、その時点からの時間や経路は円形を離した後(マウスアップ)の円形の運動には直接関係はなく、円形を離す前(マウスアップ)の数秒前の経路が影響するからです。

そこで、数ステップサンプリングし、そこから円形のベクトルを算出したいと思います。

まず、経路のポイントを何個確保するかの定数sampleSizeと、そのデータを保持するpositionsを定義。

let positions = [];
const sampleSize = 5;

マウスダウンイベントでpositionsを初期化します。

positions = []; 

続いてマウスダウンイベントの処理を変更

function onMouseMove(e) {
  if (isDragging) {
    const x = e.clientX;
    const y = e.clientY;
    positions.push({ x, y, time: Date.now() });

    // 位置データを一定間隔で取得
    if (positions.length > sampleSize) {
      positions.shift();
    }
    circle.style.left = `${startLeft + x - startX}px`;
    circle.style.top = `${startTop + y - startY}px`;
  }
}

経路のポイントを何個確保するかを定義した定数sampleSizeの数だけサンプリング。

マウスアップイベントでpositionsの最後と最初を取得し、x, yのdelta、velocityを算出。

function onMouseUp(e) {
  if (isDragging) {
    isDragging = false;
    const endTime = Date.now();
    const elapsedTime = (endTime - startTime) / 1000;
    if (positions.length > 1) {
      const lastPos = positions[positions.length - 1];
      const firstPos = positions[0];
      const deltaX = lastPos.x - firstPos.x;
      const deltaY = lastPos.y - firstPos.y;
      velocityX = deltaX / elapsedTime;
      velocityY = deltaY / elapsedTime;
    }
    document.removeEventListener('mousemove', onMouseMove);
    document.removeEventListener('mouseup', onMouseUp);
    applyPhysics();
  }
}

applyPhysicsメソッドは前回と同様。

としたところで、記事最初のアニメの様な形となります。

 

これで完璧か。というとそうでもなく湾曲のマウスの経路を取ると、ちょっと意図した形にはならなかったりもするので、もっと作り込むのであれば、遠心力も考慮した計算なども含めてば良いでしょう。

次回、そちも考慮できた実装を紹介できればと思っております。

ではではぁ。

またまたぁ。

 

 

 

 

 

 

Comment

Related Article

JavaScriptで、DOMを放り投げる処理

2024.07.27

2022 VIVA JS World Cup 開幕!! 〜 Vue3で作るサッカーゲーム 〜

2022.12.24

OAuthのフローを可視化できるツールを作ってみました。

2020.05.17

令和の時代に、JavaScriptで Shift-JISファイル作成 全銀データフォーマットに対応する。

2020.03.03

インターネットにて世論調査を行う「世論Web」サービスを始めてみました。

2020.01.31

年末のレトロゲーム熱の際、ファミコンソフト一覧パッケージ作ってました。

2020.01.24

あと10日で「jsdo.it」のサービスが終わってしまう!! ソースダウンロードまだの方は急げぇ〜!

2019.10.21

正規表現 先読み後読み 論理積

2019.07.28

「二段階認証?」という方も 5分で覚える パスワードレス WebAuthnのまとめ

2019.07.07

上級者向け JavaScript 問題集 「javascript-questions」日本語翻訳担当してます。

2019.06.22

CATEGORY LIST

LATEST NEWS

Mac VSCodeで、SFML C++開発環境を作る。

C++

2024.09.09

Rust-SDL2 examplesをすべて試す

Rust

2024.09.01

JavaScriptで、DOMを放り投げる処理

JavaScript

2024.07.27

Rustで創る MOS 6502 CPU その2

Rust

2024.07.23

Rustで創る MOS 6502 CPU その1

Rust

2024.07.19

汎用 3D mesh/model viewerを求め。と、簡単に、FBXファイルをglTF(glb)に変換ツールを求め。

C++

2024.06.06

M1 Macで、OpenGL GLUTを使ってコンパイルする

C

2024.04.27

Rust - Actix Web mongo ユーザー登録 JWT認証

Rust

2024.03.24

Rust - Actix Web JWT 認証認可 APIの作成

Rust

2024.02.25

Rust - Actix Web × JSON 静的ファイルをAPIで返却

Rust

2024.01.19

Rust - Actix Web × MongoDB環境をサクッと起動

Rust

2024.01.18

5分で学ぶ RustでWave Function Collapse (波動関数崩壊アルゴリズム)

Rust

2024.01.15

RANKING

Follow

SPONSOR

現在、掲載募集中です。



Links

About Us

WEBデザイナーの、WEBデザイナーによる、WEBデザイナーの為のサイト。「みんなで書こう!」と仲間を募ってみたが、結局書くのは自分だけとなってしまいました。日々のメモを綴っていきます。

Entry Profile

Graphical FrontEnd Engineer
- Daisuke Takayama

MAD CITY 北九州市で生まれ育つ。20代はバンド活動に明け暮れ、ふと「webデザイナーになりたい。」と思い、デジタルハリウッド福岡校入学。卒業後、数々の賞を受賞、web業界をざわつかせる。
現在、主に、ゲーム制作中心に港区六本木界隈で活動中。

FOLLOW US