What is a canvas in JavaScript?

HTML5 Canvas: Touch til Musekoordinater

27/01/2022

Rating: 3.92 (1661 votes)
Indholdsfortegnelse

Få Din HTML5 Canvas Tegneapplikation til at Virke på iPhone

Har du nogensinde oplevet, at din ellers velfungerende tegneapplikation på en HTML5 Canvas pludselig opfører sig mærkeligt på mobile enheder som iPhones? Det er et almindeligt problem, hvor touch-begivenheder ikke oversættes korrekt til de muse-lignende interaktioner, som din kode forventer. Typisk manifesterer dette sig som enkelte prikker i stedet for linjer, når du forsøger at tegne, og siden fortsætter med at rulle, selvom du forsøger at interagere med canvaset. Dette skyldes primært forskellen i, hvordan touch-begivenheder og muse-begivenheder leverer koordinatdata. Denne artikel vil guide dig trin for trin gennem processen med at konvertere touch-koordinater til musekoordinater, så din applikation kan give en problemfri oplevelse på tværs af alle enheder.

How to convert touch coordinates to mouse coordinates in HTML5 canvas?
In your putPoint function cancel the default events and propagation. e.preventDefault(); e.stopPropagation(); if (dragging) { .... Then look at Using Touch Events with the HTML5 Canvas to handle how to convert your touch coordinates to mouse coordinates. var touch = e.touches; var mouseEvent = new MouseEvent("mousemove", {

Forståelse af Forskellen: Touch vs. Muse Begivenheder

På desktop-computere interagerer brugere typisk med en canvas via en mus. Muse-begivenheder som mousedown, mousemove og mouseup leverer koordinatdata direkte via egenskaber som offsetX og offsetY. Disse egenskaber angiver den relative position af musemarkøren inden for det element, der udløste begivenheden (i dette tilfælde din canvas).

På mobile enheder, især smartphones og tablets, er interaktionen primært touch-baseret. Touch-begivenheder som touchstart, touchmove og touchend har en anderledes struktur. I stedet for en enkelt mus-pointer, kan der være flere fingre på skærmen samtidigt. Begivenhederne indeholder en touches-egenskab, som er en liste over alle aktive touch-punkter. Hvert touch-punkt i denne liste har sine egne koordinat-egenskaber, typisk clientX og clientY.

Problemet opstår, når din kode udelukkende er skrevet til at håndtere muse-begivenhedernes koordinatstruktur. Når en touch-begivenhed indtræffer, forsøger din kode at tilgå offsetX og offsetY, som enten ikke eksisterer eller indeholder forkerte værdier for touch-begivenheder. Dette resulterer i de observerede fejl.

Konvertering af Touch Koordinater: En Trinvis Guide

For at løse dette problem skal vi tilpasse vores getMousePosition funktion, så den kan håndtere både muse- og touch-begivenheder korrekt. Målet er at udtrække de korrekte X- og Y-koordinater uanset begivenhedstypen og derefter skalere dem til canvasets faktiske tegningsdimensioner.

1. Håndtering af Begivenhedsobjektet

Først skal vi tjekke, hvilken type begivenhed der er tale om. Hvis det er en touch-begivenhed, vil event.touches være tilgængelig og indeholde mindst ét touch-punkt. Vi er interesserede i det første touch-punkt (indeks 0), da brugere typisk kun bruger én finger ad gangen til at tegne.

Hvis det er en muse-begivenhed, vil event.offsetX og event.offsetY være de primære kilder til koordinater.

2. Udtrækning af Rå Koordinater

Vi skal nu definere, hvordan vi udtrækker de rå X- og Y-koordinater fra begge begivenhedstyper:

  • For Muse-begivenheder: Brug event.offsetX og event.offsetY.
  • For Touch-begivenheder: Brug event.touches[0].clientX og event.touches[0].clientY. Disse er viewport-koordinater, så vi skal justere dem baseret på canvasets position på siden.

3. Justering for Canvas Position og Skalering

Muse-begivenhedernes offsetX og offsetY er allerede relativt til canvasets indre indhold, hvilket er ideelt. Touch-begivenhedernes clientX og clientY er derimod relative til browserens viewport. For at få korrekte koordinater inden for canvaset skal vi tage højde for canvasets position på websiden. Vi kan bruge canvas.getBoundingClientRect() til at få canvasets position og dimensioner, som de vises i viewportet.

Formlen for at konvertere viewport-koordinater (clientX, clientY) til canvas-koordinater (x, y) er:

var rect = canvas.getBoundingClientRect(); var x = event.touches[0].clientX - rect.left; var y = event.touches[0].clientY - rect.top; 

Denne simple subtraktion korrigerer for canvasets offset fra viewportets top-venstre hjørne.

4. Skalering til Canvasets Tegningsdimensioner

Et andet vigtigt aspekt er, at canvasets CSS-dimensioner (canvas.style.width, canvas.style.height) kan være forskellige fra dets interne tegningsdimensioner (canvas.width, canvas.height). Hvis din canvas er skaleret via CSS (f.eks. til at fylde hele skærmen), skal du skalere de udtrækkede koordinater for at matche de interne tegningsdimensioner.

Den oprindelige kode indeholder allerede en god tilgang til dette:

var mouseX = e.offsetX * canvas.width / canvas.clientWidth | 0; var mouseY = e.offsetY * canvas.height / canvas.clientHeight | 0; 

Vi skal anvende en lignende logik på de touch-koordinater, vi har udvundet.

Opdateret `getMousePosition` Funktion

Lad os nu kombinere disse principper i en forbedret getMousePosition funktion:

function getMousePosition(evt) { var rect = canvas.getBoundingClientRect(); var clientX, clientY; if (evt.touches) { // Touch event clientX = evt.touches[0].clientX; clientY = evt.touches[0].clientY; } else { // Mouse event clientX = evt.clientX; clientY = evt.clientY; } var x = clientX - rect.left; var y = clientY - rect.top; // Skaler koordinaterne til canvasets interne dimensioner var mouseX = x * canvas.width / rect.width; var mouseY = y * canvas.height / rect.height; return { x: mouseX, y: mouseY }; } 

Vigtige punkter i den opdaterede funktion:

  • Vi bruger evt.touches til at identificere touch-begivenheder.
  • Vi bruger clientX og clientY fra det første touch-punkt.
  • Vi bruger canvas.getBoundingClientRect() til at få canvasets aktuelle position og dimensioner i viewportet.
  • Vi trækker rect.left og rect.top fra touch-koordinaterne for at få dem relativt til canvasets top-venstre hjørne.
  • Vi skalerer disse koordinater ved at gange med forholdet mellem canvasets interne dimensioner (canvas.width, canvas.height) og dets CSS-dimensioner (rect.width, rect.height). Dette sikrer, at tegningen er korrekt, selvom canvaset er skaleret.

Opdatering af Event Listeners

Med den opdaterede getMousePosition funktion skal vi sikre os, at alle relevante begivenheder bruger den. Den oprindelige kode har allerede en god start ved at tilføje listeners for touch-begivenheder:

canvas.addEventListener('mousedown', engage); canvas.addEventListener('mousemove', putPoint); canvas.addEventListener('mouseup', disengage); // Dokumentet lytter også for at fange mus-slip uden for canvas document.addEventListener('mouseup', disengage); canvas.addEventListener('contextmenu', disengage); // Touch events canvas.addEventListener('touchstart', engage, false); canvas.addEventListener('touchmove', putPoint, false); canvas.addEventListener('touchend', disengage, false); 

Den nøgle, der mangler, er at sikre, at putPoint, engage og disengage funktionerne alle kalder den *opdaterede* getMousePosition.

Lad os gennemgå den oprindelige kode og se, hvor getMousePosition bruges:

  • putPoint(e) kalder getMousePosition(e) to gange.
  • engage(e) kalder putPoint(e), som indirekte bruger getMousePosition(e).

Da getMousePosition nu er opdateret til at håndtere begge begivenhedstyper, skal selve event listeners ikke ændres, så længe de sender den korrekte begivenhed (e) videre.

Håndtering af Side-scrolling på Touch-enheder

Et andet problem, du nævner, er, at siden fortsætter med at rulle, når du prøver at tegne. Dette sker, fordi touch-begivenheder som standard tillader browseren at fortolke en "swipe" som en scroll-handling. For at forhindre dette, skal vi bruge event.preventDefault() inden for vores touch-begivenhedshåndteringsfunktioner, specifikt når vi tegner.

Den mest effektive måde at gøre dette på er i engage og putPoint funktionerne, når de håndterer touch-input.

Lad os modificere engage og putPoint:

var putPoint = function (e) { if (dragging) { // Forhindrer standard touch-adfærd (f.eks. scrolling) if (e.touches) { e.preventDefault(); } var pos = getMousePosition(e); context.lineTo(pos.x, pos.y); context.lineWidth = radius * 2; context.stroke(); context.beginPath(); context.arc(pos.x, pos.y, radius, 0, Math.PI * 2); context.fill(); context.beginPath(); context.moveTo(pos.x, pos.y); } }; var engage = function (e) { // Forhindrer standard touch-adfærd ved start af tegning if (e.touches) { e.preventDefault(); } dragging = true; putPoint(e); }; var disengage = function () { dragging = false; context.beginPath(); }; 

Ved at tilføje e.preventDefault() i engage og putPoint, fortæller vi browseren, at vi selv vil håndtere denne touch-begivenhed, og den skal derfor ikke udføre sin standard handling (som at scrolle siden).

Samlet Kodeeksempel

Her er et samlet overblik over den modificerede JavaScript-kode:

var clearButton = document.getElementById('doodle-bin'); var canvascontainer = document.getElementById('doodle-canvas-container'); var canvas = document.getElementById('doodle-canvas'); var context = canvas.getContext('2d'); // Juster radius baseret på containerens størrelse var radius = (canvascontainer.clientWidth + canvascontainer.clientHeight) / 150; var dragging = false; // Opdateret funktion til at få muse-/touch-position function getMousePosition(evt) { var rect = canvas.getBoundingClientRect(); var clientX, clientY; if (evt.touches) { // Touch event clientX = evt.touches[0].clientX; clientY = evt.touches[0].clientY; } else { // Mouse event clientX = evt.clientX; clientY = evt.clientY; } // Beregn koordinater relativt til canvas var x = clientX - rect.left; var y = clientY - rect.top; // Skaler til canvas' interne tegningsdimensioner var mouseX = x * canvas.width / rect.width; var mouseY = y * canvas.height / rect.height; return { x: mouseX, y: mouseY }; } // Canvas indstillinger context.mozImageSmoothingEnabled = false; context.imageSmoothingEnabled = false; // Sæt canvasets interne dimensioner (høj opløsning) canvas.width = 1280; canvas.height = 720; // Sæt canvasets CSS dimensioner (til at fylde containeren) canvas.style.width = '100%'; canvas.style.height = '100%'; /* CLEAR CANVAS */ function clearCanvas() { context.clearRect(0, 0, canvas.width, canvas.height); } clearButton.addEventListener('click', clearCanvas); var putPoint = function (e) { if (dragging) { // Forhindrer standard touch-adfærd (f.eks. scrolling) for touch-events if (e.touches) { e.preventDefault(); } var pos = getMousePosition(e); context.lineTo(pos.x, pos.y); context.lineWidth = radius * 2; context.stroke(); context.beginPath(); context.arc(pos.x, pos.y, radius, 0, Math.PI * 2); context.fill(); context.beginPath(); context.moveTo(pos.x, pos.y); } }; var engage = function (e) { // Forhindrer standard touch-adfærd ved start af tegning if (e.touches) { e.preventDefault(); } dragging = true; putPoint(e); }; var disengage = function () { dragging = false; context.beginPath(); }; // Mouse Events canvas.addEventListener('mousedown', engage); canvas.addEventListener('mousemove', putPoint); canvas.addEventListener('mouseup', disengage); // Lytter på dokumentet for at fange mouseup uden for canvas document.addEventListener('mouseup', disengage); canvas.addEventListener('contextmenu', disengage); // Touch Events canvas.addEventListener('touchstart', engage, false); canvas.addEventListener('touchmove', putPoint, false); canvas.addEventListener('touchend', disengage, false); 

Opsummering og Bedste Praksis

Ved at implementere disse ændringer sikrer du, at din HTML5 Canvas tegneapplikation kan håndtere både muse- og touch-input korrekt. Den opdaterede getMousePosition funktion er kernen i løsningen, da den pålideligt konverterer forskellige koordinattyper til et ensartet format. Derudover er brugen af event.preventDefault() afgørende for at forhindre uønsket side-scrolling på touch-enheder.

Husk altid at:

  • Test på rigtige enheder: Simulatorer kan være nyttige, men den bedste test foretages på faktiske smartphones og tablets.
  • Overvej flere touch-punkter: Hvis din applikation skal understøtte multi-touch-bevægelser (som zoom eller pan), skal du udvide din logik til at håndtere evt.touches-arrayet fuldt ud.
  • CSS vs. Canvas Dimensioner: Vær altid opmærksom på forskellen mellem canvasets CSS-dimensioner og dets interne width/height attributter. Korrekt skalering er essentiel for et skarpt billede.
  • Ydeevne: For meget komplekse tegninger eller hyppige opdateringer, overvej optimeringsteknikker som debouncing eller throttling af event-håndteringen.

Med disse justeringer bør din tegneapplikation nu fungere problemfrit på iPhones og andre touch-enheder, hvilket giver dine brugere en intuitiv og responsiv oplevelse.

Ofte Stillede Spørgsmål (FAQ)

Hvorfor tegner min canvas kun prikker på iPhone?
Dette skyldes sandsynligvis, at din getMousePosition funktion ikke korrekt fortolker touch-begivenhedernes koordinatstruktur, hvilket resulterer i, at tegningskommandoerne kun udføres ét sted i stedet for at følge brugerens bevægelse.
Hvordan forhindrer jeg, at siden scroller, når jeg tegner med fingeren?
Brug event.preventDefault() inden i dine touch-begivenhedshåndteringsfunktioner (især touchstart, touchmove) for at fortælle browseren, at du selv håndterer interaktionen.
Er offsetX og offsetY tilgængelige for touch-begivenheder?
Nej, offsetX og offsetY er typisk kun tilgængelige for muse-begivenheder. For touch-begivenheder skal du bruge clientX, clientY fra event.touches[0] og derefter justere dem baseret på canvasets position på siden.
Hvad er forskellen på clientX/clientY og pageX/pageY?
clientX/clientY er koordinater relativt til viewportet, mens pageX/pageY er koordinater relativt til hele dokumentet (inklusive eventuelt scroll). For at placere noget på en canvas, er clientX/clientY normalt at foretrække, da de er uafhængige af scroll-positionen.
Hvordan skal jeg håndtere canvasets skalering?
Sørg for at skaleringsfaktoren (forholdet mellem canvasets interne dimensioner og dets viste CSS-dimensioner) anvendes korrekt på de udtrækkede koordinater. Dette sikrer, at tegningen matcher brugerens input, uanset hvordan canvaset er skaleret på skærmen.

Hvis du vil læse andre artikler, der ligner HTML5 Canvas: Touch til Musekoordinater, kan du besøge kategorien Teknologi.

Go up