resize картинки в браузере

Тугай

Новичок
на форме выбираем .jpg файл, делаем resize на javascript через canvas, дальше из уменьшеного еще и thumbnail формируем
код такой:
Код:
imgInput.addEventListener('change', function changeImg(e) {
    if (e.target.files) {
        let imageFile = e.target.files[0];
        var reader = new FileReader();
        img_preview.src = "";

        document.getElementById("img_name").value = e.target.files[0].name;

        reader.onload = function (e) {
            var img = document.createElement("img");
            img.onload = function (event) {
                const MAX_WIDTH = 1024;
                const MAX_HEIGHT = 768;

                const THUMB_WIDTH = 128;
                const THUMB_HEIGHT = 96;

                var width = img.width;
                var height = img.height;

                if (width > height) {
                    if (width > MAX_WIDTH) {
                        height = height * (MAX_WIDTH / width);
                        width = MAX_WIDTH;
                    }
                } else {
                    if (height > MAX_HEIGHT) {
                        width = width * (MAX_HEIGHT / height);
                        height = MAX_HEIGHT;
                    }
                }
                var canvas = document.createElement("canvas");
                canvas.width = width;
                canvas.height = height;

                var ctx = canvas.getContext("2d");
                ctx.drawImage(img, 0, 0, width, height);
                const dataUrl = canvas.toDataURL(imageFile.type, 0.8);
                img_preview.src = dataUrl;
                img_file_data.value = dataUrl.slice(23,);

                // thumb
                if (width > height) {
                    if (width > THUMB_WIDTH) {
                        height = height * (THUMB_WIDTH / width);
                        width = THUMB_WIDTH;
                    }
                } else {
                    if (height > THUMB_HEIGHT) {
                        width = width * (THUMB_HEIGHT / height);
                        height = THUMB_HEIGHT;
                    }
                }
                setTimeout(function () {
                canvas = document.createElement("canvas");
                canvas.width = width;
                canvas.height = height;
                ctx = canvas.getContext("2d");
                ctx.drawImage(img_preview, 0, 0, width, height);
                const dataThumbUrl = canvas.toDataURL(imageFile.type, 0.8);
                document.getElementById("img_thumb").src = dataThumbUrl;
                img_thumb_file_data.value = dataThumbUrl.slice(23,);
                }, 500);
            }
            img.src = e.target.result;
        }
        reader.readAsDataURL(imageFile);
    }
});
Если не писать setTimeout(function () { ...
для формирования thumb то он получается черный, как будто img_preview.src = dataUrl; не отрабатывает.

Может кто в курсе как такое правильно делается ?
 

WMix

герр M:)ller
Партнер клуба
точно не скажу, но вроде ты опираешься на img_preview
ну те вместо setIntervall можно к событию прицепиться img_preview.onload = function (event) { ... }
 

Тугай

Новичок
спасибо, с толку сбило то, что в dataURL уже лежат данные картинки, но может же быть и обычный url и загрузка асинхроная в любом случае.
с img_preview.onload теперь корректный код будет :)
 

AnrDaemon

Продвинутый новичок
Вообще говоря, не стоит несколько операций заворачивать в одну функцю. Такой код невозможно тестировать.
 

Тугай

Новичок
у меня требования windows xp chrome 49

поэтому promise и async/await не вариант и усложнят только
логика асинхронная не привычна, а так цепочка input -> img -> img_priview -> img_thumb ну не сложная
пишешть reader.read а отрабатывет в onload, специфика видимо :)

