Как я могу нарисовать изображение из файлового API HTML5 на холсте?

Я хотел бы нарисовать изображение, открытое с помощью API файлов HTML5, на холсте.

В методе handleFiles(e) я могу получить доступ к файлу с помощью e.target.files[0], но я не могу нарисовать это изображение напрямую с помощью drawImage. Как нарисовать изображение из File API на холсте HTML5?

Вот код, который я использовал:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<script>
window.onload = function() {
    var input = document.getElementById('input');
    input.addEventListener('change', handleFiles);
}

function handleFiles(e) {
    var ctx = document.getElementById('canvas').getContext('2d');
    ctx.drawImage(e.target.files[0], 20,20);
    alert('the image is drawn');
}
</script>
</head>
<body>
<h1>Test</h1>
<input type="file" id="input"/>
<canvas width="400" height="300" id="canvas"/>
</body>
</html>

person Jonas    schedule 21.07.2011    source источник


Ответы (3)


У вас есть экземпляр File, который не является изображением.

Чтобы получить изображение, используйте new Image(). src должен быть URL-адресом, ссылающимся на выбранный File. Вы можете использовать URL.createObjectURL, чтобы получить URL-адрес, ссылающийся на Blob (File также является Blob): http://jsfiddle.net/t7mv6/86/.

var ctx = document.getElementById('canvas').getContext('2d');
var img = new Image;
img.onload = function() {
    ctx.drawImage(img, 20,20);
    alert('the image is drawn');
}
img.src = URL.createObjectURL(e.target.files[0]);

Примечание. не забудьте отозвать URL-адрес объекта, когда закончите с ним, иначе произойдет утечка памяти. Если вы не делаете ничего слишком сумасшедшего, вы можете просто вставить URL.revokeObjectURL(img.src) в функцию img.onload.

Использованная литература:

person pimvdb    schedule 21.07.2011
comment
Прикрепите onload перед src. Событие load может сработать между этими двумя строками. (Если изображение кэшировано и т. д.) - person Raynos; 21.07.2011
comment
@Raynos: Спасибо. (Хотя в этом случае файл должен был загружаться очень быстро...) - person pimvdb; 21.07.2011
comment
Из любопытства не могли бы вы указать, где в спецификации HTML5 говорится, что установка .src для Blob/File должна работать? У меня есть небольшая загвоздка, что это может быть проприетарное расширение. Это работает в Chrome / FF6 (у вас нет Opera, IE10 или Saf для тестирования) - person Raynos; 21.07.2011
comment
@Raynos: я не устанавливаю src из File. Скорее есть готовые к работе File внутри e.target.files. - person pimvdb; 21.07.2011
comment
img.src = event.target.result; вы устанавливаете .src изображения на File/Blob в result - person Raynos; 21.07.2011
comment
Спасибо, это тоже работает, но только онлайн в Chrome, а не в том случае, если у меня есть файл .html локально. - person Jonas; 21.07.2011
comment
@Raynos: Моя вина, я неправильно прочитал. Похоже, это отвечает на ваши сомнения: w3.org/TR/FileAPI/#dfn- readAsDataURL. Set the result attribute to be blob's data content represented as a Data URL [DataURL]; on getting, the result attribute returns the (complete) data of blob as a Data URL. Очевидно, что URL-адрес данных может быть установлен на src Image; это загрузится нормально. - person pimvdb; 21.07.2011
comment
@Jonas: не будет исправлено: code.google.com/p /chromium/issues/detail?id=60889. - person pimvdb; 21.07.2011
comment
@pimvdb спасибо, что прояснили это. Я искал это :) - person Raynos; 21.07.2011
comment
@Raynos, насколько я понимаю, событие onload не может быть запущено до тех пор, пока после не завершится текущий выполняемый скрипт, после чего событие onload будет привязано, а это означает, что нет проблем с порядок, в котором установлены атрибуты src и onload. - person zzzzBov; 16.08.2011
comment
@zzzzBov зависит от реализации. Известно, что некоторые браузеры немедленно запускают onload при установке .src, потому что они синхронно проверяют кеш и загружаются немедленно. Вам нужно действительно тщательное стресс-тестирование, чтобы увидеть, что безопасно или всегда устанавливайте onload перед src. - person Raynos; 16.08.2011
comment
@Raynos, если у вас есть пример, демонстрирующий этот эффект, мне было бы интересно его увидеть. - person zzzzBov; 16.08.2011
comment
@zzzzBov известно, что это происходит для Image в IE, когда src является URL-адресом. Я не знаю, как IE10 обрабатывает URL-адреса больших двоичных объектов, но я бы предпочел просто установить onload перед src, а затем беспокоиться об этом. Test, однако у меня нет копии IE. - person Raynos; 16.08.2011
comment
@Raynos, действительно очень интересно. Я мог бы заставить проблему возникнуть в режиме совместимости с IE9, но я не могу легко проверить IE7 и 8, чтобы увидеть, в чем проблема (режим совместимости IE9 похож на IE7, но не идентичен). В любом случае, я узнал что-то новое. - person zzzzBov; 16.08.2011

