blob: 5dac89006634105587ad27f3953c8267287d68a9 [file] [log] [blame]
<!DOCTYPE html>
<html>
<head>
<title>Touch and Stylus Events</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<style>
body {
margin: 0;
padding: 0;
background-color: #eee;
}
#container {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
box-sizing: border-box;
}
#touches {
top: 0;
left: 0;
width: 100vw;
height: 100vh;
margin: 0;
padding: 0;
}
#touches div {
position: absolute;
top: 0;
left: 0;
width: 200px;
height: 200px;
margin-top: -100px;
margin-left: -100px;
will-change: transform;
transform: translate(0, 0);
}
#touches div svg.touch {
width: 200px;
height: 200px;
}
#touches div svg.touch circle {
cx: 100px;
cy: 100px;
fill: #cccf;
stroke: #aaa;
stroke-width: 1px;
}
#touches div.stylus svg.touch circle {
fill: #fccf;
stroke: #faa;
}
#touches div svg.touch line {
stroke: #666;
stroke-width: 4px;
stroke-linecap: round;
}
#touches div svg.altitude {
position: absolute;
top: -100px;
left: 50px;
width: 100px;
height: 100px;
}
#touches div svg.altitude line.x-axis {
stroke: green;
stroke-opacity: 0.6;
stroke-width: 2px;
}
#touches div svg.altitude line.angle {
stroke: #666;
stroke-width: 2px;
}
</style>
<script>
const SVGNS = "http://www.w3.org/2000/svg";
var container;
var touchObjects = {};
class TouchObject {
constructor(touch) {
this.element = document.createElement("div");
this._touchType = touch.touchType;
this.root = document.createElementNS(SVGNS, "svg");
this.root.setAttribute("class", "touch");
this.touchCircle = document.createElementNS(SVGNS, "circle");
this.root.appendChild(this.touchCircle);
if (this._touchType == "stylus") {
this.element.className = touch.touchType;
this.line = document.createElementNS(SVGNS, "line");
this.line.setAttribute("x1", 100);
this.line.setAttribute("y1", 100);
this._azimuthAngle = touch.azimuthAngle;
this.root.appendChild(this.line);
var altitudeDiagram = document.createElementNS(SVGNS, "svg");
altitudeDiagram.setAttribute("class", "altitude");
var screenAxis = document.createElementNS(SVGNS, "line");
screenAxis.setAttribute("class", "x-axis");
screenAxis.setAttribute("x1", 0);
screenAxis.setAttribute("y1", 100);
screenAxis.setAttribute("x2", 100);
screenAxis.setAttribute("y2", 100);
altitudeDiagram.appendChild(screenAxis);
this.altitudeLine = document.createElementNS(SVGNS, "line");
this.altitudeLine.setAttribute("class", "angle");
this.altitudeLine.setAttribute("x1", 1);
this.altitudeLine.setAttribute("y1", 99);
altitudeDiagram.appendChild(this.altitudeLine);
this._altitudeAngle = touch.altitudeAngle;
this.element.appendChild(altitudeDiagram);
}
this.element.appendChild(this.root);
this._x = 0;
this._y = 0;
this._size = 0;
touchObjects[touch.identifier] = this;
document.getElementById("touches").appendChild(this.element);
this._update();
}
get position() {
return {x: this._x, y: this._y};
}
set position(newPosition) {
this._x = newPosition.x;
this._y = newPosition.y;
this._update();
}
get size() {
return this._size;
}
set size(newSize) {
this._size = newSize;
this._update();
}
get azimuthAngle() {
return this._azimuthAngle;
}
set azimuthAngle(newAzimuthAngle) {
this._azimuthAngle = newAzimuthAngle;
this._update();
}
get altitudeAngle() {
return this._altitudeAngle;
}
set altitudeAngle(newAltitudeAngle) {
this._altitudeAngle = newAltitudeAngle;
this._update();
}
delete () {
document.getElementById("touches").removeChild(this.element);
this.element = null;
}
_update() {
this.element.style.transform = `translate(${this._x}px, ${this._y}px)`;
this.touchCircle.setAttribute("r", 50 + Math.round(this._size * 49));
if (this._touchType == "stylus") {
var x = Math.round(100 - Math.cos(this._azimuthAngle) * 80);
var y = Math.round(100 - Math.sin(this._azimuthAngle) * 80);
this.line.setAttribute("x2", x);
this.line.setAttribute("y2", y);
x = Math.round(Math.cos(this._altitudeAngle) * 99);
y = Math.round(100 - Math.sin(this._altitudeAngle) * 99);
this.altitudeLine.setAttribute("x2", x);
this.altitudeLine.setAttribute("y2", y);
}
}
}
window.addEventListener("load", init, false);
function handleTouchStart(event) {
event.preventDefault();
window.addEventListener("touchmove", handleTouchMove, false);
window.addEventListener("touchforcechange", handleTouchMove, false);
window.addEventListener("touchend", handleTouchEnd, false);
window.addEventListener("touchcancel", handleTouchEnd, false);
Array.from(event.changedTouches).forEach(function (touch) {
touchObjects[touch.identifier] = new TouchObject(touch);
});
updateTouches(event.touches);
}
function handleTouchMove(event) {
event.preventDefault();
updateTouches(event.touches);
}
function handleTouchEnd(event) {
event.preventDefault();
Array.from(event.changedTouches).forEach(function (touch) {
var touchObject = touchObjects[touch.identifier];
touchObject.delete();
touchObjects[touch.identifier] = undefined;
});
updateTouches(event.touches);
if (!event.touches || !event.touches.length) {
window.removeEventListener("touchmove", handleTouchMove);
window.removeEventListener("touchforcechange", handleTouchMove);
window.removeEventListener("touchend", handleTouchEnd);
window.removeEventListener("touchcancel", handleTouchEnd);
}
}
function updateTouches(touches) {
Array.from(touches).forEach(function (touch) {
var touchObject = touchObjects[touch.identifier];
if (touchObject) {
touchObject.position = {x: touch.pageX, y: touch.pageY};
if (touch.force) {
touchObject.size = touch.force;
touchObject.azimuthAngle = touch.azimuthAngle;
touchObject.altitudeAngle = touch.altitudeAngle;
}
}
});
}
function init() {
container = document.getElementById("container");
container.addEventListener("touchstart", handleTouchStart, false);
}
</script>
</head>
<body>
<div id="touches"></div>
<div id="container"></div>
</body>
</html>