vue 项目图片上传裁剪功能

该组件已打包到npm i vue-zeary-ctrl组件中,可下载组件使用。

最近项目要用到图片裁剪功能,所以自己结合vue-cropper及网上列子写了个组件
1、界面呈现
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2、安装vue-cropper

npm install vue-cropper --save-dev

3、新建界面lzg-img-cropper

<template>
  <el-dialog
    :visible.sync="dialogVisible"
    :close-on-click-modal="false"
    :close-on-press-escape="false"
    :before-close="handleClose"
    custom-class="lzg-cropper"
    title="图片裁剪"
  >
    <div class="lzg-cropper-content">
      <div
        class="cropper"
        :style="{
          height: windowHeight+'px',
          width: windowWidth+'px'
        }"
      >
        <vueCropper
          v-if="isShowCropper"
          ref="cropper"
          :img="cropperImg"
          :output-size="option.size"
          :output-type="option.outputType"
          :info="true"
          :full="option.full"
          :can-move="option.canMove"
          :can-move-box="option.canMoveBox"
          :fixed-box="option.fixedBox"
          :original="option.original"
          :auto-crop="option.autoCrop"
          :auto-crop-width="option.autoCropWidth"
          :auto-crop-height="option.autoCropHeight"
          :center-box="option.centerBox"
          :high="option.high"
          :info-true="option.infoTrue"
          :enlarge="option.enlarge"
          :fixed="option.fixed"
          :fixed-number="option.fixedNumber"
          @realTime="realTime"
        />
      </div>
    </div>
    <span
      slot="footer"
      class="dialog-footer"
    >
      <el-button
        type="primary"
        @click="saveImg"
      >{{ $t('button.BtnConfirm') }}</el-button>
      <el-button
        type="info"
        @click="handleClose"
      >{{ $t('button.BtnCancel') }}</el-button>
    </span>
  </el-dialog>
</template>

<script>
import { VueCropper } from "vue-cropper";
export default {
	name: "Cropper",
	components: {
		VueCropper
	},
	props: {
    fixedBox: {
      type: Boolean,
      default: false
    },
		dialogVisible: {
			type: Boolean,
			default: false
		},
		imgType: {
			type: String,
			default: "blob"
		},
		cropperImg: {
			type: String,
			default: ""
		},
		autoCropWidth: {
			type: Number,
			default: 320
		},
		autoCropHeight: {
			type: Number,
			default: 180
		}
	},
	data() {
		return {
			previews: {},
      isShowCropper:true,
			option: {
				img: "", // 裁剪图片的地址
				size: 1, // 裁剪生成图片的质量
				full: true, // 是否输出原图比例的截图 默认false
				outputType: "png", // 裁剪生成图片的格式 默认jpg
				canMove: true, // 上传图片是否可以移动
				fixedBox: false, // 固定截图框大小 不允许改变
				original: true, // 上传图片按照原始比例渲染
				canMoveBox: true, // 截图框能否拖动
				autoCrop: true, // 是否默认生成截图框
				// 只有自动截图开启 宽度高度才生效
				autoCropWidth: 320, // 默认生成截图框宽度
				autoCropHeight: 180, // 默认生成截图框高度
				centerBox: true, // 截图框是否被限制在图片里面
				high: false, // 是否按照设备的dpr 输出等比例图片
				enlarge: 1, // 图片根据截图框输出比例倍数
				mode: "contain", // 图片默认渲染方式
				maxImgSize: 2000, // 限制图片最大宽度和高度
				limitMinSize: [100, 120], // 更新裁剪框最小属性
				infoTrue: false, // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
				fixed: false, // 是否开启截图框宽高固定比例  (默认:true)
				fixedNumber: [16, 9] // 截图框的宽高比例
			},
      windowWidth: 900, //实时屏幕宽度
      windowHeight: 540 //实时屏幕高度
		};
	},
	watch: {
		autoCropWidth: {
      immediate: true,
      handler(newVal, oldVal) {
        this.option.autoCropWidth = newVal;
      },
      deep: true
    },
		autoCropHeight: {
      immediate: true,
      handler(newVal, oldVal) {
        this.option.autoCropHeight = newVal;
      },
      deep: true
    },
    fixedBox: {
      immediate: true,
      handler(newVal, oldVal) {
        this.option.fixedBox = newVal;
      },
      deep: true
    }

	},
  mounted() {
    let that = this;
    that.getWindowSize();
    // <!--把window.onresize事件挂在到mounted函数上-->
    window.onresize = () => {
      return (() => {
        that.getWindowSize();
      })();
    };
  },
  methods: {
	  getWindowSize(){
      this.isShowCropper=false;
      let divs=document.getElementsByClassName("el-dialog__body");
	    if(divs && divs.length>0)
      {
        this.windowHeight =divs[0].clientHeight-20; // 高
        this.windowWidth = divs[0].clientWidth; // 宽
      }
	    else{
        this.windowHeight = 540; // 高
        this.windowWidth = 900; // 宽
      }
	    this.$nextTick(()=>{
        this.isShowCropper=true;
      })
    },
		// 裁剪时触发的方法,用于实时预览
		realTime(data) {
			this.previews = data;
		},
		// 重新上传
		uploadBth() {
			this.$emit("update-cropper");
		},
		// 取消关闭弹框
		handleClose() {
			this.$emit("colse-dialog", false);
		},
		// 获取裁剪之后的图片,默认blob,也可以获取base64的图片
		saveImg() {
			if (this.imgType === "blob") {
				this.$refs.cropper.getCropBlob(data => {
					this.$emit("upload-img", data);
				});
			} else {
				this.$refs.cropper.getCropData(data => {
					this.uploadFile = data;
					this.$emit("upload-img", data);
				});
			}
		}
	}
};
</script>

