cropperjs - yangmengwen/cropperjs GitHub Wiki

最近公司项目要求做一个上传照片并裁剪为一寸照片的功能,中间遇到各种坑,最后还好都解决了,下面是我的踩坑之路

先下载cropperjs,也可以直接npm cropperjs 在组件里引入cropperjs,我是把他写成一个组件引用,需要的同学可以自行设计 在需要的页面引入该组件

<--组件cropper.vue-->
<template>
	<div id="demo">
		<!-- 遮罩层 -->
		<div class="container" v-show="panel">
			<div>
				<img id="image" :src="url" alt="Picture">
			</div>
			<button type="button" id="button" @click="commit" style="background:#1AAD19">确定</button>
			<button type="button" id="cancel" @click="cancel">取消</button>
		</div>
		<div class="vue-box">
			<div class="show">
				<div class="picture" :style="'backgroundImage:url('+headerImage+')'"></div>
			</div>
			<input type="file" class="file" accept="image/*" capture="camera" multiple @change="change" v-if="appOs == 'android'">
			<input type="file" class="file" accept="image/png,image/jpg,image/jpeg" @change="change" v-else>
		</div>

	</div>
</template>
<script>
import Cropper from "cropperjs";
export default {
      props: {
			// 定义的宽高比
	widthRate: {
		type: Number,
		default: 21,
	},
	// 定义的宽高比
	heightRate: {
		type: Number,
		default: 27
	},
	imgUrl: {
		type: String,
		default: function () {
			return ''
		}
	},
	postUrl: ''
    },
mounted() {
			
	//初始化这个裁剪框
	var self = this;
	var image = document.getElementById("image");
	this.cropper = new Cropper(image, {
	aspectRatio: 21/27,
	autoCropArea: 0.8,
	viewMode: 1,
	guides: true,
	cropBoxResizable: false, //是否通过拖动来调整剪裁框的大小
	movable: true,
	cropBoxMovable: false, //是否通过拖拽来移动剪裁框。
	dragCrop: true,
	dragMode: "move",//‘crop’: 可以产生一个新的裁剪框3 ‘move’: 只可以移动3 ‘none’: 什么也不处理
	center: true,
	zoomable: true, //是否允许放大图像。
	zoomOnTouch: true,//是否可以通过拖动触摸来放大图像。
	scalable: true,
	background: false,
	checkOrientation: true,//self.appOs == 'android' ? true : false,
	checkCrossOrigin: true,
	zoomable: true,
	toggleDragModeOnDblclick: false,
	rotatable: true,
	ready: function() {
		self.croppable = true;
	}
	});
},
methods: {
	//取消上传
	cancel() {
		this.panel = false;
		this.$emit('getStatus',true)

	},
	//创建url路径
		getObjectURL(file) {
		var url = null;
		if (window.createObjectURL != undefined) {
					// basic
			url = window.createObjectURL(file);
		} else if (window.URL != undefined) {
			// mozilla(firefox)
			url = window.URL.createObjectURL(file);
		} else if (window.webkitURL != undefined) {
			// webkit or chrome
			url = window.webkitURL.createObjectURL(file);
		}
		return url;
		},
		//input框change事件,获取到上传的文件
	change(e) {
		let that = this
		let files = e.target.files || e.dataTransfer.files;
		if (!files.length) return;
		let type = files[0].type; //文件的类型,判断是否是图片
		let size = files[0].size; //文件的大小,判断图片的大小
		that.$alert('为了您能够顺利通过阿里实人认证,请勿使用手机拍摄已有照片,保证照片清晰,人物完整。')
		if (that.imgCropperData.accept.indexOf(type) == -1) {
			that.$alert("请选择我们支持的图片格式!");
			that.cancel();
		}else if (size > 5242880) {
			// that.$alert("请选择5M以内的图片!"); //这里可以根据需要自己设置
			// that.cancel();
		}
		that.picValue = files[0];

				///////////////////////////////////
		var canvas = document.createElement("canvas");

		var img = new Image();
		var mpImg = new mimage(this.picValue);
				//图片方向,判断图片方向解决剪切之后图片旋转问题
		if (!!window.webkitURL) {
			img.src = window.webkitURL.createObjectURL(this.picValue);
		} else {
			img.src = window.URL.createObjectURL(this.picValue);
		}
			let _this = this;
			this.imgOrientationfn(img, function (o) {
			var w = img.width;
			var h = img.height;
			//需要上传的图片
			mpImg.render(canvas, {
				maxWidth: w,
				maxHeight: h,
				orientation: o
			});
			//上传图片
			var dataurl = canvas.toDataURL("image/jpeg");
			let newFile = _this.dataURLtoBlob(dataurl);
			let originUrl = _this.getObjectURL(newFile);

			//每次替换图片要重新得到新的url
			if (_this.cropper) {
				_this.cropper.replace(originUrl);
			}
			_this.panel = true;
			_this.$emit('getStatus',false)

		})
////////////////////////////////////
			},
		//确定提交
		commit() {
			var croppedCanvas;
			var roundedCanvas;
			if (!this.croppable) {
				return;
			}
				// Crop
			croppedCanvas = this.cropper.getCroppedCanvas();
				// Round
			roundedCanvas = this.getRoundedCanvas(croppedCanvas);
			this.headerImage = roundedCanvas.toDataURL();
				// console.log(this.headerImage)
				//上传图片
			this.postImg();
		},
			//canvas画图
		getRoundedCanvas(sourceCanvas) {
		let canvas = document.createElement('canvas');
		let context = canvas.getContext('2d');
				//   let width = sourceCanvas.width;
				//   let height = sourceCanvas.height;

                //这里设置的宽高是产品要求,如果没有具体尺寸要求可使用上面注释的宽高
		let width = 210 * 4;
		let height = 270 * 4;

		canvas.width = width;
		canvas.height = height;
		if (this.Orientation == 6) {
			context.rotate(90 * Math.PI / 180)
			context.translate(0, -840)
		}
		context.imageSmoothingEnabled = true;
		context.drawImage(sourceCanvas, 0, 0, width, height);
		context.globalCompositeOperation = 'destination-in';
		context.beginPath();
		context.rect(0, 0, 210 * 4, 270 * 4);
		context.fill();

			return canvas;
		},
			//提交上传函数
		postImg() {
			let _this = this;
			let blob = _this.dataURLtoBlob(this.headerImage);
			// 生成FormData对象
			let fd = new FormData();
			// 注:此处 file 应和后台接收参数匹配
			fd.append('file', blob, Date.now() + '.jpg');
			_this.$loading(true);
			api[_this.postUrl](fd, function (isSuccess, data, err) {
				_this.$loading(false);
				if (data.status == "200") {
					_this.$emit('callback', data.body)
						_this.panel = false;
					} else {
						_this.$tips(data.message);
					}
				});
				//_this.destoried();
			},
			newBlob(data, datatype) {
				var out;
				try {
					out = new Blob([data], {type: datatype});
				} catch (e) {
					window.BlobBuilder = window.BlobBuilder ||
							window.WebKitBlobBuilder ||
							window.MozBlobBuilder ||
							window.MSBlobBuilder;

					if (e.name == 'TypeError' && window.BlobBuilder) {
						var bb = new BlobBuilder();
						bb.append(data.buffer);
						out = bb.getBlob(datatype);
					}
					else if (e.name == "InvalidStateError") {
						out = new Blob([data], {type: datatype});
					}
					else {
					}
				}
				return out;
			},

			dataURLtoBlob(data) {
				let self = this;
				var tmp = data.split(',');
				tmp[1] = tmp[1].replace(/\s/g, '');
				var binary = atob(tmp[1]);
				var array = [];
				for (var i = 0; i < binary.length; i++) {
					array.push(binary.charCodeAt(i));
				}
				return new self.newBlob(new Uint8Array(array), 'image/jpeg');
			},
			imgOrientationfn: function (file, cb) {
				file.onload = function () {
					var $this = $(file).exifLoad(function () {
						var Orientation = $this.exif("Orientation");
						if (undefined != Orientation && "" != Orientation && Orientation.length > 0) {
							Orientation = Orientation[0];
						} else {
							Orientation = 0;
						}
						cb(Orientation);
					});
				}
			},

		}
</script>
<style>
	.vue-box {
		opacity: 0;
		height: 4.26rem;
		width: 3.3rem;
		position: relative;
		z-index: 1;
		top: 0;
	}

	.vue-box .file {
		width: 100%;
		height: 100%;
		position: absolute;
    	top: -2.5rem;
	}


	#demo #button,#demo #cancel {
		position: absolute;
		right: 0.8rem;
		bottom: 1rem;
		width: 1.4rem;
		height: 0.6rem;
		border: none;
		border-radius: 5px;
		background: #f95454;
		color: #fff;
		line-height: 0.6rem;
	}
	#demo #cancel{left:0.8rem;}
	#demo .show {
		width: 100%;
		height: 100%;
		overflow: hidden;
		position: relative;
		/* border: 1px solid #d5d5d5; */
	}
	#demo .picture {
		width: 100%;
		height: 100%;
		overflow: hidden;
		background-position: center center;
		background-repeat: no-repeat;
		background-size: cover;
	}
	#demo .container {
		z-index: 9999;
		position: fixed;
		/* padding-top: 0; */
		left: 0;
		top: 0;
		right: 0;
		bottom: 0;
		background: rgba(0, 0, 0, 1);
		width: 100%;
		height: 100%;
	}
	#demo .container>div{
		position: absolute;
		width: 100%;
		height: 100%;
		top: 50%;
		left: 50%;
		-webkit-transform: translate(-50%, -50%);
		transform: translate(-50%, -50%);
	}
	
	#demo #image {
		max-width: 100%;
	}
	.cropper-view-box,
	.cropper-face {
	}
	/*!
	 * Cropper.js v1.0.0-rc
	 * https://github.com/fengyuanchen/cropperjs
	 *
	 * Copyright (c) 2017 Fengyuan Chen
	 * Released under the MIT license
	 *
	 * Date: 2017-03-25T12:02:21.062Z
	 */
	.cropper-container {
		font-size: 0;
		line-height: 0;
		position: relative;
		-webkit-user-select: none;
		-moz-user-select: none;
		-ms-user-select: none;
		user-select: none;
		direction: ltr;
		-ms-touch-action: none;
		touch-action: none;
	}
	.cropper-container img {
		display: block;
		min-width: 0 !important;
		max-width: none !important;
		min-height: 0 !important;
		max-height: none !important;
		width: 100%;
		height: 100%;
		image-orientation: 0deg;
	}
	.cropper-wrap-box,
	.cropper-canvas,
	.cropper-drag-box,
	.cropper-crop-box,
	.cropper-modal {
		position: absolute;
		top: 0;
		right: 0;
		bottom: 0;
		left: 0;
	}
	.cropper-wrap-box {
		overflow: hidden;
	}
	.cropper-drag-box {
		opacity: 0;
		background-color: #fff;
	}
	.cropper-modal {
		opacity: 0.8;
		background-color: #000;
	}
	.cropper-view-box {
		display: block;
		overflow: hidden;
		width: 100%;
		height: 100%;
		outline: 1px solid #39f;
		outline-color: rgba(51, 153, 255, 0.75);
	}
	.cropper-dashed {
		position: absolute;
		display: block;
		opacity: 0.5;
		border: 0 dashed #eee;
	}
	.cropper-dashed.dashed-h {
		top: 33.33333%;
		left: 0;
		width: 100%;
		height: 33.33333%;
		border-top-width: 1px;
		border-bottom-width: 1px;
	}
	.cropper-dashed.dashed-v {
		top: 0;
		left: 33.33333%;
		width: 33.33333%;
		height: 100%;
		border-right-width: 1px;
		border-left-width: 1px;
	}
	.cropper-center {
		position: absolute;
		top: 50%;
		left: 50%;
		display: block;
		width: 0;
		height: 0;
		opacity: 0.75;
	}
	.cropper-center:before,
	.cropper-center:after {
		position: absolute;
		display: block;
		content: " ";
		background-color: #eee;
	}
	.cropper-center:before {
		top: 0;
		left: -3px;
		width: 7px;
		height: 1px;
	}
	.cropper-center:after {
		top: -3px;
		left: 0;
		width: 1px;
		height: 7px;
	}
	.cropper-face,
	.cropper-line,
	.cropper-point {
		position: absolute;
		display: block;
		width: 100%;
		height: 100%;
		opacity: 0.1;
	}
	.cropper-face {
		top: 0;
		left: 0;
		background-color: #fff;
	}
	.cropper-line {
		background-color: #39f;
	}
	.cropper-line.line-e {
		top: 0;
		right: -3px;
		width: 5px;
		cursor: e-resize;
	}
	.cropper-line.line-n {
		top: -3px;
		left: 0;
		height: 5px;
		cursor: n-resize;
	}
	.cropper-line.line-w {
		top: 0;
		left: -3px;
		width: 5px;
		cursor: w-resize;
	}
	.cropper-line.line-s {
		bottom: -3px;
		left: 0;
		height: 5px;
		cursor: s-resize;
	}
	.cropper-point {
		width: 5px;
		height: 5px;
		opacity: 0.75;
		background-color: #39f;
	}
	.cropper-point.point-e {
		top: 50%;
		right: -3px;
		margin-top: -3px;
		cursor: e-resize;
	}
	.cropper-point.point-n {
		top: -3px;
		left: 50%;
		margin-left: -3px;
		cursor: n-resize;
	}
	.cropper-point.point-w {
		top: 50%;
		left: -3px;
		margin-top: -3px;
		cursor: w-resize;
	}
	.cropper-point.point-s {
		bottom: -3px;
		left: 50%;
		margin-left: -3px;
		cursor: s-resize;
	}
	.cropper-point.point-ne {
		top: -3px;
		right: -3px;
		cursor: ne-resize;
	}
	.cropper-point.point-nw {
		top: -3px;
		left: -3px;
		cursor: nw-resize;
	}
	.cropper-point.point-sw {
		bottom: -3px;
		left: -3px;
		cursor: sw-resize;
	}
	.cropper-point.point-se {
		right: -3px;
		bottom: -3px;
		width: 20px;
		height: 20px;
		cursor: se-resize;
		opacity: 1;
	}
	@media (min-width: 768px) {
		.cropper-point.point-se {
			width: 15px;
			height: 15px;
		}
	}
	@media (min-width: 992px) {
		.cropper-point.point-se {
			width: 10px;
			height: 10px;
		}
	}
	@media (min-width: 1200px) {
		.cropper-point.point-se {
			width: 5px;
			height: 5px;
			opacity: 0.75;
		}
	}
	.cropper-point.point-se:before {
		position: absolute;
		right: -50%;
		bottom: -50%;
		display: block;
		width: 200%;
		height: 200%;
		content: " ";
		opacity: 0;
		background-color: #39f;
	}
	.cropper-invisible {
		opacity: 0;
	}
	.cropper-bg {
		background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC");
	}
	.cropper-hide {
		position: absolute;
		display: block;
		width: 0;
		height: 0;
	}
	.cropper-hidden {
		display: none !important;
	}
	.cropper-move {
		cursor: move;
	}
	.cropper-crop {
		cursor: crosshair;
	}
	.cropper-disabled .cropper-drag-box,
	.cropper-disabled .cropper-face,
	.cropper-disabled .cropper-line,
	.cropper-disabled .cropper-point {
		cursor: not-allowed;
	}
</style>
<--页面中引入组件-->
<tempalte>
  <div class="wrap">
      <div class="yi" v-if="!!signupInfo.facePic">
         <img class="sel-img" :src="signupInfo.facePic " />
      <!-- <img class="sel-img" :src=" signupInfo.facePic " /> -->
         <cropper-photo @callback="imgUrl" :postUrl="facepicUpload" @getStatus="getStatus"></cropper-photo>
              <div class="di">点此上传</div>
      </div>
  </div>
</template>
<script>
import cropperPhoto from './cropper'
export default{
 components: { cropperPhoto,uploadImg },
 data(){
    signupInfo: {
         facePic:"",
         compressFacePic: ''
    },
 }
 methods: {
    imgUrl(imgData){
            this.signupInfo.facePic = imgData.url;//原始图片地址
            this.signupInfo.compressFacePic = imgData.resizedUrl;//改变尺寸后的图片地址
            // this.signupInfo.facePic = this.host.OP_KB + '/employee/view/img/download?fileName=' + cosUrl;
            // console.log(this.signupInfo.facePic,this.signupInfo.compressFacePic)
    },
 }
}
</script>
⚠️ **GitHub.com Fallback** ⚠️