#javascript #reactjs #canvas #async-await #html5-canvas
Вопрос:
Я работаю над приложением nft-art-generator, приложение генерирует несколько и разных HTML-холстов (в виде изображения). Каждый холст представляет собой комбинацию из 3 изображений, другими словами, на каждом холсте должно быть нарисовано 3 изображения, и после рисования на холсте я загружаю его.
Проблема в том, что когда я проверяю загруженный холст, я получаю тот же холст. Чтобы быть более точным, я получаю, что последний сгенерированный холст был загружен несколько раз, как будто все сгенерированные ранее холсты исчезли.
Я думаю, это произошло потому, что что-то в моем коде работает асинхронно.
Вот мой React.js Код
const generateCollection = async()=>{
//In this exemple i want to generate 2 canvas only!!
for(var k = 1; k <= 2; k ){
drawLayer(data, k)
}
}
const drawLayer = (data, index)=>{
var canvas = document.getElementById('myCanvas');
var context = canvas.getContext('2d')
let sources = {};
//Get some random images url
const objURLs = getImagesURLs(data, index);
objURLs.forEach((url,i)=>{
const key = `image${i 1}`;
Object.assign(sources, {[key]: url})
})
loadImages(sources, function(images) {
objURLs.forEach((url,i)=>{
if (Boolean(url)) {
context.drawImage(images[`image${i 1}`], 0, 0, 230, 230)
}
})
});
setTimeout(()=> download(canvas.current, index), 1000);
}
const loadImages = (sources, callback)=> {
var images = {};
var loadedImages = 0;
var numImages = 0;
// get num of sources
for(var src in sources) {
numImages ;
}
for(var src in sources) {
images[src] = new Image();
images[src].onload = function() {
if( loadedImages >= numImages) {
console.log("!! ", images )
callback(images);
}
};
images[src].src = sources[src];
}
}
const download = (canvas, index)=>{
console.log("Download: ", canvas)
var url = canvas.toDataURL("image/png");
var link = document.createElement('a');
link.download = `img_${index}.png`;
link.href = url;
link.click();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<canvas
id="myCanvas"
ref={canvas}
width={230}
height={230}
/>
<Button onClick={()=>generateCollection()} ><Button>
Вот живой демонстрационный код, основанный на
https://jsfiddle.net/vprdhomf/1/
I will appreciate any help, or suggestion!!
Комментарии:
1. (1) загрузка изображений происходит асинхронно, поэтому
callback
вызывается асинхронно. (2) если все комбинации canvas были завершены менее чем за секунду,setTimeout
вызовdownload
только видит холст в его конечном состоянии — либо дождитесь каждой загрузки перед созданием следующей загрузки, либо используйте несколько элементов canvas. (3) Если изображения загружаются через домены, а сервер не разрешает междоменные запросы ,canvas.toDataURL
генерирует исключение. Из-за этого исправление скрипки кажется невозможным.2. Большое вам спасибо, ваш ответ был ясным и очень полезным !!. То, что я сделал, это ожидание каждой загрузки перед созданием следующей загрузки. Функция setTimeout в этом случае была бесполезной.
Ответ №1:
Снять с вопроса комментарий:
- Загрузка изображений происходит асинхронно, поэтому
callback
называется асинхронной. - Если все комбинации холстов (из разных наборов нескольких изображений) были завершены менее чем за секунду,
setTimeout
вызовdownload
только видит холст в его конечном состоянии. Чтобы решить эту проблему, вы можете либо- Дождитесь начала каждой загрузки, прежде чем создавать следующую загрузку, или
- Используйте несколько элементов canvas или, возможно,
- Храните URL-адреса данных в массиве для каждой комбинации canvas для последующей загрузки.
- Если изображения загружаются через домены, а сервер не разрешает междоменные запросы,
canvas.toDataURL
генерируется исключение безопасности. Из-за этого исправление скрипки с использованием приведенных в ней URL-адресов стало невозможным.