<style lang="scss" scoped>
 .lzg-cropper{
    width: 70%;
    top: 10%;
    right: 15%;
    bottom: 10%;
    left: 15%;
    transform: translate(0%, 0%);
  }
  .lzg-cropper .el-dialog__body{
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    overflow-y: auto;
  }
  // 截图
  .lzg-cropper-content {
    display:flex;
    justify-content:center;
    align-items:center;
  }
</style>

4、新建界面lzg-img-upload

<template>
  <div :class="$options.name">
   <el-upload
      ref="fileUpload"
      class="upload-el"
      accept="image/*"
      name="pic"
      :action="action"
      :data="uploadData"
      :on-change="selectChange"
      :show-file-list="false"
      :auto-upload="false"
      :http-request="httpRequest"
      :with-credentials="true"
      :headers="getHeader"
    >
      <div
        class="upload-border"
        :style="{ height: imageHeight+'px', width: imageWidth+'px' }"
      >
        <img
          v-if="resultImg"
          :style="{ height: imageHeight+'px', width: imageWidth+'px' }"
          :src="resultImg"
          class="upload-border"
        >
        <i
          v-else
          class="el-icon-upload"
          :style="{ lineHeight: imageHeight+'px' }"
        />
      </div>
    </el-upload>
      <div>{{ "尺寸必须是"+autoCropWidth+"*"+autoCropHeight+";jpge,jpg,png格式;图片小于5M;" }}</div>
    <cropper
      v-if="showCropper"
      :dialog-visible="showCropper"
      :cropper-img="cropperImg"
      :fixed-box="fixedBox"
      :auto-crop-width="autoCropWidth"
      :auto-crop-height="autoCropHeight"
      @update-cropper="updateCropper"
      @colse-dialog="closeDialog"
      @upload-img="uploadImg"
    />
  </div>
</template>

