在 Svelte 中制作基于精灵的动画的正确方法

Yos*_*ima 1 animation dom image svelte

我有几十个 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>
Run Code Online (Sandbox Code Playgroud)

(选择图像的实际逻辑有点复杂,但为了说明目的,这里对其进行了简化。并且是position从所属组件传入的。您可以在此处看到它的运行: https: //tinlizzie.org/~ohshima/猫/

这工作正常。但我想知道是否有更好的方法,而不是在显示场景中显示所有 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
Run Code Online (Sandbox Code Playgroud)

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

Ric*_*ris 5

制作基于精灵的动画(无论是否使用 Svelte)的标准方法是生成所有图像的精灵表,然后使用 CSS 制作动画。所以你会有这样的图像......

\n\n

Neko 精灵表

\n\n

...以及 32px x 32px div,并background-position每 100 毫秒更改一次。这样做有几个好处:

\n\n
    \n
  • 图像第一次加载 \xe2\x80\x94 时不闪烁,要么它们全部存在,要么全部不存在
  • \n
  • 更少的 HTTP 请求
  • \n
  • 通常,精灵表的大小会比各个文件的总和小很多
  • \n
  • 浏览器要做的工作更少,因为只有一个元素,并且您根本不需要更改文档的结构
  • \n
\n\n

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

\n\n

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

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

CSS 看起来像这样:

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

把它们放在一起,应用程序看起来像这样:https ://svelte.dev/repl/b66dbc755bf84053914350c2f07d5f2a?version=3.12.1

\n