原文地址:http://leonshi.com/2015/10/31/html5-canvas-image-compress-crop/?utm_source=tuicool&utm_medium=referral

前面的话

早些时候用 Node-webkit(现在叫 nw.js) 编写过一个辅助前端切图的工具,其中图片处理部分用到了 gm,gm 虽然功能强大,但用于 Node-webkit 却有点发挥不了用处,gm 强依赖于用户的本地环境安装 imagemagick 和 graphicsmagick,而安装 imagemagick 和 graphicsmagick 非常不方便,有时候还需要翻墙,所以这个工具大多数时候是我自己在玩。

为了降低安装成本,这两天开始研究去掉图片处理功能中的 gm 依赖,替换为 HTML5 Canvas 来实现。

在这之前没有深入研究过 canvas,通过这两天的查资料过程,发现 canvas 的 API 非常丰富,实现本文的功能可以说只用到了 canvas 的冰山一角。

功能实现主要用到了 CanvasRenderingContext2D.drawImage 和 HTMLCanvasElement.toDataURL两个方法,接下来先介绍一下这两个方法,如果想直接看结果,可以跳到文章结尾查看完整的例子和代码。


CanvasRenderingContext2D.drawImage()

drawImage 方法是 Canvas 2D 对象的方法,作用是将一张图片绘制到 canvas 画布中。

创建一个 Canvas 2D 对象:

var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');

drawImage 有 3 种调用方式:

ctx.drawImage(image, dx, dy);
ctx.drawImage(image, dx, dy, dWidth, dHeight);
ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);

各个参数的意义:

  • image 图片元素,除了图片,还支持其他 3 种格式,分别是 HTMLVideoElement HTMLCanvasElement ImageBitmap,本文只涉及图片,如果想了解其余格式可以参考 这里
  • sx 要绘制到 canvas 画布的源图片区域(矩形)在 X 轴上的偏移量(相对源图片左上角)
  • sy 与 sx 同理,只是换成 Y 轴
  • sWidth 要绘制到 canvas 画布中的源图片区域的宽度,如果没有指定这个值,宽度则是 sx 到图片最右边的距离
  • sHeight 要绘制到画布中的源图片区域的高度,如果没有指定这个值,高度则是 sy 到图片最下边的距离
  • dx 源图片左上角在 canvas 画布 X 轴上的偏移量
  • dy 源图片左上角在画布 Y 轴上的偏移量
  • dWidth 绘制图片的 canvas 画布宽度
  • dHeight 绘制图片的画布高度

是不是有点晕了?下面这张图可以直观地说明它们的关系:

还是不好理解?那换个姿势,可以这么理解:首先用 sx 和 sy 这两个值去定位图片上的坐标,再根据这个坐标点去图片中挖出一个矩形,矩形的宽高就是 sWidth 和 sHeight 了。矩形挖出来了,现在要把它绘制到画布中去,这时用 dx 和 dy 两个值来确定矩形在画布中的坐标位置,再用 dWidth 和 dHeight 确定划出多少画布区域给这个矩形。

HTMLCanvasElement.toDataURL()

toDataURL 是 canvas 画布元素的方法,返回指定图片格式的 data URI,也就是 base64 编码串。

toDataURL 方法最多接受两个参数,并且这两个参数都是可选的:

  • type 图片格式。支持 3 种格式,分别是 image/jpeg image/png image/webp,默认是 image/png。其中 image/webp 只有 chrome 才支持。
  • quality 图片质量。0 到 1 之间的数字,并且只在格式为 image/jpeg 或 image/webp 时才有效,如果参数值格式不合法,将会被忽略并使用默认值。

另外,如果对应的 canvas 画布宽度或高度为 0,将会得到字符串 data:,,若图片格式不是 image/png,却得到一个以 data:image/png 开头的值,则说明不支持此图片格式。

图片质量

对于图片质量参数的默认值,官方文档并没有说明,这里 提到 Firefox 的默认值是 0.92,我在最新 chrome 浏览器中测试发现大概也是这个数字。不过要想达到各平台统一表现,最好的办法是手动设置此参数。


