Firefox中img.onload未触发、Promise始终Pending问题求助
问题:Firefox中Promise.all始终处于pending状态,无法完成Canvas图片叠加
我正在实现从本地存储加载一张或多张图片,叠加到另一张图片后绘制到Canvas的功能。我将每张图片的加载过程封装为Promise,仅在图片加载完成后resolve;待所有Promise都resolve后,再发送生成的图像数据。
Chrome浏览器中可以成功生成叠加图像,控制台日志显示Promise.all的回调已执行;但Firefox浏览器中无法生成叠加图像,代码从未进入Promise.all的回调,Promise始终处于pending状态。
核心实现代码
var Promises = []; for (const selectedOverlayImage of selectedOverlayImages.values()) { Promises.push(new Promise((resolve, reject) => { const overlayImg = new Image(); overlayImg.onerror = () => { console.log(`Image ${selectedImage.value} loading error`); reject(); }; overlayImg.onload = () => { canvasContext.drawImage(overlayImg, 0, 0, canvas.width, canvas.height); resolve(); }; overlayImg.src = `overlayImages/${selectedOverlayImage.value}.png`; console.log(selectedOverlayImage.value); })); } console.log("1"); var ret = Promise.all(Promises).then(() => { console.log("IN"); let imageData = canvas.toDataURL('image/png'); fetch("http://localhost:8000/picture-editing.php", { //Call php to create picture in database method: "POST", headers: {"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"}, body: `imageData=${imageData}` }); alert('Images successfully loaded!'); //This creates a gain of time before the refresh, allowing the new picture to be present more often after refresh window.location.reload(); }).catch((error) => {console.log(error);}); console.log("2"); console.log(ret); setTimeout(() => { console.log('the queue is now empty'); console.log(ret); });
错误复现步骤
- 在index.php中写入以下代码:
<h1 class="pageTitle">Create Picture</h1> <form method="get"> <input name="modeOfPicture" type="submit" value="webcam"/> <input name="modeOfPicture" type="submit" value="upload"/> </form> <div class="wrapperPictureEditing"> <div id="getPicture" class="block1PictureEditing"></div> <canvas id="takePictureCanvas" style="display:none;" width="320" height="240"></canvas> <form id="takePictureButton" class="block2PictureEditing"> <p>Choose Overlay Image:</p> <label><input type="checkbox" name="overlay" value="wine"> Wine</label><br> <button type="submit">Take photo</button><br> </form> <script> function displaySelectedPicture(input) { var selectedPictureDisplay = document.getElementById('selectedPictureDisplay'); if (input.files && input.files[0]) { var reader = new FileReader(); reader.onload = function (e) { selectedPictureDisplay.src = e.target.result; }; reader.readAsDataURL(input.files[0]); } else { selectedPictureDisplay.removeAttribute('src') } } function takePicture(camera) { const takePictureButton = document.getElementById('takePictureButton'); takePictureButton.addEventListener("submit", () => { let canvas = document.getElementById('takePictureCanvas'); let canvasContext = canvas.getContext('2d'); selectedOverlayImages = document.querySelectorAll('input[name="overlay"]:checked'); console.log(selectedOverlayImages); if (camera) { const stream = document.querySelector('video'); canvasContext.drawImage(stream, 0, 0, canvas.width, canvas.height); } else { const selectedImage = document.getElementById('selectedPictureDisplay'); canvasContext.drawImage(selectedImage, 0, 0, canvas.width, canvas.height); } var Promises = []; for (const selectedOverlayImage of selectedOverlayImages.values()) { Promises.push(new Promise((resolve, reject) => { const overlayImg = new Image(); overlayImg.onerror = () => { console.log(`Image ${selectedImage.value} loading error`); reject(); }; overlayImg.onload = () => { canvasContext.drawImage(overlayImg, 0, 0, canvas.width, canvas.height); resolve(); }; overlayImg.src = `image.png`; console.log(selectedOverlayImage.value); })); } console.log("1"); var ret = Promise.all(Promises).then(() => { console.log("IN"); let imageData = canvas.toDataURL('image/png'); alert('Images successfully loaded!'); //This creates a gain of time before the refresh, allowing the new picture to be present more often after refresh window.location.reload(); }).catch((error) => {console.log(error);}); console.log("2"); console.log(ret); setTimeout(() => { console.log('the queue is now empty'); console.log(ret); }); }); } function setupVideoStream(stream) { const video = document.querySelector('video'); video.srcObject = stream; video.play(); } function errorMsg(msg, error) { const errorElement = document.getElementById('error'); errorElement.innerHTML += `${msg}`; if (typeof error !== 'undefined') { console.error(error); } } function streamError(error) { if (error.name === 'NotAllowedError') { errorMsg('Permissions have not been granted to use your camera' + ', you need to allow the page access to your camera in ' + 'order to take pictures. Instead you can upload an image.'); } else if (error.name === "NotFoundError") { errorMsg('No camera has been found on this device. ' + 'Instead you can upload an image.'); } else { errorMsg(`getUserMedia error: ${error.name}`, error); } } async function init() { var getPictureHTML = document.getElementById('getPicture'); var modeOfPicture = (new URLSearchParams(document.location.search)).get('modeOfPicture'); if (modeOfPicture === null || modeOfPicture === "webcam") { try { const stream = await navigator.mediaDevices.getUserMedia({video: true}); getPictureHTML.insertAdjacentHTML('afterbegin', "<video width='320' height='240' autoplay></video><br>"); setupVideoStream(stream); takePicture(true); } catch (error) { getPictureHTML.insertAdjacentHTML('afterbegin', "<p id='error' class='error'></p>" + "<label><input type='file' name='selectedPicture' accept='image/*' " + "onchange='displaySelectedPicture(this);'><br>Upload image<br></label>" + "<br><img id='selectedPictureDisplay' width='320' height='240'/>"); streamError(error); takePicture(false); } } else { getPictureHTML.insertAdjacentHTML('afterbegin', "<label><input type='file' name='selectedPicture' accept='image/*' " + "onchange='displaySelectedPicture(this);'><br>Upload image<br></label>" + "<br><img id='selectedPictureDisplay' width='320' height='240'/>"); takePicture(false); } } init(); </script>
- 在同一目录下放置名为
image.png的图片 - 在该目录下执行命令启动服务器:
php -S localhost:8000 - 分别在Chrome和Firefox中访问
localhost:8000,勾选Wine叠加图后点击拍照,成功时控制台应输出"IN"
内容的提问来源于stack exchange,提问作者artainmo
相关产品推荐
相关产品推荐

