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

Posted

技术标签:

【中文标题】在 html5 画布上旋转单个图像(但不是其他图像)?【英文标题】:Rotating a single image (but not the others) on an html5 canvas? 【发布时间】:2011-03-14 07:59:40 【问题描述】:

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

有人知道怎么做吗?

【问题讨论】:

How do I rotate a single object on an html 5 canvas? 的可能重复项 【参考方案1】:

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

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

我已经看到以前的答案显示一个简单的水平翻转只是平移轴的原点和翻转(反比例),但是这没有考虑到精灵,翻转原点会弄乱精灵的注册点(它在画布上的顶部和左侧)。

这个问题表现在精灵被正确镜像,但它的位置偏离了精灵的宽度。我通过考虑精灵的宽度来解决它。请注意我是如何使用带有 9 个参数的 CanvasRenderingContext2D.prototype.drawImage 的,因为我正在从精灵表中切出一个精灵:

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

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

【讨论】:

【参考方案2】:

只需重绘精灵,使用rotate 转换。 Transformations in HTML Canvas 2D Context

画布只是一个离屏缓冲区。除非您告诉它,否则它不会被清除,除非您告诉它,否则不会更改任何其他内容。

在许多不同的情况下,您可能需要重新绘制精灵的区域或周围。否则,您将获得重影效果,旧精灵的一部分在新图形下方仍然可见,或者其他图形变得模糊。一些原因是:

您的精灵是部分透明的, 您的精灵是部分半透明的, 其他绘图是在您的 sprite 之上制作的, 您的精灵不是矩形的, 您正在做的翻转不是 90 度的倍数。

所以这可能需要更多的工作,并且有几种不同的方法可以做到这一点。您可以简单地重绘整个场景,或者只重绘该位置的特定对象,也许使用clip 方法。

一个完全不同的方向可能是使用其他 HTML 元素,imgdiv,具有绝对定位和 CSS3 转换。将场景的渲染委托给浏览器基本上有点花招。

【讨论】:

我想我可能误解了旋转。我认为旋转(从我看到的示例中)似乎旋转了画布上的所有内容。这种方法是否适用于单个图像而不旋转其他图像?我试试看。 rotate 改变变换矩阵,它是画布状态的一部分。旋转之后的所有内容都会受到旋转的影响。您可以使用saverestore 在旋转之前制作画布状态的快照,然后在完成绘图后恢复该快照。您通常在绘图操作周围应用saverestore 就像一个块。 嗯,我已经考虑过这一点,这似乎是为游戏执行此操作的错误方法。将旋转应用于画布而不是图像本身的想法似乎非常笨拙。例如,对于每个改变方向的精灵,我都必须保存、旋转、绘制、恢复。也许我错过了什么?在 actionscript 中,可以创建一个矩阵,将其应用于位图并完成。 例如:myBitmap = BitmapData.loadBitmap("test") //翻转垂直矩阵 flipVerticalMatrix = new Matrix() flipVerticalMatrix.scale(1,-1) flipVerticalMatrix.translate(0,myBitmap.height ) //翻转水平矩阵 flipHorizo​​ntalMatrix = new Matrix() flipHorizo​​ntalMatrix.scale(-1,1) flipHorizo​​ntalMatrix.translate(myBitmap.width,0) flippedBitmap = new BitmapData(myBitmap.width,myBitmap.height,false,0x FFCC00) flippedBitmap .draw(myBitmap,flipHorizo​​ntalMatrix) this.attachBitmap(flippedBitmap,1)【参考方案3】:

虽然我很欣赏 Shtéf 的回答,但经过一番研究,我发现旋转您实际用于显示的画布似乎并不理想。尝试创建复杂动画时的保存、旋转和恢复(也就是街头霸王 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 方面都是业余爱好者 - 所以请随时评论或纠正我,我会很乐意改变这个答案。 根据我的经验,转换不会导致闪烁。除此之外,对整个画布应用转换可能看起来很大,但它实际上只是一个状态变量,一些数字。我想,无论你的船漂浮着什么。 :)【参考方案4】:

为什么不使用 save() 并恢复

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)

How to rotate one image in a canvas?

【讨论】:

以上是关于在 html5 画布上旋转单个图像(但不是其他图像)?的主要内容,如果未能解决你的问题,请参考以下文章

html5图像旋转缩放并上传

在画布html5上导入图像

如何旋转 HTML5 画布的现有内容?

处理相对于画布而不是图像可拖动的旋转画布图像

颤动 - 如何使用画布围绕中心旋转图像

如何使用html5画布元素绘制透明图像