【问题标题】:The right way to do sprite-based animation in Svelte在svelte中做基于精灵的动画的正确方法
【发布时间】:2020-01-30 09:19:42
【问题描述】:

我有几十个 png 文件(大小相同),并希望将它们用于简单的动画。 (图片取自经典的“xneko”程序。)

现在,我的 neko.svelte 看起来像:

<script>
  export let position = {left: 100, top: 100};
  let current = 0;

  let names = [
    'awake', 'down1', 'down2', 'dtogi1', 'dtogi2', 'dwleft1', 'dwleft2',
    'dwright1', 'dwright2', 'jare2', 'kaki1', 'kaki2', 'left1', 'left2',
    'ltogi1', 'ltogi2', 'mati2', 'mati3', 'right1', 'right2', 'rtogi1', 'rtogi2',
    'sleep1', 'sleep2', 'up1', 'up2', 'upleft1', 'upleft2',
    'upright1', 'upright2', 'utogi1', 'utogi2'
  ];

  setInterval(() => current = (current + 1) % names.length, 100);
</script>

<style>
  .neko {
    position: relative;
  }
</style>

<div class='neko' style="
  left: {position.left + 'px'};
  top: {position.top + 'px'};
  width: 32px;
  height: 32px;">
{#each names as name (name)}
  <img id={name} width=32 height=32
       style='display: {names[current] === name ? "inherit" : "none"}'
       src={'https://tinlizzie.org/~ohshima/neko/neko/' + name + '.png'} alt={name}/>
{/each}
</div>

(选择图像的实际逻辑涉及更多,但为了说明目的,此处对其进行了简化。position 是从拥有的组件传入的。您可以在此处看到它运行:https://tinlizzie.org/~ohshima/neko/

这工作正常。但我想知道是否有比在显示场景中必须显示所有 img 元素但不使用display: none 来显示其中一个更好的方法。如果它是在直接 DOM 中完成的,它会创建 img 元素的列表并将其中一个附加到 div 中。但似乎我不能写这样的东西:

<script>
let allImages = ...;                     // a dictionary of img elements
let currentImg = allIamges[name];        // an img element
</script>

<div class='neko'>{currentImg}</div> // reference to the img

请让我知道做这样的精灵动画的建议方法是什么。

【问题讨论】:

    标签: image animation dom svelte


    【解决方案1】:

    使用或不使用 Svelte 的基于 sprite 的动画的标准方法是生成所有图像的 spritesheet,然后使用 CSS 进行动画处理。所以你会有这样的图像......

    ...和一个 32px x 32px 的 div,每 100 毫秒更改一次 background-position。这有几个好处:

    • 第一次加载图像时不闪烁 - 要么全部存在,要么都不存在
    • HTTP 请求更少
    • 通常,spritesheet 将比单个文件的总和小很多
    • 浏览器要做的工作更少,因为只有一个元素,而且您根本不会更改文档的结构

    巧合的是,我上周为一个项目编写了一个名为 sevenup 的 spritesheet 生成器。通过它运行这些图像给了我上面的 spritesheet,以及一堆 CSS。

    我的 Neko.svelte 组件如下所示:

    <script>
        import { onMount } from 'svelte';
    
        export let pos = { x: 0, y: 0 };
    
        const names = [
        'awake', 'down1', 'down2', 'dtogi1', 'dtogi2', 'dwleft1', 'dwleft2',
        'dwright1', 'dwright2', 'jare2', 'kaki1', 'kaki2', 'left1', 'left2',
        'ltogi1', 'ltogi2', 'mati2', 'mati3', 'right1', 'right2', 'rtogi1', 'rtogi2',
        'sleep1', 'sleep2', 'up1', 'up2', 'upleft1', 'upleft2',
        'upright1', 'upright2', 'utogi1', 'utogi2'
      ];
    
        let current = 0;
        $: name = names[current];
    
        onMount(() => {
            const interval = setInterval(() => {
                current = (current + 1) % names.length;
            }, 100);
    
            return () => clearInterval(interval);
        });
    </script>
    
    <div data-sevenup="{name}.png" style="transform: translate({pos.x}px,{pos.y}px)"></div>
    

    CSS 如下所示:

    [data-sevenup] { background-image: url(./sprites.png); background-size: 192px 192px }
    [data-sevenup="awake.png"] { width: 32px; height: 32px; background-position: 0px 0px }
    [data-sevenup="down1.png"] { width: 32px; height: 32px; background-position: -32px 0px }
    [data-sevenup="down2.png"] { width: 32px; height: 32px; background-position: 0px -32px }
    [data-sevenup="dtogi1.png"] { width: 32px; height: 32px; background-position: -32px -32px }
    ...
    

    综合起来,应用看起来像这样:https://svelte.dev/repl/b66dbc755bf84053914350c2f07d5f2a?version=3.12.1

    【讨论】:

    • 非常感谢。闪烁是我遇到的一个问题,很高兴不必保留多个 img 元素。
    • 真是太好了!我很好奇,但不应该使用requestAnimationFrame 吗?
    • 没有。 requestAnimationFrame 每秒运行 60 次(假设没有延迟)。在这种情况下,帧应该每秒只改变 10 次。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-01
    • 2013-04-02
    • 1970-01-01
    相关资源
    最近更新 更多