JavaScript

JavaScriptのMath.tanh():入力に応じた移動量を滑らかに制限する

この記事でわかること

Math.tanh()で正負の入力を一定範囲の移動量へ変換する方法を解説。引数・戻り値、ブラウザーとNode.jsの実行手順、期待出力、入力検証や丸め誤差の注意点を学べます。

目的:大きな入力でも移動量が増えすぎないようにする

ドラッグ操作やポインターに反応する演出では、入力をそのまま移動量にすると、操作が大きいほど要素が遠くまで動いてしまいます。一方、境界で突然打ち切る方法では、上限付近で反応の変化が急になります。

Math.tanh()は、数値の双曲線正接を返す静的メソッドです。ゼロ付近では入力の変化を比較的よく伝え、絶対値が大きくなるほど出力の変化が小さくなります。この性質を使うと、上限付近で反応を緩やかにできます。

この記事では、正負の入力を最大約40ピクセルの移動量に変換します。上限へ近づく数値変換を扱うもので、時間に沿って動きを補間するアニメーション機能ではありません。

動作環境と互換性

2026年10月24日時点で確認したMDNでは、Math.tanh()は広く利用可能な機能に分類され、2015年7月からブラウザー間で利用可能と説明されています。現在のChrome、Edge、Firefox、Safariで使えます。古い組み込みブラウザーなどでは、利用前に機能の有無を確認してください。MDNのMath.tanh()

ブラウザーとNode.jsの両方で動作します。本記事のNode.js側の対象は22系・24系です。DOMや外部ライブラリーを使わないため、同じ計算コードを両環境で利用できます。Node.js公式の配布情報でも22系・24系がLTSとして掲載されています。Node.js公式のバージョン情報

ここで挙げたNode.jsのバージョンは記事の対象環境であり、APIが初めて導入されたバージョンを意味しません。

引数と戻り値

呼び出し方は次のとおりです。

JavaScript
Math.tanh(x);
  • x:双曲線正接を求める数値です。
  • 戻り値:計算結果を表すnumberです。
  • 0に対しては0、-0に対しては-0を返します。
  • Infinityに対しては1、-Infinityに対しては-1を返します。
  • NaNに対してはNaNを返します。

Mathのインスタンスを作る必要はありません。常にMath.tanh(x)という形で呼び出します。基本仕様と無限大・ゼロの動作は、MDNの引数・戻り値と使用例で確認できます。

出力範囲の考え方

数学上、有限の入力に対する値は-1より大きく、1より小さくなります。ただし、JavaScriptの浮動小数点演算では、十分に大きな有限値でも丸めによって1や-1になることがあります。「有限値なら端点には絶対に到達しない」という判定には使わないでください。

数値はIEEE 754の倍精度形式で表現されるため、計算には精度の限界があります。MDNのNumber

コピーして実行するコード

次のコード全体で、入力から移動量への変換と、不正入力の処理を確認できます。画面上の要素を操作せず、計算結果だけを出力するため、ブラウザーでもNode.jsでも同じ手順で結果を比較できます。

JavaScript
(() => {
  "use strict";

  if (typeof Math.tanh !== "function") {
    console.log("この環境はMath.tanh()に対応していません");
    return;
  }

  function toOffset(input, scale = 100, limit = 40) {
    if (!Number.isFinite(input)) {
      throw new TypeError("inputは有限の数値にしてください");
    }
    if (!Number.isFinite(scale) || scale <= 0) {
      throw new RangeError("scaleは正の有限数にしてください");
    }
    if (!Number.isFinite(limit) || limit <= 0) {
      throw new RangeError("limitは正の有限数にしてください");
    }

    return limit * Math.tanh(input / scale);
  }

  const inputs = [-300, -100, 0, 100, 300];

  for (const input of inputs) {
    const offset = toOffset(input);
    console.log(`${input} -> ${offset.toFixed(2)}px`);
  }

  try {
    toOffset("100");
  } catch (error) {
    console.log(`${error.name}: ${error.message}`);
  }

  try {
    toOffset(100, 0);
  } catch (error) {
    console.log(`${error.name}: ${error.message}`);
  }
})();

ブラウザーでの実行方法

通常のWebページを開き、開発者ツールのConsoleを選びます。コードブロックの中身全体を貼り付けて実行してください。貼り付け制限が表示される場合は、使用するブラウザーの案内に従います。

