应广大读者建议,已经将该项目源码提交到地址: 
https://github.com/devilyouwei/dashen

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

此次app开发中需要做到用户选择本地相册或者进行拍照->对照片进行裁剪->最后同时更新本地头像和服务器端的图片。(app常见套路)

我将要结合:mui,cropper,jquery开发!

实现思路:

1.用户点击头像,打开actionsheet

2.选择图片或者拍照后返回的图片绝对地址传入单独的裁剪页面,跳转到裁剪页面

3.裁剪页面裁剪后选择确认则将裁减后图片保存到localstorage中(其实是把整个图片保存到本地数据库中)

4.触发一个更新上一页头像的事件,返回上一页

5.看到图片已经更改

6.保存,图片上传到服务器(json)

7.服务器将图片的base64保存到数据库(text类型),实现数据库保存图片,当然可以使用后端语言的方法还原为图片后保存在文件系统中(建议:小图可保存在数据库中,大图保存在文件系统)

实现工具和插件,直接看代码:

js

<script type="text/javascript" src="../js/jquery-1.11.2.min.js"></script>
<script src="../js/mui.min.js"></script>
<script type="text/javascript" src="../js/exif.js"></script>
<script src="../js/cropper.min.js"></script>
<script src="../js/fastclick.js"></script>

css

<link href="../css/mui.min.css" rel="stylesheet" />
<link href="../css/iconfont.css" rel="stylesheet" />
<link href="../css/cropper.css" rel="stylesheet" />

这里我们主要使用了一个cropper.js的插件,自行百度下载,目前比较好用的插件只有这个,大神的话自己写一个吧

注意cropper必须使用jquery,而jquery比较臃肿,在其他mui中尽量不要引入,我也是迫不得已,使用jq并非我本意

fastclcik.js是加速点击触发,一般在手机端开发其实用不到,因为一般使用tap,这里我加了进去,似乎对裁剪图片时手势操作有帮助,(+_+)?,不加也没有影响,测试过

iconfont是按钮图标

exif.js是可以检测图片拍摄时采用横向还是纵向

这几个请自行百度下载!

接下来可以开发了:

一共两个页面,

headinfo.html——编辑页面

cropper.html——裁剪页面

headinfo.html

