我做了一个 Web 本地图片 canvas 截取上传 的demo。发现了几个问题,记录下:
1. Canvas 元素大小 (css width height) 和表面大小(canvas 自身的 width height 属性)两个概念是不一样的,当两个大小不一致时,坐标需要进行转换计算。
// 其中 x, y 是视口坐标 function windowToCanvas(canvas, x, y) { var bbox = canvas.getBoundingClientRect(); return { x: (x - bbox.left) * (canvas.width / bbox.width), y: (y - bbox.top) * (canvas.height / bbox.height) }; }
2. a ndroid 老的原生 手机浏览器 Blob 构造函数有 bug (比如使用微信或qq浏览器, new Blob() 会抛出异常) , 我的解决方法是使用 base64 上传,服务端解码。由于 base64 大小为原来的 4/3 倍,自然会想能不能像 c 语言那样接把字符当作 unsigned char 来看待。仔细一想 js 是不行的。因 text+=1 ,数字 1 将被转换为字符串 "1" ,而 text[i] 是仅可读,不修改!
var clearWidth, clearHeight; var imageLoaded = false; var cutPoint = {x:0, y:0}; var gFileName = ""; document.getElementById('uploadbtn').onclick = function(){ if(!imageLoaded){ return; } var originalCanvas = document.getElementById('original'); var canvas = document.createElement('canvas'); var context = canvas.getContext('2d'); canvas.width = clearWidth; canvas.height = clearHeight; context.drawImage(originalCanvas, cutPoint.x, cutPoint.y, clearWidth, clearHeight, 0, 0, clearWidth, clearHeight); // 注意 toDataURL 返回的默认是 png 格式 // data 是 base64 编码 var data = canvas.toDataURL(); // 第二个参数的值如果在0.0和1.0之间的话, 会被看作是图片质量参数 // 但是我测试大小没什么变化 //var data = canvas.toDataURL('image/png', 0.5); var encodeData = data.split(',')[1]; // 解 base64 编码 var decodedData = window.atob(encodeData); var ia = new Uint8Array(decodedData.length); for (var i = 0; i < decodedData.length; i++) { ia[i] = decodedData.charCodeAt(i); }; var blob; try{ // toDataURL 返回的默认是 png 格式,所以这里固定为 image/png blob = new Blob([ia], {type:"image/png"}); }catch(e){ // 使用 http://haomou.net/2016/01/14/2016_android_blob/ 仍然无法解决 // android 手机浏览器 Blob 构造函数 bug // 我的解决方法是使用 base64 上传,服务端解码 // alert("new Blob exception:" + e); // // TypeError old chrome and FF // var BlobBuilder = window.BlobBuilder || // window.WebKitBlobBuilder || // window.MozBlobBuilder || // window.MSBlobBuilder; // // alert("BlobBuilder:" + typeof(BlobBuilder)); // if(e.name == 'TypeError' && BlobBuilder){ // var bb = new BlobBuilder(); // bb.append([ia.buffer]); // blob = bb.getBlob("image/png"); // } else if(e.name == "InvalidStateError"){ // // InvalidStateError (tested on FF13 WinXP) // blob = new Blob( [ia.buffer], {type : "image/png"}); // } // else{ // // We're screwed, blob constructor unsupported entirely // alert("blob constructor unsupported entirely"); // return; // } } // 修改文件名后缀格式 var filename = gFileName; var index = filename.lastIndexOf('.'); if(index >= 0){ filename = filename.substring(0, index); filename += ".png"; } var fd = new FormData(); if(blob){ fd.append('image', blob, filename); }else{ // 采用 base64 上传 fd.append("filename", filename); fd.append("image", encodeData); // 由于 base64 大小为原来的 4/3 倍,自然会想能不能像 c 语言那样 // 直接把字符当作 unsigned char 来看待。js 是不行的。因为 // text += 1 ,数字1将被转换为字符串"1" ,而 text[i] 是仅可读,不 // 可修改! } // 使用 ajax 发送 $.ajax({ url:"http://192.168.3.102:8080/upload", type:"POST", data:fd, processData: false, // 告诉jQuery不要去处理发送的数据 contentType: false, // 告诉jQuery不要去设置Content-Type请求头 success:function() { console.log("post success."); }, error: function(){ console.log("post failed."); } }); }