13
Mobile web 优优优优优( 优优优优优 ) 优优优 @ 优优优优 && 优优优 2013/0 优优 web, 优优优优优优优 (优 优优优 / 优优优优 /ISO6 优 bug) 优优优优优

Html5 create thumbnail

  • Upload
    leon-xu

  • View
    1.983

  • Download
    4

Embed Size (px)

Citation preview

Page 1: Html5 create thumbnail

Mobile web 优化图片上传 ( 前端缩略图 )

徐海亮 @ 长沙墨研 && 發燒網 2013/08/01

手机 web, 上传图片遇到的问题 ( 上传速度慢 / 图片旋转 /ISO6 的 bug) 及解决方案

Page 2: Html5 create thumbnail

遇到的问题 :现在的手机越来越牛 X( 拍摄功能比数码相机还厉害 ~)图片体积也越来越大

上面是 iPhone5/4 拍摄的照片 , 1-2M 的大小是小 case 了 ,iOS6 一般2M 以上 .一个几 M 的图 , 也许我们只是用来生成一个 100*100 的小图 , 上传到后端再生成缩略图就大大的浪费了 , 而且提交表单的时间也比较久 , 对用户体验也不好 .

Safari on iOS supports file uploading—that is, <input type=“file”> elements—on iOS 6 and later. (iOS6 也支持上传了 )

HTML5 应用越来越多的当下 , 还继续使用 input type=“file” 交给后端生成缩略图 ?

Page 3: Html5 create thumbnail

前端生成缩略图的大体思路如下 :

首先判断是否支持 fileReader( 支持 fileReader, canvas 就不在话下了 )

不支持的话 : 不做任何操作 , 默认的 input type="file" 上传 , 靠后端生成缩略图 .

支持的情况 : input change 时 , 判断选择的是图片 , 就创建一个隐藏的canvas, 并把图片画到 canvas 里 . 因为要生成缩略图 , 所以在 canvas 里画图的时候 , 控制剪裁坐标和被剪裁的宽高就 OK 了 .

最后 canvas.toDataURL 转成 base64, post 到后端 ( 先把 input type=“file” 移除 , 再生成个新的 input type=“hidden” 储存图片数据 ), 再提交表单 , 后端接收后直接保存为图片就 OK 了 .

主要用到 : FileReader 和 canvas, 一个用来读取本地图片 , 一个用来生成缩略图 .

Page 4: Html5 create thumbnail

应该差不多了吗 ? 其实还有 bug 等着你 ~

先看一个有 bug 的案例 : https://github.com/kairyou/html5-make-thumb/tree/old/

Page 5: Html5 create thumbnail

遇到的问题 :

1. 选相册里图片上传没有问题,但直接拍照上传 (iOS 6 的内置相机 ) ,图片截取的不完整 ( 被压扁了 ), 比如 :

2. 即使选相册的图片能成功生成缩略图 , 但是也有问题 , 生成后的缩略图有些是东倒西歪或者直接上下颠倒了 .

-->

Page 6: Html5 create thumbnail

1 是 iOS6 的 bug, 较大的图片可能会发生 ( 大概 >2M), 已经有人提交 bug 给 Apple 了 (iOS7 未测试 ).2 是因为相机拍摄的图片本身可能就是反转的 , 拍摄的图片里 EXIF 信息的 orientation 记录了图片的转向 ( 取值范围是 1/3/6/8, 一般是 :1/3/6/8).

默认竖拍的时候其实就是翻转的 , 这时的 orientation=6( 相当于向左转 90 度 , -90deg) 倒立拍照时 =8 ( 向右 90 度 , 90deg). 横着拍时 :iPhone 的按钮在右侧 , (orientation=1, 0deg ) 才是默认的不反转的 .iPhone 的按钮在左侧 , (orientation=3, 180deg ) 是上下倒立的 .在电脑 / 手机查看这些图片都是正确的方向 ? 看图软件大多都支持自动旋转 .另外还有比较少见的 :2/4/5/7( 原图经过反转了 ), 比如 :

原图 : http://www.csharphelper.com/howto_show_exif_orientations.pnghttp://beradrian.files.wordpress.com/2008/11/operations.gif

Page 7: Html5 create thumbnail

用到的 js:

1. megapix-image.js, https://github.com/stomita/ios-imagefile-megapixel2. exif.js, https://github.com/jseidelin/exif-js3. binaryajax.js, https://github.com/jseidelin/binaryajax/

首先自己 input file选择的图片readAsDataURL获取到base64的字符串 , 再用binaryajax获取图片的二进制数据 (data-url to a binary string).然后根据二进制数据利用exif.js获取exif信息 (主要是exif.Orientation), 再用MegaPixImage去生画图 , 再读取新的base64图片 , 提交给后端储存 .

var mpImg = new MegaPixImage(file);mpImg.render(canvas, { maxWidth: opts.width, maxHeight: opts.height, orientation: orientation });

也许有人已经想到为什么不直接用readAsBinaryString读取二进制 , 然后再用exif.js获取exif数据 , 还能省去一个 js, 原因 :1. exif.js对binaryajax的方法有依赖 ;2. 生成的缩略图是空白的 , (猜测是没有在 image.onload后执行生成缩略图导致 , 因为

这里得到的不是图片的base64数据 , 还要另外花费代码去转换数据类型 ,再去加载 , 那就做了更多的事情 , 没意义了 )

