canvas.toBlob 有时给出 null
canvas.toBlob giving null sometimes
我正在设置一个 canvas,然后将图像绘制到 canvas 中,然后调用 canvas.toBlob(function(blob)...)
,但我在 [=15= 中找到了 blob 参数] 有时是 null
.
为什么会这样?我应该在 drawImage
之后等待什么(即使在代码片段中 - 你可以看到我在继续之前等待图像加载)?
//--------------------------------------------------
function doImageFileInsert(fileinput) {
var newImg = document.createElement('img');
var img = fileinput.files[0];
let reader = new FileReader();
reader.onload = (e) => {
let base64 = e.target.result;
newImg.src = base64;
doTest(newImg);
};
reader.readAsDataURL(img);
fileinput.value = ''; // reset ready for another file
}
//--------------------------------------------------
function doTest(imgElem) {
console.log('doTest');
var canvas = document.createElement("canvas");
var w = imgElem.width;
var h = imgElem.height;
canvas.width = w;
canvas.height = h;
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(imgElem, 0, 0, w, h);
canvas.toBlob(function(blob) {
if (blob) {
console.log('blob is good');
} else {
console.log('blob is null');
alert('blob is null');
}
}, 'image/jpeg', 0.9);
}
canvas, div {
border:solid 1px grey;
padding:10px;
margin:5px;
border-radius:9px;
}
img {
width:100%;
height:auto;
}
<input type='file' value='Add' onChange='doImageFileInsert(this)'>
也可在 https://jsfiddle.net/Abeeee/rtwcge5h/24/ 获得。
如果您通过“选择文件”按钮添加图像的次数足够多,就会遇到问题 (alert('blob is null'))。
好吧,问题似乎出在 canvas 工作的调用者身上 - 事实上,在 drawImage(可能)为 运行 时图像还没有完全加载.
最初对 doTest() 的调用是
function doImageFileInsert(fileinput) {
var contenteditable = document.getElementById('mydiv');
var newImg = document.createElement('img');
//contenteditable.appendChild(newImg);
var img = fileinput.files[0];
let reader = new FileReader();
reader.onload = (e) => {
let base64 = e.target.result;
newImg.src = base64;
doTest(newImg); <-----
};
reader.readAsDataURL(img);
}
但错误的是调用
改为
function doImageFileInsert(fileinput) {
var contenteditable = document.getElementById('mydiv');
var newImg = document.createElement('img');
//contenteditable.appendChild(newImg);
var img = fileinput.files[0];
let reader = new FileReader();
reader.onload = (e) => {
let base64 = e.target.result;
newImg.src = base64;
//doTest(newImg);
newImg.onload = (e) => { doTest(newImg); }; <-----
};
reader.readAsDataURL(img);
}
中查看工作版本
如果您想使用,这里有一个更现代的方法
/**
* @param {HTMLInputElement} fileInput
*/
async function doImageFileInsert (fileInput) {
const img = new Image()
img.src = URL.createObjectURL(fileInput.files[0])
await img.decode()
doTest(img)
}
或者这个不适用于所有浏览器(需要 polyfill)
/**
* @param {HTMLInputElement} fileInput
*/
function doImageFileInsert (fileInput) {
createImageBitmap(fileInput.files[0]).then(doTest)
}
FileReader 现在是一种遗留模式,blob 本身有新的基于 promise 的读取方法,您也可以使用 URL.createObjectURL(file)
而不是浪费时间将文件编码为 base64 url(只是被 <img>
再次解码回二进制)这是浪费时间、处理和 RAM。
toBlob
会产生 null
的原因只有几个:
- 浏览器编码器中的错误(我自己从未见过)。
- 面积大于the maximum supported by the UA的canvas。
- 宽度或高度为
0
的canvas。
由于您没有等待图像加载,它的 width
和 height
属性仍然是 0
,并且您属于上面的第三个项目符号,因为您确实设置了canvas 的尺码。
因此,要修复您的错误,请等待图像加载后再对其进行任何操作。
另请注意,您应该 FileReader.readAsDataURL()
, and certainly not to display media files from a Blob, instead generate a blob://
URI from these Blobs using URL.createObjectURL().
但在您的情况下,您甚至可以使用更好的 createImageBitmap API which will take care of loading the image in your Blob and will generate a memory friendly ImageBitmap 对象,它已准备好由 GPU 绘制,无需任何更多计算。
只有 Safari 还没有实现这个 API 的基础,但他们应该很快(它暴露在标志后面,在 TP 版本中没有标记),并且 I wrote a polyfill 你可以用来修复各种实现中的漏洞.
const input = document.querySelector("input");
input.oninput = async (evt) => {
const img = await createImageBitmap(input.files[0]);
const canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
// I hope you'll do something more here,
// reencoding an iamge to JPEG through the Canvas API is a bad idea
// Canvas encoders aim at speed, not quality
canvas.toBlob( (blob) => {
console.log( blob );
}, "image/jpeg", 0.9 );
};
<!-- createImageBitmap polyfill for Safari -->
<script src="https://cdn.jsdelivr.net/gh/Kaiido/createImageBitmap/dist/createImageBitmap.js"></script>
<input type="file" accept="image/*">
我正在设置一个 canvas,然后将图像绘制到 canvas 中,然后调用 canvas.toBlob(function(blob)...)
,但我在 [=15= 中找到了 blob 参数] 有时是 null
.
为什么会这样?我应该在 drawImage
之后等待什么(即使在代码片段中 - 你可以看到我在继续之前等待图像加载)?
//--------------------------------------------------
function doImageFileInsert(fileinput) {
var newImg = document.createElement('img');
var img = fileinput.files[0];
let reader = new FileReader();
reader.onload = (e) => {
let base64 = e.target.result;
newImg.src = base64;
doTest(newImg);
};
reader.readAsDataURL(img);
fileinput.value = ''; // reset ready for another file
}
//--------------------------------------------------
function doTest(imgElem) {
console.log('doTest');
var canvas = document.createElement("canvas");
var w = imgElem.width;
var h = imgElem.height;
canvas.width = w;
canvas.height = h;
var ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(imgElem, 0, 0, w, h);
canvas.toBlob(function(blob) {
if (blob) {
console.log('blob is good');
} else {
console.log('blob is null');
alert('blob is null');
}
}, 'image/jpeg', 0.9);
}
canvas, div {
border:solid 1px grey;
padding:10px;
margin:5px;
border-radius:9px;
}
img {
width:100%;
height:auto;
}
<input type='file' value='Add' onChange='doImageFileInsert(this)'>
也可在 https://jsfiddle.net/Abeeee/rtwcge5h/24/ 获得。
如果您通过“选择文件”按钮添加图像的次数足够多,就会遇到问题 (alert('blob is null'))。
好吧,问题似乎出在 canvas 工作的调用者身上 - 事实上,在 drawImage(可能)为 运行 时图像还没有完全加载.
最初对 doTest() 的调用是
function doImageFileInsert(fileinput) {
var contenteditable = document.getElementById('mydiv');
var newImg = document.createElement('img');
//contenteditable.appendChild(newImg);
var img = fileinput.files[0];
let reader = new FileReader();
reader.onload = (e) => {
let base64 = e.target.result;
newImg.src = base64;
doTest(newImg); <-----
};
reader.readAsDataURL(img);
}
但错误的是调用
改为
function doImageFileInsert(fileinput) {
var contenteditable = document.getElementById('mydiv');
var newImg = document.createElement('img');
//contenteditable.appendChild(newImg);
var img = fileinput.files[0];
let reader = new FileReader();
reader.onload = (e) => {
let base64 = e.target.result;
newImg.src = base64;
//doTest(newImg);
newImg.onload = (e) => { doTest(newImg); }; <-----
};
reader.readAsDataURL(img);
}
中查看工作版本
如果您想使用,这里有一个更现代的方法
/**
* @param {HTMLInputElement} fileInput
*/
async function doImageFileInsert (fileInput) {
const img = new Image()
img.src = URL.createObjectURL(fileInput.files[0])
await img.decode()
doTest(img)
}
或者这个不适用于所有浏览器(需要 polyfill)
/**
* @param {HTMLInputElement} fileInput
*/
function doImageFileInsert (fileInput) {
createImageBitmap(fileInput.files[0]).then(doTest)
}
FileReader 现在是一种遗留模式,blob 本身有新的基于 promise 的读取方法,您也可以使用 URL.createObjectURL(file)
而不是浪费时间将文件编码为 base64 url(只是被 <img>
再次解码回二进制)这是浪费时间、处理和 RAM。
toBlob
会产生 null
的原因只有几个:
- 浏览器编码器中的错误(我自己从未见过)。
- 面积大于the maximum supported by the UA的canvas。
- 宽度或高度为
0
的canvas。
由于您没有等待图像加载,它的 width
和 height
属性仍然是 0
,并且您属于上面的第三个项目符号,因为您确实设置了canvas 的尺码。
因此,要修复您的错误,请等待图像加载后再对其进行任何操作。
另请注意,您应该 FileReader.readAsDataURL()
, and certainly not to display media files from a Blob, instead generate a blob://
URI from these Blobs using URL.createObjectURL().
但在您的情况下,您甚至可以使用更好的 createImageBitmap API which will take care of loading the image in your Blob and will generate a memory friendly ImageBitmap 对象,它已准备好由 GPU 绘制,无需任何更多计算。
只有 Safari 还没有实现这个 API 的基础,但他们应该很快(它暴露在标志后面,在 TP 版本中没有标记),并且 I wrote a polyfill 你可以用来修复各种实现中的漏洞.
const input = document.querySelector("input");
input.oninput = async (evt) => {
const img = await createImageBitmap(input.files[0]);
const canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
const ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
// I hope you'll do something more here,
// reencoding an iamge to JPEG through the Canvas API is a bad idea
// Canvas encoders aim at speed, not quality
canvas.toBlob( (blob) => {
console.log( blob );
}, "image/jpeg", 0.9 );
};
<!-- createImageBitmap polyfill for Safari -->
<script src="https://cdn.jsdelivr.net/gh/Kaiido/createImageBitmap/dist/createImageBitmap.js"></script>
<input type="file" accept="image/*">