最近做了一个项目,这个项目中需要实现的一个功能是:用户自定义头像(用户在本地选择一张图片,在本地将图片裁剪成满足系统要求尺寸的大小)。这个功能的需求是:头像最初剪切为一个正方形。如果选择的图片小于规定的头像要求尺寸,那么这整张图片都会作为头像。如果大于规定的尺寸,那么用户可以选择要裁剪的区域。用户点击确定按钮,就将裁剪得到的图片数据发送到服务器,在后端将图片数据保存成一个文件。
要完成上述功能,涉及到的知识有: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。
我从这个项目中抽离出了这个功能。下面是这个功能的详细代码。
- <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>
- .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 的运行结果如下:
- 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 = $('
- 你的浏览器不支持自定义头像,可更换高版本的浏览器自定义头像
- ');
- $('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;
- /**
- * 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 = 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;
- 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;
- 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'
- })
- ]
- };
效果:
- 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]));
- }
来源: http://www.cnblogs.com/QxQstar/p/6607039.html