Очистить весь, преобразованный холст HTML5 при сохранении преобразования контекста

Я хочу масштабировать и панорамировать холст HTML5, преобразовывая контекст с помощью translate() и scale(), очищая холст, а затем перерисовывая. Обратите внимание, что я явно не вызываю save() и restore() вокруг моих преобразований.

Если я выполню стандартный ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height), то весь видимый холст не будет очищен; масштабирование или панорамирование может привести к тому, что этот начальный прямоугольник не будет точно закрывать область рисования.

Если я выполняю метод очистки, удобный для Webkit...

var w=canvas.width;
canvas.width = 0;
canvas.width = w;

... тогда кумулятивное преобразование контекста reset.

Как я могу наилучшим образом очистить весь контекст canvas без потери трансформации?

Ответ 1

Отслеживание всей информации о трансформации, как вы, по-видимому, делаете, - это то, что делали еще несколько других (например, cake.js и моя собственная библиотека для двоих). Я думаю, что делать это в значительной степени будет неизбежностью для любой большой библиотеки холстов.

Ilmari of cake.js даже жаловался на мозилла: https://bugzilla.mozilla.org/show_bug.cgi?id=408804

Вместо этого вы можете вызвать сохранение/восстановление вокруг вашего четкого метода:

// I have lots of transforms right now
ctx.save();
ctx.setTransform(1,0,0,1,0,0);
// Will always clear the right space
ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height);
ctx.restore();
// Still have my old transforms

Не будет ли это соответствовать вашему делу?

Ответ 2

Для тех, кто хочет отслеживать их полные трансформации контекста, здесь мой код для этого по требованию, для каждого контекста. Это предваряет использование, которое показывает, как очистить полный прямоугольник на основе преобразованных координат. Вы можете увидеть используемый вами код на моем веб-сайте.

window.onload = function(){
  var canvas = document.getElementsByTagName('canvas')[0];
  var ctx = canvas.getContext('2d');
  trackTransforms(ctx);
  function redraw(){
    var p1 = ctx.transformedPoint(0,0);
    var p2 = ctx.transformedPoint(canvas.width,canvas.height);
    ctx.clearRect(p1.x,p1.y,p2.x-p1.x,p2.y-p1.y);
    // ... 
  }
}


// Adds ctx.getTransform(), returning an SVGMatrix
// Adds ctx.transformedPoint(x,y), returning an SVGPoint
function trackTransforms(ctx){
  var svg = document.createElementNS("http://www.w3.org/2000/svg",'svg');
  var xform = svg.createSVGMatrix();
  ctx.getTransform = function(){ return xform; };

  var savedTransforms = [];
  var save = ctx.save;
  ctx.save = function(){
    savedTransforms.push(xform.translate(0,0));
    return save.call(ctx);
  };
  var restore = ctx.restore;
  ctx.restore = function(){
    xform = savedTransforms.pop();
    return restore.call(ctx);
  };

  var scale = ctx.scale;
  ctx.scale = function(sx,sy){
    xform = xform.scaleNonUniform(sx,sy);
    return scale.call(ctx,sx,sy);
  };
  var rotate = ctx.rotate;
  ctx.rotate = function(radians){
    xform = xform.rotate(radians*180/Math.PI);
    return rotate.call(ctx,radians);
  };
  var translate = ctx.translate;
  ctx.translate = function(dx,dy){
    xform = xform.translate(dx,dy);
    return translate.call(ctx,dx,dy);
  };
  var transform = ctx.transform;
  ctx.transform = function(a,b,c,d,e,f){
    var m2 = svg.createSVGMatrix();
    m2.a=a; m2.b=b; m2.c=c; m2.d=d; m2.e=e; m2.f=f;
    xform = xform.multiply(m2);
    return transform.call(ctx,a,b,c,d,e,f);
  };
  var setTransform = ctx.setTransform;
  ctx.setTransform = function(a,b,c,d,e,f){
    xform.a = a;
    xform.b = b;
    xform.c = c;
    xform.d = d;
    xform.e = e;
    xform.f = f;
    return setTransform.call(ctx,a,b,c,d,e,f);
  };
  var pt  = svg.createSVGPoint();
  ctx.transformedPoint = function(x,y){
    pt.x=x; pt.y=y;
    return pt.matrixTransform(xform.inverse());
  }
}