【问题标题】:How to scale a canvas during an animation?如何在动画期间缩放画布?
【发布时间】:2014-11-06 19:36:39
【问题描述】:

我有两个按钮可以放大和缩小我的画布动画

<button id="zoomInButton">Zoom ++</button>
<button id="zoomOutButton">Zoom -- </button>

 <canvas id="myCanvasmatchPage" > 
        Sorry, your browser doesn't support canvas technology
</canvas>

我有一个使用 setInterval 和画布的动画循环(省略)。为了不让每个 setInterval 发生缩放,它必须在绘图函数循环之外。事实上 ctx.scale 在按钮点击时被重置。

$('#myCanvasmatchPage').attr({width:100,height:200}).css({width:'100px',height:'200px'});       

var canvas = document.getElementById("myCanvasmatchPage");
ctx = canvas.getContext("2d");
ctx.translate(0.5,0.5);

var nextFrame =0;
var animationData = [];

var canvasZoom = 1; //initial is scale 100%
var incrementZoom = 0.05;
$("#zoomInButton").click(function ()
   {
     canvasZoom += incrementZoom;
     ctx.scale(canvasZoom, canvasZoom);
   });
 $("#zoomOutButton").click(function ()
   {
     if (canvasZoom > 1){//i dont want to allow a less than 100% scale zoom
           canvasZoom -= incrementZoom; 
           ctx.scale(canvasZoom, canvasZoom);
    }
   });  


var draw = function(){
    //cannot set scale within animation loop! keeps resetting!
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.drawImage(images['background'], 0, 0, 622, 924, 0, 0, 311, 462);

                if (nextFrame<=animationData.length){
                    //animation loop
                }else{
                    clearTimeout(draw);//get out of the loop
                    return;
                }

                nextFrame++;    
    }
    setInterval(draw,200);  

我目前对这段代码的体验是 zoomIn '有效',但 zoomOut 没有。控制台日志向我显示,canvasZoom 确实上下一致地上下浮动 0.05(有时也是 0.05000001 哈哈),但画布不会负向缩放。

这让我觉得问题在于画布的清除而不是缩放。

【问题讨论】:

  • 除了缩放(旋转/平移/倾斜)之外,您还打算做其他事情吗?
  • @GameAlchemist,实际上是的,旋转以及移动动画“中心点”的一种方式。

标签: javascript jquery animation html5-canvas


【解决方案1】:

当 Canvas 的 context2D 是一个状态机时,您犯了一个非常常见的错误,即认为 scale 将 设置 比例,它会将每个变化累积在彼此之上(expl scale X2 然后 scale x2 == 比例 X4 )。

这个问题的常见答案是保存()你的上下文,做你的变换,做你的图画,然后恢复()它。

所以只是给你方向(未经测试):

function Transform() {
   this.scale = 1;
   this.rotation = 0;
   this.translation = {x:0, y:0 };
   this.zoomIn = function () {
    this.scale+=0.5;
   };
   this.zoomOut = function () {
    this.scale-=0.5;
   };
   this.translate(x,y) = function (x,y)  { 
     this.translation.x+=x;  // OR +=x*currentZoom;
     this.translation.y+=y;  // OR +=y*currentZoom;
   };
   this.rotate = function (angle) {
      this.rotation+=angle;
   };
   this.applyTransform = function (ctx) {
       ctx.translate(translation.x, translation.y);  // ?? order ??
       ctx.scale(currentZoom, currentZoom);          // ?? order ??
       ctx.rotate(rotation);                   
   }
}

var imageTransform = new Transform();

function drawImage(img) { 
  ctx.save();
  ctx.translate(img.width/2, img.height/2);
  imageTransform.applyTransform(ctx);
  ctx.drawImage(img, -img.wdth/2, -img.height/2);
  ctx.restore();
}

平移/缩放/旋转的顺序实际上取决于您的意图:您是在移动相机还是移动图像?你是缩放相机还是图像? (仅当要显示的项目不止一项时,问题才相关)。

希望这会有所帮助。

【讨论】:

  • 谢谢!我将在一分钟内完成规模的更改。关于其他变换的优先级(非常聪明,您预见到了这种可能性),那么场景是您正在观看足球比赛的动画(因此要变换的对象不止一个)。观看者将能够放大/缩小并保持球的中心舞台或远离球以检查球场的不同部分。旋转将允许查看纵向或横向。
  • 我现在要看看你的答案,你最初的评论足以以原始的方式解决这个问题。 (见下面我的回答)再次感谢。立即研究您的解决方案。
  • 我担心,正如我在我的问题中所说,在动画的绘制函数中包含变换是不可取的。也许仅通过单击功能引用上下文的更原始方法更好。你怎么看?
  • 我在处理画布时简化的首要任务是始终“保持干净”,这意味着只进行保存/转换/绘制/恢复操作。当我不这样做时仍然有特殊情况:最简单的例子是,如果我将临时画布用于特殊目的,那么我让它的上下文处于任何状态。您的案例的两个示例:1)您可能希望始终在相同的位置/大小绘制分数 2)您可能希望绘制游戏的小地图以显示整体动作,即使在放大时也是如此。对于这些情况,我会选择多层画布...或保持上下文始终干净。
  • GameAlchemist 的目标!守门员在左上角没有机会,尤其是当他意识到他也想改变画布尺寸时!谢谢!
【解决方案2】:

虽然我研究了 @GameAlchemist 的更彻底的解决方案,但这里将是一个简单转换问题的解决方案。感谢 GameAlchemist 指出(非常重要!)规模变化是累积的!

在点击函数中,保留一个干净的基础副本以进行缩放(此处为 initialZoom)很重要

    var initialZoom = 1;    //100% same as container (less border)
    var zoomCounter = 1;
    var incrementZoom = 0.05;
    var canvasZoom = 1;//just to declare

 $("#zoomInButton").click(function ()
   {
     canvasZoom = initialZoom + incrementZoom;
    zoomCounter = zoomCounter + incrementZoom;//so i can track compared to original and block cumulative < 100&
     ctx.scale(canvasZoom, canvasZoom);
   });
 $("#zoomOutButton").click(function ()
   {
     if (zoomCounter > 1){//i dont want to allow a less than 100% scale zoom
           canvasZoom = initialZoom - incrementZoom;
           zoomCounter = zoomCounter - incrementZoom;
           ctx.scale(canvasZoom, canvasZoom);
    }
   }); 

现在看看 GameAlchemist-s 解决方案是否能让我更轻松地进行多重变换!

【讨论】:

  • 虽然这应该只适用于缩放,但我担心添加旋转/平移时会让人头疼。事实上,我只考虑两个可行的选项:第一个就像我的答案,但使用 OOP,所以所有属性都在一个对象内,这不仅更干净,而且还允许补间或其他功能。第二个是完全一样的,但是您需要自己构建一个变换矩阵,以使用单个 setTransform 更新变换。它会更快,但我担心它过度优化,第一个解决方案应该足够快。
猜你喜欢
  • 1970-01-01
  • 2016-08-30
  • 2010-10-23
  • 1970-01-01
  • 2011-11-14
  • 2014-11-07
  • 2012-07-27
  • 1970-01-01
  • 2021-08-19
相关资源
最近更新 更多