javascript - 使用图片标签预加载图像以防止闪烁
问题描述
在我的代码中,我有一张标签图片,我更改了它的内容以加载其他图片。但我注意到闪烁。这是我的玩具代码。
setTimeout(replaceImage,3000);
function replaceImage(){
let pictureNode = document.getElementById('picture-carousel');
let markup = `
<source media="(min-width:50em)" srcset="http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/debris2_blue.png">
<source media="(min-width:25em)" srcset="http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/debris2_brown.png">
<source srcset="http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/debris3_blue.png">
<img src='http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/debris2_blue.png' title='Bla bla' alt='bla bla'>
`
pictureNode.innerHTML = markup;
}
<div>
<picture id='picture-carousel'>
<source media="(min-width:50em)" srcset="http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/asteroid_blend.png">
<source media="(min-width:25em)" srcset="http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/asteroid_blue.png">
<source srcset="http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/asteroid_brown.png">
<img src='http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/asteroid_blend.png' title='Bla bla' alt='bla bla'> <!-- fallback -->
</picture>
</div>
我知道要为图像添加闪烁,我可以像这样预加载图像:
function loadImage(){
var img = new Image(),
x=document.getElementById('myImg');
img.onload=function(){
x.src = img.src;
}
img.src ='http://codeskulptor-demos.commondatastorage.googleapis.com/GalaxyInvaders/back03.jpg'; // this is the new url img to load';
}
<img src='http://codeskulptor-demos.commondatastorage.googleapis.com/GalaxyInvaders/back02.jpg' id='myImg'>
<button onclick='loadImage();'>Load new Image</button>
当我使用图片标签而不是 img 标签时,如何预加载图像?
解决方案
以数据的形式构建您的<picture>
元素。这样我们就可以构建一个函数来接收数据并逐个构建元素。
这样做时,我们可以检查media
每个单独<source>
标签的值。使用该值,我们可以运行window.matchMedia()
以确定元素上的媒体查询是否与当前视图匹配。例如,您可以检查min-width: 50em
当前是否为真。
有了这些知识,您就可以确定要预加载的图像。您只需加载与媒体查询匹配的图像。
检查下面的示例,它执行上述过程。如果您有任何问题,请与我们联系。
const pictureData = [
{
tag: 'source',
attributes: {
media: '(min-width: 50em)',
srcset: 'http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/debris2_blue.png'
}
},
{
tag: 'source',
attributes: {
media: '(min-width: 25em)',
srcset: 'http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/debris2_brown.png'
}
},
{
tag: 'source',
attributes: {
srcset: 'http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/debris3_blue.png'
}
},
{
tag: 'img',
attributes: {
src: 'http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/debris2_blue.png',
title: 'Bla bla',
alt: 'bla bla'
}
}
];
/**
* Loads an image and returns a promise.
* The promise will resolve on the load event of the image.
*/
const preloadImage = url => new Promise((resolve, reject) => {
const image = new Image();
image.onload = () => resolve();
image.onerror = error => reject(error);
image.src = url;
})
/**
* Takes in an object of pictureData.
* It then creates a <picture> element and the children specified in the object.
* Whenever a tag is source, it will use the media value, if present,
* to detect if the image is elligable to be loaded.
* If so it will preload the image and resolve the promise after it has been loaded.
*/
const buildPicture = async pictureData => {
const picture = document.createElement('picture');
let imageToPreload = null;
// Loop through all the data and start constructing.
for (const { tag, attributes } of pictureData) {
const child = document.createElement(tag);
if (imageToPreload === null) {
// Check source tags for media queries.
if (tag === 'source') {
const { media, srcset } = attributes;
// If there is a media query, check it.
if (!media) {
const { matches } = window.matchMedia(media);
// If the query matches, use this as the image.
if (matches === true) {
imageToPreload = srcset;
}
// No media query found.
} else {
imageToPreload = srcset;
}
}
// If no match has been found yet, just load the image.
if (tag === 'img') {
const { src } = attributes;
imageToPreload = src;
}
}
// Set all the properties and values of the attributes.
for (const [ property, value ] of Object.entries(attributes)) {
child.setAttribute(property, value);
}
picture.append(child);
}
if (imageToPreload !== null) {
try {
await preloadImage(imageToPreload);
} catch (error) {
console.error(error);
}
}
return picture;
};
setTimeout(async () => {
const currentPicture = document.getElementById('picture-carousel');
const picture = await buildPicture(pictureData);
currentPicture.replaceWith(picture);
}, 2000);
<picture id='picture-carousel'>
<source media="(min-width:50em)" srcset="http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/asteroid_blend.png">
<source media="(min-width:25em)" srcset="http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/asteroid_blue.png">
<source srcset="http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/asteroid_brown.png">
<img src='http://commondatastorage.googleapis.com/codeskulptor-assets/lathrop/asteroid_blend.png' title='Bla bla' alt='bla bla'>
<!-- fallback -->
</picture>
推荐阅读
- javascript - 每1秒AJAX请求不阻塞网页,如何处理
- c# - raycast 用于防止摄像机穿过天花板和地板使玩家卡住 Unity
- jira - 如何使用 Jira Rest API 获取每个测试的前置条件
- javascript - 如何获得品牌属性的价值?
- javascript - 如何从网站 javascript 中获取价值?
- python - 重复高阶函数
- javascript - Angular - 输入预测值调用 api
- javascript - NodeJS:如何使用 Loopback4 建立多对多关系
- windows - 在 Windows 容器模式下的 Windows 10 上,Docker 拉取失败
- reactjs - 警告:无法对未安装的组件执行 React 状态更新......componentWillUnmount 方法