提问人:Fabrício Matté 提问时间:5/24/2012 最后编辑:Jimbo JonnyFabrício Matté 更新时间:8/30/2021 访问量:58528
在Chrome上重置带有display:none的GIF动画的正确方法
Proper way to reset a GIF animation with display:none on Chrome
问:
标题是不言自明的,但我将提供关于此事的分步视图。希望我不是第一个在 Webkit/Chrome 上注意到这个(显然)错误的人。
我想重置GIF动画。到目前为止,我看到的所有示例要么简单地将图像设置为自身,要么将其设置为空字符串,然后再次将原始字符串设置为原始字符串。src
src
请看一下这个 JSFiddle 以供参考。GIF 在 IE、Firefox 和 Chrome 上重置得非常好。
我遇到的问题是图像仅在 Google Chrome 上。display:none
检查此 JSFiddle。GIF 在显示在页面中之前在 IE 和 Firefox 上可以正常重置,但 Chrome 只是拒绝重置其动画!
到目前为止,我尝试过:
- 像在 Fiddle 中一样将其设置为自身,在 Chrome 中不起作用。
src
- 将 设置为空字符串并将其恢复为默认值也不起作用。
src
- 在图像周围放置一个包装器,清空容器并将图像放回其中,也不起作用。
.html('')
- 在设置之前通过或更改图像也不起作用。
display
.show()
.fadeIn()
src
到目前为止,我发现的唯一解决方法是将图像保留为默认值,并在必要时通过不透明度、高度和可见性来操纵它以模拟行为。display
.animate()
.css()
display:none
这个问题的主要原因(上下文)是我想在页面中淡入淡出之前重置ajax加载器GIF。
所以我的问题是,有没有一种正确的方法可以重置 GIF 图像的动画(避免 Chrome 的“错误”),或者它实际上是一个错误?display:none
(附言。您可以更改小提琴中的 GIF,以获得更合适/更长的动画 gif 以进行测试)
答:
“重置”GIF 最可靠的方法是附加随机查询字符串。但是,这确实意味着每次都会重新下载 GIF,因此请确保它是一个小文件。
// reset a gif:
img.src = img.src.replace(/\?.*$/,"")+"?x="+Math.random();
评论
display:none
background-image
img.css('background-image', img.css('background-image').replace(".gif", ".gif" + "?x=" + Math.random()));
img.src=img.src
对我来说似乎已经足够了(我使用的是 Chrome 48)。任何人都可以与其他浏览器确认吗?
此解决方案预加载 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>
Chrome 处理样式更改的方式与其他浏览器不同。
在 Chrome 中,当您在没有参数的情况下调用时,该元素实际上不会立即显示在您调用它的位置。相反,Chrome 会在评估当前的 JavaScript 块后,将新样式的应用程序排队等待执行;而其他浏览器会立即应用新的样式更改。但是,不会排队。因此,您实际上是在尝试根据 Chrome 设置元素何时仍然不可见,并且当原始元素和新元素相同时,Chrome 不会对此采取任何措施。.show()
.attr()
src
src
src
相反,您需要做的是确保应用了 jQuery 设置 after。你可以利用 来实现这个效果:src
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/
感谢 shoky in #jquery freenode IRC 提供了更简单的答案。
或者,您可以强制重绘以刷新批处理的样式更改。例如,可以通过访问元素的属性来完成此操作:offsetHeight
$('img').show().each(function() {
this.offsetHeight;
}).prop('src', 'image src');
演示:http://jsfiddle.net/F8Q44/266/
评论
$('img').prop('src',function(){return this.src;})
是这个答案的最小版本
这是我对背景图片的技巧:
$(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;
});
评论
这似乎在 Chrome 中对我有用,它每次都在我淡入图像之前运行并清除然后重新填充 src,我的动画现在每次都从头开始。
var imgsrc = $('#my_image').attr('src');
$('#my_image').attr('src', '');
$('#my_image').attr('src', imgsrc);
在搜索了许多其他线程后,我遇到了这个线程。David Bell 的帖子引导我找到了我需要的解决方案。
我想我会发布我的经验,以防它对任何试图完成我所追求的事情的人都有用。这适用于 HTML5/JavaScript/jQuery Web 应用程序,该应用程序将通过 PhoneGap 成为 iPhone 应用程序。在 Chrome 中进行测试。
目标:
- 当用户点击/单击按钮 A 时,会出现并播放动画 gif。
- 当用户点击/单击按钮 B 时,gif 会消失。
- 当用户再次点击/单击按钮 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 设置为空字符串。
解决方案:
看完David Bell的帖子后,我得出了答案。
- 我定义了一个全局变量(我们称之为 myVar),它保存了容器 div 和图像(带有源路径)。
- 在按钮 A 的点击/单击功能中,我将该容器 div 附加到 dom 中现有的父 div。
- 在该函数中,我创建了一个新变量来保存 gif 的 src 路径。
就像 David 建议的那样,我这样做了(加上一个附加):
$('#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 的后续点击中从头开始加载和播放,没有任何问题。
仅仅因为我仍然时不时地需要它,我认为我使用的纯 JS 函数可能对其他人有所帮助。这是一种纯粹的 JS 方式,无需重新加载动画 gif。您可以从链接和/或文档加载事件中调用它。
<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() 调用。
--尼科
评论
img.src = "#";
我为这个问题制定了一个完整的解决方案。可以在这里找到它: https://stackoverflow.com/a/31093916/1520422
我的解决方案重新启动动画,而无需从网络重新加载图像数据。
它还强制图像重新绘制,以修复发生的一些绘画伪影(在镀铬中)。
我有一个按钮,里面有一个动画无循环图像。我只是用一些jquery重新加载图像,这似乎对我有用。
var asdf = $(".settings-button img").attr("src");
$(".settings-button img").attr("src", "").attr("src", asdf);
我在使用上述所有解决方案时都遇到了问题。最终起作用的是用透明的 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;
};
已经有好几年了,我决定重新审视这个问题,因为我们有许多新的选择可供我们使用。
我之前回答的问题是,每次您想重新启动 GIF 时,它都会强制重新下载 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();
}
评论
data:
重置 gif 动画
当浏览器渲染时,属性中指定的源将缓存到内存中以备将来重用。这可以提高页面加载/重新加载的速度,并减少网络上的负载。这种行为几乎适合所有人,因为实际上,这是最优化和最需要的选择。img
src
但是,与往常一样,也有例外。我想出了一个基于 dataUrl 的动画更新选项,可以解决几个问题。
解决的问题:
需要显示带有动画的 gif 图像,没有循环 (loop = 1),这可能具有相同的 .但是当出现一张这样的图片时,它有必要播放动画而不改变其他图片的动画。同一张图片只能从服务器加载一次。堆栈溢出
src
src
重置 gif 动画。
悬停时开始动画
重置 src 属性
如果我们使用清除图像属性的解决方案,那么具有相同源的所有图像都将重播其动画。不幸的是,我仍然不完全理解为什么会发生这种情况,但它干扰了正确的工作。src
缺点
- 使用相同的 src 重置所有图像的动画。
- 移动设备存在问题
优点
- 简单快捷
使用随机查询修改网址
此解决方案包括在属性的末尾添加一个随机查询参数,以便所有图像都具有不同的来源,因此它们将彼此独立地进行动画处理。有一个很大的 NO:这将导致不断请求服务器下载图片,因此它们将不再被缓存。如果我们需要显示 100 张相同的图片,那么将有 100 个请求到服务器。粗糙而坚韧,但它总是有效。src
缺点
- 每张具有唯一查询的图片都将从服务器重新加载。
优点
- 简单快捷
修改 dataUrl(建议的解决方案)
数据 URL,以 data: 方案为前缀的 URL,允许内容创建者在文档中内嵌小文件。它们以前称为“数据 URI”,直到 WHATWG 停用该名称。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 会与同一图像不同。
广义算法:
- 我们通过常规请求加载图像,并从 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);
});
创建/编辑具有属性的元素
img
src
"src=data:image/gif;base64;${Math.random()}${dataUrl}"
仅此而已!
示例: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>
示例 React
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;
评论