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

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. CAS进行https到http的改造方案,结合cookie源码分析

    先说具体的改造方案: 服务端: 一.CAS Server端的修改 1.找到cas\WEB-INF\deployerConfigContext.xml 对以下Bean增加参数p:requireSecur ...

  2. 【树莓派】Linux应用相关:自动删除n天前日志

    linux是一个很能自动产生文件的系统,日志.邮件.备份等.虽然现在硬盘廉价,我们可以有很多硬盘空间供这些文件浪费,让系统定时清理一些不需要的文件很有一种爽快的事情.不用你去每天惦记着是否需要清理日志 ...

  3. Windows Phone下页面跳转动画的实现

    写在前面的一些废话: 前段时间一直忙于其他的事情,好长时间没有更新博客,很多东西虽然看过.学过,但是没有仔细去思考,去总结,长时间不用或者用的少难免会遗忘.最近由于家里以及感情方面的事,人也变得有点怨 ...

  4. 利用python的爬虫技术爬取百度贴吧的帖子

    在爬取糗事百科的段子后,我又在知乎上找了一个爬取百度贴吧帖子的实例,为了巩固提升已掌握的爬虫知识,于是我打算自己也做一个. 实现目标:1,爬取楼主所发的帖子 2,显示所爬去的楼层以及帖子题目 3,将爬 ...

  5. iOS 如何保持线程一直在运转(二)

    一.接着上一篇通过NSThread可以方便的创建一个线程,并且启动线程的Runloop,在线程体中执行一个while循环 然后我们就可以方便得利用这个线程了 - (void)threadRun:(NS ...

  6. oracle学习 笔记(1)

    题记:之前用的是SQL server数据库,现因需求使用Oracle数据库,写此博客来记录学习: 一.表空间管理.用户管理.给用户授权 1.在Oracle中每创建数据库会在系统服务中多一个数据库实例, ...

  7. (转)crontab安装(command not found)

    1. 确认crontab是否安装:执行 crontab -l 命令如果报 command not found,就表明没有安装 2. 安装 crontab执行 yum install -y vixie- ...

  8. LVS的原理介绍

    DR模式  LVS 的VIP 和 realserver 必须在同一个网段,不然广播后所有的包都会丢掉: 提前确认LVS/硬件LB 是什么模式,是否需要在同一个网段 所有的realserver 都必须绑 ...

  9. js放大镜

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  10. ASP.NET Web服务(ASMX)学习和代理生成

    第一步:按照http://www.c-sharpcorner.com/article/getting-started-with-asp-net-web-services-part-one/ 建立项目和 ...