スライダーバー2021/01/20

 スライダーバーによる入力を行うためには、直前までのポインタ座標を退避しておく必要があります。

 スライダーバーでリトルが左右に移動し、上ボタンでジャンプするサンプルです。

 スライダーバーの押下判定は、現在座標ではなく押下開始位置の座標を参照したほうが誤動作の減少を期待できます。
 一般的なスライダーバーやスクロールバーは限界以上にドラッグしたときも、そのドラッグ量が認識され続けて同じ量だけ戻さないとスライダーも戻りません。しかし今回のサンプルでは、指が滑ったかのように無視されるようにしています。
var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame || window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
var frame = document.getElementById("frame");
var chara = { node:document.getElementById("chara"), x:0, y:64, u:1, v:0, count:0 };
var keys = new Array(2);
var bUpdateKeyPress = false;
initialize();

function initialize()
{
	var ic, ib, nodes;
	// ボタン構造配列
	for(ic=0; ic<keys.length; ic++)
	{
		keys[ic] = { node:document.getElementById("key"+ic), bDown:0, ip:null, x:0, lx:0 };
		nodes = keys[ic].node.childNodes;
		for(ib=0; ib<nodes.length; ib++)
		{
			if(nodes[ib].getAttribute && nodes[ib].getAttribute("class") == "button"){ keys[ic].button = nodes[ib]; }
			if(nodes[ib].getAttribute && nodes[ib].getAttribute("class") == "slider"){ keys[ic].slider = nodes[ib]; }
		}
	}
	// イベント登録
	if(window.PointerEvent)
	{
		frame.addEventListener("pointerdown", onPress, false);
		document.addEventListener("pointermove", onPress, false);
		document.addEventListener("pointerup", onRelease, false);
	}else
	{
		frame.addEventListener("touchstart", onPress, false);
		frame.addEventListener("mousedown", onPress, false);
		document.addEventListener("touchmove", onPress, false);
		document.addEventListener("mousemove", onPress, false);
		document.addEventListener("touchend", onRelease, false);
		document.addEventListener("mouseup", onRelease, false);
	}
	frame.addEventListener("selectstart", function(event){ event.preventDefault(); }, false);
	frame.addEventListener("contextmenu", function(event){ event.preventDefault(); }, false);
	if(requestAnimationFrame){ requestAnimationFrame(onAnimationFrame) }else{ setInterval(onInterval, 16); }
	document.addEventListener("keydown", onKey, false);
	document.addEventListener("keyup", onKey, false);
}
// ポインタイベント
function onPress(event)
{
	var it, ic, ip, x, y, key, rect, mx;
	if(event.touches)
	{
		for(it=0; it<event.touches.length; it++){ onPress(event.touches[it]); }
		if(event.cancelable){ event.preventDefault(); }
		return;
	}
	ip = event.pointerId || event.identifier || 0;
	x = event.clientX; y = event.clientY;
	for(ic=0; ic<keys.length; ic++)
	{
		key = keys[ic];
		rect = key.node.getClientRects()[0];
		if(x >= rect.left && x < rect.right && y >= rect.top && y < rect.bottom)
		{
			if(key.ip != ip && ((event.type != "pointermove" || event.pointerType != "mouse") && event.type != "mousemove" || event.buttons > 0))
			{
				if(key.slider == null || event.type.indexOf("move") < 0)
				{
					key.ip = ip; key.x = 0; key.lx = x;
					if(!key.bDown){ key.bDown = true; bUpdateKeyPress = true; }
				}
			}
		}else
		{
			if(key.ip == ip && key.slider == null)
			{
				key.ip = null; key.x = 0;
				key.bDown = false; bUpdateKeyPress = true;
			}
		}
		if(key.ip == ip && key.slider != null)
		{
			mx = (x-key.lx)/24;
			if(mx != 0)
			{
				key.x = Math.min(1, Math.max(-1, key.x+mx)); key.lx = x; bUpdateKeyPress = true;
			}
		}
	}
}
function onRelease(event)
{
	var it, ic, ip, key;
	if(event.touches)
	{
		for(ic=0; ic<keys.length; ic++)
		{
			for(var it=0; it<event.touches.length; it++){ if(keys[ic].ip == event.touches[it].identifier){ break; } }
			if(it == event.touches.length){ onRelease({ identifier:keys[ic].ip }); }
		}
		if(event.cancelable){ event.preventDefault(); }
		return;
	}
	ip = event.pointerId || event.identifier || 0;
	for(ic=0; ic<keys.length; ic++)
	{
		key = keys[ic];
		if(key.ip == ip)
		{
			key.ip = null; key.x = 0;
			key.bDown = false; bUpdateKeyPress = true;
		}
	}
}
// キーイベント
function onKey(event)
{
	var ic = -1, id, code = event.code;
	if(code != undefined)
	{
		if(code == "ArrowLeft" || code == "Numpad4" || code == "KeyA"){ ic = 0; id = 0; }
		if(code == "ArrowRight" || code == "Numpad6" || code == "KeyD"){ ic = 0; id = 1; }
		if(code == "ArrowUp" || code == "Numpad8" || code == "KeyW" || code ==  "KeyZ" || code ==  "KeyX" || code == "Space"){ ic = 1; id = 2; }
	}else
	{
		code = event.keyCode;
		if(code == 37 || code == 100 || code == 65){ ic = 0; id = 0; }
		if(code == 39 || code == 102 || code == 68){ ic = 0; id = 1; }
		if(code == 38 || code == 104 || code == 87 || code == 88 || code ==  90 || code == 32){ ic = 1; id = 2; }
	}
	if(ic >= 0)
	{
		var key = keys[ic];
		var before = key.bDown*2 + key.x;
		key[id] = event.type == "keydown";
		key.x = (key[1]?1:0) - (key[0]?1:0);
		key.bDown = key[0] || key[1] || key[2];
		if(before != key.bDown*2 + key.x){ bUpdateKeyPress = true; }
		event.preventDefault();
	}
}
// 定期処理
function onAnimationFrame(){ onInterval(); requestAnimationFrame(onAnimationFrame); }
function onInterval()
{
	// ボタン押下チェック
	var ic;
	if(bUpdateKeyPress)
	{
		bUpdateKeyPress = false;
		for(ic=0; ic<keys.length; ic++)
		{
			if(keys[ic].button != null){ keys[ic].button.style.fillOpacity = keys[ic].bDown ? "1":""; }
			if(keys[ic].slider != null){ keys[ic].slider.setAttribute("x", keys[ic].x*24+32); }
		}
	}
	/*
	1フレームの処理
	*/
}