如何使用AJAX读取图像文件并渲染到Canvas?

时间:2016-04-15 14:02:23

标签: javascript jquery ajax html5 canvas

我想使用Ajax从我自己的服务器读取图像并将其渲染到Canvas。

现在我知道这可以使用普通的图像标签和画布画来实现,如下所示:

<img id="myImage" src="./ColorTable.PNG" style="display:none;"/>
var img = document.getElementById('myImage'); 
canvas.width = img.width;
canvas.height = img.height;
var ctx = canvas.getContext('2d');
ctx.drawImage(img, 0, 0);

但是我不想使用它,而是查看我们是否可以使用Ajax调用图像src位置读取文件,然后将其渲染到画布,但对我来说它只是显示错误的图像: 这就是我的所作所为:

var xhr = new XMLHttpRequest();
xhr.open('GET', './ColorTable.PNG', true);
xhr.responseType = 'arrayBuffer';
xhr.onload = function(e) {
    var data = this.response;
    var buf = new ArrayBuffer(data.length);
    var bufView = new Uint8Array(buf);
    for (var index = 0; index < data.length; index++) {
        bufView[index] = data.charCodeAt(index);
    }
    //initialize and get context and then render the image
    var ctx = canvas.getContext('2d');
    var imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
    for (var  i = 0 ; i < imgData.data.byteLength; i++) {
        imgData.data[i] = bufView[i];
        imgData.data[i++] = bufView[i];
        imgData.data[i++] = bufView[i];
        imgData.data[i++] = 255;
    }
    ctx.putImageData(imgData, 0, 0);
};
xhr.send();

但是第二种方法对我来说并不适用,因为渲染的图像是错误的。 有人可以帮忙吗?

2 个答案:

答案 0 :(得分:1)

您正在尝试将未解析的二进制PNG文件中的数据复制到画布,当然,由于PNG包含块和其他数据,因此图像本身是压缩的二进制数据,必须放气。通过AJAX加载不会解析PNG,它将被视为任何其他文件的原始二进制文件,结果将是随机颜色的噪声。

您必须加载PNG并将其用作图像元素的源,该图像元素将负责解析和放气,然后将图像元素绘制到画布(除非您想手动解析文件,即{{3 }})。

您可以直接按照说法加载图像,而无需使用AJAX:

var img = new Image();
img.onload = function() {
    var ctx = canvas.getContext('2d');
    ctx.drawImage(this, 0, 0, canvas.width, canvas.height);
    // next step here...
};
img.src = url;

这些方法在获取像素数据方面没有区别 - 在这两种情况下都必须满足CORS要求。

但是,如果你出于某种原因绝对需要AJAX(这没有意义,因为仍然必须通过图像元素传递它):将图像数据加载为Blob而不是ArrayBuffer,然后为它创建一个对象URL,可以用作图像元素的图像源:

var xhr = new XMLHttpRequest();
xhr.open('GET', './ColorTable.PNG', true);
xhr.responseType = 'blob';                        // we want a blob
xhr.onload = function(e) {
    var url = URL.createObjectURL(this.response); // ! URL may need prefix
    var img = new Image();
    img.onload = function() {
        URL.revokeObjectURL(this.src);            // auto-revoke can be set in some browsers
        var ctx = canvas.getContext('2d');
        ctx.drawImage(this, 0, 0, canvas.width, canvas.height)
    };
    img.src = url;

};
xhr.send();

答案 1 :(得分:0)

代码中存在将bufview复制到imgData的问题。

for (var  i = 0 ; i < imgData.data.byteLength; i++) {
    imgData.data[i] = bufView[i];
    imgData.data[i++] = bufView[i];
    imgData.data[i++] = bufView[i];
    imgData.data[i++] = 255;
}

后增量++运算符将在递增之前返回i。循环的第一次迭代将等同于......

imgData.data[0] = bufView[0];
imgData.data[0] = bufView[1];
imgData.data[1] = bufView[2];
imgData.data[2] = 255;

我假设您打算使用预增量++运算符,这可能会产生您想要的结果。

for (var  i = 0 ; i < imgData.data.byteLength; i++) {
    imgData.data[i] = bufView[i];
    imgData.data[++i] = bufView[i];
    imgData.data[++i] = bufView[i];
    imgData.data[++i] = 255;
}

但是,对于在表达式中使用多一次的变量,使用pre或post increment ++运算符是不好的做法。在没有运算符的变量实例之前或之后访问具有运算符的变量的实例并不总是很清楚。在您的场景中,我建议只在循环语句中递增变量。例如......

for (var  i = 0 ; i < imgData.data.byteLength; i += 4) {
    imgData.data[i] = bufView[i];
    imgData.data[i+1] = bufView[i+1];
    imgData.data[i+2] = bufView[i+2];
    imgData.data[i+3] = 255;
}