оставил такой вариант:
Код:
imgInput.addEventListener('change', function changeImg(e) {
    if (e.target.files.length) {
        let imageFile = e.target.files[0];
        var reader = new FileReader();
        img_preview.src = "";

        document.getElementById("img_name").value = e.target.files[0].name;

        reader.onload = function (e) {
            var img = document.createElement("img");
            img.onload = function (event) {
                const MAX_WIDTH = 1024;
                const MAX_HEIGHT = 768;

                var width = img.width;
                var height = img.height;

                if (width > height) {
                    if (width > MAX_WIDTH) {
                        height = height * (MAX_WIDTH / width);
                        width = MAX_WIDTH;
                    }
                } else {
                    if (height > MAX_HEIGHT) {
                        width = width * (MAX_HEIGHT / height);
                        height = MAX_HEIGHT;
                    }
                }
                var canvas = document.createElement("canvas");
                canvas.width = width;
                canvas.height = height;

                var ctx = canvas.getContext("2d");
                ctx.drawImage(img, 0, 0, width, height);
                const dataUrl = canvas.toDataURL(imageFile.type, 0.8);

                img_preview.onload = function (event) {
                    // thumb
                    const THUMB_WIDTH = 128;
                    const THUMB_HEIGHT = 96;

                    if (width > height) {
                        if (width > THUMB_WIDTH) {
                            height = height * (THUMB_WIDTH / width);
                            width = THUMB_WIDTH;
                        }
                    } else {
                        if (height > THUMB_HEIGHT) {
                            width = width * (THUMB_HEIGHT / height);
                            height = THUMB_HEIGHT;
                        }
                    }
                    
                    canvas = document.createElement("canvas");
                    canvas.width = width;
                    canvas.height = height;
                    ctx = canvas.getContext("2d");
                    ctx.drawImage(img_preview, 0, 0, width, height);
                    const dataThumbUrl = canvas.toDataURL(imageFile.type, 0.8);
                    document.getElementById("img_thumb").src = dataThumbUrl;
                    img_thumb_file_data.value = dataThumbUrl.slice(23);
                };

                img_preview.src = dataUrl;
                img_file_data.value = dataUrl.slice(23);
            }
            img.src = e.target.result;
        }
        reader.readAsDataURL(imageFile);
    }
});
 

Тугай

Новичок
Задал этот же вопрос ChatGPT :) Он красавчик сразу указал на onload, но и предложил второй варинат с Promise, его правда глючит и он не может додумать до конца но идеи дает неплохие, код ChatGPT:
Код:
imgInput.addEventListener('change', function changeImg(e) {
    if (e.target.files.length) {
        let imageFile = e.target.files[0];
        var reader = new FileReader();
        img_preview.src = "";

        document.getElementById("img_name").value = e.target.files[0].name;

        const loadImg = (imgUrl) => {
            return new Promise((resolve, reject) => {
                const img = document.createElement("img");
                img.onload = function (event) {
                    const MAX_WIDTH = 1024;
                    const MAX_HEIGHT = 768;

                    let width = img.width;
                    let height = img.height;

                    if (width > height) {
                        if (width > MAX_WIDTH) {
                            height = height * (MAX_WIDTH / width);
                            width = MAX_WIDTH;
                        }
                    } else {
                        if (height > MAX_HEIGHT) {
                            width = width * (MAX_HEIGHT / height);
                            height = MAX_HEIGHT;
                        }
                    }

                    const canvas = document.createElement("canvas");
                    canvas.width = width;
                    canvas.height = height;

                    const ctx = canvas.getContext("2d");
                    ctx.drawImage(img, 0, 0, width, height);
                    const dataUrl = canvas.toDataURL(imageFile.type, 0.8);
                    resolve(dataUrl);
                };
                img.onerror = reject;
                img.src = imgUrl;
            });
        };

        reader.onload = function (e) {
            loadImg(e.target.result).then(dataUrl => {
                img_preview.onload = function (event) {
                    // thumb
                    const THUMB_WIDTH = 128;
                    const THUMB_HEIGHT = 96;

                    let width = img_preview.width;
                    let height = img_preview.height;

                    if (width > height) {
                        if (width > THUMB_WIDTH) {
                            height = height * (THUMB_WIDTH / width);
                            width = THUMB_WIDTH;
                        }
                    } else {
                        if (height > THUMB_HEIGHT) {
                            width = width * (THUMB_HEIGHT / height);
                            height = THUMB_HEIGHT;
                        }
                    }

                    const canvas = document.createElement("canvas");
                    canvas.width = width;
                    canvas.height = height;
                    const ctx = canvas.getContext("2d");
                    ctx.drawImage(img_preview, 0, 0, width, height);
                    const dataThumbUrl = canvas.toDataURL(imageFile.type, 0.8);
                    document.getElementById("img_thumb").src = dataThumbUrl;
                    img_thumb_file_data.value = dataThumbUrl.slice(23);
                };

                img_preview.src = dataUrl;
                img_file_data.value = dataUrl.slice(23);
            }).catch(err => console.error(err));
        };

        reader.readAsDataURL(imageFile);
    }
});

