基于HTML5+JS实现本地图片裁剪并上传功能


Posted in Javascript onMarch 24, 2017

最近做了一个项目,这个项目中需要实现的一个功能是:用户自定义头像(用户在本地选择一张图片,在本地将图片裁剪成满足系统要求尺寸的大小)。这个功能的需求是:头像最初剪切为一个正方形。如果选择的图片小于规定的头像要求尺寸,那么这整张图片都会作为头像。如果大于规定的尺寸,那么用户可以选择要裁剪的区域。用户点击确定按钮,就将裁剪得到的图片数据发送到服务器,在后端将图片数据保存成一个文件。

要完成上述功能,涉及到的知识有:ajax,canvas和html5中的files接口。我将实现这个功能的代码封装到了4个模块中,分别是ajax.js,preview.js,shear.js和customerImg.js。

ajax.js:用于发送ajax请求。

preview.js:用于图片预览

shear.js:用于裁剪图片

customer.js:自定义头像。在这个模块中药引入ajax.js,preview.js和shear.js

我使用webpack进行打包。我还使用了jquery和jquery-ui。

我从这个项目中抽离出了这个功能。下面是这个功能的详细代码。

1.HTML代码

<div class="m-warp" id="warp">
  <div class="item">
   <input type="file" name="img" id="img" hidden>
   <label for="img">选择图片</label>
  </div>
  <div class="item clearfix">
   <div class="col col-1">
    <div class="preview" id="preview">
     <div class="mask"></div>
     <canvas class="cvsMove" id="cvsMove"></canvas>
    </div>
   </div>

   <div class="thum col-2 col">
    <p>预览</p>
    <img src="" id="thum">
    <p class="f-text-l f-marTop-20">
     <button class="shear" id="submit">确定</button>
    </p>
   </div>
  </div>
 </div>

2.CSS代码

.clearfix:after{
 content: "";
 display: block;
 clear: both;
 height: 0;
 overflow: hidden;
 visibility: hidden;
}
img{
vertical-align: middle;

max-width:100%
}
.m-warp{
 width: 800px;
}
.item{
 margin-top: 20px;
}
.col{
 float: left;
}
.col-1{
 position: relative;
 width: 450px;
 height: 450px;
 outline: 1px solid #333;
}
.preview{
 display: inline-block;
}
.col-2{
 width: 300px;
 margin-left: 50px;
}
label{
 display: block;
 text-align: center;
 width: 100px;
 font-size: 16px;
 color: #fff;
 background-color: #888888;
 height: 30px;
 line-height: 30px;
}
.mask{
 position: absolute;
 z-index: 1;
 top:0;
 left: 0;
 bottom: 0;
 right: 0;
 background-color: rgba(0,0,0,.4);
}
.cvsMove{
 position: absolute;
 z-index: 2;
 outline: 2px dotted #333;
 cursor: move;
 display: none;
}

有了css和html的运行结果如下:

基于HTML5+JS实现本地图片裁剪并上传功能

3.js代码

customerImg.js

var $ = require('jquery');
var ajax = require('./ajax.js');
var preview = require('./preview.js');
var shear = require('./shear.js');
/**
 * 自定义头像
 * @constructor
 */
function CustomerImg() {
 this.isSupport = null;
 this.previewBox = null;
 this.warp = null;
}
/**
 * 入口
 * @param warp 操作区域 jquery节点
 */
CustomerImg.prototype.start = function (warp) {
 var info,me,warpBox;
 me = this;
 this.isSupport = this.__isSupport();
 if(!this.isSupport) {
  info = $('<p>你的浏览器不支持自定义头像,可更换高版本的浏览器自定义头像</p>');
  $('body').html(info);
  return this;
 }
 //判断操作区域示范存在
 if(warp && warp.length > 0){
  this.warp = warp;
 }else{
  return this;
 }
 //预览
 preview.start(warp,shear.start.bind(shear,warp));
 this.previewBox = warp.find('#preview');
 //确定
 warp
  .find('#submit')
  .unbind('click')
  .on('click',me.__submit.bind(me));
};
/**
 * 提交
 * @private
 */