Живой пример

function handleFiles(e) {
    var ctx = document.getElementById('canvas').getContext('2d');
    var url = URL.createObjectURL(e.target.files[0]);
    var img = new Image();
    img.onload = function() {
        ctx.drawImage(img, 20, 20);    
    }
    img.src = url;   
}

window.URL.createObjectUrlдокументы

Вместо этого вы также можете использовать FileReader для создания URL-адреса объекта.

FileReader немного лучше поддерживается браузерами.

Подход FileReader работает в FF6/Chrome. Я не уверен, что установка Img.src на Blob действительна и кросс-браузерна.

Создание URL-адресов объектов - правильный способ сделать это.

Изменить:

Как упоминалось в комментарии, поддержка window.URL в автономном режиме кажется недоступной в FF6/Chrome.

person Raynos    schedule 21.07.2011
comment
Это работает, но только онлайн. Если я попробую это с локальным файлом .html в Chrome, это не сработает. - person Jonas; 21.07.2011
comment
Подход FileReader тоже не работал локально... Спасибо. - person Jonas; 21.07.2011
comment
@ Джонас, это может быть связано с разрешениями. Я не удивлюсь, если они заблокированы по умолчанию. Включение его, вероятно, находит какой-то непонятный флаг. - person Raynos; 21.07.2011
comment
@Jonas Похоже, неясный флаг --allow-file-access-from-files. для хрома. Справочник - person Raynos; 21.07.2011

Вот полный пример (Fiddle) с использованием FileReader (который лучше поддерживается браузером, как упоминал Raynos) . В этом примере я также масштабирую Canvas, чтобы он соответствовал изображению.

В реальном примере вы можете масштабировать изображение до некоторого максимума, чтобы ваша форма не взорвалась ;-). Вот пример с масштабированием (Fiddle).

var URL = window.webkitURL || window.URL;

window.onload = function() {
    var input = document.getElementById('input');
    input.addEventListener('change', handleFiles, false);
    
    // set original canvas dimensions as max
    var canvas = document.getElementById('canvas');
    canvas.dataMaxWidth = canvas.width;
    canvas.dataMaxHeight = canvas.height;
}

function handleFiles(e) {
    var ctx = document.getElementById('canvas').getContext('2d');
    var reader  = new FileReader();
    var file = e.target.files[0];
    // load to image to get it's width/height
    var img = new Image();
    img.onload = function() {
        // setup scaled dimensions
        var scaled = getScaledDim(img, ctx.canvas.dataMaxWidth, ctx.canvas.dataMaxHeight);
        // scale canvas to image
        ctx.canvas.width = scaled.width;
        ctx.canvas.height = scaled.height;
        // draw image
        ctx.drawImage(img, 0, 0
            , ctx.canvas.width, ctx.canvas.height
        );
    }
    // this is to setup loading the image
    reader.onloadend = function () {
        img.src = reader.result;
    }
    // this is to read the file
    reader.readAsDataURL(file);
}

// returns scaled dimensions object
function getScaledDim(img, maxWidth, maxHeight) {
    var scaled = {
        ratio: img.width / img.height,
        width: img.width,
        height: img.height
    }
    if (scaled.width > maxWidth) {
        scaled.width = maxWidth;
        scaled.height = scaled.width / scaled.ratio;
    }
    if (scaled.height > maxHeight) {
        scaled.height = maxHeight;
        scaled.width = scaled.height / scaled.ratio;
    }
    return scaled;
}
canvas {
    border:1px solid black
}
<input type="file" id="input"/>
<div>
    <canvas width="400" height="300" id="canvas"/>
</div>

person Nux    schedule 13.10.2015
comment
Ответы должны показывать код в вопросе, а не только во внешней ссылке. - person Heretic Monkey; 13.07.2021
comment
@HereticMonkey Я не думаю, что эта опция была доступна в stackO еще в 2015 году ????, но я добавил код. - person Nux; 15.07.2021
comment
Я был на Stack Overflow еще в 2015 году. Тогда вы тоже могли добавить код в свой ответ. Может быть, не в сниппете, но вы определенно можете добавить код: P. - person Heretic Monkey; 15.07.2021