在HTML5画布上旋转单个图像(而不是其他图像)?

4

我有一个精灵,正在使用普通的精灵表格blitting在html画布上进行动画。在某些关键事件上,我想改变精灵的方向(即翻转它或将其旋转180度),而不改变画布上的其他任何东西。

有人知道如何做到这一点吗?


可能是重复的问题:如何在 HTML 5 画布上旋转单个对象? - John
4个回答

2

我遇到了一个游戏问题,我的游戏有向上、向下和向左的动画,但没有向右的动画。所以我需要翻转向左的动画来绘制向右的动画。

对于每个精灵,我会跟踪它在画布中的当前顶部和左侧位置,以及精灵表中每个单元格的顶部和左侧位置。

我曾经看过之前的答案,其中简单的水平翻转只是转换原点并翻转(反比例缩放)轴,这不考虑到翻转原点会弄乱精灵的注册点(即其在画布上的顶部和左侧位置)。

这个问题表现为精灵被正确镜像,但其位置偏移了精灵的宽度。我通过考虑精灵的宽度来解决了这个问题。请注意,我正在使用CanvasRenderingContext2D.prototype.drawImage和9个参数进行绘图,因为我正在从精灵表中切割精灵:

// check if we need to flip image (special case for right movement)
if(sprite.translated){
  context.save();
  context.translate(context.canvas.width, 0);
  context.scale(-1, 1);
  context.drawImage(sprite.sheet,
    cell.left,
    cell.top,
    sprite.width,
    sprite.height,
    // secret sauce: change the destination's X registration point
    context.canvas.width - sprite.left - sprite.width,
    sprite.top,
    sprite.width, sprite.height);
  context.restore();
} else {
  // Assumes cells are all the same width and height, set in sprite
  context.drawImage(sprite.sheet, cell.left, cell.top, sprite.width,
  sprite.height, sprite.left, sprite.top, sprite.width, sprite.height);
}

注意:我也可以在翻译中进行数学计算,因为它旨在简化其他地方的计算。

1

虽然我很感激Shtééf的回答,但经过一番研究,我发现旋转你实际使用来显示的画布似乎并不理想。在尝试创建复杂动画(比如Street Fighter 2而不是astroids)时,保存、旋转和恢复会导致画布甚至在Chrome中闪烁。

然而,我找到了一个可用的策略。这里的想法是你实际上创建两个画布,一个用于游戏,另一个则是某种后备缓冲区,它将用于旋转或缩放你的精灵。你基本上转换后备缓冲区画布,绘制相关图像,然后将其传输到主画布并恢复(或不恢复)后备缓冲区。通过这种方式,你只旋转隐藏的画布,并且只影响相关精灵而不是整个游戏板。

代码看起来像这样(正在进行中):

mainContext.clearRect(lastXpos, lastYpos, lastWidth, lastHeight);
backContext.clearRect(0, 0, lastWidth, lastHeight);
lastXpos = xpos;
lastYpos = ypos;
lastWidth = width;
lastHeight = height;


backContext.save();

//check the direction of the sprite
//turn the backContext to this direction   
//SPRITE_INVERTED==-1
if (spriteXDirection == SPRITE_INVERTED || spriteYDirection == SPRITE_INVERTED) 
{
    var horScale = 0;
    var verScale = 0;

    if (spriteXDirection == SPRITE_INVERTED)
    {
    horScale = width;
    }

    if (spriteYDirection == SPRITE_INVERTED)
    {
    verScale = height;
    }


    backContext.translate(horScale, verScale);
    backContext.scale(spriteXDirection, spriteYDirection);


}


//draw the sprite not we always use 0,0 for top/left
backContext.drawImage(animations[currentPlay].sheet,
                animationX,
                animationY,
                width,
                height, 0, 0, width, height);

//Get the image data from the back context
var image = backContext.getImageData(0, 0, width, height);


//flip the back context back to center - or not, I haven't decided how to optimize this yet.
backContext.restore();


//transfer the image to your main context
mainContext.putImageData(image, xpos, ypos);

这让我在理解如何在没有使游戏板上的所有内容移动的情况下翻译我的精灵时避免了很多头痛。它似乎也比修改主上下文执行得更好。


免责声明:我对游戏开发和HTML5都是完全的业余爱好者 - 因此请随意评论或纠正我的错误,我将非常乐意更改这个答案。 - j03m
1
根据我的经验,变换并不会导致闪烁。除此之外,将变换应用于整个画布可能看起来很大,但实际上只是一个状态变量,一些数字而已。无论你喜欢什么方式,我想。 :) - Stéphan Kochen

1

简单地重新绘制精灵,使用rotate转换。 HTML Canvas 2D上下文中的变换

画布只是一个屏幕外缓冲区。除非您告诉它清除,否则它不会被清除,并且除非您告诉它,否则不会有任何其他更改。

有许多不同的情况可能需要重新绘制精灵周围或周围区域。否则,您将获得一种类似幽灵的效果,在新绘制下部分旧精灵仍然可见,或者其他绘图被遮挡。一些原因是:

  • 您的精灵部分透明,
  • 您的精灵部分半透明,
  • 其他图形位于您的精灵之上,
  • 您的精灵不是矩形的,
  • 您正在进行不是90度倍数的翻转。

因此,这可能需要更多的工作,并且有几种不同的方法可以实现。您可以简单地重新绘制整个场景,或仅在该位置重新绘制特定对象,也许使用clip方法。

一个完全不同的方向可能是使用其他HTML元素,如imgdiv,并使用绝对定位和CSS3变换。这基本上是一种诡计,将您场景的渲染委托给浏览器。

我猜也许我误解了旋转。我认为旋转(从我看到的例子中)似乎会将画布上的所有内容都旋转。这种方法对于单个图像而言是否有效,而不会旋转其他图像?我会尝试一下。 - j03m
rotate 改变了转换矩阵,这是画布状态的一部分。旋转后的所有内容都会受到影响。您可以使用 saverestore 在旋转之前对画布状态进行快照,然后在绘制完成后恢复该快照。通常,您会像块一样应用 saverestore 来围绕您的绘图操作。 - Stéphan Kochen
嗯,我思考了一下,这似乎不是游戏中正确的做法。旋转应该应用于图像本身而不是画布,否则会非常难以处理。例如,每个改变方向的精灵都需要保存、旋转、绘制和恢复。也许我漏掉了什么?在ActionScript中,可以创建一个矩阵,将其应用于位图,然后就完成了。 - j03m
myBitmap = BitmapData.loadBitmap("test")//翻转垂直矩阵 flipVerticalMatrix = new Matrix() flipVerticalMatrix.scale(1,-1) flipVerticalMatrix.translate(0,myBitmap.height)//翻转水平矩阵 flipHorizontalMatrix = new Matrix() flipHorizontalMatrix.scale(-1,1) flipHorizontalMatrix.translate(myBitmap.width,0)flippedBitmap = new BitmapData(myBitmap.width,myBitmap.height,false,0x FFCC00) flippedBitmap.draw(myBitmap,flipHorizontalMatrix) this.attachBitmap(flippedBitmap,1) - j03m

0
为什么不使用 save() 和 restore() 呢?
ctx.save(); // save current state
ctx.rotate(Math.PI); // rotate
ctx.drawImage(link,x,y,20,20); // draws a chain link or dagger
ctx.restore(); // restore original states (no rotation etc)

如何在画布中旋转一张图片?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接