CustomerImg.prototype.__submit = function () {
 var cvsMove,data,fd;
 cvsMove = this.previewBox.find('#cvsMove');
 data = cvsMove[0].toDataURL('image/jpg',1);
 fd = {
  'customerImg':data
 };
 ajax.upload(fd);
};
/**
 * 判断是否支持自定义头像
 * @returns {boolean}
 * @private
 */
CustomerImg.prototype.__isSupport = function () {
 var canvas,context;
 canvas= document.createElement('canvas');
 if(typeof FileReader === 'function'&& canvas.getContext && canvas.toDataURL){
  return true;
 }else{
  return false;
 }
};
var customerImg = new CustomerImg();
module.exports = customerImg;

preview.js

/**
 * Created by star on 2017/3/7.
 */
var $ = require('jquery');
/**
 * 预览类
 * @constructor
 */
function Preview() {
 this.boxElem = null;
 this.callback = null;
 this.type = null;
}
/**
 * 入口
 * @param boxElem 操作区域
 * @param callback 预览结束的回调函数
 */
Preview.prototype.start = function (boxElem,callback) {
 var chooseFile,me;
 me = this;
 if(! boxElem || boxElem.length <= 0) return this;
 this.boxElem = boxElem;
 if(typeof callback === 'function'){
  this.callback = callback;
 }
 if(this.__isSupport()){
  chooseFile = boxElem.find('input[type="file"]');
  chooseFile
   .on('change',me.fileChange.bind(me))
 }
};
/**
 * 选择图片的事件处理程序
 * @param event
 */
Preview.prototype.fileChange = function (event) {
 var target,reader,file,me,type;
 target = event.target;
 me = this;
 file = target.files[0];
 type = file.type;
 this.type = type;
 if(type !== 'image/png' && type !== 'image/jpg' && type !== 'image/jpeg'){
  alert('文件格式不正确');
  return this;
 }
 reader = new FileReader();
 if(file){
  reader.readAsDataURL(file);
 }
 reader.onload = function () {
  me.show(reader);
 }
};
/**
 * 显示从本地选择的图片
 * @param reader fileReader对象
 */
Preview.prototype.show = function (reader) {
 var preView,img,me;
 preView = this.boxElem.find('#preview');
 img = preView.find('#preImg');
 me = this;
 if(img.length <= 0){
  preView.append($('<img id="preImg">'));
 }
 img = preView.find('#preImg');
 //确保图片加载完成后再执行回调
 img.on('load',function () {
  if(me.callback){
   me.callback(me.type);
  }
 });
 img.attr('src',reader.result);
};
/**
 * 是否支持预览
 * @returns {boolean}
 * @private
 */
Preview.prototype.__isSupport = function () {
 return typeof FileReader === 'function';
};
var preview = new Preview();
module.exports = preview;

shear.js

var $ = require('jquery');
//由于要使用jquery-ui,所以将$暴露到window上。
window.$ = $;
require('./jquery-ui.min.js');
/**
 * 切割
 * @constructor
 */
function Shear() {
 this.previewBox = null;
 this.cvsMove = null;
 this.maxW = 200;
 this.maxH = 200;
 this.thum = null;
 this.fileType = 'image/jpeg';
}
/**
 * 入口
 * @param previewBox 预览元素的父元素
 * @param fileType 裁剪的图片的类型 如:'image/jpg'
 * @returns {Shear}
 */
Shear.prototype.start = function (previewBox,fileType) {
 if(!arguments.length) return this;
 var me = this;
 this.previewBox = previewBox;
 if(fileType){
  this.fileType = fileType;
 }
 this.thum = this.previewBox.find('#thum');
 this.cvsMove = this.previewBox.find('#cvsMove');
 this.showCanvas();
 return this;

};
/**
 * 显示出canvas
 */