还有 , Binaryajax兼容性更佳 (里面还有兼容 IE6的code)

Page 8: Html5 create thumbnail

Megapix 原理 :1. 先按图片的 exif 信息进行旋转 .2. 首先检测图片是否绘制的时候出现异常 :对于尺寸大于 1024*1024 的图片 , 把图片插到 canvas, 只画图片的右上角 1*1像素的部分 , 然后 getImageData 读取这个点的 RGBA 值 , 取返回的 array[3](第 4 个值是透明度 ), 等于 0说明没绘制上去 , 出现异常了 .这时的异常是指图片高宽各缩了一半 .3. 后面要检测图片在垂直方向压扁 (jpg类型的图片才需要检测 ):画宽为 1, 高为图片实际宽度 ( 如果第 1部检测出异常 , 宽高要减半 ) 的矩形 , 把图片插入后 , 再判断最下面 1*1 是否透明 ( 原图的左下角 ), 如果透明再到高度减半的位置判断 (1/2, ¼, 1/8…), 直到遇到不透明的点 . 根据图片顶部到这个点的距离 / 图片高度得到 1 个比率 .4. 开始绘制 , 如果遇到 2 或 3 图片被拉伸的情况 , 这里就相应的拉伸回正常的比例 .

新的方案 : https://github.com/kairyou/html5-make-thumb/

Page 9: Html5 create thumbnail

附加内容 :

判断是否支持 FileReader:$.support.filereader = !!(window.File && window.FileReader && window.FileList && window.Blob);

Php处理 post 过来的图片// save img$img = $_POST['base64'];$uploadFile = $_FILES['upfile'];$target = 'tmp.jpg’;if (isset($img)) { # dataURI

if (preg_match('/data:([^;]*);base64,(.*)/', $img, $matches)) { $img = base64_decode($matches[2]); file_put_contents($target, $img);}

} else { # 普通上传if (isset($uploadFile) && is_uploaded_file($uploadFile['tmp_name']) && $uploadFile['error'] == 0) { move_uploaded_file($uploadFile['tmp_name'], $target); }

}Megapix 里面的一些奇淫技巧 : num >> 1 代替 num *0.5 ( 右移运算 ,目标值 *2 的 1次方 =num; 另外 , 左移类似 5<<4 二进制左移 4位 ( 相当与此数乘以 2 的 4次方 ) ,也就是 80=5 *16 ) 参见 :http://t.cn/zONS2VQnum >> 0 代替 Math.floor() 来取整 (位移的结果没有小数 , 结果向下取整 , 所以这里 num<<0 也同样 ).

Page 10: Html5 create thumbnail

Todo:1. 图片加水印 , 可以是图片水印或文字水印 , 文字水印可以设置字

体 / 大小 /颜色等样式 ; 可以多张水印 ; 可以设置水印的位置 (居中或四个角 ).

2. 可选生成缩略图的方式 , 相当于 css3 的 background-size: contain | cover | auto(效果 :http://t.cn/zQXYpYr), auto: 不强制拉伸图片 , 图片水平垂直居中 ( 大图被切掉 , 小图四周留空 ), cover: 填满元素 , 小图等比拉伸 , 超过的部分被切掉 , 不会有空白 , contain: 等比缩小图片来适应元素的尺寸 , 可能会产生空白 , 图片不会被切掉 .

3. Canvas 里的图片实际尺寸小于缩略图规定的尺寸 (auto 或 contain时 ), 这时会有空白区域 , 可选是否可以把空白也切掉 ( 最后目标图片的尺寸可能会不统一 ); 可选空白区域的背景色 ( 比如白色 , 黑色 , 透明 . 为了照顾 iOS6, 要先生成缩图在填充背景 , 因为前面提到判断图片绘制异常的时候会读取 1*1像素判断是否透明 ).

这些功能在之前写的生成缩略图里面基本都有了 , 新的方案由于依赖megapix, 所以大概会参考megapix 解决 iOS6bug 的部分把代码重新写一份 .

Page 11: Html5 create thumbnail

使用 android低版本的童鞋们哭了 ~ (根本没考虑他们的感受 ).

问题 : canvas.toDataURL() 会返回空数据 ( 应该是 android4.0 以下的版本 , 测试 2.3.7 是不支持的 ), 有个 todataurl-png-js 的解决方案 ( 未测试 ).还有 FileReader/createObjectURL 等没有测试支持情况 .所以照顾低版本 android 的用户 , 还有一些路要走 .还要思考这个是不是有必要 ? 有没有那么多的用户群体 .

因为现在的方案在不支持前端生成缩略图的情况 , 就用后端生成 , 所以android低版本也不会存在问题 .

Page 12: Html5 create thumbnail

普通的 web 表单 , 上传图片靠后端来生成缩略图很平常 , 但有了 HTML5, 针对移动 Web开发可以考虑使用前端生成缩略图了 .

Thanks!

Page 13: Html5 create thumbnail

希望大家多关注 长沙墨研

在这里可以经常接到技术含量较高的 HTML5效果 :比如 : 刮刮卡 /类似 LINE Card 的图片贺卡对话框 /mardown编辑器 (拖曳

上传 / 截图直接粘贴 )

http://cdpn.io/wcIlthttp://sandbox.runjs.cn/show/igpzkemh

欢迎靠谱人才来靠谱团队