在 Web Worker 中调整和压缩图像。我可以使用 canvas 吗?
Resize & Compress Image in Web Worker. Can I use a canvas?
背景
我现在正在客户端处理 select 图片。
我想对该图像执行两个操作,并输出 base64 编码的字符串。
- 如果图片尺寸的宽度或高度大于 1000,请调整尺寸。
- 使用质量为 0.5 的 jpeg 压缩图像。
所以现在我将在主脚本中执行以下操作:
$(function() {
$('#upload').change(function() {
var URL = window.URL || window.webkitURL;
var imgURL = URL.createObjectURL(this.files[0]);
var img = new Image();
img.onload = function() {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var w0 = img.width;
var h0 = img.height;
var w1 = Math.min(w0, 1000);
var h1 = h0 / w0 * w1;
canvas.width = w1;
canvas.height = h1;
ctx.drawImage(img, 0, 0, w0, h0,
0, 0, canvas.width, canvas.height);
// Here, the result is ready,
var data_src = canvas.toDataURL('image/jpeg', 0.5);
$('#img').attr('src', data_src);
URL.revokeObjectURL(imgURL);
};
img.src = imgURL;
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="upload" type="file" accept="image/*" />
<img id="img" />
问题
但是,我的代码仍然可以在移动设备上运行,上面的过程(调整大小和压缩)不能很快运行。它会导致 GUI 停止响应片刻。
我希望程序在另一个线程中工作,使用 web worker。所以它不会阻塞UI,所以用户体验会更好。
问题来了,网络工作者似乎无法处理 canvas,我该如何解决这个问题?
一些事件驱动编码
遗憾的是 Web 工作者还没有准备好浏览器支持。
网络工作者对 toDataURL
的有限支持意味着需要另一种解决方案。请参阅页面中间的 MDN web worker APIs (ImageData),目前仅适用于 Firefox。
看看您的 onload
,您在对 onload
的一次阻塞调用中完成了所有繁重的工作。在创建新 canvas、获取其上下文、缩放和 toDataURL
(不知道 revokeObjectURL
做什么)的过程中,您正在阻止 UI。发生这种情况时,您需要让 UI 接几个电话。因此,一个小的事件驱动处理将有助于减少故障,如果不是让它不明显的话。
尝试如下重写onload函数。
// have added some debugging code that would be useful to know if
// this does not solve the problem. Uncomment it and use it to see where
// the big delay is.
img.onload = function () {
var canvas, ctx, w, h, dataSrc, delay; // hosit vars just for readability as the following functions will close over them
// Just for the uninitiated in closure.
// var now, CPUProfile = []; // debug code
delay = 10; // 0 could work just as well and save you 20-30ms
function revokeObject() { // not sure what this does but playing it safe
// as an event.
// now = performance.now(); // debug code
URL.revokeObjectURL(imgURL);
//CPUProfile.push(performance.now()-now); // debug code
// setTimeout( function () { CPUProfile.forEach ( time => console.log(time)), 0);
}
function decodeImage() {
// now = performance.now(); // debug code
$('#img').attr('src', dataSrc);
setTimeout(revokeObject, delay); // gives the UI a second chance to get something done.
//CPUProfile.push(performance.now()-now); // debug code
}
function encodeImage() {
// now = performance.now(); // debug code
dataSrc = canvas.toDataURL('image/jpeg', 0.5);
setTimeout(decodeImage, delay); // gives the UI a second chance to get something done.
//CPUProfile.push(performance.now()-now); // debug code
}
function scaleImage() {
// now = performance.now(); // debug code
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
setTimeout(encodeImage, delay); // gives the UI a second chance to get something done.
//CPUProfile.push(performance.now()-now); // debug code
}
// now = performance.now(); // debug code
canvas = document.createElement('canvas');
ctx = canvas.getContext('2d');
w = Math.min(img.width, 1000);
h = img.height / img.width * w;
canvas.width = w;
canvas.height = h;
setTimeout(scaleImage, delay); // gives the UI a chance to get something done.
//CPUProfile.push(performance.now()-now); // debug code
};
setTimeout
允许当前调用退出,释放调用堆栈并允许 UI 在 DOM 上获得它的手套。我给了 10 毫秒,我个人会从 0 毫秒开始,因为调用堆栈访问没有被阻止,但我正在安全地玩
运气好的话问题会大大减少。如果它仍然是不可接受的延迟,那么我可以查看 CPU 配置文件,看看是否无法通过定位瓶颈找到解决方案。我的猜测是 toDataURL
是负载所在的位置。如果是,一个可能的解决方案是找到一个用 javascript 编写的 JPG 编码器,它可以转换为事件驱动的编码器。
问题不在于处理数据需要多长时间,而在于你阻塞了多长时间UI。
背景
我现在正在客户端处理 select 图片。
我想对该图像执行两个操作,并输出 base64 编码的字符串。
- 如果图片尺寸的宽度或高度大于 1000,请调整尺寸。
- 使用质量为 0.5 的 jpeg 压缩图像。
所以现在我将在主脚本中执行以下操作:
$(function() {
$('#upload').change(function() {
var URL = window.URL || window.webkitURL;
var imgURL = URL.createObjectURL(this.files[0]);
var img = new Image();
img.onload = function() {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
var w0 = img.width;
var h0 = img.height;
var w1 = Math.min(w0, 1000);
var h1 = h0 / w0 * w1;
canvas.width = w1;
canvas.height = h1;
ctx.drawImage(img, 0, 0, w0, h0,
0, 0, canvas.width, canvas.height);
// Here, the result is ready,
var data_src = canvas.toDataURL('image/jpeg', 0.5);
$('#img').attr('src', data_src);
URL.revokeObjectURL(imgURL);
};
img.src = imgURL;
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="upload" type="file" accept="image/*" />
<img id="img" />
问题
但是,我的代码仍然可以在移动设备上运行,上面的过程(调整大小和压缩)不能很快运行。它会导致 GUI 停止响应片刻。
我希望程序在另一个线程中工作,使用 web worker。所以它不会阻塞UI,所以用户体验会更好。
问题来了,网络工作者似乎无法处理 canvas,我该如何解决这个问题?
一些事件驱动编码
遗憾的是 Web 工作者还没有准备好浏览器支持。
网络工作者对 toDataURL
的有限支持意味着需要另一种解决方案。请参阅页面中间的 MDN web worker APIs (ImageData),目前仅适用于 Firefox。
看看您的 onload
,您在对 onload
的一次阻塞调用中完成了所有繁重的工作。在创建新 canvas、获取其上下文、缩放和 toDataURL
(不知道 revokeObjectURL
做什么)的过程中,您正在阻止 UI。发生这种情况时,您需要让 UI 接几个电话。因此,一个小的事件驱动处理将有助于减少故障,如果不是让它不明显的话。
尝试如下重写onload函数。
// have added some debugging code that would be useful to know if
// this does not solve the problem. Uncomment it and use it to see where
// the big delay is.
img.onload = function () {
var canvas, ctx, w, h, dataSrc, delay; // hosit vars just for readability as the following functions will close over them
// Just for the uninitiated in closure.
// var now, CPUProfile = []; // debug code
delay = 10; // 0 could work just as well and save you 20-30ms
function revokeObject() { // not sure what this does but playing it safe
// as an event.
// now = performance.now(); // debug code
URL.revokeObjectURL(imgURL);
//CPUProfile.push(performance.now()-now); // debug code
// setTimeout( function () { CPUProfile.forEach ( time => console.log(time)), 0);
}
function decodeImage() {
// now = performance.now(); // debug code
$('#img').attr('src', dataSrc);
setTimeout(revokeObject, delay); // gives the UI a second chance to get something done.
//CPUProfile.push(performance.now()-now); // debug code
}
function encodeImage() {
// now = performance.now(); // debug code
dataSrc = canvas.toDataURL('image/jpeg', 0.5);
setTimeout(decodeImage, delay); // gives the UI a second chance to get something done.
//CPUProfile.push(performance.now()-now); // debug code
}
function scaleImage() {
// now = performance.now(); // debug code
ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
setTimeout(encodeImage, delay); // gives the UI a second chance to get something done.
//CPUProfile.push(performance.now()-now); // debug code
}
// now = performance.now(); // debug code
canvas = document.createElement('canvas');
ctx = canvas.getContext('2d');
w = Math.min(img.width, 1000);
h = img.height / img.width * w;
canvas.width = w;
canvas.height = h;
setTimeout(scaleImage, delay); // gives the UI a chance to get something done.
//CPUProfile.push(performance.now()-now); // debug code
};
setTimeout
允许当前调用退出,释放调用堆栈并允许 UI 在 DOM 上获得它的手套。我给了 10 毫秒,我个人会从 0 毫秒开始,因为调用堆栈访问没有被阻止,但我正在安全地玩
运气好的话问题会大大减少。如果它仍然是不可接受的延迟,那么我可以查看 CPU 配置文件,看看是否无法通过定位瓶颈找到解决方案。我的猜测是 toDataURL
是负载所在的位置。如果是,一个可能的解决方案是找到一个用 javascript 编写的 JPG 编码器,它可以转换为事件驱动的编码器。
问题不在于处理数据需要多长时间,而在于你阻塞了多长时间UI。