JavaScript
iOS
Safari
ForceTouch

iOS 9 Mobile Safari was enabled force touch event

More than 3 years have passed since last update.

iPhone 6s と force touch event

iPhone 6s から ForceTouchEvent が利用可能になりました。
機能を体感できる 簡単なお絵かきデモ を作成しました。 iPhone 6s の Safari でアクセスし、指で画面をなぞってみてください。

Demo Code

デモのコードはこのようになります。

<!DOCTYPE html><html><head><title>ForceTouch Event demo</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<style>
html, body, ul { margin: 0; padding: 0; }
canvas { margin: 2px; padding: 0; }
.touchable { outline: 1px outset gray; }
.pallet { list-style: none; }
.pallet li { display: inline }
.pallet li button { width: 40px; height: 40px; border-radius: 32px; }
</style>
</head>
<body>
<canvas class="touchable" width="370" height="480"></canvas>
<ul class="pallet">
  <li><button style="background-color:pink"  onclick="changePenColor(this)"></button></li>
  <li><button style="background-color:red"   onclick="changePenColor(this)"></button></li>
  <li><button style="background-color:blue"  onclick="changePenColor(this)"></button></li>
  <li><button style="background-color:green" onclick="changePenColor(this)"></button></li>
  <li><button style="background-color:white" onclick="changePenColor(this)"></button></li>
  <li><button style="background-color:black" onclick="changePenColor(this)"></button></li>
</ul>

<script>
var ctx = null;
var pen = { x: 0, y: 0, color: "black" };

function changePenColor(target) {
  pen.color = window.getComputedStyle(target, null).backgroundColor;
  ctx.fillStyle = pen.color;
}
window.onload = function() {
  var node = document.querySelector("canvas");
  node.addEventListener("touchstart", handleEvent);
  node.addEventListener("touchmove", handleEvent);
  node.addEventListener("touchend", handleEvent);
  ctx = node.getContext("2d");
};

function handleEvent(event) {
  var type  = event.type;
  var touch = event.touches[0];
  var force = touch ? touch.force || 0.0 : 1.0;
  var x     = touch ? touch.clientX : pen.x;
  var y     = touch ? touch.clientY : pen.y;

  pen.x = x;
  pen.y = y;
  event.preventDefault();

  switch (type) {
  case "touchstart": drawCircle(ctx, x, y, force); break;
  case "touchmove":  drawCircle(ctx, x, y, force); break;
  case "touchend":   drawCircle(ctx, x, y, 0.0);   break;
  }
}

function drawCircle(ctx, x, y, force) {
  var radius = force * 20 + 4;
  var alpha  = force * 0.4;
  alpha = alpha < 0.1 ? 0.1 : alpha;

  ctx.globalAlpha = alpha;
  ctx.beginPath();
  ctx.arc(x, y, radius, 0, Math.PI * 2, false);
  ctx.fill();
  ctx.closePath();
}
</script>

</body></html>

勘所

ForceTouch イベントは、touchstart/touchmove の event.touches[n].force から取得できます。force の値は 0.0〜1.0 です。

node.addEventListener("touchstart", handleEvent);
node.addEventListener("touchmove", handleEvent);

function handleEvent(event) {
  var touch = event.touches[0];
  if (touch) {
    touch.force // -> 0.0 〜 1.0
  }
}

Chrome の DevTools 上でも、force と webkitForce の両方が取得できますので、iPhone 6s の実機がなくてもある程度の動作確認が可能です。
(感圧デバイスが無い場合は、force 値は常に1.0になります)

最後に

当初は、WWDC で発表された内容に従い、以下の新しいイベントを駆使してハンドリングしようとしましたが、全く動作せず…

  • webkitmouseforcecancelled
  • webkitmouseforcechanged
  • webkitmouseforceclick
  • webkitmouseforcedown
  • webkitmouseforceup
  • webkitmouseforcewillbegin

ドキュメントも色々とあたりましたが、「ルークよ… webkitmouseforcexxx を使うのだ…」といったドキュメントばかりです。謎が謎を生む展開です。

結局 WebKit のコードを確認して、正解にたどり着きました。
答えが分かってしまえばとっても簡単ですね! 参考になさってください。

(ε・ ワ ・)з May the Force be with you. (フォースと共にあらんことを)