HTML5 CANVAS 实现图片压缩和裁切
原文地址: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 种格式,分别是
HTMLVideoElementHTMLCanvasElementImageBitmap,本文只涉及图片,如果想了解其余格式可以参考 这里 - 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/jpegimage/pngimage/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 实现图片压缩和裁切的更多相关文章
- 前端通过canvas实现图片压缩
在一次的项目中,需要用户上传图片,目前市场随便一个手机拍出来的照片都是好几兆,直接上传特别占用带宽,影响用户体验,所以要求对用户上传图片进行压缩后再上传:那么前端怎么实现这个功能呢? 亲测可将4M图片 ...
- Html5 Canvas 实现图片合成
多个图片合成一张 <!doctype html> <html> <head> <meta charset="utf-8"> < ...
- 使用FormData数据做图片上传: new FormData() canvas实现图片压缩
使用FormData数据做图片上传: new FormData() canvas实现图片压缩 ps: 千万要使用append不要用set 苹果ios有兼容问题导致数据获取不到,需要后台 ...
- 使用html5 canvas绘制图片
注意:本文属于<html5 Canvas绘制图形入门详解>系列文章中的一部分.如果你是html5初学者,仅仅阅读本文,可能无法较深入的理解canvas,甚至无法顺畅地通读本文.请点击上述链 ...
- Js利用Canvas实现图片压缩
最近做的APP项目涉及到手机拍照上传图片,因为手机拍照的图片通常都比较大,所以上传的时候就会很慢.为此,需要对图片进行压缩处理来优化上传功能.以下是具体实现: /* * 图片压缩 * img 原始图片 ...
- html5 canvas实现图片玻璃碎片特效
今天要为大家带来一款html5 canvas实现的图片玻璃碎片特效.图片以玻璃碎片的形式出现到界面中,然后似玻璃被打碎的效果渐消息.效果图如下: 在线预览 源码下载 实现代码: html代码: & ...
- 利用html5的画布canvas进行图片压缩处理
在网上找的代码,按自己的需求改了下,忘记在哪找的了.这里记着方便自己以后学习. // 参数,最大高度 //var MAX_HEIGHT = 100; var MAX_WIDTH = 200; // 渲 ...
- jquery+html5+canvas实现图片 预览 压缩 上传
javascirpt (function($){ $.fn.extend({ aiiUpload:function(obj) { if(typeof obj !="object") ...
- HTML5 Canvas 绘制图片不显示的问题
问题: 慕名赶来,却一脚踩空,低头一看,地上一个大坑. 事情是这样的,在我看完w3c的介绍和很有说服力和教学力的demo后,本着实践出真知的思想决定上手一试,这一试不要紧~ 我按照流水线工程铺设以下几 ...
随机推荐
- 也谈微信小程序
小程序是一种不需要下载安装即可使用的应用,它实现了应用"触手可及"的梦想,用户扫一扫或者搜一下即可打开应用.也体现了"用完即走"的理念,用户不关心是否安装太 ...
- 浏览器桌面通知--Notification
前言 最近项目上要用到浏览器桌面通知,之前虽然知道有这个东西,但是一直没有用过,借此机会了解下桌面通知的机制,在此分享下. 1.权限 首先需要明确的是,不是所有网页都可以发桌面通知的,不然不得烦死,那 ...
- SSM框架-----------SpringMVC+Spring+Mybatis框架整合详细教程
1.基本概念 1.1.Spring Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One ...
- python os 命令,及判断文件夹是否存在
使用前 import os导入模块 os模块: os.sep 可以取代操作系统特定的路径分割符 os.linesep 字符串给出当前平台使用的行终止符.例如,Windows使用'\r\n ...
- C#操作Mongodb的心得
Mongodb是一个强大的文档型数据库,采用BSON的数据格式.本文主要采用其官方的C#驱动来操作其表中的集合.驱动版本为1.1.0,下载地址为: http://mongodb.github.io/m ...
- FORTRAN 90标准函数(一) (转)
符号约定: l I代表整型;R代表实型;C代表复型;CH代表字符型;S代表字符串;L代表逻辑型;A代表数组;P代表指针;T代表派生类型;AT为任意类型. l s:P表示s类型为P类型(任意kind ...
- Timusoj 1982. Electrification Plan
http://acm.timus.ru/problem.aspx?space=1&num=1982 1982. Electrification Plan Time limit: 0.5 sec ...
- JS新API标准 地理定位(navigator.geolocation)/////////zzzzzzzzzzz
在新的API标准中,可以通过navigator.geolocation来获取设备的当前位置,返回一个位置对象,用户可以从这个对象中得到一些经纬度的相关信息. navigator.geolocation ...
- 为什么C#中要设计IntPtr?
示例代码: IntPtr vertex = someObj.Get().Lock(0, someObj.Get().GetSizeInBytes(), HardwareBuffer.LOCKOPTIO ...
- java 图片处理工具类
import java.awt.Image; import java.awt.Rectangle; import java.awt.geom.AffineTransform; import ja ...