应广大读者建议,已经将该项目源码提交到地址:
https://download.csdn.net/download/u014466109/10465677
与本博客相关的多图压缩上传代码在dashen/service/ask.html,请解压项目并移动到hbuilder中打开。html
下面一个个实现上述需求,从简单到复杂:html5
用户返回弹出提示框,使用mui.confirm以下:算法
var oldBack = mui.back; mui.back=function(e){ mui.confirm("还没有提交,返回后将会丢失填写内容物,是否返回?","返回确认",['返回','取消'],function(e){ if(e.index==0) oldBack(); }) }
(以上代码买应写在mui.plusReady()之中,由于里面须要用到html5+的方法,mui.back()就是5+方法)数组
第一步克隆了一个mui.back,由于下面他本身会被重写了,而真正返回的时候仍是须要用到原来的mui.back()!服务器
重写是为了再返回前执行一段逻辑,按照官方的说法,confirm弹出是异步执行(非阻塞)因此另一种在mui.init({beforeBack:function})的方式是不合适,由于beforeBack选项要求的是阻塞的,可能会致使尚未按下confirm中的按钮,由于执行了return true而退出了,弹出窗口就显得没有意义了!只能使用重写back方法的办法了!网络
mui.confirm传入四个参数,提示主内容,标题,按钮数组,回调函数(对按钮数组的下表进行判断)app
qq空间发表说说能够携带图片,经过缩略图的形式让用户修改本身要上传的图片,不过腾讯作的那个高级多了,还能够拖拽图片打开大图编辑等等,在此只实现最简单的功能。dom
专门写一个函数来实现这个功能:init_image_add()异步
//初始化图片添加器 function init_image_add() { //上传图片上限,超过不现实加号 if(question.files.length >= IMG_MAX_NUM) return; var placeholder = document.createElement('div'); placeholder.setAttribute('class', 'image-item space'); //删除图片 var closeButton = document.createElement('div'); closeButton.setAttribute('class', 'image-close'); closeButton.innerHTML = 'X'; //小X的点击事件 closeButton.addEventListener('tap', function(event) { removeFile(getChildrenIndex(placeholder)); //删除实际图片数组元素,先删除数组中的 imageList.removeChild(placeholder); //删除ui,必须后删除,不然节点会找不到了 if(question.files.length >= IMG_MAX_NUM - 1) init_image_add(); event.stopPropagation(); }, false); placeholder.addEventListener('tap', function(event) { var btnArray = [{ title: "相册" }, { title: "拍照" }]; //actionsheet plus.nativeUI.actionSheet({ title: "选择图片", cancel: "取消", buttons: btnArray }, function(e) { var i = e.index; switch(i) { case 0: break; case 1: plus.gallery.pick(function(e) { plus.io.resolveLocalFileSystemURL(e, function(entry) { var url = entry.toLocalURL(); var name = url.substr(e.lastIndexOf('/') + 1); //压缩取得缩略图 plus.zip.compressImage({ src: url, dst: '_doc/' + name, overwrite: true, quality: 50, height: '300px', clip: { top: "25%", left: "25%", width: "300px", height: "300px" } }, function(zip) { placeholder.style.backgroundImage = "url('" + zip.target + "')"; if(!placeholder.classList.contains('space')) { //已有图片 exFile(getChildrenIndex(placeholder), url); } else { //加号 placeholder.classList.remove('space'); addFile(url); init_image_add(); } }, function(zip) { mui.toast('压缩失败!'); }); }, function(e) { console.log("读取相册文件错误:" + e.message); }); }, function(e) { console.log(e.message); }, {}); break; case 2: plus.camera.getCamera().captureImage(function(e) { plus.io.resolveLocalFileSystemURL(e, function(entry) { var url = entry.toLocalURL(); var name = url.substr(e.lastIndexOf('/') + 1); //压缩 plus.zip.compressImage({ src: url, dst: '_doc/' + name, overwrite: true, quality: 50, height: '300px', clip: { top: "25%", left: "25%", width: "300px", height: "300px" } }, function(zip) { placeholder.style.backgroundImage = "url('" + zip.target + "')"; if(!placeholder.classList.contains('space')) { //已有图片 exFile(getChildrenIndex(placeholder), url); } else { //加号 placeholder.classList.remove('space'); addFile(url); init_image_add(); } }, function(zip) { mui.toast('压缩失败!'); }); }, function(e) { console.log("读取拍照文件错误:" + e.message); }); }, function(s) { console.log("error" + s); }, {}); break; } }); }, false);
代码有点长,只能做为参考,讲一下其中的算法:函数
首先这个函数应该在plusReady()内调用,而且在载入页面的时候就要调用了!
这个函数做用:生成一个图片添加按钮:
如图所示的“+”号,而且为这个新增的加号添加监听事件
添加的方式是使用js生成dom而且插入到相应的节点
var placeholder = document.createElement('div'); placeholder.setAttribute('class', 'image-item space'); //删除图片 var closeButton = document.createElement('div'); closeButton.setAttribute('class', 'image-close'); closeButton.innerHTML = 'X';
如下是将会生成的对应的html代码
<div class="image-item space" id="img1"> <div class="image-close">x</div> </div>
能够想到这个函数应该要递归调用,这个递归是基于事件的,什么事件呢?
就是每一次添加完一张图片的事件,好比途中加号前面一张图被添加完成后马上就会递归一次,调用init_image_add()本身
给个流程图:
使用:plus.zip.compressImage
html5+官方文档:
例:
//压缩取得缩略图 plus.zip.compressImage({ src: url, dst: '_doc/' + name, overwrite: true, quality: 50, height: '300px', clip: { top: "25%", left: "25%", width: "300px", height: "300px" } }, function(zip) { placeholder.style.backgroundImage = "url('" + zip.target + "')"; if(!placeholder.classList.contains('space')) { //已有图片 exFile(getChildrenIndex(placeholder), url); } else { //加号 placeholder.classList.remove('space'); addFile(url); init_image_add(); } }, function(zip) { mui.toast('压缩失败!'); });
方法传入的参数:
成功回调函数
失败回调函数
上传原图不在赘述,直接跳过此处,参考uploader上传便可^_^
这里是有一点小麻烦,我的折腾了一个小时才算弄完美了
千万注意,compressImage方法是异步执行的,也就是说,若是你打算将全部要上传的图片在for循环中遍历而且压缩是不稳当的,由于这些图片将会并行压缩,而因为是多图上传,你不知道全部图片压缩完成是何时,一张图的话能够直接在成功的回调函数中执行后面的逻辑
我采用了递归的方法解决了多图压缩而且压缩所有完成后再执行后面的逻辑,至关于强行把一个异步的函数写成了同步(阻塞)函数,须要结合“回调函数”+“递归调用”!
代码以下:
//用户未选取上传原图时上传前调用 function zip_upload_imgs(len = question.files.length - 1) { //第一次递归显示等待 if(len == question.files.length - 1) plus.nativeUI.showWaiting("正在压缩图片...", { back: "none" }); //当长度小于0时,结束递归 if(len < 0) { //关闭等待 plus.nativeUI.closeWaiting(); return submitAsk(); } //上传压缩图 var url = question.files[len]; plus.zip.compressImage({ src: url, dst: '_doc/zip_' + url.substr(url.lastIndexOf('/') + 1), overwrite: true, quality: 50, height: "90%", }, function(zip) { //压缩成功,替换原图路径 question.files[len] = zip.target; zip_upload_imgs(--len); }, function() { //压缩失败 mui.toast('压缩失败!'); }); }
这个函数就有意思了,递归出如今当前图片压缩成功后调用(在回调函数中递归),这样就解决了异步的问题,等待压缩而不执行以后的逻辑!
固然这里由于压缩时间可能会长一点,须要用到等待窗口提供用户友好,以避免用户不知道这段时间是在压缩!
这个递归函数默认传入的是须要压缩的图片的数组长度值,这个图片数组(question.files)是个全局或者说相对于函数来讲是更加全局的,他不会由于函数结束而回收!处于函数做用域以外!
数组是反向递归的,下标从大到小,最大的时候第一次执行函数因此显示等待提示,最后一次是当下标小于0时(数组下标越界)结束递归,中间每一次执行完,递归前将下标-1,传入下一次递归!
在return后面紧跟着的是一个一直在等待着的sumitAsk()函数,这个函数是最终上传图片+提交表单的!之因此说是“一直等待着的提交函数”,由于他原本会由于异步执行的“plus.zip.compressImage”而先执行掉,致使图片没有压缩就上传了,我以前折腾了好久就是由于这个问题,若是不按照上述的回调+递归模式,图片还在压缩,sumit就执行了,那么图片数组没有变,依然上传了原图!
JavaScript中有的是异步函数,有的是同步函数,须要严格注意,异步函数会从新打开一个“时间线”去执行本身,忽略掉同步的函数,因此应该要作到等待异步函数执行完成后继续执行同步函数!
使用函数:plus.uploader.createUoload()
官方说明
我须要上传
示例以下:
function submitAsk() { //创建链接 var url = HTTP_DOMAIN + "Service/ask"; var uploader = plus.uploader.createUpload(url, { method: 'POST' }, function(upload, status) { plus.nativeUI.closeWaiting(); if(status == 200) { var res = JSON.parse(upload.responseText); //服务器方登录失效 if(res.login == 0) { plus.nativeUI.toast(res.info); app.clearToken(); app.toLogin(); return false; } console.log(upload.responseText); if(res.status == 1) { mui.alert("您的问题已提交,等待附近的人解答", "发表成功", "肯定", function() { mui.back(); }); } else { mui.toast(res.info); } } else { mui.toast("网络服务器链接失败!稍后重试"); } }); //添加上传数据 for(key in question) { if(key == "files") continue; uploader.addData(key, question[key]); } //若是有礼物图片就上传 if(question.gift_img != "") { uploader.addFile(question.gift_img, { key: "gift_img" }); } //添加上传文件 for(var i = 0; i < question.files.length; i++) { uploader.addFile(question.files[i], { key: "img" + i }); } //开始上传任务 plus.nativeUI.showWaiting("正在提交...",{back:"none"}); uploader.start(); }
注意addData和addFile的使用:
其中addData是上传数据的键值对,就像表单name和value同样一一对应,在这以前已经放入了question对象之中:
document.getElementById("submit").addEventListener("tap", function() { //获取表单数据 question.title = document.getElementById("title").value; question.content = document.getElementById("content").value; question.gift_img = document.getElementById("gift_img").getAttribute("src"); question.reward = document.getElementById("reward").value; question.message = document.getElementById("message").value; question.price = document.getElementById("price").value; console.log(JSON.stringify(question)); /* * 必填项目:标题,内容,难度 */ if(plus.networkinfo.getCurrentType() == plus.networkinfo.CONNECTION_NONE) return mui.toast("链接网络失败,请稍后再试"); if(trim(question.title) == "") return mui.toast("请给出问题标题!"); if(trim(question.content) == "") //判断网络链接 return mui.toast("没法提交,请详细填写如下问题内容!"); if(question.star <= 0 || question.star > 5) return mui.toast("请给出问题难度!"); //用户未选择上传原图时,压缩全部上传图片 if(!document.getElementById("high_img").classList.contains("mui-active") && question.files.length > 0) { zip_upload_imgs(); } else { submitAsk(); } }, false);
关于异步上传plus.uploader,详细请参阅:
http://www.html5plus.org/doc/zh_cn/uploader.html#plus.uploader.createUpload