【问题标题】:Asynchronous playback of several <img> tags with same animated GIF多个 <img> 标签的异步播放,带有相同的动画 GIF
【发布时间】:2013-05-15 02:18:19
【问题描述】:

我在一个 HTML 页面上有很多 &lt;img&gt; 标签,其中一些标签共享同一个动画 GIF 文件。 src URL 是由 JavaScript 动态设置的,但在我的代码中,相同的字符要么再次播放,要么在最后一帧立即显示后续字符。

想法:这是一个艺术项目。输入一些文本后,字符会以特殊字体显示,并且每个字符一个字符(一个接一个)完成一些转换,我只能通过使用动画 GIF 来实现。文本被拆分为单个字符,&lt;img&gt; 标签创建如下:

<!-- delivered HTML at page load -->
<img src="initial/f.png"> <!-- I used to PNG here to outline -->
<img src="initial/o.png"> <!-- the animation is NOT set a first -->
<img src="initial/o.png">

当动画开始时,第一个 &lt;img&gt; 的 URL 设置为 transition/f.gif。只有在它完成播放后(由时间确定),才会设置第二张图像等。

<!-- while 2nd animation is running -->
<img src="transition/f.gif"> <!-- already finished, displays last frame -->
<img src="transition/o.gif"> <!-- being animated right now -->
<img src="transition/o.png"> <!-- will be reset to o.gif in next step -->

我无法解决的问题: 示例中的o.gif 播放后,下一个O 的动画无法正常工作。

我尝试为每个角色定义一个类并将图像分配为background-image。一旦任何出现的字符已经播放,任何后续出现的相同字母都会在显示时立即显示该 GIF 的最后一帧(结束状态)。

我也尝试直接使用 JavaScript 设置 src 属性,但是当第二个字母 O 设置为这样时,第一个字母 O 的动画会同步重新启动。但它应该停留在最后一帧。

由于 HTML 页面也应该在本地文件系统上工作,我认为像 o.gif?random=argument 这样绕过缓存机制的参数将不起作用或没有任何效果(可能在某些计算机上,我自己的测试不会伪造)。

【问题讨论】:

  • “一种特殊的字体和一些转换是每个字符(一个接一个)完成的,我只能通过使用动画 GIF 来实现” - 我相信你可以使用其他东西来做到这一点,例如画布,特别是如果它用于艺术项目,因此您可能不关心浏览器支持
  • 广泛的浏览器支持不是必须的,但总是好的。动画是由不会编写代码的艺术家自己创作的。由于时间和预算的限制,我们肯定会坚持使用 GIF 文件。易于在这里创建和我使用(除了这个问题)。
  • 我讨厌推荐互联网的壁橱里的骷髅,但你考虑过 Flash 吗?在大多数 GIF 动画师中易于学习、制作动画,并且您可以完全控制同步和时间。
  • “Flash”的有趣解释:-) 好吧,我从来没有学过 Flash 并且拒绝为一个小艺术项目做。当我第一次与艺术家见面时,我也提到了 Flash,甚至她作为一个非技术人员也强行拒绝了它。可以这么说,是的,我们考虑过 Flash 但不想使用它。 GIF 问题是常见问题吗?
  • 并非如此,有几个 javascript 插件至少可以对 GIF 进行某种逐帧控制,但它们看起来都不容易进行逆向工程。您将要为这个项目学习一些新东西,我建议您构建自己的超级简单的 Javascript 动画师。让它将一些图像加载到一个数组中,并使用setTimeout() 循环浏览它们。这样您就可以完全控制一切!

标签: javascript html browser asynchronous animated-gif


【解决方案1】:

考虑不使用 GIF,而是使用图像序列和 Javascript。我整理了一个粗略的可实例化 Javascript 对象,它就是这样做的:

<html>
    <head>
        <script>
            function Animator(target, frames, delay)
            {
                var _this = this;
                // array of frames (image objects)
                var f = [];
                // a reference to the object where our frames will appear
                var t = document.getElementById(target);
                // the interval variable.  Used to stop and start
                var t_loop;
                // create a new image element and remember it
                var img_el = document.createElement("img");
                // current frame
                var c_frame = 0;

                t.innerHTML = "";
                t.appendChild(img_el);

                var i, img;

                // preload immediately
                for(i = 0; i < frames.length; i++)
                {
                    img = new Image();
                    img.src = frames[i];
                    f.push(img);
                }

                this.play = function()
                {
                    t_loop = window.setInterval(_this.frame, delay);
                }

                this.stop = function()
                {
                    clearInterval(t_loop);
                }

                this.gotoFrame = function(frame)
                {
                    c_frame = frame;
                    _this.frame();
                }

                this.frame = function()
                {
                    img_el.src = f[c_frame].src;

                    c_frame++;

                    if(c_frame >= f.length)
                    {
                        c_frame = 0;
                    }
                }
            }

            function loaded()
            {
                var quip_frames = [
                    "http://www.abcteach.com/images/abma_thumb.gif",
                    "http://www.abcteach.com/images/abcu_thumb.gif",
                    "http://www.abcteach.com/images/zbma_thumb.gif",
                    "http://www.abcteach.com/images/zbcu_thumb.gif",
                    "http://www.abcteach.com/images/dnma_thumb.gif",
                    "http://www.abcteach.com/images/dncu_thumb.gif"
                ];

                var anim1 = new Animator("target1", quip_frames, 100);

                var anim2 = new Animator("target2", quip_frames, 100);

                var anim3 = new Animator("target3", quip_frames, 100);

                anim1.play();
                window.setTimeout(anim2.play, 200);
                window.setTimeout(anim3.play, 300);
            }
        </script>
    </head>
    <body onload="loaded()">
        <div id="target1">
        </div>
        <div id="target2">
        </div>
        <div id="target3">
        </div>
    </body>
</html>

感谢ABCTeach 提供图片

该对象循环加载图像,将它们存储在一个数组中,然后通过简单地使用setInterval() 以所需的速率换出帧。当页面加载时,它会生成三个这样的对象,每个都指向不同的 DIV,但每个都有相同的框架。然后它告诉每个人以不同的延迟播放(第一个立即播放,第二个在 200 毫秒后,第三个在 300 毫秒后)。在您的情况下,您将为您使用的每个字母创建一个数组:

var A = [*array of URLs to images of the letter A*];
var B = [*array of URLs to images of the letter B*];    
...

然后为页面上的每个 div 创建一个新的 Animator 对象,并将其发送到适当的字母数组:

var letter_so_and_so = new Animator("div_so_and_so", A, 100);
var letter_this_and_that = new Animator("div_this_and_that", B, 100);
var another_letter = new Animator("another_div", B, 100);

最后给每个人一个偏移开始,或者调用他们的Animator.gotoFrame(f)方法在不同的帧上开始他们(不要忘记Animator.play()!)。

JSFiddle

尝试在你的代码中采用这样的东西,我想你会发现它会产生奇迹。

【讨论】:

    【解决方案2】:

    您必须为图像创建唯一的 url,因此浏览器将始终重新加载字母而不是使用缓存的字母。 例如,您可以使用一个小型 php 来实现这一点,它为参数输出正确的 gif。

    <img src="gif_printer.php?letter=o&t=1234567">
    

    t参数应该是创建img标签时的时间戳。这提供了足够的差异来重新下载。

    在 php 中设置 headers 以限制/禁用缓存和输出类型为 gif,并将图像的内容简单地倒入 stdout。

    timestamp 参数和缓存控制标头应该足以让浏览器重新下载 evert letter。

    如果您的测试环境反应不正确,您可以尝试 htaccess 解决方案,但为此您必须使用 Web 服务器(这可能已经足以让之前的解决方案工作)。

    这意味着将所有指向 images/[a-z].gif 的 url 重写为 images/[a-z].gif,因此只需删除除第一个字母和扩展名之外的其他干扰。

    【讨论】:

    • 我已经需要一个 HTTP 服务器来使用 PHP [或任何其他脚本语言] 脚本。您期望在 .htaccess 文件中使用重写规则有什么好处?
    • 好吧,我测试了使用本地文件引用o.gif?pos=LETTER_INDEX,令人惊讶的是它可以工作。我认为它也适用于 HTTP,因为浏览器无法知道它们实际上是同一个文件。因为显然可以对任何文件使用? 语法,所以不需要HTTP 服务器(用于本地文件)或脚本语言(用于Web 服务)。当然,解决实际问题比使用“不同”文件要好得多——所以我会保持这个开放一段时间并寻找其他答案。
    • 据我所知,问题是浏览器同步相同的文件,因此您必须对其进行调整以将相同的字母视为不同的字母。我也密切关注,以进一步了解这一点。
    猜你喜欢
    • 1970-01-01
    • 2021-10-17
    • 1970-01-01
    • 2015-07-13
    • 1970-01-01
    • 2021-05-17
    • 2012-07-29
    • 2013-12-22
    • 1970-01-01
    相关资源
    最近更新 更多