<!doctype html>
<html> <head>
<meta charset="UTF-8">
<title>我的资料</title>
<meta name="viewport" content="width=device-width, initial-scale=1,maximum-scale=1,user-scalable=no">
<link href="../css/mui.min.css" rel="stylesheet" />
<link href="../css/style.css" rel="stylesheet" />
<style type="text/css">
.head {
height: 40px;
} #head {
line-height: 40px;
} .head-img {
width: 40px;
height: 40px;
} #head-img1 {
position: absolute;
bottom: 10px;
right: 40px;
width: 40px;
height: 40px;
} .mui-fullscreen {
position: fixed;
z-index: 20;
background-color: #000;
} .mui-content {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
}
</style>
</head> <body>
<header class="mui-bar mui-bar-nav">
<button type="button" id="finish" class="mui-left mui-btn mui-btn-link mui-btn-nav mui-pull-left">
<span class="mui-icon mui-icon-left-nav"></span>完成
</button>
<h1 class="mui-title">头信息</h1>
</header>
<div class="mui-content"> <ul class="mui-table-view">
<li class="mui-table-view-cell">
<a id="head" class="mui-navigate-right">个人头像
<span class="mui-pull-right head">
<img class="head-img mui-action-preview" id="head-img1" src=""/>
</span>
</a>
</li>
</ul> <div class="mui-input-row mui-margin-vertical">
<h5 class="mui-margin-horizontal-xs">个人签名:</h5>
<input class="mui-padding-lg" id="signature" type="text" placeholder="个人签名" />
</div>
</div> <script src="../js/mui.min.js"></script>
<script src="../js/app.js"></script>
<script type="text/javascript">
(function($) {
mui.init({
swipeBack: true
}); $.plusReady(function() {
//初始化头像,和预览图
setTimeout(function() {
defaultInfo();
setTimeout(function() {
initImgPreview();
}, 200);
}, 0); //弹出菜单
mui(".mui-table-view-cell").on("tap", "#head", function(e) {
if(mui.os.plus) {
var a = [{
title: "拍照"
}, {
title: "从手机相册选择"
}];
plus.nativeUI.actionSheet({
title: "修改头像",
cancel: "取消",
buttons: a
}, function(b) {
switch(b.index) {
case 0:
break;
case 1:
getImage();
break;
case 2:
galleryImg();
break;
default:
break
}
})
} }); //完成并返回
document.getElementById("finish").addEventListener("tap", function() {
var newSign = document.getElementById("signature").value;
var oldSign = app.getUserInfo().signature;
if(oldSign === newSign)
$.back();
else {
app.request("User", "updateSignature", {
'signature': newSign
}, function(res) {
if(res.login == 0) {
mui.toast(res.info);
app.clearToken();
app.toLogin();
return false;
}
if(res.status == 1) {
app.setSignature(newSign);
mui.toast(res.info);
var view = plus.webview.getWebviewById("index");
$.fire(view, "updateHeadInfo");
$.back();
} else {
mui.toast(res.info);
}
});
} }); }); //拍照
function getImage() {
var c = plus.camera.getCamera();
c.captureImage(function(e) {
//存储到本地
plus.io.resolveLocalFileSystemURL(e, function(entry) {
cutImage(entry.toLocalURL());
}, function(e) {
console.log("读取拍照文件错误:" + e.message);
});
}, function(s) {
console.log("error" + s);
}, {
filename: "_doc/head.jpg"
})
} //相册
function galleryImg() {
plus.gallery.pick(function(a) {
plus.io.resolveLocalFileSystemURL(a, function(entry) { //entry为图片原目录(相册)的句柄
cutImage(entry.toLocalURL());
}, function(e) {
console.log("读取图片错误:" + e.message);
});
}, function(a) {}, {
filter: "image"
})
}; //设置默认头像
function defaultInfo() {
var my_icon = app.getHeadImg(); //头像
var signature = app.getUserInfo().signature; //签名
if(my_icon && my_icon != "") {
document.getElementById("head-img1").src = my_icon;
} else {
document.getElementById("head-img1").src = '../images/my_icon.jpg';
}
if(signature && signature != "") {
document.getElementById("signature").value = signature;
} else {
document.getElementById("signature").value = "";
}
} //预览图像
document.getElementById("head-img1").addEventListener('tap', function(e) {
e.stopPropagation(); //阻止冒泡
}); function initImgPreview() {
var imgs = document.querySelectorAll("img.mui-action-preview");
imgs = mui.slice.call(imgs);
if(imgs && imgs.length > 0) {
var slider = document.createElement("div");
slider.setAttribute("id", "__mui-imageview__");
slider.classList.add("mui-slider");
slider.classList.add("mui-fullscreen");
slider.style.display = "none";
slider.addEventListener("tap", function() {
slider.style.display = "none";
});
slider.addEventListener("touchmove", function(event) {
event.preventDefault();
})
var slider_group = document.createElement("div");
slider_group.setAttribute("id", "__mui-imageview__group");
slider_group.classList.add("mui-slider-group");
imgs.forEach(function(value, index, array) {
//给图片添加点击事件,触发预览显示;
value.addEventListener('tap', function() {
slider.style.display = "block";
_slider.refresh();
_slider.gotoItem(index, 0);
})
var item = document.createElement("div");
item.classList.add("mui-slider-item");
var a = document.createElement("a");
var img = document.createElement("img");
img.setAttribute("src", value.src);
a.appendChild(img)
item.appendChild(a);
slider_group.appendChild(item);
});
slider.appendChild(slider_group);
document.body.appendChild(slider);
var _slider = mui(slider).slider();
}
} //裁剪图片
function cutImage(path) {
$.openWindow({
url: 'cropper.html',
id: 'cropper',
extras: {
path: path,
},
show: {
aniShow: 'zoom-fade-in',
duration: 800
},
waiting: {
autoShow: true
}
});
} //更新用户头像
function update_head_img(e) {
var my_icon = e.detail.img;
//先上传
app.request("User", "saveHeadImg", {
'my_icon': my_icon
}, function(res) {
if(res.login == 0) {
mui.toast(res.info);
app.clearToken();
app.toLogin();
return false;
} if(res.status == 1) {
app.setHeadImg(my_icon); //确认上传成功后存储到本地
if(my_icon == "")
my_icon = "../images/my_icon.jpg";
document.getElementById("head-img1").src = my_icon; //刷新小图
document.querySelector("#__mui-imageview__group .mui-slider-item img").src = my_icon; //刷新预览图
//顺带触发上一个页面的updateHeadInfo
var view = plus.webview.getWebviewById("index");
$.fire(view, "updateHeadInfo");
mui.toast(res.info);
} else {
mui.toast(res.info);
} }); }
window.addEventListener("updateHeadImg", update_head_img); //添加自定义事件,其他页面调用
})(mui);
</script>
</body> </html>

