分类目录归档:js

Html5 create thumbnail



  • Mobile web优化图片上传(前端缩略图) 徐海亮 @ 长沙墨研 && 發燒網 2013/08/01 手机web, 上传图片遇到的问题(上传速度慢/ 图片旋转/ISO6的bug)及解决方案

  • 遇到的问题: 现在的手机越来越牛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”交给后端生成 缩略图?

  • 前端生成缩略图的大体思路如下: 首先判断是否支持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, 一个用来读取本地图片, 一个用来生成缩 略图.

  • 应该差不多了吗? 其实还有bug等着你~ 先看一个有bug的案例: https://github.com/kairyou/html5-make-thumb/tree/old/

  • 遇到的问题: 1. 选相册里图片上传没有问题,但直接拍照上传(iOS 6 的内置相机),图片截取的 不完整(被压扁了), 比如: 1. 即使选相册的图片能成功生成缩略图, 但是也有问题, 生成后的缩略图有些是东 倒西歪或者直接上下颠倒了. –>

  • 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.png http://beradrian.files.wordpress.com/2008/11/operations.gif

  • 用到的js: 1. megapix-image.js, https://github.com/stomita/ios-imagefile-megapixel 2. exif.js, https://github.com/jseidelin/exif-js 3. 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)

  • 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/

  • 附加内容: 判断是否支持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/zONS2VQ num >> 0 代替 Math.floor()来取整(位移的结果没有小数, 结果向下取整, 所以这里 num<<0 也同样).

  • 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的部分把代码重新写一
    份.

  • 使用android低版本的童鞋们哭了~ (根本没考虑他们的感受). 问题: canvas.toDataURL() 会返回空数据(应该是android4.0以下的版本, 测试 2.3.7是不支持的), 有个todataurl-png-js的解决方案(未测试). 还有FileReader/createObjectURL等没有测试支持情况. 所以照顾低版本android的用户, 还有一些路要走. 还要思考这个是不是有必要? 有没有那么多的用户群体. 因为现在的方案在不支持前端生成缩略图的情况, 就用后端生成, 所以
    android低版本也不会存在问题.

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

  • 希望大家多关注 长沙墨研 在这里可以经常接到技术含量较高的HTML5效果: 比如: 刮刮卡/类似LINE Card的图片贺卡对话框/mardown编辑器(拖曳上传/截 图直接粘贴) http://cdpn.io/wcIlt http://sandbox.runjs.cn/show/igpzkemh 欢迎靠谱人才来靠谱团队

转自:http://www.slideshare.net/99leon/html5-create-thumbnail

HTML5之FileReader的使用



HTML5定义了FileReader作为文件API的重要成员用于读取文件,根据W3C的定义,FileReader接口提供了读取文件的方法和包含读取结果的事件模型。

FileReader的使用方式非常简单,可以按照如下步骤创建FileReader对象并调用其方法:

1.检测浏览器对FileReader的支持

  1. if(window.FileReader) {  
  2.     var fr = new FileReader();  
  3.     // add your code here  
  4. }  
  5. else {  
  6.     alert(“Not supported by your browser!”);  
  7. }  

2. 调用FileReader对象的方法


FileReader 的实例拥有 4 个方法,其中 3 个用以读取文件,另一个用来中断读取。下面的表格列出了这些方法以及他们的参数和功能,需要注意的是 ,无论读取成功或失败,方法并不会返回读取结果,这一结果存储在 result属性中。







方法名

参数

描述

abort

none

中断读取

readAsBinaryString

file

将文件读取为二进制码

readAsDataURL

file

将文件读取为 DataURL

readAsText

file, [encoding]

将文件读取为文本


readAsText:该方法有两个参数,其中第二个参数是文本的编码方式,默认值为 UTF-8。这个方法非常容易理解,将文件以文本方式读取,读取的结果即是这个文本文件中的内容。
readAsBinaryString:该方法将文件读取为二进制字符串,通常我们将它传送到后端,后端可以通过这段字符串存储文件。
readAsDataURL:这是例子程序中用到的方法,该方法将文件读取为一段以 data: 开头的字符串,这段字符串的实质就是 Data URL,Data URL是一种将小文件直接嵌入文档的方案。这里的小文件通常是指图像与 html 等格式的文件。


3. 处理事件


FileReader 包含了一套完整的事件模型,用于捕获读取文件时的状态,下面这个表格归纳了这些事件。









事件

描述

onabort

中断时触发

onerror

出错时触发

onload

文件读取成功完成时触发

onloadend

读取完成触发,无论成功或失败

onloadstart

读取开始时触发

onprogress

读取中


文件一旦开始读取,无论成功或失败,实例的 result 属性都会被填充。如果读取失败,则 result 的值为 null ,否则即是读取的结果,绝大多数的程序都会在成功读取文件的时候,抓取这个值。

  1. fr.onload = function() {  
  2.     this.result;  
  3. };  

