23

标题是不言自明的,但我将提供有关此事的逐步视图。希望我不是第一个在 Webkit/Chrome 上注意到这个(显然)错误的人。

我想重置 GIF 动画。到目前为止,我看到的所有示例要么简单地将src图像的 设置为自身,要么将其设置为空字符串,然后src再次设置原始字符串。

看看这个JSFiddle以供参考。GIF 在 IE、Firefox 和 Chrome 上重置得非常好。

我遇到的问题是图像仅display:noneGoogle Chrome上。

检查这个JSFiddle。GIF 在显示在页面中之前在 IE 和 Firefox 上可以很好地重置,但 Chrome 只是拒绝重置其动画!

到目前为止我已经尝试过:

  • 像在 Fiddle 中一样设置src自身,在 Chrome 中不起作用。
  • 将 设置src为空字符串并将其恢复为默认值也不起作用。
  • 在图像周围放置一个包装器,清空容器.html('')并将图像放回其中,也不起作用。
  • 通过或在设置之前更改display图像的也不起作用。.show().fadeIn()src

到目前为止,我发现的唯一解决方法是将图像保持为默认值,并在需要模拟行为时display通过.animate()ing 和ing 不透明度、高度和可见性对其进行操作。.css()display:none

这个问题的主要原因(上下文)是我想在页面中淡入之前重置一个 ajax 加载器 GIF。

所以我的问题是,是否有正确的方法来重置 GIF 图像的动画(避免 Chrome 的display:none“错误”)或者它实际上是一个错误?

(ps。您可以更改小提琴中的 GIF 以获得更合适/更长的动画 gif 以进行测试)

4

12 回答 12

33

“重置” GIF 最可靠的方法是附加一个随机查询字符串。但是,这确实意味着每次都会重新下载 GIF,因此请确保它是一个小文件。

// reset a gif:
img.src = img.src.replace(/\?.*$/,"")+"?x="+Math.random();
于 2012-05-24T02:08:26.727 回答
31

Chrome 处理样式更改的方式与其他浏览器不同。

在 Chrome 中,当您.show()不带参数调用时,该元素实际上不会立即显示在您调用它的位置。相反,Chrome在评估当前的 JavaScript 块,将新样式的应用程序排队等待执行;而其他浏览器会立即应用新的样式更改。但是,不会排队。因此,您实际上是在尝试根据 Chrome 设置元素仍然不可见的时间,而当原始元素和新元素相同时,Chrome 不会对其执行任何操作。.attr()srcsrcsrc

相反,您需要做的是确保 jQuery 设置src after display:block被应用。你可以利用setTimeout来实现这个效果:

var src = 'http://i.imgur.com/JfkmXjG.gif';
$(document).ready(function(){
    var $img = $('img');
    $('#target').toggle(
        function(){
            var timeout = 0; // no delay
            $img.show();
            setTimeout(function() {
                $img.attr('src', src);
            }, timeout);
        },
        function(){
            $img.hide();
        }
    );
});

这确保了在应用于元素之后src设置。 display:block

这样做的原因是因为 setTimeout 将函数排队等待稍后执行(不管多久之后),所以该函数不再被认为是 JavaScript 当前“块”的一部分,它为 Chrome 呈现和应用提供了一个间隙第display:block一个,从而使元素在其src属性设置之前可见。

演示:http: //jsfiddle.net/F8Q44/19/

感谢 freenode IRC 的 #jquery 中的 shoky 提供了一个更简单的答案。


或者,您可以强制重绘以刷新批量样式更改。例如,这可以通过访问元素的offsetHeight属性来完成:

$('img').show().each(function() {
    this.offsetHeight;
}).prop('src', 'image src');

演示:http: //jsfiddle.net/F8Q44/266/

于 2014-01-14T02:13:07.167 回答
5

只是因为我仍然时不时需要这个,所以我认为我使用的纯 JS 函数可能对其他人有帮助。这是一种重新启动动画 gif 的纯 JS 方式,无需重新加载。您可以从链接和/或文档加载事件中调用它。

<img id="img3" src="../_Images/animated.gif">

<a onClick="resetGif('img3')">reset gif3</a>

<script type="text/javascript">

// reset an animated gif to start at first image without reloading it from server.
// Note: if you have the same image on the page more than ones, they all reset.
function resetGif(id) {
    var img = document.getElementById(id);
    var imageUrl = img.src;
    img.src = "";
    img.src = imageUrl;
};