cropper.html

<!DOCTYPE html>
<html> <head>
<meta charset="utf-8">
<title>裁剪头像</title>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
<link href="../css/mui.min.css" rel="stylesheet" />
<link href="../css/cropper.css" rel="stylesheet" />
<style type="text/css">
body {
background-color: #000000;
} #cropper-example-1 {
background-color: #000000;
height: 93%;
width: 100%;
position: absolute;
} .divbut {
width: 100%;
text-align: center;
position: fixed;
z-index: 2;
bottom: 0px;
background-color: #000000;
height: 7.5%;
line-height: 50px;
} .divbut>div:first-child {
float: left;
width: 20%;
} .divbut>div:last-child {
float: right;
width: 20%;
} img#im {
height: 100%;
width: 100%;
}
</style>
</head> <body>
<div id="cropper-example-1" class="mui-hidden">
<img id="im" alt="Picture" />
</div> <div class="divbut">
<div>
<p id="quxiao" class="mui-icon mui-icon-closeempty"></p>
</div>
<div>
<p id="xuanqu" class="mui-icon mui-icon-checkmarkempty"></p>
</div>
</div>
<img src="" alt="" class="mui-hidden" id="im_exif" /> <script type="text/javascript" src="../js/jquery-1.11.2.min.js"></script>
<script src="../js/mui.min.js"></script>
<script type="text/javascript" src="../js/exif.js"></script>
<script src="../js/cropper.min.js"></script>
<script src="../js/fastclick.js"></script>
<script src="../js/app.js"></script> <script>
$(function() {
//尽量用一下fastclick
FastClick.attach(document.body);
! function() {
var i = {
aspectRatio: 1 / 1
};
}()
}); (function(c) {
var Cro = function() {}
c.extend(Cro.prototype, {
orientation: null,
urldata: null,
view: null,
num: 0,
sbx: null,
sby: null,
n: 0,
onReady: function() {
var that = this;
mui.init();
that.bindEvent();
that.view = plus.webview.currentWebview(); var img = document.getElementById("im_exif");
img.src = that.view.path;
img.addEventListener("load", function() {
//exif调整图片的横竖
EXIF.getData(this, function() {
var orientation = EXIF.getAllTags(this).Orientation;
$("#im").attr("src", that.loadcopyImg(img, orientation));
document.getElementById("cropper-example-1").classList.remove("mui-hidden"); //显示裁剪区域
that.cropperImg();
});
})
},
cropperImg: function() {
var that = this;
$('#cropper-example-1 > img').cropper({
aspectRatio: 1 / 1,
autoCropArea: 1,
strict: true,
background: false,
guides: false,
highlight: false,
dragCrop: false,
movable: false,
resizable: false,
crop: function(data) {
that.urldata = that.base64(data);
}
});
},
loadcopyImg: function(img, opt) {
var that = this;
var canvas = document.createElement("canvas");
var square = 500;
var imageWidth, imageHeight;
if(img.width > img.height) {
imageHeight = square;
imageWidth = Math.round(square * img.width / img.height);
} else {
imageHeight = square; //this.width;
imageWidth = Math.round(square * img.width / img.height);
}
canvas.height = imageHeight;
canvas.width = imageWidth;
if(opt == 6) {
that.num = 90;
} else if(opt == 3) {
that.num = 180;
} else if(opt == 8) {
that.num = 270;
}
if(that.num == 360) {
that.num = 0;
} var ctx = canvas.getContext("2d");
ctx.translate(imageWidth / 2, imageHeight / 2);
ctx.rotate(that.num * Math.PI / 180);
ctx.translate(-imageWidth / 2, -imageHeight / 2);
ctx.drawImage(img, 0, 0, imageWidth, imageHeight);
var dataURL = canvas.toDataURL("image/jpeg", 1);
return dataURL;
},
bindEvent: function() {
var that = this;
document.getElementById("quxiao").addEventListener("tap", function() {
mui.back(); //取消就直接返回
});
document.getElementById("xuanqu").addEventListener("tap", function() {
//触发上一个页面刷新图片事件
var preView = plus.webview.getWebviewById('user/headinfo');
mui.fire(preView, 'updateHeadImg', {
'img': that.urldata
}); //不能保存图片,需要判断上传性,所以选择传值的方式,传递图片,格式为json
mui.back();
});
},
base64: function(data) {
var that = this;
var img = document.getElementById("im"); var canvas = document.createElement("canvas");
//像素
canvas.height = 400;
canvas.width = 400;
var bx = data.x;
var by = data.y;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, bx, by, data.width, data.height, 0, 0, 400, 400);
var dataURL = canvas.toDataURL("image/jpeg", 0.5); //第二个参数是质量
return dataURL;
}
}); var cro = new Cro(); c.plusReady(function() {
cro.onReady();
})
})(mui)
</script>
</body> </html>