下面通过一个上传图片预览和带进度条上传来展示FileReader的使用。

  1. <script type=“text/javascript”>  
  2.         function showPreview(source) {  
  3.             var file = source.files[0];  
  4.             if(window.FileReader) {  
  5.                 var fr = new FileReader();  
  6.                 fr.onloadend = function(e) {  
  7.                     document.getElementById(“portrait”).src = e.target.result;  
  8.                 };  
  9.                 fr.readAsDataURL(file);  
  10.             }  
  11.         }  
  12.     </script>  
  13.   
  14. <input type=“file” name=“file” onchange=“showPreview(this)” />  
  15.                                 <img id=“portrait” src=“” width=“70” height=“75”>  


如果要限定上传文件的类型,可以通过文件选择器获取文件对象并通过type属性来检查文件类型

  1. if(!/image/w+/.test(file.type)){  
  2.     alert(“请确保文件为图像类型”);  
  3.     return false;  
  4. }  

不难发现这个检测是基于正则表达式的,因此可以进行各种复杂的匹配,非常有用。

如果要增加一个进度条,可以使用HTML 5的progress标签,通过下面的代码实现。

  1. <form>  
  2.     <fieldset>  
  3.         <legend>分度读取文件:</legend>  
  4.         <input type=“file” id=“File” />  
  5.         <input type=“button” value=“中断” id=“Abort” />  
  6.         <p>  
  7.             <label>读取进度:</label><progress id=“Progress” value=“0” max=“100”></progress>  
  8.         </p>  
  9.         <p id=“Status”></p>  
  10.     </fieldset>  
  11. </form>  


  1. var h = {  
  2.     init: function() {  
  3.         var me = this;  
  4.            
  5.         document.getElementById(‘File’).onchange = me.fileHandler;  
  6.         document.getElementById(‘Abort’).onclick = me.abortHandler;  
  7.            
  8.         me.status = document.getElementById(‘Status’);  
  9.         me.progress = document.getElementById(‘Progress’);  
  10.         me.percent = document.getElementById(‘Percent’);  
  11.            
  12.         me.loaded = 0;  
  13.         //每次读取1M  
  14.         me.step = 1024 * 1024;  
  15.         me.times = 0;  
  16.     },  
  17.     fileHandler: function(e) {  
  18.         var me = h;  
  19.            
  20.         var file = me.file = this.files[0];  
  21.            
  22.         var reader = me.reader = new FileReader();  
  23.            
  24.         //  
  25.         me.total = file.size;  
  26.            
  27.         reader.onloadstart = me.onLoadStart;  
  28.         reader.onprogress = me.onProgress;  
  29.         reader.onabort = me.onAbort;  
  30.         reader.onerror = me.onerror;  
  31.         reader.onload = me.onLoad;  
  32.         reader.onloadend = me.onLoadEnd;  
  33.         //读取第一块  
  34.         me.readBlob(file, 0);  
  35.     },  
  36.     onLoadStart: function() {  
  37.         var me = h;  
  38.     },  
  39.     onProgress: function(e) {  
  40.         var me = h;  
  41.            
  42.         me.loaded += e.loaded;  
  43.         //更新进度条  
  44.         me.progress.value = (me.loaded / me.total) * 100;  
  45.     },  
  46.     onAbort: function() {  
  47.         var me = h;  
  48.     },  
  49.     onError: function() {  
  50.         var me = h;  
  51.            
  52.     },  
  53.     onLoad: function() {  
  54.         var me = h;  
  55.    
  56.         if(me.loaded < me.total) {  
  57.             me.readBlob(me.loaded);  
  58.         } else {  
  59.             me.loaded = me.total;  
  60.         }  
  61.     },  
  62.     onLoadEnd: function() {  
  63.         var me = h;  
  64.            
  65.     },  
  66.     readBlob: function(start) {  
  67.         var me = h;  
  68.            
  69.         var blob,  
  70.             file = me.file;  
  71.            
  72.         me.times += 1;  
  73.            
  74.         if(file.webkitSlice) {  
  75.             blob = file.webkitSlice(start, start + me.step + 1);  
  76.         } else if(file.mozSlice) {  
  77.             blob = file.mozSlice(start, start + me.step + 1);  
  78.         }  
  79.            
  80.         me.reader.readAsText(blob);  
  81.     },  
  82.     abortHandler: function() {  
  83.         var me = h;  
  84.            
  85.         if(me.reader) {  
  86.             me.reader.abort();  
  87.         }  
  88.     }  
  89. };  
  90.    
  91. h.init(); 

转自:http://blog.csdn.net/jackfrued/article/details/8967667

HTML5 Canvas的内容保存为图片toDataURL()


将HTML5 Canvas的内容保存为图片主要思想是借助Canvas自己的API – toDataURL()来实现,具体实现如下,感兴趣的朋友可以参考下哈,希望对你有所帮助-

