Start · Sprachen · JavaScript · Referenz · CanvasRenderingContext2D.isPointInPath()

CanvasRenderingContext2D.isPointInPath()

Methode

Prüft, ob der Punkt <code>(x, y)</code> innerhalb des aktuellen Pfads oder eines übergebenen <code>Path2D</code>-Objekts liegt, und gibt <code>true</code> oder <code>false</code> zurück.

seit JavaScript Baseline: alle Browser (Path2D-Variante seit ca. 2 Kategorie: webapi

Signatur

CanvasRenderingContext2D.prototype.isPointInPath([path, ] x, y [, fillRule])

Beschreibung

isPointInPath() prüft, ob ein Punkt in der Füllfläche eines Pfads liegt. Ohne path-Argument wird der aktuelle Pfad des Kontexts geprüft, also der, der mit beginPath(), moveTo(), arc(), rect() usw. aufgebaut wurde. Alternativ kann man ein Path2D-Objekt übergeben. Das ist in der Praxis meist die bessere Wahl, weil sich damit mehrere Formen unabhängig voneinander speichern und prüfen lassen.

Typisch ist der Einsatz beim Hit-Testing: Man findet heraus, ob ein Mausklick oder eine Berührung eine gezeichnete Form getroffen hat, etwa in Diagrammen, Spielen, Editoren oder Karten. Weil das Canvas nur Pixel speichert und keine Objekte, ist das der Standardweg, um interaktive Formen umzusetzen.

Die Koordinaten x und y gelten im Koordinatensystem des Canvas-Bitmaps und werden von der aktuellen Transformation nicht beeinflusst. Der Pfad dagegen wird mit der Transformation ausgewertet, die beim Aufbau galt (beim aktuellen Pfad) bzw. die jetzt gilt (bei Path2D). Mausposition aus einem Event muss man deshalb in Canvas-Pixel umrechnen. Das gilt besonders, wenn das Canvas per CSS skaliert ist oder für hochauflösende Bildschirme (devicePixelRatio) hochgerechnet wurde.

Mit dem Parameter fillRule legt man fest, welche Regel entscheidet, was „innen“ ist: "nonzero" (Standard) oder "evenodd". Das ist wichtig bei Pfaden, die sich selbst schneiden oder Löcher haben, etwa ein Ring aus zwei Kreisen.

Parameter

Name Typ Default Beschreibung
$path Path2D aktueller Pfad Optionales Path2D-Objekt, das geprüft werden soll. Fehlt es, wird der aktuelle Pfad des Kontexts verwendet.
$x Pflicht number X-Koordinate des zu prüfenden Punkts in Canvas-Pixeln (unabhängig von der aktuellen Transformation).
$y Pflicht number Y-Koordinate des zu prüfenden Punkts in Canvas-Pixeln.
$fillRule "nonzero" | "evenodd" "nonzero" Füllregel, die festlegt, ob der Punkt innerhalb des Pfads liegt: "nonzero" (Winding-Regel ungleich null) oder "evenodd" (Gerade-Ungerade-Regel).

Rückgabewert

Typ
boolean
Beschreibung
true, wenn der Punkt in der Füllfläche des Pfads liegt, sonst false. Punkte direkt auf dem Rand gelten nach Spezifikation als innen.

Beispiele

Einfacher Test am aktuellen Pfad

const canvas = document.createElement('canvas');
canvas.width = 200;
canvas.height = 200;
const ctx = canvas.getContext('2d');

ctx.beginPath();
ctx.rect(50, 50, 100, 60);

console.log(ctx.isPointInPath(80, 70));   // innerhalb
console.log(ctx.isPointInPath(10, 10));   // außerhalb
true false

Klick-Erkennung mit Path2D und CSS-skaliertem Canvas

const canvas = document.querySelector('#chart');
const ctx = canvas.getContext('2d');

const shapes = [
  { name: 'Kreis', color: 'tomato', path: new Path2D() },
  { name: 'Quadrat', color: 'steelblue', path: new Path2D() }
];
shapes[0].path.arc(80, 80, 50, 0, Math.PI * 2);
shapes[1].path.rect(160, 40, 90, 90);

const draw = (hovered) => {
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  for (const s of shapes) {
    ctx.fillStyle = s === hovered ? 'gold' : s.color;
    ctx.fill(s.path);
  }
};

// Mausposition in Canvas-Pixel umrechnen (berücksichtigt CSS-Skalierung)
const toCanvasCoords = (event) => {
  const rect = canvas.getBoundingClientRect();
  return {
    x: (event.clientX - rect.left) * (canvas.width / rect.width),
    y: (event.clientY - rect.top) * (canvas.height / rect.height)
  };
};

canvas.addEventListener('pointermove', (event) => {
  const { x, y } = toCanvasCoords(event);
  const hit = shapes.findLast((s) => ctx.isPointInPath(s.path, x, y));
  canvas.style.cursor = hit ? 'pointer' : 'default';
  draw(hit);
});

canvas.addEventListener('click', (event) => {
  const { x, y } = toCanvasCoords(event);
  const hit = shapes.findLast((s) => ctx.isPointInPath(s.path, x, y));
  if (hit) console.log(`Angeklickt: ${hit.name}`);
});

draw();
Angeklickt: Kreis // bei Klick auf den Kreis

Ring mit Loch: nonzero vs. evenodd

const ctx = document.createElement('canvas').getContext('2d');

const ring = new Path2D();
ring.arc(100, 100, 80, 0, Math.PI * 2);
ring.arc(100, 100, 40, 0, Math.PI * 2); // innerer Kreis, gleiche Drehrichtung

// Punkt im Loch (Mittelpunkt)
console.log(ctx.isPointInPath(ring, 100, 100));             // nonzero
console.log(ctx.isPointInPath(ring, 100, 100, 'evenodd'));  // evenodd
// Punkt im Ring selbst
console.log(ctx.isPointInPath(ring, 100, 35, 'evenodd'));
true false true

// Wichtig · Fallstricke

  • Koordinaten umrechnen: event.offsetX/clientX sind CSS-Pixel. Ist das Canvas per CSS skaliert oder mit devicePixelRatio hochgerechnet, muss man mit canvas.width / rect.width multiplizieren, sonst liegen die Treffer daneben.
  • Transformationen: Der Testpunkt wird nicht transformiert. Bei Path2D wird die aktuelle Transformation auf den Pfad angewendet. Wurde die Form mit translate()/scale() gezeichnet, muss beim Test dieselbe Transformation aktiv sein (z. B. zwischen save() und restore()).
  • Nur Füllfläche: Die Methode ignoriert die Linienstärke. Für Treffer auf Linien und Konturen nimmt man isPointInStroke().
  • Aktueller Pfad ist flüchtig: Ohne Path2D wird der zuletzt aufgebaute Pfad geprüft. Nach dem nächsten beginPath() ist er weg. Für mehrere Formen sollte man deshalb Path2D verwenden.
  • Nicht endliche Werte (NaN, Infinity) für x/y ergeben false.
  • Kompatibilität: Die Grundform funktioniert in allen Browsern. Die Überladung mit Path2D und fillRule geht in allen modernen Browsern, aber nicht im Internet Explorer. Auch OffscreenCanvasRenderingContext2D bietet die Methode an.
  • Leistung: Bei sehr vielen Formen jede einzeln zu prüfen, kann langsam werden. Dann hilft eine Vorauswahl über Begrenzungsrechtecke (Bounding Boxes).