如何从 Canvas 上的 HTML5 文件 API 绘制图像?

IT技术 javascript canvas drawimage fileapi
2021-02-21 16:36:32

我想在画布上绘制使用 HTML5 文件 API 打开的图像。

在该handleFiles(e)方法中,我可以使用 访问文件,e.target.files[0]但无法直接使用drawImage. 如何从 HTML5 画布上的 File API 绘制图像?

这是我使用的代码:

<!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>
3个回答

您有一个File不是图像实例。

要获取图像,请使用new Image(). src需求是一个URL引用到选定的File您可以使用URL.createObjectURL来获取引用 a Blob(aFile也是 a Blob的 URL 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函数中添加 a 。

参考:

出于好奇,您能否参考一下“设置.src为 Blob/文件”应该在 HTML5 规范中起作用的地方吗?我有一个小小的障碍,这可能是一个专有扩展。它恰好适用于 Chrome / FF6(没有歌剧、IE10 或 Saf nightlies 来测试)
2021-04-22 16:36:32
请附上onload之前srcload事件可以在这两行之间触发。(如果图像被缓存等)
2021-04-25 16:36:32
@Raynos:我的错,我误读了。看起来这可以回答您的疑问:w3.org/TR/FileAPI/#dfn-readAsDataURLSet 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 可以设置为一个Image;的 src 这将加载良好。
2021-04-25 16:36:32
2021-04-28 16:36:32
@Raynos:谢谢。(虽然在这种情况下文件必须加载得非常快......)
2021-05-05 16:36:32

现场示例

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为 aBlob是否有效和跨浏览器。

创建对象 url 是正确的方法。

编辑:

正如评论window.URL支持中提到的,离线似乎在 FF6/Chrome 中不可用。

@Jonas 似乎晦涩的标志是--allow-file-access-from-files.针对 chrome 的。参考
2021-05-02 16:36:32
@Jonas 这可能是权限问题。如果默认情况下它们被阻止,我不会感到惊讶。打开它可能会发现一些晦涩的标志。
2021-05-05 16:36:32
这有效,但仅限在线。如果我在 Chrome 中使用本地 .html 文件尝试此操作,则它不起作用。
2021-05-12 16:36:32
FileReader 方法在本地也不起作用......谢谢。
2021-05-14 16:36:32

这是一个完整的示例(小提琴) using 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>

@HereticMonkey 我不认为该选项在 2015 年的 stackO 上可用😉,但我添加了代码。
2021-05-05 16:36:32
答案应该在问题中显示代码,而不仅仅是在外部链接中。
2021-05-09 16:36:32
2015 年我在 Stack Overflow 上。那时你也可以在你的答案中添加代码。也许不在片段中,但您绝对可以添加代码:P。
2021-05-18 16:36:32