如何在p5.js中通过预加载JSON同步加载依赖图片?
问题
我有一个包含待加载图片文件名的JSON文件:
{ "images": ["1.jpg", "2.jpg", "3.jpg"] }
我用p5.js的preload()函数,通过loadJSON()加载该JSON文件,但loadJSON是异步的,没法立即拿到数据。进入setup()时虽然已经拿到JSON内容,但用loadImage()加载图片(耗时更长且同样异步)时,代码会在图片加载完成前就执行,引发问题。
我试过用回调函数递增计数器,等计数器达到图片总数时判定全部加载完成,示例代码如下:
let imgData; let numImagesLoaded = 0; function preload() { imgData = loadJSON("images.json") } function setup() { // 此处本应将图片存入数组,暂不关注此部分 let img; for (let filename of imgData.images) { // 图片加载完成时递增计数器 loadImage(filename, img => { numImagesLoaded += 1; }); } // 循环等待直到所有图片加载完成 while (numImagesLoaded < imgData.images.length) {} // 循环结束,所有图片已就绪 }
但这种方法很不规范。请问能不能在preload()中同步调用loadJSON(),阻塞执行直到JSON数据可用(耗时极短),随后在preload()内加载图片,确保进入setup()时所有资源都已加载完成?我觉得preload()的设计逻辑本应如此,但这种有依赖关系的预加载场景,肯定有简便的标准实现方式。
标准解决方案
你完全可以利用p5.js的preload()机制解决这个问题——p5.js在preload()中会自动阻塞代码执行,直到所有通过p5加载函数发起的请求完成后,才会进入setup()。
正确实现代码
let images = []; // 存储加载完成的图片 let imgData; function preload() { // 在preload中调用loadJSON,p5会自动等待JSON加载完成才继续执行 imgData = loadJSON("images.json"); // 拿到JSON数据后,循环加载所有图片 // 每调用一次loadImage,p5都会将其加入加载队列,直到全部完成才进入setup for (let filename of imgData.images) { images.push(loadImage(filename)); } } function setup() { createCanvas(800, 600); // 此时所有图片都已加载完毕,可以直接使用 // 示例:依次绘制所有图片 for (let i = 0; i < images.length; i++) { image(images[i], i * 100, 0); } }
为什么你的原方法不可行
你写的while循环会导致浏览器主线程阻塞:JavaScript是单线程语言,这个无限循环会占满线程资源,导致loadImage的回调函数根本没有机会执行,numImagesLoaded永远不会增加,最终陷入死循环,浏览器会假死。
额外说明
如果需要自定义加载完成后的逻辑(比如记录加载进度),可以使用loadImage的回调函数,但依然不需要手动维护计数器——p5的preload()会自动等待所有加载任务结束:
function preload() { imgData = loadJSON("images.json"); for (let filename of imgData.images) { loadImage(filename, (img) => { images.push(img); console.log(`已加载图片:${filename}`); }); } }
此时setup()依然会等所有图片加载完成后才执行,回调只是额外的通知逻辑。
内容的提问来源于stack exchange,提问作者user20396240