</script>

在某些浏览器上,您只需要将 img.src 重置为自身,它就可以正常工作。在 IE 上,您需要在重置之前清除它。这个 resetGif() 从图像 id 中选择图像名称。如果您曾经更改给定 id 的实际图像链接,这很方便,因为您不必记住更改 resetGiF() 调用。

——尼科

于 2015-02-24T15:21:20.873 回答
4

解决方案预加载 gif 并将其从 dom 中取出,然后返回 src(从而避免再次下载)

我刚刚使用 jquery 对其进行了测试以删除该属性,并且效果很好。

例子:

<html>
<head>

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js" type="text/javascript"></script>

<script>

$(function() {

    $('.reset').click(resetGif);

    function resetGif() 
    {
        $('.img1').removeAttr('src', '');
    }
});

</script>

</head>

<body>
    <img class="img1" src="1.gif" />
    <a href="#" class="reset">reset gif</a>
</body>
</html>
于 2012-11-19T12:27:06.987 回答
2

这似乎在 Chrome 中对我有用,它每次在我淡入图像并清除然后重新填充 src 之前运行,我的动画现在每次都从头开始。

var imgsrc = $('#my_image').attr('src');
$('#my_image').attr('src', '');
$('#my_image').attr('src', imgsrc);
于 2014-04-14T15:02:50.830 回答
1

这是我对背景图像的破解:

$(document).on('mouseenter', '.logo', function() {
  window.logo = (window.logocount || 0) + 1;
  var img = new Image();
  var url = "/img/mylogimagename.gif?v=" + window.logocount;
var that = this;
  $(img).load(function(){

     $(that ).css('background-image','url(' + url + ')');
  });
  img.src = url;
});
于 2014-01-25T15:26:36.843 回答
1

我有一个带有动画无循环图像的按钮。我只是用一些 jquery 重新加载图像,这似乎对我有用。

var asdf = $(".settings-button img").attr("src");
$(".settings-button img").attr("src", "").attr("src", asdf);
于 2016-08-30T17:23:36.913 回答
1

我在上述所有解决方案中都遇到了问题。最终起作用的是用透明的 1px gif 临时替换 src:

var transparent1PxGif = 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7';
var reloadGif = function(img) {
    var src = img.src;
    img.src = transparent1PxGif;
    img.offsetHeight; // triggers browser redraw
    img.src = src;
};
于 2017-06-13T08:42:46.693 回答
0

在搜索了许多其他线程后,我遇到了这个线程。David Bell 的帖子让我找到了我需要的解决方案。

我想我会发布我的经验,以防它对任何试图完成我所追求的事情的人有用。这是一个 HTML5/JavaScript/jQuery Web 应用程序,它将通过 PhoneGap 成为 iPhone 应用程序。在 Chrome 中测试。

目标:

  1. 当用户点击/单击按钮 A 时,会出现并播放一个动画 gif。
  2. 当用户点击/单击按钮 B 时,gif 消失。
  3. 当用户再次点击/单击按钮 A 时,在点击/单击按钮 B 后,动画 gif 应该重新出现并从头开始播放。

问题:

  • 在点击/单击按钮 A 时,我将 gif 附加到现有 div。它会玩得很好。
  • 然后,在点击/单击按钮 B 时,我隐藏了容器 div,然后将 gif 的 img src 设置为空字符串 ('')。同样,没问题(也就是说,问题还不明显。)
  • 然后,在点击/单击按钮 A 时,在点击/单击按钮 B 后,我将 gif 的路径重新添加为 src。

    -这不起作用。gif 将显示在按钮 A 的后续点击/点击中......但是,我点击/点击按钮 A 的次数越多,gif 加载和重新开始的次数就越多。也就是说,如果我来回点击/单击按钮 A 然后按钮 B 3 次,gif 会出现,然后启动/停止/启动 3 次......我的整个应用程序开始突突。我猜 gif 被多次加载,即使我在点击/单击按钮 B 时将 src 设置为空字符串。

解决方案:

看了大卫贝尔的帖子后,我得到了答案。

  1. 我定义了一个全局变量(我们称之为 myVar),其中包含容器 div 和图像(带有源路径)。
  2. 在按钮 A 的点击/单击功能上,我将该容器 div 附加到 dom 中现有的父 div。
  3. 在那个函数中,我创建了一个新变量来保存 gif 的 src 路径。

