canvas儲存為data:image擴充套件功能的實現
阿新 • • 發佈:2019-01-13
【已知】
canvas提供了toDataURL的介面,可以方便的將canvas畫布轉化成base64編碼的image。目前支援的最好的是png格式,jpeg格式的現代瀏覽器基本也支援,但是支援的不是很好。
【想要的】
往往這麼簡單直接的介面通常都滿足不了需求。我想要的不僅是簡單的通過畫布生成一個png,我不想新開一個tab,然後還要右鍵另存為…
我還需要更方便的自由的配置生成的圖片的大小,比例等。
另外如果我還要別的圖片格式,比如點陣圖bmp,gif等怎麼辦…
【解決辦法】
a)想直接把圖片生成後download到本地,其實辦法也很簡單。直接改圖片的mimeType,強制改成steam流型別的。比如‘image/octet-stream’,瀏覽器就會自動幫我們另存為..
b)圖片大小,及比例的可控倒也好辦,我們新建一個我們想要大小的canvas,把之前的canvas畫布重新按照所要的比例,及大小draw到新的canvas上,然後用新的canvas來toDataURL即可。
c)想要bmp點陣圖會麻煩些… 沒有直接的介面,需要我們自己來生成。生成圖片的響應頭和響應體有一定的規則,略顯麻煩。不過還能接受。剩下的就是效能問題,按畫素級別來操作,對於一個大圖來說計算量很有壓力。
/**
* covert canvas to image
* and save the image file
*/
var Canvas2Image = function () {
// check if support sth.
var $support = function () {
var canvas = document.createElement('canvas'),
ctx = canvas.getContext('2d');
return {
canvas: !!ctx,
imageData: !!ctx.getImageData,
dataURL: !!canvas.toDataURL,
btoa: !!window.btoa
};
}();
var downloadMime = 'image/octet-stream';
function scaleCanvas (canvas, width, height) {
var w = canvas.width,
h = canvas.height;
if (width == undefined) {
width = w;
}
if (height == undefined) {
height = h;
}
var retCanvas = document.createElement('canvas');
var retCtx = retCanvas.getContext('2d');
retCanvas.width = width;
retCanvas.height = height;
retCtx.drawImage(canvas, 0, 0, w, h, 0, 0, width, height);
return retCanvas;
}
function getDataURL (canvas, type, width, height) {
canvas = scaleCanvas(canvas, width, height);
return canvas.toDataURL(type);
}
function saveFile (strData) {
document.location.href = strData;
}
function genImage(strData) {
var img = document.createElement('img');
img.src = strData;
return img;
}
function fixType (type) {
type = type.toLowerCase().replace(/jpg/i, 'jpeg');
var r = type.match(/png|jpeg|bmp|gif/)[0];
return 'image/' + r;
}
function encodeData (data) {
if (!window.btoa) { throw 'btoa undefined' }
var str = '';
if (typeof data == 'string') {
str = data;
} else {
for (var i = 0; i < data.length; i ++) {
str += String.fromCharCode(data[i]);
}
}
return btoa(str);
}
function getImageData (canvas) {
var w = canvas.width,
h = canvas.height;
return canvas.getContext('2d').getImageData(0, 0, w, h);
}
function makeURI (strData, type) {
return 'data:' + type + ';base64,' + strData;
}
/**
* create bitmap image
* 按照規則生成圖片響應頭和響應體
*/
var genBitmapImage = function (data) {
var imgHeader = [],
imgInfoHeader = [];
var width = data.width,
height = data.height;
imgHeader.push(0x42); // 66 -> B
imgHeader.push(0x4d); // 77 -> M
var fsize = width * height * 3 + 54; // header size:54 bytes
imgHeader.push(fsize % 256); // r
fsize = Math.floor(fsize / 256);
imgHeader.push(fsize % 256); // g
fsize = Math.floor(fsize / 256);
imgHeader.push(fsize % 256); // b
fsize = Math.floor(fsize / 256);
imgHeader.push(fsize % 256); // a
imgHeader.push(0);
imgHeader.push(0);
imgHeader.push(0);
imgHeader.push(0);
imgHeader.push(54); // offset -> 6
imgHeader.push(0);
imgHeader.push(0);
imgHeader.push(0);
// info header
imgInfoHeader.push(40); // info header size
imgInfoHeader.push(0);
imgInfoHeader.push(0);
imgInfoHeader.push(0);
// 橫向info
var _width = width;
imgInfoHeader.push(_width % 256);
_width = Math.floor(_width / 256);
imgInfoHeader.push(_width % 256);
_width = Math.floor(_width / 256);
imgInfoHeader.push(_width % 256);
_width = Math.floor(_width / 256);
imgInfoHeader.push(_width % 256);
// 縱向info
var _height = height;
imgInfoHeader.push(_height % 256);
_height = Math.floor(_height / 256);
imgInfoHeader.push(_height % 256);
_height = Math.floor(_height / 256);
imgInfoHeader.push(_height % 256);
_height = Math.floor(_height / 256);
imgInfoHeader.push(_height % 256);
imgInfoHeader.push(1);
imgInfoHeader.push(0);
imgInfoHeader.push(24); // 24位bitmap
imgInfoHeader.push(0);
// no compression
imgInfoHeader.push(0);
imgInfoHeader.push(0);
imgInfoHeader.push(0);
imgInfoHeader.push(0);
// pixel data
var dataSize = width * height * 3;
imgInfoHeader.push(dataSize % 256);
dataSize = Math.floor(dataSize / 256);
imgInfoHeader.push(dataSize % 256);
dataSize = Math.floor(dataSize / 256);
imgInfoHeader.push(dataSize % 256);
dataSize = Math.floor(dataSize / 256);
imgInfoHeader.push(dataSize % 256);
// blank space
for (var i = 0; i < 16; i ++) {
imgInfoHeader.push(0);
}
var padding = (4 - ((width * 3) % 4)) % 4;
var imgData = data.data;
var strPixelData = '';
var y = height;
do {
var offsetY = width * (y - 1) * 4;
var strPixelRow = '';
for (var x = 0; x < width; x ++) {
var offsetX = 4 * x;
strPixelRow += String.fromCharCode(imgData[offsetY + offsetX + 2]);
strPixelRow += String.fromCharCode(imgData[offsetY + offsetX + 1]);
strPixelRow += String.fromCharCode(imgData[offsetY + offsetX]);
}
for (var n = 0; n < padding; n ++) {
strPixelRow += String.fromCharCode(0);
}
strPixelData += strPixelRow;
} while(-- y);
return (encodeData(imgHeader.concat(imgInfoHeader)) + encodeData(strPixelData));
};
/**
* saveAsImage
* @param canvasElement
* @param {String} image type
* @param {Number} [optional] png width
* @param {Number} [optional] png height
*/
var saveAsImage = function (canvas, width, height, type) {
if ($support.canvas && $support.dataURL) {
if (type == undefined) { type = 'png'; }
type = fixType(type);
if (/bmp/.test(type)) {
var data = getImageData(scaleCanvas(canvas, width, height));
var strData = genBitmapImage(data);
saveFile(makeURI(strData, downloadMime));
} else {
var strData = getDataURL(canvas, type, width, height);
saveFile(strData.replace(type, downloadMime));
}
}
}
var convertToImage = function (canvas, width, height, type) {
if ($support.canvas && $support.dataURL) {
if (type == undefined) { type = 'png'; }
type = fixType(type);
if (/bmp/.test(type)) {
var data = getImageData(scaleCanvas(canvas, width, height));
var strData = genBitmapImage(data);
return genImage(makeURI(strData, 'image/bmp'));
} else {
var strData = getDataURL(canvas, type, width, height);
return genImage(strData);
}
}
}
return {
saveAsImage: saveAsImage,
saveAsPNG: function (canvas, width, height) {
return saveAsImage(canvas, width, height, 'png');
},
saveAsJPEG: function (canvas, width, height) {
return saveAsImage(canvas, width, height, 'jpeg');
},
saveAsGIF: function (canvas, width, height) {
return saveAsImage(canvas, width, height, 'gif')
},
saveAsBMP: function (canvas, width, height) {
return saveAsImage(canvas, width, height, 'bmp');
},
convertToImage: convertToImage,
convertToPNG: function (canvas, width, height) {
return convertToImage(canvas, width, height, 'png');
},
convertToJPEG: function (canvas, width, height) {
return convertToImage(canvas, width, height, 'jpeg');
},
convertToGIF: function (canvas, width, height) {
return convertToImage(canvas, width, height, 'gif');
},
convertToBMP: function (canvas, width, height) {
return convertToImage(canvas, width, height, 'bmp');
}
};
}();
【Demo】
http://hongru.github.com/proj/canvas2image/index.html
可以試著在canvas上塗塗畫畫,然後儲存看看。如果用bmp格式的話,需要支援 btoa 的base64編碼,關於base64編碼規則可看上一篇博文
【不完美的地方】
1)jpeg介面本身就不完善,當canvas沒有填充顏色或圖片時,儲存的jpeg由於是直接由png的alpha通道強制轉換過來的,所以在png的透明部分在jpeg裡面就是黑色的。
2)gif的限制太多。且可用性不大,有png就夠了
3)bmp點陣圖生成,計算量稍顯大了。
4)由於是強制改mimeType來實現的自動下載,所以下載的時候檔案型別不會自動識別。
轉載自:http://www.cnblogs.com/hongru/archive/2012/01/14/2322540.html