如何使用 HTML5 Canvas 水平翻转精灵?

Posted

技术标签:

【中文标题】如何使用 HTML5 Canvas 水平翻转精灵?【英文标题】:How to flip sprites horizontally with HTML5 Canvas? 【发布时间】:2018-01-05 23:58:17 【问题描述】:

我正在尝试使用纹理图集制作动画:

当角色朝右时效果很好。我尝试水平翻转它,但它的位置错误:

这是我当前的代码:

<canvas id="c"   style="background: #000"></canvas>

var metaData = [
        x:0,y:0,w:35,h:38,offsetX:3,offsetY:9,
        x:37,y:0,w:31,h:37,offsetX:6,offsetY:10,
        x:70,y:0,w:65,h:47,offsetX:0,offsetY:1,
        x:137,y:0,w:65,h:47,offsetX:0,offsetY:1,
        x:204,y:0,w:61,h:46,offsetX:1,offsetY:1,
        x:267,y:0,w:42,h:46,offsetX:1,offsetY:1,
        x:311,y:0,w:43,h:44,offsetX:1,offsetY:3,
        x:356,y:0,w:38,h:37,offsetX:6,offsetY:10,
        x:396,y:0,w:35,h:34,offsetX:6,offsetY:13,
        x:433,y:0,w:33,h:37,offsetX:7,offsetY:10,
        x:468,y:0,w:36,h:40,offsetX:5,offsetY:7,
        x:506,y:0,w:34,h:39,offsetX:6,offsetY:8
],
dx = 0, //position x
dy = 0, //position y
index = 0; //frame index

(function draw() 
    context2D.clearRect(0,0,c.width,c.height);

    var cur = metaData[index];

    if(facingRight) 
        context2D.drawImage(
            img,
            cur.x, cur.y,
            cur.w, cur.h,
            dx + cur.offsetX, dy + cur.offsetY,
            cur.w, cur.h
        );
     else 
        context2D.save();
        context2D.translate(cur.w,0);
        context2D.scale(-1,1);
        context2D.drawImage(
            img,
            cur.x, cur.y,
            cur.w, cur.h,
            dx, dy + cur.offsetY,
            cur.w, cur.h
        );
        context2D.restore();
    

    index = ++index % metaData.length;

    setTimeout(draw,100);
)();

我使用scale(-1,1) 来翻转精灵,但我不知道如何让它保持在相同的位置,比如面向右侧。我应该修复偏移值吗?

请,任何帮助将不胜感激:)

【问题讨论】:

@Kaiido 我让它们大小一样,这个纹理图集由 TexturePacker 打包,它生成一个 JSON 文件,我使用这些信息来制作元数据。 我不知道这个TexturePacker,但这些不是纹理,这些是精灵。为了便于使用,您需要对齐它们,并将它们设置为相同大小的不可见框。目前他们根本不是。他们甚至没有对齐。 (字符精灵最好底部对齐) @Kaiido 我在 PhotoShop 中对齐它们,并由 TexturePacker 打包,它给了我在 JSON 文件中的正确位置,如下所示: "filename": "00.png", "frame": "x ":0,"y":0,"w":35,"h":38,“旋转”:假,“修剪”:真,“spriteSourceSize”:“x”:3,“y”: 9,"w":35,"h":38, "sourceSize": "w":65,"h":49, "pivot": "x":0.5,"y":1 ,每帧是 65X49,它适用于向右运动,除了翻转它。 【参考方案1】:

您将节省大量时间使用图像编辑器并使所有精灵适合整个精灵表中相同大小的区域。

不需要metadata 怪异,代码更简单(单个w、单个hx = w * i 等。

例如,您最大的精灵大约是 70 像素宽,因此您应该将所有其他精灵放在这样的盒子之一中:

现在,您的所有精灵似乎都共享前脚的相同位置。所以你应该使用它作为对齐所有精灵的锚点。

类似这样的:

请注意,对于所有精灵,前脚相对于其自己的盒子始终位于相同的位置。

现在编写这个精灵表的动画非常容易,甚至翻转它:

const ssheet = new Image();
ssheet.src = 'https://i.stack.imgur.com/kXKIc.png'; // same without borders
ssheet.onload = startSheetAnim;

function startSheetAnim(evt) 
  const ctx = c.getContext('2d');
  const h = 49;
  const w = 70;
  let i = 0;

  function anim() 
    ctx.setTransform(1, 0, 0, 1, 0, 0);
    ctx.clearRect(0, 0, c.width, c.height);
    ctx.drawImage(ssheet,
      (i * w), 1, w, h,
      0, 0, w, h
    );
    // scale (flip-x) and translate
    ctx.setTransform(-1, 0, 0, 1, w * 2, 0);
    ctx.drawImage(ssheet,
      (i * w), 1, w, h,
      0, 0, w, h
    );

    i = (i + 1) % 12
    setTimeout(anim, 100);
  
  anim();
&lt;canvas id="c"&gt;&lt;/canvas&gt;

【讨论】:

感谢您的详细解答!但是我还有一个问题,我从一些网站获取精灵,他们总是打包精灵并裁剪透明像素,this spritesheet 减少不必要的透明区域以提高性能,我可以直接使用还是必须重新定位它们? 在我看来,重新定位它们更容易是的......我不知道他们为什么一开始就这样打包。如果是为了图像大小,那么他们做错了:我的精灵表比您问题中的压缩表具有更好的压缩率,因此更轻。空像素一般不会太重,您避免使用 JSON。 这会导致 GPU 性能下降吗?因为会有很多透明区域会被不必要地渲染。 不,不是以敏感的方式。使用更少的 cpu 即可获得大致相同的 gpu 影响。 非常感谢!我想我可以从你的经历中学到很多:)

以上是关于如何使用 HTML5 Canvas 水平翻转精灵?的主要内容,如果未能解决你的问题,请参考以下文章

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

Swift SpriteKit:翻转父精灵随机子节点水平对齐

更改正在动画的精灵表时 HTML5 Canvas 挂起(逐帧播放视频)

在画布中翻转精灵

html5 canvas大sprite sheet图片动画性能优化

Adobe Animate CC、HTML5 Canvas - 将实例名称捕获为动态文本?