一开始对于headinfo.html我是打算先将拍照和相册获取的图片使用plus的io保存到项目app的_doc目录下再编辑裁剪,但是发现可以精简,直接把图片的绝对地址传到cropper中,直接编辑后保存为base64,放在localstorage中,以后需要头像的地方直接从localstorage中获取即可!我在app.js(这是我写的项目的公共js类库,处处引用)

/*
*这只是其中一部分,在裁剪过程中需要用到的库函数
*主要是方便,随时可以获取保存在localstorage中的头像base64图
*/ (function($, owner) { // 获取用户个人信息
owner.getUserInfo = function() {
var userText = localStorage.getItem('$user') || "{}";
return JSON.parse(userText);
} // 存储用户个人信息
owner.setUserInfo = function(user) {
user = user || {};
localStorage.setItem('$user', JSON.stringify(user));
} // 获取用户头像
owner.getHeadImg = function() {
var headImg = owner.getUserInfo().my_icon || "";
return headImg;
} //设置用户头像
owner.setHeadImg = function(baseData) {
var userInfo = owner.getUserInfo();
userInfo.my_icon = baseData; //只对my_icon这一项进行修改,其他不动
owner.setUserInfo(userInfo);
} }(mui, window.app = {}));

base64的图片格式是html5 canvas所使用的图片格式,可以直接放入img的src中!

注:如果想使用js直接在手机端将base64转换为图片文件请看:mui开发app之js将base64转图片文件

效果图:

               

