【已知】
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来实现的自动下载,所以下载的时候文件类型不会自动识别。

canvas保存为data:image扩展功能的实现的更多相关文章

  1. js将canvas保存成图片并下载

    <canvas id="canvas" width="400" height="400"></canvas> < ...

  2. 毕业论文—使用js将canvas保存为图片文件,并且自定义文件名

    该文章引用http://blog.csdn.net/qq547276542/article/details/51906741 1.从canvas中直接提取图片元数据 // 图片导出为 png 格式 v ...

  3. [ html canvas getImageData Object.data.length ] canvas绘图属性 getImageData Object.data.length 属性讲解

    <!DOCTYPE html> <html lang='zh-cn'> <head> <title>Insert you title</title ...

  4. canvas 保存状态

    1.保存和恢复绘图状态: 在绘制图形时,难免会重复使用某个样式,甚至有时会在不同颜色之间来回切换. 那么为了减少代码冗余,我们可以调用画布中的save()方法,来帮我们 保存一些样式和属性,这样我们就 ...

  5. Canvas保存为图片

    public static void GenerateCanvas(string imgSaveName, int canvasWidth, int canvasHeight, string imgD ...

  6. canvas 保存bitmap到本地

    File f = new File("/sdcard/DCIM/Camera/0.png"); FileOutputStream fos = null; try { fos = n ...

  7. canvas绘制图片

    canvas保存为data:image扩展功能的实现 [已知]canvas提供了toDataURL的接口,可以方便的将canvas画布转化成base64编码的image.目前支持的最好的是png格式, ...

  8. HTML5 Canvas 2D绘图

    为了防止无良网站的爬虫抓取文章,特此标识,转载请注明文章出处.LaplaceDemon/ShiJiaqi. http://www.cnblogs.com/shijiaqi1066/p/4851774. ...

  9. 基于html2canvas实现网页保存为图片及图片清晰度优化

    一.实现HTML页面保存为图片 1.1 已知可行方案 现有已知能够实现网页保存为图片的方案包括: 方案1:将DOM改写为canvas,然后利用canvas的toDataURL方法实现将DOM输出为包含 ...

随机推荐

  1. CSS架构

    CSS架构 有趣的是,我们通常不这样评判其他语言.一个Rails开发者不会因为他写的代码规范就认为他是一个好的开发者.因为这是最基本的.当然它必须是满 足规范,除此以外还要考虑其他方面:代码是否有可读 ...

  2. HDU 2089 数位dp入门

    开始学习数位dp...一道昨天看过代码思想的题今天打了近两个小时..最后还是看了别人的代码找bug...(丢丢) 传说院赛要取消 ? ... 这么菜不出去丢人也好吧~ #include<stdi ...

  3. SSH服务器拒绝了密码 请再试一次

    应该是sshd的设置不允许root用户用密码远程登录. 修改: vim /etc/ssh/sshd_config 找到: # Authentication:LoginGraceTime 120Perm ...

  4. 状态模式 java && php

    状态模式 java && php     状态模式 输入信号是事件:输出是状态结果,状态模式状态机就是一个黑盒子.状态模式主要突出了两个字:”改变”,对象的状态决定了状态的行为,事物的 ...

  5. php调用empty出现错误Can't use function return value in write context

    php调用empty出现错误Can't use function return value in write context 2012-10-28 09:33:22 | 11391次阅读 | 评论:0 ...

  6. 大话数据结构(十二)java程序——KMP算法及改进的KMP算法实现

    1.朴素的模式匹配算法 朴素的模式匹配算法:就是对主串的每个字符作为子串开头,与要连接的字符串进行匹配.对主串做大循环,每个字符开头做T的长度的小循环,直到成功匹配或全部遍历完成为止. 又称BF算法 ...

  7. kuaisupaixu

    #include<stdio.h> void quiksort(int a[],int low,int high) {  int i = low;     int j = high;    ...

  8. JAVA基础讲义

    一.安装JDK 第一步:双击JDK的exe文件. JDK(Java开发包),JRE(Java运行环境) 第二步:配置 path:jdk的根目录,jdk下的bin目录(两个目录之间记得用分号隔开) cl ...

  9. 三 mybatis typeAlias(别名)使用和resultMap使用

     1.MyBatis提供的typeAlias

  10. 蓝牙Host Controller Interface笔记

    1.概述     HCI提供了一个统一的使用蓝牙控制器(BR/EDR Controller,BR/EDR/LE Controller,LE Controller,AMP Controller等)的方法 ...