就像大卫建议的那样,我这样做了(加上一个附加):

$('#mainParent').append(myVar);
var imgsrc = $('#my_image').attr('src');
$('#my_image').attr('src', '');
$('#my_image').attr('src', imgsrc);

然后,在按钮 B 的函数中,我将 src 设置为空字符串,然后删除包含 gif 的 div:

$('#my_image').attr('src', '');
$('#mainParent').find('#my_image').remove();

现在,我可以整天点击/单击按钮 A,然后是按钮 B,然后是按钮 A,等等。gif 在点击/单击按钮 A 时加载和播放,然后在点击/单击按钮 B 时隐藏,然后在每次点击按钮 A 时从头开始加载和播放,没有任何问题。

于 2014-10-30T03:16:58.910 回答
0

我为这个问题制定了一个完整的解决方案。可以在这里找到:https ://stackoverflow.com/a/31093916/1520422

我的解决方案在不从网络重新加载图像数据的情况下重新启动动画。

它还强制图像重新绘制以修复发生的一些绘画伪影(在 chrome 中)。

于 2015-06-27T22:02:28.053 回答
0

已经好几年了,我决定重新审视这一点,因为我们有许多新的选择可供我们使用。

我之前回答的问题是,每次您想重新启动它时,它都会强制重新下载 GIF。虽然这对于小文件来说很好,但如果可能的话,它仍然是最好避免的开销。

考虑到这一点,我有了一个新的解决方案,它使用 AJAX 下载 GIF 一次,然后将其转换为数据 URL(通过 FileReader)并将其用作附加随机查询字符串的源。

这样,浏览器只下载一次图像,甚至可以正确缓存它,并且“重置”从该预下载资源中提取。

当然,唯一的问题是您必须确保它已正确加载,然后才能使用它。

演示:http ://adamhaskell.net/misc/numbers/numbers.html

相关代码:

var url = "something.gif"; // fallback until the FileReader is done
function setup() {
    var xhr = new XMLHttpRequest();
    xhr.open("GET",url,true);
    xhr.responseType = "blob";
    xhr.onreadystatechange = function() {
        if( this.readyState == 4) {
            var fr = new FileReader();
            fr.onload = function() {
                url = this.result; // overwrite URL with the data one
            };
            fr.readAsDataURL(this.response);
        }
    };
    xhr.send();
}
function getGIF() {
    return url+"?x="+Math.random();
}
于 2017-07-05T15:15:15.977 回答
0

重置 gif 动画

当浏览器渲染时,属性中img指定的源被缓存到内存中以供将来重用。src这可以提高页面加载/重新加载的速度,并减少网络上的负载。这种行为几乎适合所有人,因为实际上,这是最理想和最需要的选择。

但是,与往常一样,也有例外。我想出了一个基于 dataUrl 的动画更新选项,它解决了几个问题。

解决的问题:

  1. 需要显示带有动画的gif图像,没有循环 ( loop = 1 ),可能具有相同的src. 但是当出现这样一张图片时,它必须播放动画而不改变其他相同图片的动画src。同一张图片应该只从服务器加载一次。堆栈溢出

  2. 重置 gif 动画。

  3. 悬停时开始动画

重置 src 属性

如果我们使用清除src图像属性的解决方案,那么所有具有相同来源的图像都会重播它们的动画。不幸的是,我仍然不完全理解为什么会发生这种情况,但它会干扰正确的工作。

缺点

  • 重置具有相同 src 的所有图像的动画。
  • 移动设备存在问题

优点

  • 简单快捷

使用随机查询修改 url

该解决方案包括在src属性末尾添加一个随机查询参数,以便所有图像都有不同的来源,因此它们将相互独立地进行动画处理。有一个很大的NO:这将导致向服务器不断请求下载图片,因此它们将不再被缓存。而如果我们需要展示 100 张相同的图片,那么就会有 100 个请求向服务器发送。粗糙而艰难,但它总是有效的。

缺点

  • 每张具有唯一查询的图片都将从服务器重新加载。

优点

  • 简单快捷

修改dataUrl(建议解决方案)

数据 URL,以 data: 为前缀的 URL 方案,允许内容创建者在文档中嵌入小文件。在 WHATWG 停用该名称之前,它们以前被称为“数据 URI”。 MDN

本文档中的 dataUrl 结构:

data:[<mediatype>][;base64],<data>

这就是规范中的指示:

dataurl    := "data:" [ mediatype ] [ ";base64" ] "," data
mediatype  := [ type "/" subtype ] *( ";" parameter )
data       := *urlchar
parameter  := attribute "=" value

如果您仔细查看 的描述mediatype,就会发现那里有些奇怪 parameter。但是,还有一个规范

     attribute := token
                  ; Matching of attributes
                  ; is ALWAYS case-insensitive.

     value := token / quoted-string

     token := 1*<any (US-ASCII) CHAR except SPACE, CTLs, or tspecials>

     tspecials :=  "(" / ")" / "<" / ">" / "@" /
                   "," / ";" / ":" / "\" / <">
                   "/" / "[" / "]" / "?" / "="
                   ; Must be in quoted-string,
                   ; to use within parameter values

可以看出,我们可以指定任何参数,主要是它满足上面提出的要求!因此,我们可以在 mediatype 中嵌入一个额外的属性,它不会对图片产生任何影响,但数据 url 会与同一张图片不同。

广义算法:

  1. 我们通过常规请求加载图像,并从 blob 创建的 dataUrl 中删除元数据。
fetch("https://cdn140.picsart.com/330970106009201.gif").then(async (res) => {
  const blob = await res.blob();
  const reader = new FileReader();
  reader.onload = (ev) => {
    // It would be reasonable to remove metadata to the point!
    // But for simplicity, I'm using this implementation.
    const dataUrl = ev.currentTarget.result.replace(
      "data:image/gif;base64",
      ""
    );
  };
  reader.readAsDataURL(blob);
});
  1. 使用属性创建/编辑img元素src"src=data:image/gif;base64;${Math.random()}${dataUrl}"

  2. 就这些!

示例 Vanila JS

const url = "https://cdn140.picsart.com/330970106009201.gif";

function loadImage(src) {
  fetch(src)
    .then((res) => res.blob())
    .then(async(blob) => {
      const reader = new FileReader();
      reader.onload = (ev) => {
        const dataUrl = ev.currentTarget.result.replace("data:image/gif;base64", "")
        const container = document.getElementById("container");
        while (container.firstChild) {
          container.firstChild.remove()
        }
        for (let i = 0; i < 6; i++) {
          const img = document.createElement("img");
          img.setAttribute("src", `data:image/gif;base64;gif-id=${Date.now()}${dataUrl}`)
          container.appendChild(img);
          img.addEventListener('click', ev => {
            img.setAttribute("src", `data:image/gif;base64;gif-id=${Date.now()}${dataUrl}`)
          })
        }
      };
      reader.readAsDataURL(blob);
    });
}

loadImage(url);

function updateImage() {
  const newSrc = document.getElementById("image-src");
  loadImage(document.getElementById("image-src").value);
}
#main {
  display: flex;
  flex-direction: column;
  gap: 5px;
}

img {
  width: 128px;
  height: 128px;
  padding: 5px;
}
<div id="main">
  <label>Change gif url if current will become unavailable </label>
  <input id="image-src" value="https://cdn140.picsart.com/330970106009201.gif"></input>
  <button onclick="updateImage()">Update image source attribute</button>
  <span>Click to reset!</span>
  <div id="container">
  </div>
</div>

示例反应

import React, { useState, useRef } from "react";

function App() {
  const [stars, setStars] = useState(0);
  const [data, setData] = useState(null);

  const ref = useRef(null);

  React.useEffect(() => {
    fetch("https://cdn140.picsart.com/330970106009201.gif")
      .then((res) => res.blob())
      .then(async (text) => {
        const reader = new FileReader();
        reader.onload = (ev) => {
          setData(ev.currentTarget.result.replace("data:image/gif;base64", ""));
        };
        reader.readAsDataURL(text);
      });
  }, []);

  return (
    <React.Fragment>
      <p onClick={() => setStars((s) => s + 1)}>+</p>
      {data &&
        new Array(stars).fill().map((s, ind) => {
          return <Star src={data} key={ind}></Star>;
        })}
      <p onClick={() => setStars((s) => (s === 0 ? 0 : s - 1))}>-</p>
    </React.Fragment>
  );
}

export function Star(props) {
  const [id] = useState(Math.random());

  return (
    <img
      className="icon"
      src={`data:image/gif;base64;gif-id=${id}` + props.src}
      alt="animated star"
    />
  );
}

export default App;
于 2021-08-26T07:30:25.423 回答