mui开发app之cropper裁剪后上传头像的实现的更多相关文章

  1. HTML5 开发APP(打开相册以及图片上传)

    我们开发app,常常会遇到让用户上传文件的功能.比如让用户上传头像.我公司的业务要求是让用户上传支付宝收款二维码,来实现用户提现的功能.想要调用相册要靠HTML Plus来实现.先上效果图 基本功能是 ...

  2. jquery.cropper 裁剪图片上传

    https://github.com/fengyuanchen/cropper 1.必要的文件引用: <script src="/path/to/jquery.js"> ...

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

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

  4. 图片裁剪(cropper)后上传问题

    最近工作需要处理头像裁剪以及上传,研究了几天,写点心得,提醒自己记住踩过的坑,能帮助别人当然更好. 功能基本就是这样: 这里需要注意的是:拿到需求后,不要急于直接上手,花费半个小时,甚至更长时间缕清整 ...

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

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

  6. mui开发app前言(一)

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

  7. 【jQuery插件】使用cropper实现简单的头像裁剪并上传

    插件介绍 这是一个我在写以前的项目的途中发现的一个国人写的jQuery图像裁剪插件,当时想实现用户资料的头像上传功能,并且能够预览图片,和对图片进行简单的裁剪.旋转,花了不少时间才看到了这个插件,感觉 ...

  8. jquery photoClip支持手机端,PC端 本地裁剪图片后上传插件

    支持手机,PC最好的是jquery photoClip插件,下载地址&示例:https://github.com/topoadmin/photoClip demo.html 代码: <! ...

  9. ajax实现注册并选择头像后上传

    在初次接触ajax后,我们做了一个crm训练的项目,大多数小组都有注册用户这一项,但是都忽略掉了一个功能,那就是,很多网站的注册是可以上传头像的,在这里我做了一个在已有的头像数组里选择图片上传作头像的 ...

随机推荐

  1. 万人迷”微信小程序似乎开始掉粉 为什么呢?

    "万人迷"微信小程序最近似乎开始掉粉. 距离1月9日小程序上线已有一周,相比浓烈的讨论气氛,用户的使用热情逐步降低,而部分公司开始撤离小程序. 其中,逻辑思维旗下产品"得 ...

  2. CentOS 安装mysql-5.7.10(glibc版)

    注:所有shell命令都以root用户执行. 一.下载 shell> cd /home/user/Downloads shell> wget http://mirrors.sohu.com ...

  3. 通过Eclipse3.1以上启动Tomcat访问不到tomcat管理界面的问题(转载)

    通过Eclipse插件启动Tomcat的问题 默认分类   2009-10-23 15:54   阅读118   评论0   字号: 大  中  小 目前在通过Eclipse中插件启动Tomcat时遇 ...

  4. windows phone 8.1开发 onedrive操作详解

    原文出自:http://www.bcmeng.com/onedrive/ 小梦今天给大家分享一下windows phone 8.1开发 onedrive中的一些操作: Windows phone 8. ...

  5. C# const和readonly修饰符的区别

    const 的概念就是一个包含不能修改的值的变量.常数表达式是在编译时可被完全计算的表达式.因此不能从一个变量中提取的值来初始化常量.如果 const int a = b+1;b是一个变量,显然不能再 ...

  6. 读书笔记 effective c++ Item 35 考虑虚函数的替代者

    1. 突破思维——不要将思维限定在面向对象方法上 你正在制作一个视频游戏,你正在为游戏中的人物设计一个类继承体系.你的游戏处在农耕时代,人类很容易受伤或者说健康度降低.因此你决定为其提供一个成员函数, ...

  7. Android-自定义控件之时针-霞辉

    注释已经比较详细了,废话就不多说了.贴代码了 时针分针秒钟都做上去了,采用的方法也很简单,仔细看一会就能看懂 自定义View类 package com.xh.mytime; import java.u ...

  8. win32/mfc/qt 异常处理与总结

    际异常一: libcpmtd.lib(xmbtowc.obj) : error LNK2001: unresolved external symbol __CrtDbgReport Debug/B机. ...

  9. Unity 3D Framework Designing(8)——使用ServiceLocator实现对象的注入

    对象的 『注入』 是企业级软件开发经常听到的术语.如果你是一个 Java 程序员,一定对注入有着深刻的映像.不管是SSH框架还是SSM框架,Spring 全家桶永远是绕不过去的弯.通过依赖注入,可以有 ...

  10. XJOI练习2神奇的供水系统

    神奇的供水系统 在游遍神秘岛过程中,Z4发现每一个小岛上都有若干个奇怪的类似小水缸似的立方体,这另到Z4相当迷惑不解!这天晚上,忽然下起了一场大雨,在中心岛小树屋上类似那个圆形石槽中间的小孔中涌出了一 ...