pointer info

by edwardsharp

HTML

<div id="app">
  <div id="controls">
    <button id="clear">Clear</button>
    <button id="toggleLog">Toggle Event Log</button>
    <span id="status">Waiting for pointer...</span>
  </div>

  <div id="testArea">
    <div id="crosshair"></div>

    <div id="output"></div>

    <div id="log"></div>
  </div>
</div>

CSS

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
  background: #111;
  color: #eee;
  font-family:
    -apple-system, BlinkMacSystemFont, 'SF Mono', Menlo, Monaco, monospace;
}

body {
  overflow: hidden;
}

#app {
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100%;
}

#controls {
  flex: 0 0 auto;
  padding: 10px;
  background: #222;
  border-bottom: 1px solid #444;
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
}

button {
  font: inherit;
  color: white;
  background: #444;
  border: 1px solid #666;
  border-radius: 6px;
  padding: 7px 12px;
}

button:active {
  background: #666;
}

#status {
  color: #aaa;
  font-size: 12px;
}

#testArea {
  flex: 1 1 auto;
  min-height: 0;

  /*
       * Critical for touch/stylus testing:
       * don't let the browser interpret the gesture as scrolling,
       * zooming, navigation, etc.
       */
  touch-action: none;

  position: relative;
  overflow: hidden;

  background:
    linear-gradient(rgba(255, 255, 255, 0.04) 1px, transparent 1px),
    linear-gradient(90deg, rgba(255, 255, 255, 0.04) 1px, transparent 1px);
  background-size: 25px 25px;
}

#crosshair {
  position: absolute;
  width: 30px;
  height: 30px;
  margin-left: -15px;
  margin-top: -15px;
  border: 1px solid #0ff;
  border-radius: 50%;
  pointer-events: none;
  display: none;
}

#crosshair::before,
#crosshair::after {
  content: '';
  position: absolute;
  background: #0ff;
}

#crosshair::before {
  width: 1px;
  height: 44px;
  left: 14px;
  top: -8px;
}

#crosshair::after {
  width: 44px;
  height: 1px;
  left: -8px;
  top: 14px;
}

#output {
  position: absolute;
  inset: 10px;
  pointer-events: none;
  overflow: hidden;
}

.event {
  position: absolute;
  left: 0;
  top: 0;
  min-width: 300px;
  max-width: min(650px, calc(100vw -...

JavaScript

"use strict";

const testArea = document.getElementById("testArea");
const output = document.getElementById("output");
const log = document.getElementById("log");
const crosshair = document.getElementById("crosshair");
const status = document.getElementById("status");

const MAX_LOG_LINES = 100;

document.getElementById("clear").addEventListener("click", () => {
  output.innerHTML = "";
  log.innerHTML = "";
  status.textContent = "Cleared";
});

document.getElementById("toggleLog").addEventListener("click", () => {
  log.hidden = !log.hidden;
});

/*
 * Properties defined by PointerEvent / MouseEvent / Event.
 *
 * We intentionally probe the properties rather than assuming that
 * every browser implements every one.
 */
const EVENT_PROPERTIES = [
  // Event
  [
    "Event",
    [
      "type",
      // "timeStamp",
      // "bubbles",
      // "cancelable",
      // "defaultPrevented",
      // "isTrusted",
      // "composed",
      // "eventPhase",
    ],
  ],

  // Pointer identity
  ["Pointer", ["pointerId", "pointerType", "isPrimary"]],

  // Position
  [
    "Position",
    [
      // "clientX",
      // "clientY",
      // "pageX",
      // "pageY",
      // "screenX",
      // "screenY",
      // "offsetX",
      // "offsetY",
      "movementX",
      "movementY",
    ],
  ],

  // Contact geometry
  ["Contact Geometry", ["width", "height", "pressure", "tangentialPressure"]],

  // Pen orientation
  [
    "Pen Orientation",
    ["tiltX", "tiltY", "twist", "altitudeAngle", "azimuthAngle"],
  ],

  // Buttons
  ["Buttons", ["button", "buttons"]],

  // Modifier keys
  ["Modifier Keys", ["ctrlKey", "shiftKey", "altKey", "metaKey"]],

  // Target / capture
  ["Target", ["target", "currentTarget"]],
];

/*
 * These are the Pointer Events we try to observe.
 *
 * pointerrawupdate is particularly interesting for high-frequency
 * stylus input because it can expose updates...