应广大读者建议,已经将该项目源码提交到地址:

https://github.com/devilyouwei/dashen

与本博客相关的多图压缩上传代码在dashen/service/ask.html,请解压项目并移动到hbuilder中打开。

欲实现效果图

提出需求点:

  1. 用户可自由添加删除替换多张图片,并且显示相应缩略图,限制为8张
  2. 用户可选择压缩图或直接上传原图功能
  3. 返回提醒用户会丢失填写的信息

下面一个个实现上述需求,从简单到复杂:

需求3:

用户返回弹出提示框,使用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传入四个参数,提示主内容,标题,按钮数组,回调函数(对按钮数组的下表进行判断)

自由增删改图片

qq空间发表说说可以携带图片,通过缩略图的形式让用户修改自己要上传的图片,不过腾讯做的那个高级多了,还可以拖拽图片打开大图编辑等等,在此只实现最简单的功能。

专门写一个函数来实现这个功能: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()内调用,并且在载入页面的时候就要调用了!

这个函数作用:生成一个图片添加按钮:

如图所示的“+”号,并且为这个新增的加号添加监听事件

  1. 点击“+”号弹出actionsheet选择相册或者相机添加图片
  2. 点击右上角“x”号可以删除一张图片

添加的方式是使用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('压缩失败!');
});

方法传入的参数:

  1. 压缩参数:



  1. 成功回调函数

  2. 失败回调函数

最终上传,多图压缩!

上传原图不在赘述,直接跳过此处,参考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()

官方说明

我需要上传

  1. 图片文件(根据上述的图片数组,question.files,其中保存的都是要上传的图片的绝对路径)
  2. 表单数据

示例如下:

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

mui开发app之多图压缩与上传(仿qq空间说说发表)的更多相关文章

  1. Hbuilder开发app实战-识岁03-文件上传

    前言 做app不得不谈的问题就是文件上传.用hbuilder开发app让上传变的非常easy. Uploader Uploader模块管理网络上传任务,用于从本地上传各种文件到server,并支持跨域 ...

  2. vue开发中vue-resource + canvas 图片压缩、上传、预览

    1.使用vue-resource上传,也可以自定义ajax上传: 2.使用<input type="file" @change="submit()" na ...

  3. mui开发app之js将base64转图片文件

    之前我已经做过一个利用cropper裁剪并且制作头像的功能.如何在mui app中实现相册或相机获取图片后裁剪做头像请看另一篇博客:mui开发app之cropper裁剪后上传头像的实现 但是当时裁剪后 ...

  4. mui开发app之cropper裁剪后上传头像的实现

    在大多数app项目中,都需要对用户头像的上传,之前做web开发的时候,我主要是通过input type=file的标签实现的,上传后,使用php对图片进行裁剪,这种方式比较传统简单. 此次app开发中 ...

  5. 利用 MUI开发app, 如何实现侧滑菜单及其主体部分上下滑动

     利用mui开发APP 之侧滑菜单主内容滚动问题 MUI作为开发者常用的框架之一,其号称最接近原生APP体验的高性能前端框架.因此利用mui开发移动APP,可以为开发者提供很大的便利和接近原生的体验. ...

  6. mui开发app前言(一)

    dcloud mui开发app前言 大一那会就听说html5快要发布了,前景无量,厉害到能写操作系统==|||(什么???蛤?) 似乎html5标准还没正式发布那会,使用hybrid模式开发app已经 ...

  7. 项目分享五:H5图片压缩与上传

    一.简介 图片的压缩与上传,是APP里一个很常用的功能.我们来年看 ChiTuStore 是怎样做的.相关文件 App/Module/User/UserInfo.html,App/Module/Use ...

  8. iOS传感器集锦、飞机大战、开发调试工具、强制更新、Swift仿QQ空间头部等源码

    iOS精选源码 飞机大作战 MUPhotoPreview -简单易用的图片浏览器 LLDebugTool是一款针对开发者和测试者的调试工具,它可以帮... 多个UIScrollView.UITable ...

  9. HTML5 file API加canvas实现图片前端JS压缩并上传

    一.图片上传前端压缩的现实意义 对于大尺寸图片的上传,在前端进行压缩除了省流量外,最大的意义是极大的提高了用户体验. 这种体验包括两方面: 由于上传图片尺寸比较小,因此上传速度会比较快,交互会更加流畅 ...

随机推荐

  1. boost.asio源码阅读(2) - task_io_service

    1.0 task_io_service 在boost.asio源码阅读(1)中,代码已经查看到task_io_service中. 具体的操作调用void task_io_service::init_t ...

  2. Angular2发布思路(整理官网Deployment页面)

    本文是按着ng2官网的高级内容“Deployment”的思路整理得出的,原文虽然在angular2的中文站下挂着,截止目前却还是英文版未翻译,笔者就在这里结合自己的理解给出原文的一点点整理.这是原文地 ...

  3. python中的列表list

    序列是Python中最基本的数据结构.序列中的每个元素都分配一个数字 - 它的位置,或索引,第一个索引是0,第二个索引是1,依此类推. Python有6个序列的内置类型,但最常见的是列表和元组. 序列 ...

  4. ng-clip angualr 的copy功能

    每次写博客都想由衷的给我的老大膜拜一番!以前刚开始做angular的项目的时候就有说要有点击复制的功能因为当时菜啊,不懂啊.也就没做,今天老大又给了我一个资料!“ng-clip”.跟着老大最大的收获就 ...

  5. Hive 伪分布式下安装

    本安装过程只作为个人笔记用,非标准教程,请酌情COPY.:-D Hive下载 下载之前,需先查看兼容的Hadoop版本,并安装hadoop,参考 http://www.cnblogs.com/yong ...

  6. Android中查看布局文件中的控件(view,id)在哪里被调用(使用)

    在阅读别人的代码时通常是很痛苦的,有时很想要看一看布局中的控件在哪里被调用了,为之很苦恼 在这里提供一种方法.   复制要查看的控件ID,到R文件中搜索到该ID,   接下来就好办的了,选中ID按下C ...

  7. The superclass “javax.servlet.http.HttpServlet" was not found on the Java Build Path错误

    1.异常信息 创建maven web项目时,出现 The superclass "javax.servlet.http.HttpServlet" was not found on ...

  8. P2物理引擎中文文档

    P2物理引擎中文文档地址:https://github.com/schteppe/p2.js/wiki/Chinese-wiki-%E4%B8%AD%E6%96%87%E7%BB%B4%E5%9F%B ...

  9. webpack点滴记录

    有了webpack..(不借助gulp/grunt)代码压缩 图片base64,解析less/sass coffee css压缩,MD5加密都帮你做了..还支持按需加载..还有热替换 webpack常 ...

  10. 老李分享:接电话之uiautomator 2

    case解释 首先要了解进入uiwatcher方法中的机制,是在你某个控件找不到的情况下会进入.但是你得保证进入以后处理完来电界面以后,这条case得保证正确,那么说明回来以后这个控件要能找到.刚开始 ...