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

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. C++—引用的作用

    引入 C语言中函数有两种传参的方式: 传值和传址.以传值方式, 在函数调用过程中会生成一份临时变量用形参代替, 最终把实参的值传递给新分配的临时变量即形参. 它的优点是避免了函数调用的副作用, 确无法 ...

  2. php文件管理与基础功能的实现

    文件的基本操作 先来看一下PHP文件基础操作,请看强大注释 <body> <?php var_dump(filetype("./img/11.png")); // ...

  3. 实现Unity编辑器模式下的旋转

    最近在做一个模型展示的项目,我的想法是根据滑动屏幕的x方向差值和Y方向的差值,来根据世界坐标下的X轴和Y轴进行旋转,但是实习时候总是有一些卡顿.在观察unity编辑器下的旋转之后,发现编辑器下的旋转非 ...

  4. python多版本的pip共存问题解决办法

    python pip 多版本 问题情景 最开始学python的时候用的是py2,且一直用pip来安装库函数.后来py3出来了,所以就装上了,但是一装上出问题了,主要有两个主要的问题.下面将详细说明. ...

  5. 谈谈在DevOps实践中,感觉最重要的这三个技术……

    从国内众多DevOps实践中,我们能看到下面三个技术尤其重要和火热: 容器:容器从根本上解决了软件对环境的依懒性,解决了各个环境之间的差异问题:它可以加速部署的速度,提高部署的效率:降低部署的成本.容 ...

  6. ORACLE JOB创建

    Connected Connected as focususer SQL> SQL> --JOB 需要在命令行执行: SQL> --抽数job SQL> CREATE OR R ...

  7. Python__slots__详解

    摘要 当一个类需要创建大量实例时,可以通过__slots__声明实例所需要的属性, 例如,class Foo(object): __slots__ = ['foo'].这样做带来以下优点: 更快的属性 ...

  8. sublimeText3插件安装

    1,官方下载sublimeText 3(百度搜索) 2,安装成功后按Ctrl+`调出console 3,然后输入 import urllib.request,os; pf = 'Package Con ...

  9. Hibernate(四)之对象状态及一级缓存

    一.Hibernate中的对象状态 1.1.瞬时态(临时态) 没有与Hibernate产生关联 与数据库中的记录没有产生关联(有关联就是与数据库中表的id相对应) 获得:一般都只直接创建(new) 瞬 ...

  10. app专项测试自动化测试方法思路与实现

    秉着个人意愿打算把python+rf接口自动进行彻底结束再做些其它方面的输出~但事与愿违,但领导目前注重先把专项测试方面完成,借此,先暂停python+rf(主要是与Jenkins集成+导入DB+微信 ...