【问题标题】:Clear entire, transformed HTML5 Canvas while preserving context transform清除整个转换后的 HTML5 Canvas,同时保留上下文转换
【发布时间】:2011-07-28 10:38:06
【问题描述】:

我想通过使用translate()scale() 转换上下文、清除画布然后重绘来缩放和平移 HTML5 画布。请注意,我明确在我的转换周围调用save()restore()

如果我执行标准ctx.clearRect(0,0,ctx.canvas.width,ctx.canvas.height),那么整个可见画布将不会被清除;缩小或平移可能会导致此初始矩形不完全覆盖绘图区域。

如果我执行 Webkit 友好的清除方法...

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

...然后上下文的累积变换被重置。

如何在不丢失转换的情况下最好地清除整个画布上下文?

【问题讨论】:

  • 傻瓜式的方法是存储你的比例和平移,然后自己计算边界矩形,但希望有更好的方法。
  • @JakubHampl 这实际上就是我正在做的事情,因为我已经在跟踪转换并添加了从屏幕空间转换到画布空间的方法,但这似乎是一个很好的机会来指出找出这两种清理方法的问题,看看有没有更干净的东西。

标签: javascript html canvas


【解决方案1】:

像您一样跟踪所有转换信息大概是迄今为止其他几个人所做的(例如 cake.js 和我自己的库,对于两个)。我认为这样做对于任何大型画布库来说都是不可避免的。

cake.js 的 Ilmari 甚至向 mozilla 抱怨: https://bugzilla.mozilla.org/show_bug.cgi?id=408804

您可以改为围绕您的 clear 方法调用 save/restore:

// 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

这不能满足你的情况吗?

【讨论】:

  • 我真傻;我试图考虑如何获得单位矩阵,但正在考虑需要当前变换来计算逆然后乘以它。是的,这应该做得很好。
  • 很高兴我能帮上忙。我还想感谢您对 S.O. 上的画布主题所做的所有贡献
【解决方案2】:

对于那些关心跟踪其完整上下文转换的人,这是我的代码,用于根据需要、每个上下文执行此操作。这以显示如何根据转换后的坐标清除整个矩形的用法作为开头。你可以在我的网站上看到the code in use

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());
  }
}

【讨论】:

  • 这个例子很好,对我帮助很大,但还有一个问题我无法理解:如果你的图像在几个点旋转,你将如何在 redraw() 中清除画布图像的?
  • @Kevkong 就像接受的答案一样:保存上下文变换,将变换重置为单位矩阵,清除 0,0,width,height,然后恢复变换。
  • 是的,你是对的,我只是拼错了一些东西,所以“恢复”不能按预期工作,并且 transformPoint 在恢复后返回了错误的点。到目前为止,谢谢。
猜你喜欢
  • 2013-10-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-11
  • 1970-01-01
  • 2014-12-21
  • 1970-01-01
相关资源
最近更新 更多