`
天梯梦
  • 浏览: 13742225 次
  • 性别: Icon_minigender_2
  • 来自: 洛杉矶
社区版块
存档分类
最新评论

canvas保存为data:image扩展功能的实现

 
阅读更多

【已知】
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

 

index.html

<!doctype html>
<html>
<meta charset="utf-8" />
<script src="canvas2image.js"></script>
<style>
    .doc {
        width: 604px;
        margin: 0 auto;
    }
    canvas {
        display: block;
        border: 2px solid #888;
    }
</style>
<body>
<div class="doc">
    <canvas width="600" height="400" id="cvs"></canvas>
    <div>
        <p>
            <button id="save">save</button> or <button id="convert">convert to</button> as: 
            <select id="sel">
                <option value="png">png</option>
                <option value="jpeg">jpeg</option>
                <option value="bmp">bmp</option>
            </select><br/>
            width : <input type="number" value="300" id="imgW" /><br/>
            height : <input type="number" value="200" id="imgH" />
        </p>

    </div>
    <div id="imgs">
        
    </div>
</div>
<script>
    var canvas, ctx, bMouseIsDown = false, iLastX, iLastY,
        $save, $imgs,
        $convert, $imgW, $imgH,
        $sel;
    function init () {
        canvas = document.getElementById('cvs');
        ctx = canvas.getContext('2d');
        $save = document.getElementById('save');
        $convert = document.getElementById('convert');
        $sel = document.getElementById('sel');
        $imgs = document.getElementById('imgs');
        $imgW = document.getElementById('imgW');
        $imgH = document.getElementById('imgH');
        bind();
        draw();
    }
    function bind () {
        canvas.onmousedown = function(e) {
            bMouseIsDown = true;
            iLastX = e.clientX - canvas.offsetLeft + (window.pageXOffset||document.body.scrollLeft||document.documentElement.scrollLeft);
            iLastY = e.clientY - canvas.offsetTop + (window.pageYOffset||document.body.scrollTop||document.documentElement.scrollTop);
        }
        canvas.onmouseup = function() {
            bMouseIsDown = false;
            iLastX = -1;
            iLastY = -1;
        }
        canvas.onmousemove = function(e) {
            if (bMouseIsDown) {
                var iX = e.clientX - canvas.offsetLeft + (window.pageXOffset||document.body.scrollLeft||document.documentElement.scrollLeft);
                var iY = e.clientY - canvas.offsetTop + (window.pageYOffset||document.body.scrollTop||document.documentElement.scrollTop);
                ctx.moveTo(iLastX, iLastY);
                ctx.lineTo(iX, iY);
                ctx.stroke();
                iLastX = iX;
                iLastY = iY;
            }
        };
        
        $save.onclick = function (e) {
            var type = $sel.value,
                w = $imgW.value,
                h = $imgH.value;
            Canvas2Image.saveAsImage(canvas, w, h, type);
        }
        $convert.onclick = function (e) {
            var type = $sel.value,
                w = $imgW.value,
                h = $imgH.value;
            $imgs.appendChild(Canvas2Image.convertToImage(canvas, w, h, type))
        }
        
    }
    function draw () {
        ctx.fillStyle = '#ffffff';
        ctx.fillRect(0, 0, 600, 400);
        ctx.fillStyle = 'red';
        ctx.fillRect(100, 100, 50, 50);
    }
    
    
    onload = init;
</script>
</body>
</html>

 

分享到:
评论

相关推荐

    基于html2canvas生成带二维码的活动海报

    本项目"基于html2canvas生成带二维码的活动海报"提供了一个纯前端解决方案,使得开发者无需后端支持也能快速实现此类功能。html2canvas是一个JavaScript库,它能够将网页的DOM(文档对象模型)渲染为图片,从而解决...

    canvas2image.zip

    总的来说,canvas2image.js是Web开发中一个实用的工具,它扩展了canvas的功能,使开发者能够轻松地将动态图形保存为静态图像,提升了用户体验。结合indexss.js文件,可能是提供了一些示例代码或额外的功能,使得在...

    canvas电子签名,支持保存签名图片

    本主题聚焦于使用canvas技术实现电子签名并将其保存为图片的功能。Canvas是HTML5提供的一种强大的图形绘制工具,它允许开发者通过JavaScript在网页上进行动态图形编程。以下是对这个知识点的详细阐述: 首先,我们...

    js+canvas实现图片格式webp/png/jpeg在线转换

    在本文中,我们将探讨如何使用JavaScript和HTML5的Canvas API实现图片在线格式转换,包括WebP、PNG和JPEG。...此外,可以扩展此功能,实现批量图片转换或提供更丰富的图片调整选项,如压缩级别、尺寸调整等。

    canvas截图.zip

    这可以通过调用`toDataURL()`方法完成,它会返回一个dataURL,表示canvas的内容,格式通常是`data:image/png;base64`。例如: ```javascript var dataURL = canvas.toDataURL(); ``` 有了dataURL,我们就可以创建...

    canvas 涂鸦 保存 预览 下载

    5. **保存功能**: - 保存Canvas内容为图片,可以利用`toDataURL`方法,它会返回一个包含当前画布内容的data URL。 - 将这个URL转换为Blob对象,再使用FileSaver.js等库实现本地下载,或者通过Ajax发送到服务器...

    使用canvas实现图片马赛克

    以下我们将详细讲解如何使用canvas和JavaScript来实现这一功能。 首先,我们需要创建一个canvas元素,并将待处理的图片加载到canvas的上下文环境中。在JavaScript中,我们可以使用`&lt;canvas&gt;`的`getContext()`方法...

    uniapp移动app实现将网页保存为图片到手机相册

    本话题主要关注如何在uniapp中实现一个功能,即“将网页保存为图片并保存到手机相册”。这个功能在很多场合都十分实用,例如用户想保存网页内容作为参考或者分享给他人。 首先,我们需要理解uniapp的架构。uniapp是...

    html5 canvas填色画游戏代码

    HTML5 Canvas技术是Web开发中的一个关键特性,它允许开发者在网页上绘制图形,实现动态交互的效果。...通过研究这个游戏的源码,开发者可以深入理解Canvas绘图的原理,并且可以在此基础上扩展出更多有趣的互动功能。

    用canvas写的一个对比度

    这样,我们就实现了基于Canvas的对比度调整功能。这个例子中的"对比度demo"可能包含一个完整的示例代码,演示了如何在实际项目中应用上述逻辑。你可以通过下载并查看这个示例,进一步了解和学习如何在JavaScript中...

    svg保存为png图片js实例

    在某些情况下,我们可能需要将SVG图形转换为PNG格式,例如为了兼容不支持SVG的浏览器,或者在网页上实现特定功能。本文将详细介绍如何使用JavaScript实现SVG到PNG的转换,并提供一个实际的js实例。 首先,SVG到PNG...

    html5拍照功能实现代码(htm5上传文件)

    总结来说,HTML5的拍照和上传功能实现涉及到的技术点包括:Media Capture API的使用、Video标签和getUserMedia方法的应用、Canvas元素的图像绘制和数据转换,以及前端AJAX与后端数据处理的交互。通过这些技术的组合...

    canvas转图片的js

    这个方法会返回一个包含Canvas内容的data URL,其格式通常为"data:image/png;base64,",这是一种Base64编码的PNG图片数据。你可以直接将这个URL作为标签的src属性,或者通过AJAX发送到服务器保存。 以下是一个简单...

    canvas实现一个简易画板

    这可以通过Canvas的`toDataURL`方法实现,它会返回一个包含画布内容的data URL: ```javascript function exportImage() { var dataURL = canvas.toDataURL('image/png'); // 可以将dataURL设置为img元素的src,...

    JavaScript-Canvas-to-Blob.zip

    而Canvas to Blob则是一个JavaScript库,它扩展了Canvas的功能,使得我们可以将Canvas上的图形转换为Blob对象,这对于数据存储和传输非常有用。 Blob(Binary Large Object)是JavaScript中用于处理二进制数据的...

    canvas生成二维码生成

    这可以通过在canvas上先绘制二维码,再在其指定位置绘制图片实现。例如,假设我们有一个`logo.png`作为中间的图片: ```javascript var logo = new Image(); logo.src = 'logo.png'; logo.onload = function() { ...

    web手势库AlloyFinger运用案例:代言海报

    综上所述,通过结合AlloyFinger手势库和PHP,我们可以实现对Canvas图像的交互控制以及图片的保存功能。这不仅提高了用户的体验,也使得Web应用具备了更丰富的交互性。在实际项目中,开发者可以根据具体需求对这些...

    基于HTML5-Canvas的画图板设计与实现.doc

    在实现基于HTML5-Canvas的画图板时,开发者需要对Canvas元素有一个全面的理解,包括它的渲染上下文(context)、绘图路径(path)、像素操作(image data)等方面。通过这些接口,开发者可以构建出复杂的绘图应用。例如,...

    html5 canvas适用于手机端的涂鸦画板图片下载

    为了实现图片下载功能,我们可以利用Canvas的`toDataURL`方法,将Canvas当前的内容转换为一个data URL,这是一个包含了图像数据的URL,通常以"data:image/png;base64,"开头。这个URL可以作为新窗口的来源,也可以...

Global site tag (gtag.js) - Google Analytics