Shear.prototype.showCanvas = function () {
 var preImg,h,w,me,cvsH,cvsW,rateH,rateW,naturalH,naturalW,preview;
 me = this;
 preImg = this.previewBox.find('#preImg');
 preview = this.previewBox.find('#preview');
 naturalH = preImg[0].naturalHeight;
 naturalW = preImg[0].naturalWidth;
 //将canvas显示出来
 this.cvsMove.show();
 //将canvas置于(0,0)
 this.cvsMove
  .css({
   "left":'0',
   'top':'0'
  });
 h = preImg.height();
 w = preImg.width();
 //规定裁剪出的图片尺寸为200px*200px
 //要保证裁剪的图片不变形
 if(h < this.maxH || w < this.maxW){
  this.cvsMove[0].width = cvsW = Math.min(h,w);
  this.cvsMove[0].height = cvsH = Math.min(h,w);
 }else{
  this.cvsMove[0].width= cvsW = this.maxW;
  this.cvsMove[0].height= cvsH = this.maxH;
 }
 rateH = h/naturalH;
 rateW = w/naturalW;
 this.__drawImg(preImg,0,0,cvsW/rateW,cvsH/rateH,0,0,cvsW,cvsH);
 //使用jquery-ui中的功能使canvas可以移动
 this.cvsMove.draggable(
  {
   containment: "parent",
   drag:function (event,ui) {
    var left,top;
    left = ui.position.left;
    top = ui.position.top;
    //canvas每次移动都有从新绘制图案
    me.__drawImg(preImg,left/rateW,top/rateH,cvsW/rateW,cvsH/rateH,0,0,cvsW,cvsH);
   }
  }
 )
};
/**
 * 在canvas上显示图片
 * @param myImg 要显示的图片节点
 * @param sx 图片的起点在原图片上的x坐标
 * @param sy 图片的起点在原图上的y坐标
 * @param sW 在原图上的宽度
 * @param sH 在原图上的高度
 * @param dx 起点在canvas上的x坐标
 * @param dy 起点在canvas上的y坐标
 * @param dW 在canvas上的宽度
 * @param dH 在canvas上的高度
 * @private
 */
Shear.prototype.__drawImg = function (myImg,sx,sy,sW,sH,dx,dy,dW,dH) {
 var cxt,thum,me;
 me = this;
 cxt = this.cvsMove[0].getContext('2d');
 cxt.drawImage(myImg[0],sx,sy,sW,sH,dx,dy,dW,dH);
 thum = this.thum;
 //将canvas上的图案显示到右侧
 thum
  .attr('src',this.cvsMove[0].toDataURL(me.fileType,1))
  .width(this.maxW)
  .height(this.maxH)
};
var shear = new Shear();
module.exports = shear;

ajax.js

var $ = require('jquery');
function Ajax() {

}
/**
 * 上传图片数据
 */
Ajax.prototype.upload = function (data) {
 $.ajax({
  type:'POST',
  data:data,
  dataType:'json',
  url:'/test/PHP/upload.php',
  success:function (result) {
   if(result.status){
    location.reload();
   }else{
    alert(result.msg);
   }
  }
 });
};
var ajax = new Ajax();
module.exports = ajax;

最后在另一个文件中,调用customerImg对象的start方法

var $ = require('jquery');
var customerImg =require('./customerImg.js');
customerImg.start($('#warp'));

webpack的配置文件如下:

var webpack = require('webpack');
module.exports = {
 entry:{
  'customerImg':'./js/test.js',
  'jQuery':['jquery']
 },
 output:{
  filename:'[name].js',
  library:'jQuery',
  libraryTarget:'umd'
 },
 plugins:[
  new webpack.optimize.CommonsChunkPlugin({
   name:'jQuery',
   filename:'jquery.js'
  })
 ]
};

 效果:

基于HTML5+JS实现本地图片裁剪并上传功能

4.php代码

