HTML 5 error painting canvas

I am trying to create my first almost complex map based on vectors drawn in html 5 canvas.

Works well, except for scaling. I noticed the following:

  • Everything works fine in Firefox (except the mouse wheel, but it's just for testing)
  • In Chrome zoom mode with the mouse wheel and zoom factor <1, ​​it looks like the image is duplicated every time it is drawn
  • In Android and iOS, with the help of magnification gestures, the biggest problems arise: every time the image is repainted, the images are duplicated.

At first I thought it was my fault, maybe the canvas is not being cleaned. But after some testing, β€œghosts” disappear and new ghosts appear.

It would be great if someone could help.

Code - HTML:

<div> <div style="position:absolute;top:30px;z-index:102;"> <canvas id="canvas" width="1386" height="747" style="position:absolute;"></canvas> </div> <div style="position:absolute; top:30px;" id="debugText">Debug</div> <div style="position:absolute;top:30px; visibility: hidden;"> <canvas id="debugCanvas" width="1386" height="747"></canvas> </div> <div style="position:absolute; left: 200px;z-index:99;" id="debugContols"> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <a href="#" onClick="javascript:zoomIn(0, 0, 0.5);">Zoom 0.5</a> <a href="#" onClick="javascript:zoomIn(0, 0, 2);">Zoom 2</a> <a href="#" onClick="javascript:zoomIn(0, 0, 3);">Zoom 3</a> </div> </div> 

Code - Javascript:

 var canvas = document.getElementById("canvas"); var debugCanvas = document.getElementById("debugCanvas"); var ctx = canvas.getContext("2d"); var ctxDebug = debugCanvas.getContext("2d"); var context = ctx; var scale = 1; var originx = 0; var originy = 0; function draw() { // plne/Straen [find drawing in fiddle, since its too long] } this.onmousewheel = function(event) { var mousex = event.clientX - canvas.offsetLeft; var mousey = event.clientY - canvas.offsetTop; var wheel = event.wheelDelta/120;//n or -n //according to Chris comment var zoom = Math.pow(1 + Math.abs(wheel)/2 , wheel > 0 ? 1 : -1); zoomIn(mousex, mousey, zoom); return; } var isZooming = false; var distances = new Array(); function touchStart(e) { preventDefaultScroll(e); if(e.touches.length > 1 && isZooming == false) { var touch1 = event.touches[0]; var touch2 = event.touches[1]; x1 = touch1.pageX; y1 = touch1.pageY; x2 = touch2.pageX; y2 = touch2.pageY; var diffX = x2 - x1; var diffY = y2 - y1; var centerX = x1 + diffX/2; var centerY = y1 + diffY/2; //$("#debugText").text(centerX + " " + centerY); debugCanvas.width = debugCanvas.width; ctxDebug.beginPath(); ctxDebug.arc(centerX, centerY, 20, 0, 2 * Math.PI, false); ctxDebug.fillStyle = 'green'; ctxDebug.fill(); ctxDebug.lineWidth = 5; ctxDebug.strokeStyle = '#003300'; ctxDebug.stroke(); zoomCenterX = centerX; zoomCenterY = centerY; var touch1 = event.touches[0]; var touch2 = event.touches[1]; x1 = touch1.pageX; y1 = touch1.pageY; x2 = touch2.pageX; y2 = touch2.pageY; var distanz = dist(x1,y1,x2,y2); lastDistance = distanz; distanceInterval = setInterval(checkDistance,50); isZooming = true; } } var distanceInterval; var zoomCenterX; var zoomCenterY; var lastDistance = 0; function checkDistance() { $("#debugText").text("checkDist"); if(distances.length == 0) return; var distanceGesamt = 0; for(var i = 0; i < distances.length; i++) { distanceGesamt += distances[i]; } var distanceDurchschnitt = distanceGesamt / distances.length; var curDist = distanceDurchschnitt - lastDistance; var zoomFac = 1 + (curDist / 100); $("#debugText").text(distanceDurchschnitt + " " + zoomFac); distances = new Array(); zoomIn(zoomCenterX, zoomCenterY, zoomFac) lastDistance = distanceDurchschnitt; } function touchEnd(e) { if(e.touches.length < 2) { isZooming = false; clearInterval(distanceInterval); } } function dist(x1,y1,x2,y2) { return Math.sqrt((x1 -= x2) * x1 + (y1 -= y2) * y1); } function touchMove(e) { if(isZooming) { var touch1 = event.touches[0]; var touch2 = event.touches[1]; x1 = touch1.pageX; y1 = touch1.pageY; x2 = touch2.pageX; y2 = touch2.pageY; var distanz = dist(x1,y1,x2,y2); distances.push(distanz); } } function preventDefaultScroll(event) { event.preventDefault(); window.scroll(0,0); return false; } canvas.addEventListener('gestureend', function(e) { if (e.scale < 1.0) { // User moved fingers closer together } else if (e.scale > 1.0) { // User moved fingers further apart } }, false); function zoomIn(mousex, mousey, zoom) { canvas.style.display = 'none'; context.translate( originx, originy ); context.scale(zoom,zoom); context.translate( -( mousex / scale + originx - mousex / ( scale * zoom ) ), -( mousey / scale + originy - mousey / ( scale * zoom ) ) ); originx = ( mousex / scale + originx - mousex / ( scale * zoom ) ); originy = ( mousey / scale + originy - mousey / ( scale * zoom ) ); scale *= zoom; requestAnimFrame(function() { context.clearRect(0,0,canvas.width,canvas.height); draw(); }); canvas.style.display = 'block'; //context.clearRect(0,0,canvas.width,canvas.height); } $(document).ready(function() { draw(); addEventListener('touchstart', touchStart, true); addEventListener('touchmove', touchMove, true); addEventListener('touchend', touchEnd, true); addEventListener('touchcancel', touchEnd, true); }); window.requestAnimFrame = (function(){ return window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || function( callback ){ window.setTimeout(callback, 1000 / 60); }; })(); 

Here is the violin

+7
source share
2 answers

So, the first problem I found in your code was that you used context instead of ctx when you did context conversions, so I fixed this.

Then I moved clearRect to the beginning of the function and then painted after the context was already transformed.

Finally, there was a slightly duplicate map at the bottom, so I changed clearRect to double-clear the actual canvas.height*2 canvas height.

Jsfiddle

+5
source

Two thoughts:

First: Wow, the good use of paths for laying out maps is very extensive!

Secondly: since you control the scaling with scrollwheel / touch, not mouseclicks, you need to remove the requestAnimFrame animation.

The animation should gradually approach the point where the user is mouseclicked.

+3
source

All Articles