/** * covert canvas to image * and save the image file */ const Canvas2Image = (function () { // check if support sth. const $support = (function () { const canvas = document.createElement("canvas"), ctx = canvas.getContext("2d"); return { canvas: !!ctx, imageData: !!ctx.getImageData, dataURL: !!canvas.toDataURL, btoa: !!window.btoa, }; })(); const downloadMime = "image/octet-stream"; function scaleCanvas(canvas, width, height) { const w = canvas.width, h = canvas.height; if (width === undefined) { width = w; } if (height === undefined) { height = h; } let retCanvas = document.createElement("canvas"); let 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); } // save file to local with file name and file type function saveFile(strData, fileType, fileName = "name") { // document.location.href = strData; let saveLink = document.createElement("a"); // download file name saveLink.download = fileName + "." + fileType; // download file data saveLink.href = strData; // start download saveLink.click(); } function genImage(strData) { let img = document.createElement("img"); img.src = strData; return img; } function fixType(type) { type = type.toLowerCase().replace(/jpg/i, "jpeg"); const r = type.match(/png|jpeg|bmp|gif/)[0]; return "image/" + r; } function encodeData(data) { if (!window.btoa) { // eslint-disable-next-line no-throw-literal throw "btoa undefined"; } let str = ""; if (typeof data == "string") { str = data; } else { for (let i = 0; i < data.length; i++) { str += String.fromCharCode(data[i]); } } return btoa(str); } function getImageData(canvas) { const 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 * 按照规则生成图片响应头和响应体 */ const genBitmapImage = function (oData) { // // BITMAPFILEHEADER: http://msdn.microsoft.com/en-us/library/windows/desktop/dd183374(v=vs.85).aspx // BITMAPINFOHEADER: http://msdn.microsoft.com/en-us/library/dd183376.aspx // const biWidth = oData.width; const biHeight = oData.height; const biSizeImage = biWidth * biHeight * 3; const bfSize = biSizeImage + 54; // total header size = 54 bytes // // typedef struct tagBITMAPFILEHEADER { // WORD bfType; // DWORD bfSize; // WORD bfReserved1; // WORD bfReserved2; // DWORD bfOffBits; // } BITMAPFILEHEADER; // const BITMAPFILEHEADER = [ // WORD bfType -- The file type signature; must be "BM" 0x42, 0x4d, // DWORD bfSize -- The size, in bytes, of the bitmap file bfSize & 0xff, (bfSize >> 8) & 0xff, (bfSize >> 16) & 0xff, (bfSize >> 24) & 0xff, // WORD bfReserved1 -- Reserved; must be zero 0, 0, // WORD bfReserved2 -- Reserved; must be zero 0, 0, // DWORD bfOffBits -- The offset, in bytes, from the beginning of the BITMAPFILEHEADER structure to the bitmap bits. 54, 0, 0, 0, ]; // // typedef struct tagBITMAPINFOHEADER { // DWORD biSize; // LONG biWidth; // LONG biHeight; // WORD biPlanes; // WORD biBitCount; // DWORD biCompression; // DWORD biSizeImage; // LONG biXPelsPerMeter; // LONG biYPelsPerMeter; // DWORD biClrUsed; // DWORD biClrImportant; // } BITMAPINFOHEADER, *PBITMAPINFOHEADER; // const BITMAPINFOHEADER = [ // DWORD biSize -- The number of bytes required by the structure 40, 0, 0, 0, // LONG biWidth -- The width of the bitmap, in pixels biWidth & 0xff, (biWidth >> 8) & 0xff, (biWidth >> 16) & 0xff, (biWidth >> 24) & 0xff, // LONG biHeight -- The height of the bitmap, in pixels biHeight & 0xff, (biHeight >> 8) & 0xff, (biHeight >> 16) & 0xff, (biHeight >> 24) & 0xff, // WORD biPlanes -- The number of planes for the target device. This value must be set to 1 1, 0, // WORD biBitCount -- The number of bits-per-pixel, 24 bits-per-pixel -- the bitmap // has a maximum of 2^24 colors (16777216, Truecolor) 24, 0, // DWORD biCompression -- The type of compression, BI_RGB (code 0) -- uncompressed 0, 0, 0, 0, // DWORD biSizeImage -- The size, in bytes, of the image. This may be set to zero for BI_RGB bitmaps biSizeImage & 0xff, (biSizeImage >> 8) & 0xff, (biSizeImage >> 16) & 0xff, (biSizeImage >> 24) & 0xff, // LONG biXPelsPerMeter, unused 0, 0, 0, 0, // LONG biYPelsPerMeter, unused 0, 0, 0, 0, // DWORD biClrUsed, the number of color indexes of palette, unused 0, 0, 0, 0, // DWORD biClrImportant, unused 0, 0, 0, 0, ]; const iPadding = (4 - ((biWidth * 3) % 4)) % 4; const aImgData = oData.data; let strPixelData = ""; const biWidth4 = biWidth << 2; let y = biHeight; const fromCharCode = String.fromCharCode; do { const iOffsetY = biWidth4 * (y - 1); let strPixelRow = ""; for (let x = 0; x < biWidth; x++) { const iOffsetX = x << 2; strPixelRow += fromCharCode(aImgData[iOffsetY + iOffsetX + 2]) + fromCharCode(aImgData[iOffsetY + iOffsetX + 1]) + fromCharCode(aImgData[iOffsetY + iOffsetX]); } for (let c = 0; c < iPadding; c++) { strPixelRow += String.fromCharCode(0); } strPixelData += strPixelRow; } while (--y); return ( encodeData(BITMAPFILEHEADER.concat(BITMAPINFOHEADER)) + encodeData(strPixelData) ); }; /** * saveAsImage * @param canvas canvasElement * @param width {String} image type * @param height {Number} [optional] png width * @param type {string} [optional] png height * @param fileName {String} image name */ const saveAsImage = function (canvas, width, height, type, fileName) { // save file type const fileType = type; if ($support.canvas && $support.dataURL) { if (typeof canvas == "string") { canvas = document.getElementById(canvas); } if (type === undefined) { type = "png"; } type = fixType(type); if (/bmp/.test(type)) { const data = getImageData(scaleCanvas(canvas, width, height)); const strData = genBitmapImage(data); // use new parameter: fileType saveFile(makeURI(strData, downloadMime), fileType, fileName); } else { const strData = getDataURL(canvas, type, width, height); // use new parameter: fileType saveFile(strData.replace(type, downloadMime), fileType, fileName); } } }; const convertToImage = function (canvas, width, height, type) { if ($support.canvas && $support.dataURL) { if (typeof canvas == "string") { canvas = document.getElementById(canvas); } if (type === undefined) { type = "png"; } type = fixType(type); if (/bmp/.test(type)) { const data = getImageData(scaleCanvas(canvas, width, height)); const strData = genBitmapImage(data); return genImage(makeURI(strData, "image/bmp")); } else { const strData = getDataURL(canvas, type, width, height); return genImage(strData); } } }; return { saveAsImage: saveAsImage, saveAsPNG: function (canvas, width, height, fileName) { return saveAsImage(canvas, width, height, "png", fileName); }, saveAsJPEG: function (canvas, width, height, fileName) { return saveAsImage(canvas, width, height, "jpeg", fileName); }, saveAsGIF: function (canvas, width, height, fileName) { return saveAsImage(canvas, width, height, "gif", fileName); }, saveAsBMP: function (canvas, width, height, fileName) { return saveAsImage(canvas, width, height, "bmp", fileName); }, 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"); }, }; })(); // Export function, used in npm export default Canvas2Image;