Start · Sprachen · JavaScript · Referenz · DelegatedInkTrailPresenter

DelegatedInkTrailPresenter

Schnittstelle

Lässt den Kompositor des Betriebssystems den Tintenstrich zwischen zwei Zeigerereignissen vorausschauend zeichnen. Das verringert die sichtbare Verzögerung beim Zeichnen mit Stift oder Maus.

seit JavaScript Experimentell: Chromium 94+ (Chrome, Edge, Opera), Kategorie: webapi

Signatur

interface DelegatedInkTrailPresenter

Beschreibung

DelegatedInkTrailPresenter gehört zur experimentellen Ink API. Zeichnet eine Web-App auf einem <canvas>, hängt der gezeichnete Strich meist ein paar Bildschirmaktualisierungen hinter der Stift- oder Mausspitze her. Der Grund: Jedes pointermove-Ereignis muss erst den Hauptthread, das Rendering und den Kompositor durchlaufen, bevor der Strich auf dem Bildschirm erscheint. Mit dem Presenter übergibt die Seite den letzten Punkt, den sie selbst gezeichnet hat, an den Kompositor des Systems. Der Kompositor zeichnet dann das fehlende Stück bis zur aktuellen Zeigerposition direkt mit den neuesten Eingabedaten. Die Wirkung entspricht einer „Vorhersage“ des Strichs.

Eine Instanz erzeugt man nicht mit einem Konstruktor, sondern mit navigator.ink.requestPresenter(). Das Promise wird mit einem DelegatedInkTrailPresenter erfüllt. Die Schnittstelle hat zwei Bestandteile:

  • presentationArea (nur lesbar, Element | null): das Element, dessen Bereich den vom System gezeichneten Strich begrenzt. Ist der Wert null, gilt der gesamte Viewport.
  • updateInkTrailStartPoint(event, style): meldet dem Kompositor, bis zu welchem PointerEvent die App selbst gezeichnet hat und wie der vorläufige Strich aussehen soll (color, diameter). Rückgabewert: undefined.

Am besten ruft man updateInkTrailStartPoint() in jedem pointermove-Handler auf, nachdem das Segment auf dem Canvas gezeichnet wurde. Der vorläufige Strich des Kompositors verschwindet mit dem nächsten Frame und wird dabei durch den echten Strich der App ersetzt. Farbe und Durchmesser sollten deshalb genau zum eigenen Pinsel passen, sonst flackert die Darstellung sichtbar.

Parameter

Name Typ Default Beschreibung
$event Pflicht PointerEvent Parameter von updateInkTrailStartPoint(): das vertrauenswürdige Zeigerereignis (isTrusted === true), bis zu dessen Position die App den Strich bereits selbst gezeichnet hat. Meist das aktuelle pointermove-Ereignis.
$style Pflicht { color: string, diameter: number } Parameter von updateInkTrailStartPoint(): das Aussehen des vorläufigen Strichs. color ist eine CSS-Farbangabe (z. B. "#1e40af" oder "rgb(0 0 0)"), diameter die Strichstärke in CSS-Pixeln (größer als 0).

Beispiele

Presenter anfordern und Eigenschaft auslesen

const canvas = document.querySelector('#zeichenflaeche');

const initInk = async () => {
  if (!('ink' in navigator)) {
    console.log('Ink API nicht verfügbar – normales Zeichnen ohne Beschleunigung');
    return null;
  }
  const presenter = await navigator.ink.requestPresenter({ presentationArea: canvas });
  console.log(presenter instanceof DelegatedInkTrailPresenter);
  console.log(presenter.presentationArea === canvas);
  return presenter;
};

initInk();
true true

Zeichen-App mit beschleunigtem Tintenstrich

const canvas = document.querySelector('#zeichenflaeche');
const ctx = canvas.getContext('2d');
const stil = { color: '#1e40af', diameter: 4 };

ctx.strokeStyle = stil.color;
ctx.lineWidth = stil.diameter;
ctx.lineCap = 'round';

const start = async () => {
  const presenter = 'ink' in navigator
    ? await navigator.ink.requestPresenter({ presentationArea: canvas })
    : null;

  let zeichnet = false;

  canvas.addEventListener('pointerdown', (e) => {
    zeichnet = true;
    ctx.beginPath();
    ctx.moveTo(e.offsetX, e.offsetY);
  });

  canvas.addEventListener('pointermove', (e) => {
    if (!zeichnet) return;
    // Alle zusammengefassten Zwischenpunkte zeichnen
    for (const p of e.getCoalescedEvents?.() ?? [e]) {
      ctx.lineTo(p.offsetX, p.offsetY);
    }
    ctx.stroke();
    // Kompositor zeichnet ab hier bis zur aktuellen Stiftposition weiter
    presenter?.updateInkTrailStartPoint(e, stil);
  });

  const stopp = () => { zeichnet = false; };
  canvas.addEventListener('pointerup', stopp);
  canvas.addEventListener('pointerleave', stopp);
};

start();

Pinselwechsel: Stil synchron halten

const canvas = document.querySelector('#zeichenflaeche');
const ctx = canvas.getContext('2d');
const pinsel = { color: 'black', diameter: 2 };

const setzePinsel = (color, diameter) => {
  pinsel.color = color;
  pinsel.diameter = diameter;
  ctx.strokeStyle = color;
  ctx.lineWidth = diameter;
};

const presenterPromise = navigator.ink?.requestPresenter({ presentationArea: canvas });

canvas.addEventListener('pointermove', async (e) => {
  if (e.buttons !== 1) return;
  ctx.lineTo(e.offsetX, e.offsetY);
  ctx.stroke();
  const presenter = await presenterPromise;
  // Immer denselben Stil wie auf dem Canvas übergeben
  presenter?.updateInkTrailStartPoint(e, { ...pinsel });
});

document.querySelector('#rot').addEventListener('click', () => setzePinsel('red', 6));

// Wichtig · Fallstricke

  • Experimentell: Die API läuft nur in Chromium-basierten Browsern ab Version 94. Vor der Nutzung immer mit 'ink' in navigator prüfen und eine Ausweichlösung ohne Beschleunigung vorsehen.
  • Nur echte Ereignisse: updateInkTrailStartPoint() wirft einen Fehler, wenn das Ereignis synthetisch ist (isTrusted === false, etwa über dispatchEvent() erzeugt). Auch eine ungültige Farbe oder ein Durchmesser von 0 oder weniger führt zu einem Fehler.
  • presentationArea muss ein Element im selben Dokument sein. Außerhalb seiner Grenzen zeichnet der Kompositor nichts. Wird der Canvas verschoben oder skaliert, sollte der Strich trotzdem korrekt begrenzt bleiben. Transformationen und Sonderfälle wie devicePixelRatio sollte man trotzdem testen.
  • Der vorläufige Strich ist ein einfacher runder Strich in einer Farbe. Strukturpinsel, Deckkraftverläufe oder druckabhängige Strichstärken lassen sich damit nicht nachbilden. Weicht der eigene Pinsel stark davon ab, entstehen sichtbare Sprünge.
  • Den Aufruf nach dem eigenen Zeichnen im selben Handler ausführen, ohne zusätzliche Verzögerung (z. B. setTimeout). Sonst verpufft der Geschwindigkeitsvorteil.