<script>
import Cropper from "./Cropper.vue";
export default {
	name: "LzgUploadImg",
	components: {
		Cropper
	},
  model: {
    prop: "value", //这里使我们定义的v-model属性
    event: "input"
  },
	props: {
    value: String,
		autoCropWidth: {
			type: Number,
			default: 320
		},
		autoCropHeight: {
			type: Number,
			default: 180
		},
    fixedBox: {
      type: Boolean,
      default: false
    },
    showTip: {
      type: Boolean,
      default: true
    },
    imageWidth: {
      type: Number,
      default: 320
    },
    imageHeight: {
      type: Number,
      default: 180
    },
	},
	data() {
		return {
			uploadData: { // 上传需要的额外参数
				siteId: 1,
				source: 1,
				fileName: ""
			},
			action: this.$BASE_API + "/upload/imageUpload", // 上传地址,必填
			cropperImg: "", // 需要裁剪的图片
			showCropper: false, // 是否显示裁剪框
			uploadFile: "", // 裁剪后的文件
      resultImg: "" // 上传成功,后台返回的路径
		};
	},
	computed: {
		getHeader() {
			return { obtainTokenType: sessionStorage.getItem("identity") };
		}
	},
  watch: {
    value: {
      immediate: true,
      handler(newVal, oldVal) {
        this.resultImg = newVal;
      },
      deep: true
    },
  },
	methods: {
		// submit 之后会触发此方法
		httpRequest(request) {
	        //这里根据自己系统更改
			const { file, data } = request; 
	        let formData = new FormData();
	        formData.append("file", this.uploadFile,file.name);
	        this.$http.post("upload/imageUpload", formData, {contentType: false, processData: false, headers:{'Content-Type': 'application/x-www-form-urlencoded'}})
	        .then(response => {
	          const { data } = response || {};
	          if (data.code === 200)  {
	            this.$emit("input", data.url);
	            this.closeDialog();
	          } else {
	            this.$message.error(data.message);
	          } 
	        })
	        ["catch"](ex => {});
		},
		// 选择文件
		selectChange(file) {
			const { raw, name } = file;
			this.openCropper(raw);
			this.uploadData.fileName = name;
		},
		/**
       * @param {file} 上传的文件
       */
		openCropper(file) {
			var files = file;
			let isLt5M = files.size > (5 << 20);
			if (isLt5M) {
				this.$message.error("上传图片大小不能超过5M!");
				return false;
			}
			var reader = new FileReader();
			reader.onload = e => {
				let data;
				if (typeof e.target.result === "object") {
					// 把Array Buffer转化为blob 如果是base64不需要
					data = window.URL.createObjectURL(new Blob([e.target.result]));
				} else {
					data = e.target.result;
				}
				this.cropperImg = data;
			};
			// 转化为base64
			// reader.readAsDataURL(file)
			// 转化为blob
			reader.readAsArrayBuffer(files);
			this.showCropper = true;
		},
		// 上传图片
		uploadImg(file) {
			this.uploadFile = file;
			this.$refs.fileUpload.submit();
		},
		// 更新图片
		updateCropper() {
			this.$refs.fileUpload.$children[0].$el.click();
		},
		// 关闭窗口
		closeDialog() {
			this.showCropper = false;
		}
	}
};
</script>

<style lang="scss" scoped>
  .LzgUploadImg {
    .lzg-upload-border{
      border: 1px dashed #d9d9d9;
      border-radius: 6px;
      box-sizing: border-box;
    }
    .el-upload {
      display: block;
      width: 100px;
      margin: 30px auto 0;
    }

    .el-upload__tip {
      font-size: 14px;
      color: #606266;
      margin-top: 7px;
    }
    .el-icon-upload {
      font-size: 67px;
      color: #c0c4cc;
    }
    .video-image {
      display: flex;
      figure {
        img {
          width: 100%;
          height: 100%;
          display: block;
        }
      }
    }
  }
</style>

5、界面调用

 <lzg-upload-cropper v-model="picUrl"/>
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值