实现图片压缩的关键代码

HTML:

<canvas id="canvas"></canvas>
<img id="preview" src="">
<img id="source" src="" style="display: none;">

JS:

var canvas = document.getElementById('canvas');
var source = document.getElementById('source');
var preview = document.getElementById('preview'); source.onload = function() {
var width = source.width;
var height = source.height;
var context = canvas.getContext('2d'); // draw image params
var sx = 0;
var sy = 0;
var sWidth = width;
var sHeight = height;
var dx = 0;
var dy = 0;
var dWidth = width;
var dHeight = height;
var quality = 0.92; canvas.width = width;
canvas.height = height; context.drawImage(source, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight); var dataUrl = canvas.toDataURL('image/jpeg', quality);
preview.src = dataUrl;
}; source.src = 'house.jpg';

编写了一个简单的 Demo,可输入质量参数查看压缩结果。

图片压缩结果

用作测试的是一张大小为 146 KB 的 JPG 图片。

测试过程分别使用了 50%, 80%, 92%, 95%, 96%, 97%, 98%, 99%, 100% 这八个质量参数,结果如下:

查看原图

换算成图表:

问题

从图表中可以看到,压缩比为 95% 时与原图大小最接近,此后,随着压缩参数增大直到 98%,增长比较规律,但从 98 到 99 尤其是 99 到 100,增长突然变陡,比原图大小翻了将近 3 倍!

这里存在两个问题:

  • 为什么 95% 是最接近原图的压缩比?这是否普遍规律?
  • 为什么 100% 比原图增大了这么多?

在网上查了一些资料,但并没有找到确切的原因,也没有找到与之相匹配的类似问题。或许是我搜索的方式不对?如果你正好知道,欢迎留言告知。

实现图片裁切的不完全代码

function cropImage(targetCanvas, x, y, width, height) {
var targetctx = targetCanvas.getContext('2d');
var targetctxImageData = targetctx.getImageData(x, y, width, height); // sx, sy, sWidth, sHeight var c = document.createElement('canvas');
var ctx = c.getContext('2d'); c.width = width;
c.height = height; ctx.rect(0, 0, width, height);
ctx.fillStyle = 'white';
ctx.fill();
ctx.putImageData(targetctxImageData, 0, 0); // imageData, dx, dy document.getElementById('source2').src = c.toDataURL('image/jpeg', 0.92);
document.getElementById('source2').style.display = 'initial';
}

以上代码中,getImageData 和 putImageData 都是 Canvas 2D 对象的方法,前者用于获取画布上根据参数指定矩形的像素数据,返回的是一个多维数组。后者则用于将这些像素数据绘制到画布中,同样可以指定画布中的绘制位置。

裁切的原理是通过 canvas A 的 getImageData 方法取出图片中指定区域的像素数据,再用 canvas B 的 putImageData 方法将像素数据绘制到 canvas B 中,并保持 canvas B 的尺寸与取出区域的尺寸一致。canvas B 中的图片就是裁切得到的图片区域块。

比如要裁切女帝的左耳环:

简单量一下距离,就可以用下面的代码实现:

cropImage(canvas, 250, 250, 90, 80)

好了,差不多就是这些。

