博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
canvas保存为data:image扩展功能的实现
阅读量:4130 次
发布时间:2019-05-25

本文共 8999 字,大约阅读时间需要 29 分钟。

【已知】

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】

可以试着在canvas上涂涂画画,然后保存看看。如果用bmp格式的话,需要支持 btoa 的base64编码,关于base64编码规则可看上一篇博文

【不完美的地方】

1)jpeg接口本身就不完善,当canvas没有填充颜色或图片时,保存的jpeg由于是直接由png的alpha通道强制转换过来的,所以在png的透明部分在jpeg里面就是黑色的。

2)gif的限制太多。且可用性不大,有png就够了

3)bmp位图生成,计算量稍显大了。

4)由于是强制改mimeType来实现的自动下载,所以下载的时候文件类型不会自动识别。

下载文件没后缀名的解决解决:html5 的 <a> 有 download属性

[javascript] 
  1. var a = document.createElement('a');  
  2. a.download = '我是文件名' + _suffix;  
  3. a.href = _canvas.toDataURL();  
  4. a.click();  
  5.    
  6. _suffix表示图片后缀  
  7. _canvas canvas对象  

转载地址:http://ixbvi.baihongyu.com/

你可能感兴趣的文章
<iOS>关于自定义description的一点用法
查看>>
Unix 命令,常用到的
查看>>
DLL中建立进程共享数据段需要注意的语法问题
查看>>
服务器端技术----Http请求的处理过程
查看>>
C语言-预处理指令2-条件编译
查看>>
C语言-预处理指令3-文件包含
查看>>
C语言-变量类型
查看>>
C语言-static和extern关键字1-对函数的作用
查看>>
C 语言-static和extern关键字2-对变量的作用
查看>>
【JavaScript 教程】浏览器—History 对象
查看>>
还不会正则表达式?看这篇!
查看>>
100道+ JavaScript 面试题,助你查漏补缺
查看>>
JavaScript深入理解之闭包
查看>>
这才是学习Vite2的正确姿势!
查看>>
7 个适用于所有前端开发人员的很棒API,你需要了解一下
查看>>
25个构建Web项目的HTML建议,你需要了解一下!
查看>>
【web素材】02-10款大气的购物商城网站模板
查看>>
6种方式实现JavaScript数组扁平化(flat)方法的总结
查看>>
如何实现a===1 && a===2 && a===3返回true?
查看>>
49个在工作中常用且容易遗忘的CSS样式清单整理
查看>>