title: html 5 canvas drawing error
body: <p>I'm trying to build my first nearly-complex map based on vectors drawn in a html 5 canvas.</p>
  
  <p>It works well, except the scaling. I noticed the following:</p>
  
  <ul>
  <li>In Firefox everything works well (except the mousewheel, but thats just for testing)</li>
  <li>In Chrome zooming out with the mousewheel to a scale factor &lt; 1 it appears like the image gets duplicated every time its drawn</li>
  <li>In Android and iOS, using zoom gesture, there's the biggest problems: every time the image is repainted there are duplicated images. </li>
  </ul>
  
  <p>I thought at first it's my fault, maybe the canvas doesn't get cleared. But after some testing, the "ghosts" disappear and new ghosts appear.</p>
  
  <p>It would be so cool if someone could help.</p>
  
  <p>Code - HTML:</p>
  
  <pre><code>&lt;div&gt;
      &lt;div style="position:absolute;top:30px;z-index:102;"&gt;
          &lt;canvas id="canvas" width="1386" height="747" style="position:absolute;"&gt;&lt;/canvas&gt;
      &lt;/div&gt;
      &lt;div style="position:absolute; top:30px;" id="debugText"&gt;Debug&lt;/div&gt;
      &lt;div style="position:absolute;top:30px; visibility: hidden;"&gt;
          &lt;canvas id="debugCanvas" width="1386" height="747"&gt;&lt;/canvas&gt;
      &lt;/div&gt;
      &lt;div style="position:absolute; left: 200px;z-index:99;" id="debugContols"&gt;
          &amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
          &lt;a href="#" onClick="javascript:zoomIn(0, 0, 0.5);"&gt;Zoom 0.5&lt;/a&gt;
          &lt;a href="#" onClick="javascript:zoomIn(0, 0, 2);"&gt;Zoom 2&lt;/a&gt;
          &lt;a href="#" onClick="javascript:zoomIn(0, 0, 3);"&gt;Zoom 3&lt;/a&gt;
      &lt;/div&gt;
  &lt;/div&gt;
  </code></pre>
  
  <p>Code - Javascript:</p>
  
  <pre><code>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 &gt; 0 ? 1 : -1);
  
      zoomIn(mousex, mousey, zoom);
      return;
  }
  
  var isZooming = false;
  var distances = new Array();
  
  function touchStart(e) {
      preventDefaultScroll(e);
      if(e.touches.length &gt; 1 &amp;&amp; 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 &lt; 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 &lt; 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 &lt; 1.0) {
              // User moved fingers closer together
          } else if (e.scale &gt; 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);
                };
      })();
  </code></pre>
  
  <p><a href="http://jsfiddle.net/AWeyr/" rel="nofollow" title="Here's the fiddle">Here's the fiddle</a></p>