主要思想是借助Canvas自己的API – toDataURL()来实现,整个实现 

HTML + JavaScript的代码很简单。 

复制代码代码如下:

<html> 

<meta http-equiv=”X-UA-Compatible” content=”chrome=1″> 

<head> 

<script> 

window.onload = function() { 

draw(); 

var saveButton = document.getElementById(“saveImageBtn”); 

bindButtonEvent(saveButton, “click”, saveImageInfo); 

var dlButton = document.getElementById(“downloadImageBtn”); 

bindButtonEvent(dlButton, “click”, saveAsLocalImage); 

}; 

function draw(){ 

var canvas = document.getElementById(“thecanvas”); 

var ctx = canvas.getContext(“2d”); 

ctx.fillStyle = “rgba(125, 46, 138, 0.5)”; 

ctx.fillRect(25,25,100,100); 

ctx.fillStyle = “rgba( 0, 146, 38, 0.5)”; 

ctx.fillRect(58, 74, 125, 100); 

ctx.fillStyle = “rgba( 0, 0, 0, 1)”; // black color 

ctx.fillText(“Gloomyfish – Demo”, 50, 50); 

function bindButtonEvent(element, type, handler) 

if(element.addEventListener) { 

element.addEventListener(type, handler, false); 

} else { 

element.attachEvent(‘on’+type, handler); 

function saveImageInfo () 

var mycanvas = document.getElementById(“thecanvas”); 

var image = mycanvas.toDataURL(“image/png”); 

var w=window.open(‘about:blank’,’image from canvas’); 

w.document.write(“<img src='”+image+”‘ alt=’from canvas’/>”); 

function saveAsLocalImage () { 

var myCanvas = document.getElementById(“thecanvas”); 

// here is the most important part because if you dont replace you will get a DOM 18 exception. 

// var image = myCanvas.toDataURL(“image/png”).replace(“image/png”, “image/octet-stream;Content-Disposition: attachment;filename=foobar.png”); 

var image = myCanvas.toDataURL(“image/png”).replace(“image/png”, “image/octet-stream”); 

window.location.href=image; // it will save locally 

</script> 

</head> 

<body bgcolor=”#E6E6FA”> 

<div> 

<canvas width=200 height=200 id=”thecanvas”></canvas> 

<button id=”saveImageBtn”>Save Image</button> 

<button id=”downloadImageBtn”>Download Image</button> 

</div> 

</body> 

</html>

转自:http://www.jb51.net/html5/83401.html

HTML5 canvas 的一些属性


canvas 是一个标签, 使用跟 div 之类的没啥区别. 在 js 里你也可以通过 document.getElementById 等方法来获得它.

canvas 有两个方法:

  • getContext(contextId) : 获得 canvas 的 context (上下文), 上下文不好理解, 形象点说, canvas 是一张绘图纸, 那 context 就是画笔. contextId 是画笔类型, 现在基本上只有 “2d” 这一个, 就是只能花 2D 平面的图形, 已经有浏览器支持更牛的 3D 类型, 比如 “webgl” , 那个太高端了, 我还没研究过…
  • toDataURL(type, args…) : 将当前 canvas 里的内容导出成图片数据, 结果是一个以 “data:” 开头的字符串, 实际上是图片的像素数据… 把这个贴到 img 标签的 src 里, 就能看见图片了. type 是导出的 mime 类型, 比如 “image/jpeg” , “image/png” , “image/svg+xml” … 至于支持那些个 mime 类型跟浏览器有关 , 默认是 “image/png”.

Context(2D)

context 是这篇博文的主要内容, 包含了大量的绘制方法.

这些方法可以分成几大类:

状态(state)

绘制状态其实是保存你设置的一些绘制属性的栈, 这些属性包括 : transformation matrix, clipping region, fillStyle, font, globalAlpha, globalCompositeOperation, lineCap, lineJoin, lineWidth, miterLimit, shadowBlur, shadowColor, shadowOffsetX, shadowOffsetY, strokeStyle, textAlign, textBaseline.

通常在绘制前, 应该先保存当前绘制状态, 在绘制完成后恢复原始状态.

  • save() : 保存当前的绘制状态
  • restore() : 恢复到前一个状态

变换(Transformations)

变换其实就是平面几何里的矩阵变换, 通过变换可以实现缩放, 平移, 倾斜, 旋转等复杂的效果.

  • rotate(angel) : 旋转, angel 是要旋转的弧度
  • scale(x, y) : 缩放, x y 是要缩放的比例
  • translate(x, y) : 平移, x y 是要平移的距离
  • setTransform(m11, m12, m21, m22, dx, dy) : 设置当前的变换矩阵为参数里的数据
  • transform(m11, m12, m21, m22, dx, dy) : 将当前的变换矩阵乘以参数里的数据获得新的变换矩阵

转自:http://www.90focus.com/post/HTML5canvas.html