コードを即時実行関数で囲んでいるので、繰り返し実行しても、同名のトップレベル変数を再宣言する問題を避けられます。ネットワーク通信や追加パッケージは不要です。

Node.jsでの実行方法

Node.jsの22系または24系を用意し、コード全体をUTF-8のmath-tanh-demo.jsとして保存します。そのファイルがあるディレクトリーで、次のコマンドを実行します。

Shell
node --version
node math-tanh-demo.js

最初のコマンドで対象バージョンであることを確認します。ファイルを引数に渡す実行方法は、Node.js公式のスクリプト実行手順に沿っています。

期待出力と計算の読み方

対応環境での期待出力は次のとおりです。公式の数値例と計算式から確認したもので、この記事の作成時に実機で実行した結果ではありません。

CODE
-300 -> -39.80px
-100 -> -30.46px
0 -> 0.00px
100 -> 30.46px
300 -> 39.80px
TypeError: inputは有限の数値にしてください
RangeError: scaleは正の有限数にしてください

inputが100なら、input / scaleは1です。Math.tanh(1)は約0.761594なので、40を掛けると約30.46になります。入力が300でも結果は約39.80にとどまります。

この例のscaleは反応の強さを決める値です。小さくすると、小さな入力でも上限へ近づきます。大きくすると、同じ入力に対する移動量が小さくなります。limitは出力の上限の大きさです。

ゼロ付近の感度は、おおよそlimit / scaleで考えられます。この例では約0.4なので、非常に小さな入力では入力の約0.4倍が移動量になります。上限と感度を別々の設定として持つと、調整の意図を説明しやすくなります。

失敗例と制約

暗黙の型変換で不正入力を見逃す

Math.tanh()は数値への変換を行います。例えば、数値文字列は計算され、nullはゼロとして扱われます。入力ミスを検出したい処理では、この動作をそのまま受け入れると問題が隠れる場合があります。

JavaScript
console.log(Math.tanh("0"));
console.log(Math.tanh(null));
console.log(Math.tanh(undefined));

期待出力は順に0、0、NaNです。数値への変換では、BigIntやSymbolはTypeErrorになります。MDNの数値への型変換

実用例のtoOffset()は、有限のnumberだけを受け付けます。そのため文字列の"100"も拒否します。フォーム入力を使う場合は、呼び出す前に空欄を確認し、数値へ変換したうえで検証してください。

不正な設定値を境界への到達と取り違える

scaleがゼロだと、ゼロ以外の入力を割った結果は正負の無限大になります。するとMath.tanh()の結果が端点になり、設定ミスなのに正常に制限できたように見えます。コードでは正の有限数かを先に確認しています。

極端に小さな正のscaleでも、除算結果が無限大へあふれる可能性があります。この例では端点への飽和として扱いますが、通常のUI設定では極端な値を許可しない設計が扱いやすいでしょう。

処理はすべて同期的です。awaitは不要で、不正入力による例外は呼び出し元のtry...catchで処理できます。

活用事例と導入時の判断

  • ドラッグの端で抵抗感を出すための、入力距離から表示距離への変換。
  • ポインターからの距離に応じて、カードの傾きを一定範囲に収める計算。
  • ゲームの入力値から、上限のある操作量を求める処理。

この変換は非線形なので、入力が2倍でも出力は2倍になりません。正確な距離や測定値の表示には向かず、操作感を調整する用途に適しています。また、入力値だけから結果が決まるため、時間方向の揺れを取り除く効果はありません。

導入時は、まずlimitで許容する最大移動量を決め、次にscaleを調整してください。元の入力値も保持し、表示に使う値だけを変換すると、操作の記録や後からの再調整が容易になります。

公式参考資料

PREFERRED SOURCES

Googleの優先するニュース提供元にSEの部屋を追加

Googleで、いつも読みたい情報源を選べます。登録可否はGoogleの画面で確認できます。

Googleの設定画面で確認する 新しいタブで開きます

候補にSEの部屋が表示されない場合は、まだ追加できません。

y.
WRITTEN BY

y_ymo10

SEの部屋で、HTML・CSSからJavaScript・React・Next.jsまで、Web制作の開発ノートを公開しています。

ほかの開発ノートを読む →