HTML5 CANVAS 实现图片压缩和裁切的更多相关文章

  1. 前端通过canvas实现图片压缩

    在一次的项目中,需要用户上传图片,目前市场随便一个手机拍出来的照片都是好几兆,直接上传特别占用带宽,影响用户体验,所以要求对用户上传图片进行压缩后再上传:那么前端怎么实现这个功能呢? 亲测可将4M图片 ...

  2. Html5 Canvas 实现图片合成

    多个图片合成一张 <!doctype html> <html> <head> <meta charset="utf-8"> < ...

  3. 使用FormData数据做图片上传: new FormData() canvas实现图片压缩

    使用FormData数据做图片上传: new FormData()       canvas实现图片压缩 ps: 千万要使用append不要用set   苹果ios有兼容问题导致数据获取不到,需要后台 ...

  4. 使用html5 canvas绘制图片

    注意:本文属于<html5 Canvas绘制图形入门详解>系列文章中的一部分.如果你是html5初学者,仅仅阅读本文,可能无法较深入的理解canvas,甚至无法顺畅地通读本文.请点击上述链 ...

  5. Js利用Canvas实现图片压缩

    最近做的APP项目涉及到手机拍照上传图片,因为手机拍照的图片通常都比较大,所以上传的时候就会很慢.为此,需要对图片进行压缩处理来优化上传功能.以下是具体实现: /* * 图片压缩 * img 原始图片 ...

  6. html5 canvas实现图片玻璃碎片特效

    今天要为大家带来一款html5 canvas实现的图片玻璃碎片特效.图片以玻璃碎片的形式出现到界面中,然后似玻璃被打碎的效果渐消息.效果图如下: 在线预览   源码下载 实现代码: html代码: & ...

  7. 利用html5的画布canvas进行图片压缩处理

    在网上找的代码,按自己的需求改了下,忘记在哪找的了.这里记着方便自己以后学习. // 参数,最大高度 //var MAX_HEIGHT = 100; var MAX_WIDTH = 200; // 渲 ...

  8. jquery+html5+canvas实现图片 预览 压缩 上传

    javascirpt (function($){ $.fn.extend({ aiiUpload:function(obj) { if(typeof obj !="object") ...

  9. HTML5 Canvas 绘制图片不显示的问题

    问题: 慕名赶来,却一脚踩空,低头一看,地上一个大坑. 事情是这样的,在我看完w3c的介绍和很有说服力和教学力的demo后,本着实践出真知的思想决定上手一试,这一试不要紧~ 我按照流水线工程铺设以下几 ...

随机推荐

  1. goim 及时消息 集成

    https://github.com/roamdy/goim-oc-sdk goim 及时消息 集成

  2. chrome调试 JavaScript 脚本

    随着 JavaScript 应用的复杂性逐渐提高,开发者需要有力的调试工具来帮助他们快速发现问题的原因,并且能高效地修复它.Chrome DevTools 提供了一系列实用的工具使得调试 JavaSc ...

  3. php json 格式化

    header('content-type:application/json;charset=utf8'); $arr = array( 'status' => true, 'errMsg' =& ...

  4. C++之路进阶——codevs3333(高级打字机)

    3333 高级打字机  时间限制: 1 s  空间限制: 256000 KB  题目等级 : 大师 Master     题目描述 Description 早苗入手了最新的高级打字机.最新款自然有着与 ...

  5. 基础-DP

    Many years ago , in Teddy’s hometown there was a man who was called “Bone Collector”. This man like ...

  6. 【003:jsoncpp的简单使用】

    #include <json/json.h> #include <iostream> #include <string> using namespace std; ...

  7. 浅述WinForm多线程编程与Control.Invoke的应用

    VS2008.C#3.0在WinForm开发中,我们通常不希望当窗体上点了某个按钮执行某个业务的时候,窗体就被卡死了,直到该业务执行完毕后才缓过来.一个最直接的方法便是使用多线程.多线程编程的方式在W ...

  8. cocos2d-x quick 学习 一 环境

    最近几天都在学习quick 一直也在查找资料. 本来这篇文章在昨晚就能写好的.可是昨晚环境遇到点问题自己没想通. 正题:首先是环境配置: 由于我在mac下 所以在网上找了很多资料提前看了.  我之前也 ...

  9. VS2013 添加已有文件夹

    1.将现有项目或文件夹拷贝到指定目录下 2.解决方案右上有个显示所有文件的按钮 然后选中所有要添加的文件,右击

  10. Nginx的安装配置

    1.安装PCRE库 $ cd /usr/local/ $ https://sourceforge.net/projects/pcre/files/pcre/8.36/ $ tar -zxvf pcre ...