Мы создали функцию loadImg, которая принимает URL изображения и возвращает Promise, который разрешается с dataUrl уменьшенного изображения. Мы вызываем эту функцию после того, как FileReader прочитал файл и вернул URL. Затем мы используем Promise, чтобы убедиться, что уменьшенное изображение загружено, прежде чем продолжить и создавать thumbnail.

Обратите внимание, что мы добавили обработку ошибок для загрузки изображения.
Злая штука, но заставить его сгенерить два Promise с ходу не получается, если попросить генерить Promise на thumb, он тогда большую картинку грузит как в исходнике. :) Еще он пытался заюзать toBlob, но я пока бросил это дело и toBlob в новых браузерах только - мне не подходит.

Код:
Конечно, можно использовать Promise для создания thumb. Вот пример, как можно изменить код:

imgInput.addEventListener('change', function changeImg(e) {
    if (e.target.files.length) {
        const imageFile = e.target.files[0];
        const reader = new FileReader();
        img_preview.src = "";

        document.getElementById("img_name").value = imageFile.name;

        reader.readAsDataURL(imageFile);

        reader.onload = function (e) {
            const img = document.createElement("img");
            img.onload = function (event) {
                const MAX_WIDTH = 1024;
                const MAX_HEIGHT = 768;

                let width = img.width;
                let height = img.height;

                if (width > height) {
                    if (width > MAX_WIDTH) {
                        height = height * (MAX_WIDTH / width);
                        width = MAX_WIDTH;
                    }
                } else {
                    if (height > MAX_HEIGHT) {
                        width = width * (MAX_HEIGHT / height);
                        height = MAX_HEIGHT;
                    }
                }

                const canvas = document.createElement("canvas");
                canvas.width = width;
                canvas.height = height;

                const ctx = canvas.getContext("2d");
                ctx.drawImage(img, 0, 0, width, height);
                const dataUrl = canvas.toDataURL(imageFile.type, 0.8);

                img_preview.onload = function (event) {
                    createThumb(canvas, img_preview)
                        .then((dataThumbUrl) => {
                            document.getElementById("img_thumb").src = dataThumbUrl;
                            img_thumb_file_data.value = dataThumbUrl.slice(23);
                        })
                        .catch((error) => {
                            console.error(error);
                        });
                };

                img_preview.src = dataUrl;
                img_file_data.value = dataUrl.slice(23);
            };
            img.src = e.target.result;
        };
    }
});

function createThumb(canvas, img_preview) {
    return new Promise((resolve, reject) => {
        const THUMB_WIDTH = 128;
        const THUMB_HEIGHT = 96;

        let width = img_preview.width;
        let height = img_preview.height;

        if (width > height) {
            if (width > THUMB_WIDTH) {
                height = height * (THUMB_WIDTH / width);
                width = THUMB_WIDTH;
            }
        } else {
            if (height > THUMB_HEIGHT) {
                width = width * (THUMB_HEIGHT / height);
                height = THUMB_HEIGHT;
            }
        }

        const thumbCanvas = document.createElement("canvas");
        thumbCanvas.width = width;
        thumbCanvas.height = height;
        const ctx = thumbCanvas.getContext("2d");
        ctx.drawImage(img_preview, 0, 0, width, height);
        const dataThumbUrl = thumbCanvas.toDataURL("image/jpeg", 0.8);
        if (dataThumbUrl) {
            resolve(dataThumbUrl);
        } else {
            reject("Error creating thumbnail");
        }
    });
}
В функции createThumb() мы создаем новый Promise, который резолвит data URL для thumb, если все прошло успешно, и реджектит ошибку, если что-то пошло не так. В основной функции мы заменили код создания thumb на вызов createThumb() с помощью then() и catch().
 
Сверху