【发布时间】:2019-12-11 23:06:53
【问题描述】:
我想要的是以下内容:
- 预加载图像(最多 40-50 个),并在完成后进行回调(例如警报)(我认为这部分已经很好)
- 要点:在同一个
div中逐张显示图片,尽可能精确地记录显示时间(同时任何图片都可以显示多次) - 在另一个
div中显示相同的图像(如果我有第 2 点,这应该相当容易)
更多背景信息:这是一个响应时间实验,在线参与者看到图片并对其做出关键响应(并测量他们的响应时间)。 (另外,显示之间至少有200-500毫秒的时间,所以每次显示前准备一点时间也没问题。)
我已经有一个完整的工作代码,这是我从以前的各种答案中整理出来的:有人可以验证我的代码是否有意义,并且我没有其他办法可以提高它的效率吗?
让我感到困惑的一件事是“appendChild”在浏览器中的确切作用。 (换句话说:我知道该元素将被添加到页面中,但我不知道这对浏览器意味着什么。)目前我在实际显示之前将图像附加一点(100 毫秒) ,然后我通过将不透明度设置为 0 到 1 (which is presumed to be the most optimal method for precise timing) 来实现。然后在显示下一张图像之前删除图像(清空div)。但我想知道这样做是否有任何意义。例如,如果我在“预加载阶段”中附加了所有图像,并且在不需要它们时将它们的不透明度设置为 0,该怎么办?这对性能有何影响?或者,如果(尽管我不想这样做)我在不透明度更改之前附加它们怎么办? “附加”是否需要任何时间,还是会以任何方式影响即将到来的不透明度更改的时间? (编辑:现在我已经有了主要问题的答案,但如果能得到关于这一点的解释仍然会很好。)
明显的问题是我不能真正准确地测量显示时间(没有外部硬件),所以我必须依靠“似乎有意义”的东西。
无论如何,下面是我的代码。
var preload = ['https://www.gstatic.com/webp/gallery/1.jpg', 'https://www.gstatic.com/webp/gallery3/1.png', 'https://www.gstatic.com/webp/gallery/4.jpg', 'https://www.gstatic.com/webp/gallery3/5.png'];
var promises = [];
var images = {};
// the function below preloads images; its completion is detected by the function at the end of this script
for (var i = 0; i < preload.length; i++) {
(function(url, promise) {
var filename = url.split('/').pop().split('#')[0].split('?')[0];
images[filename] = new Image();
images[filename].id = filename; // i need an id to later change its opacity
images[filename].style.opacity = 0;
images[filename].style.willChange = 'opacity';
images[filename].style['max-height'] = '15%';
images[filename].style['max-width'] = '15%';
images[filename].onload = function() {
promise.resolve();
};
images[filename].src = url;
})(preload[i], promises[i] = $.Deferred());
}
// the function below does the actual display
function image_display(img_name, current_div) {
window.warmup_needed = true;
document.getElementById(current_div).innerHTML = ''; // remove previous images
document.getElementById(current_div).appendChild(images[img_name]); // append new image (invisible; opacity == 0)
chromeWorkaroundLoop(); // part of the timing mechanism
setTimeout(function() {
document.getElementById(img_name).style.opacity = 1; // HERE i make the image visible
requestPostAnimationFrame(function() {
window.stim_start = now(); // HERE i catch the time of display (image painted on screen)
warmup_needed = false;
});
}, 100); // time needed for raF timing "warmup"
}
// below are functions for precise timing; see https://stackoverflow.com/questions/50895206/
function monkeyPatchRequestPostAnimationFrame() {
const channel = new MessageChannel();
const callbacks = [];
let timestamp = 0;
let called = false;
channel.port2.onmessage = e => {
called = false;
const toCall = callbacks.slice();
callbacks.length = 0;
toCall.forEach(fn => {
try {
fn(timestamp);
} catch (e) {}
});
};
window.requestPostAnimationFrame = function(callback) {
if (typeof callback !== 'function') {
throw new TypeError('Argument 1 is not callable');
}
callbacks.push(callback);
if (!called) {
requestAnimationFrame((time) => {
timestamp = time;
channel.port1.postMessage('');
});
called = true;
}
};
}
if (typeof requestPostAnimationFrame !== 'function') {
monkeyPatchRequestPostAnimationFrame();
}
function chromeWorkaroundLoop() {
if (warmup_needed) {
requestAnimationFrame(chromeWorkaroundLoop);
}
}
// below i execute some example displays after preloading is complete
$.when.apply($, promises).done(function() {
console.log("All images ready!");
// now i can display images
// e.g.:
image_display('1.jpg', 'my_div');
// then, e.g. 1 sec later another one
setTimeout(function() {
image_display('5.png', 'my_div');
}, 1000);
});
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"></script>
<div id='my_div'></div>
【问题讨论】:
标签: javascript image performance timing preload