重置 gif 动画
当浏览器渲染时,属性中img
指定的源被缓存到内存中以供将来重用。src
这可以提高页面加载/重新加载的速度,并减少网络上的负载。这种行为几乎适合所有人,因为实际上,这是最理想和最需要的选择。
但是,与往常一样,也有例外。我想出了一个基于 dataUrl 的动画更新选项,它解决了几个问题。
解决的问题:
需要显示带有动画的gif图像,没有循环 ( loop = 1 ),可能具有相同的src
. 但是当出现这样一张图片时,它必须播放动画而不改变其他相同图片的动画src
。同一张图片应该只从服务器加载一次。堆栈溢出
重置 gif 动画。
悬停时开始动画
重置 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 会与同一张图片不同。
广义算法:
- 我们通过常规请求加载图像,并从 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>
示例反应
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;