if(!empty($_POST) && isset($_POST['customerImg'])){
 $img = $_POST['customerImg'];
 $imgdata = explode(',', $img);
 $uniName = md5 ( uniqid ( microtime ( true ), true ) );
 $a = file_put_contents('./../uploads/'.$uniName.'.jpg', base64_decode($imgdata[1]));
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js 判断js函数、变量是否存在的简单示例代码
Mar 04 Javascript
jqGrid中文文档之选项设置
Dec 02 Javascript
AngularJS入门(用ng-repeat指令实现循环输出
May 05 Javascript
使用BootStrapValidator完成前端输入验证
Sep 28 Javascript
jQuery ajaxForm()的应用
Oct 14 Javascript
利用CSS、JavaScript及Ajax实现图片预加载的三大方法
Jan 22 Javascript
js模仿微信朋友圈计算时间显示几天/几小时/几分钟/几秒之前
Apr 27 Javascript
JS实现根据指定值删除数组中的元素操作示例
Aug 02 Javascript
JavaScript对象属性操作实例解析
Feb 04 Javascript
浅谈TypeScript的类型保护机制
Feb 23 Javascript
vue+springboot+element+vue-resource实现文件上传教程
Oct 21 Javascript
Vue指令实现OutClick的示例
Nov 16 Javascript
详解Vue-基本标签和自定义控件
Mar 24 #Javascript
JS验证input输入框(字母,数字,符号,中文)
Mar 23 #Javascript
jQuery编写textarea输入字数限制代码
Mar 23 #jQuery
移动端刮刮乐的实现方式(js+HTML5)
Mar 23 #Javascript
BootStrap+Mybatis框架下实现表单提交数据重复验证
Mar 23 #Javascript
jquery实现全选、全不选以及单选功能
Mar 23 #jQuery
jQuery插件FusionCharts实现的MSBar3D图效果示例【附demo源码】
Mar 23 #jQuery
You might like
php中实现精确设置session过期时间的方法
2014/07/17 PHP
php中替换字符串函数strtr()和str_repalce()的用法与区别
2016/11/25 PHP
thinkPHP5.0框架引入Traits功能实例分析
2017/03/18 PHP
php中关于换行的实例写法
2019/09/26 PHP
js禁止页面复制功能禁用页面右键菜单示例代码
2013/08/29 Javascript
js控制分页打印、打印分页示例
2014/02/08 Javascript
JS按回车键实现登录的方法
2014/08/25 Javascript
JS关闭窗口时产生的事件及用法示例
2016/08/20 Javascript
JS身份证信息验证正则表达式
2017/06/12 Javascript
小程序开发基础之view视图容器
2018/08/21 Javascript
Element Backtop回到顶部的具体使用
2020/07/27 Javascript
js 将多个对象合并成一个对象 assign方法的实现
2020/09/24 Javascript
[02:40]DOTA2英雄基础教程 先知
2013/11/29 DOTA
[49:18]2018DOTA2亚洲邀请赛 3.31 小组赛 A组 OG vs TNC
2018/04/01 DOTA
[49:21]TNC vs VG 2019DOTA2国际邀请赛淘汰赛 胜者组赛BO3 第三场 8.20.mp4
2019/08/22 DOTA
归纳整理Python中的控制流语句的知识点
2015/04/14 Python
python线程池(threadpool)模块使用笔记详解
2017/11/17 Python
python 函数内部修改外部变量的方法
2018/12/18 Python
Python3实现的回文数判断及罗马数字转整数算法示例
2019/03/27 Python
python 实现dict转json并保存文件
2019/12/05 Python
Python中实现输入一个整数的案例
2020/05/03 Python
最简单的matplotlib安装教程(小白)
2020/07/28 Python
python3 googletrans超时报错问题及翻译工具优化方案 附源码
2020/12/23 Python
用HTML5中的Canvas结合公式绘制粒子运动的教程
2015/05/08 HTML / CSS
瑞典时尚耳机品牌:Urbanears
2017/07/26 全球购物
我能否用void** 指针作为参数, 使函数按引用接受一般指针
2013/02/16 面试题
小学生打架检讨书
2014/01/26 职场文书
人事科岗位职责范本
2014/03/02 职场文书
英语系本科生求职信
2014/07/15 职场文书
2014年减负工作总结
2014/12/10 职场文书
元宵节晚会主持词
2015/07/01 职场文书
优质护理心得体会
2016/01/22 职场文书
普希金诗歌赏析(6首)
2019/08/22 职场文书
浅谈Redis跟MySQL的双写问题解决方案
2022/02/24 Redis
win10键盘驱动怎么修复?Win10键盘驱动修复小技巧
2022/04/06 数码科技
js 实现验证码输入框示例详解
2022/09/23 Javascript