vue+element 实现图片上传和截取功能

7 篇文章 0 订阅

今天分享一个图片上传和截取功能,需求要求固定尺寸的截取,按照自己的需求进行配置,

实现效果如下:

首先参考element的上传参考地址:Element - The world's most popular Vue UI framework

截取的部分主要用到了vue-cropper

第一步:

安装   npm install vue-cropper

第二步:

1.全局使用 main.js

import VueCropper from 'vue-cropper'
Vue.use(VueCropper)

  2.组件内使用

import { VueCropper } from 'vue-cropper'
components: {
 VueCropper,
},

接下来直接上代码:

<template>
  <div>
    <el-upload
      class="heightCredential"
      :action="action"
      :headers="headers"
      :file-list="list"
      :show-file-list="isList"
      :accept="accept"
      :on-success="handleSuccess"
      :before-upload="beforeUpload"
      >
    <div class="uploadBorder flex_jc_center flex_ai_center">
      <img v-if="!isList && list.length" :src="list[0].url" @click.stop=""/>
      <i v-else class="el-icon-plus"></i>
      <slot/>
    </div>
    <i v-show="!isList && list.length" class="el-icon-close img-close" @click.stop="clearFile"></i>
  </el-upload>
    <el-dialog
      append-to-body
      title="广告裁剪"
      ref="dialog"
      width="1200"
      class="img-cropper"
      :visible.sync="dialogVisible"
      :close-on-click-modal="false"
    >
      <div class="cropper-w">
        <div class="cropper" :style="{ width: '100%', height: '360px' }">
          <vueCropper
            ref="cropper"
            :img="option.img"
            :outputSize="option.size"
            :outputType="option.outputType"
            :info="option.info"
            :full="option.full"
            :canMove="option.canMove"
            :canMoveBox="option.canMoveBox"
            :original="option.original"
            :autoCrop="option.autoCrop"
            :autoCropWidth="option.autoCropWidth"
            :autoCropHeight="option.autoCropHeight"
            :fixedBox="option.fixedBox"
          ></vueCropper>
        </div>
      </div>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="finish">确认</el-button>
      </div>
    </el-dialog>
  </div>
</template>
<script>
import axios from 'axios'

export default {
  name: 'BaseCropperUpload',
  props: ["fileList"],
  computed: {
    list() {
      return this.fileList
    }
  },
  data() {
    return {
      action: 上传图片地址,
      dialogVisible:false,
       option: {
        img: "", // 裁剪图片的地址
        info: true, // 裁剪框的大小信息
        outputSize: 0.8, // 裁剪生成图片的质量
        outputType: "jpeg", // 裁剪生成图片的格式
        canScale: false, // 图片是否允许滚轮缩放
        autoCrop: true, // 是否默认生成截图框
        autoCropWidth: 1200, // 默认生成截图框宽度
        autoCropHeight: 158, // 默认生成截图框高度
        fixedBox: true, // 固定截图框大小 不允许改变
        fixed: false, // 是否开启截图框宽高固定比例
        fixedNumber: [7, 5], // 截图框的宽高比例
        full: true, // 是否输出原图比例的截图
        canMoveBox: true, // 截图框能否拖动
        original: true, // 上传图片按照原始比例渲染
        centerBox: true, // 截图框是否被限制在图片里面
        infoTrue: true, // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
      },
    }
  },
  created() {
  },
  methods: {
    beforeUpload(file) {
          // 图片转为base64
          let self = this
          const _URL = window.URL || window.webkitURL
          const image = new Image();
          image.src = _URL.createObjectURL(file);
          self.option.img = ''
          image.onload = function () {
            let base64 = self.transBase64FromImage(image);
            self.option.img = base64
          }
          this.picWidth = 1200
          this.dialogVisible = true
    },
    // 将网络图片转换成base64格式
    transBase64FromImage(image) {
      let canvas = document.createElement("canvas");
      canvas.width = image.width;
      canvas.height = image.height;
      let ctx = canvas.getContext("2d");
      ctx.drawImage(image, 0, 0, image.width, image.height);
      // 可选其他值 image/jpeg
      return canvas.toDataURL("image/png");
    },
    // base64转成files
    transformToFiles(dataurl, filename) {
      var arr = dataurl.split(","),
        mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]),
        n = bstr.length,
        u8arr = new Uint8Array(n);
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
      }
      return new File([u8arr], filename, { type: mime });
    },
    handleSuccess({data: url, code, msg}, {name}) {
      if (code !== 200) {
        this.$message.error(msg)
      } else {
        this.$emit('handleFile', [{url, name}])
      }
    },
    clearFile() {
      this.$emit('removeImage')
      this.$emit('handleFile', [])
    },
    finish() {
		let _this = this
		this.$refs.cropper.getCropBlob(async (data) => {
			let formData = new FormData();
			console.log(data);
			  if(data){
               var timestamp = new Date().getTime() + '.jpg'
            //  将blob格式转换为文件。File() 构造器创建新的 File 对象实例。
            const newFile = new window.File([data], timestamp);

						formData.append('file',newFile)
						//调用axios上传
						let {data: res} = await axios.post(上传文件地址, formData)
						if(res.code === 200){
							let data = res.data.replace('[','').replace(']','').split(',');
							let imgInfo = {
								name : _this.Name,
								url : data[0]
							};
              this.dialogVisible = false
						}
					} else {
						this.$message.warning('请重新截图')
					}
				})
        _this.$forceUpdate()
			},
    },
  }
}

具体的配置在option里面,finish方法主要是截取并上传,这快用到的是axios进行上传的。

完整代码:

<template>
  <div>
    <el-upload
      class="avatar-uploader"
      action="上传图片地址"
      :show-file-list="false"
      :before-upload="beforeAvatarUpload"
      :data="{ deadline: 0, file_size: 512 * 1024 }"
      :model="imageUrl"
      ref="upload"
      accept="image/*"
      :limit="1"
    >
      <img v-if="imageUrl" :src="imageUrl" class="avatar" />
      <i v-else class="el-icon-plus avatar-uploader-icon"></i>
    </el-upload>
 
    <el-dialog
      title="封面裁剪"
      ref="dialog"
      width="40%"
      :visible.sync="dialogVisible"
      :close-on-click-modal="false"
    >
      <div class="cropper-w">
        <div class="cropper" :style="{ width: '100%', height: '280px' }">
          <vueCropper
            ref="cropper"
            :img="option.img"
            :outputSize="option.size"
            :outputType="option.outputType"
            :info="option.info"
            :full="option.full"
            :canMove="option.canMove"
            :canMoveBox="option.canMoveBox"
            :original="option.original"
            :autoCrop="option.autoCrop"
            :autoCropWidth="option.autoCropWidth"
            :autoCropHeight="option.autoCropHeight"
            :fixedBox="option.fixedBox"
          ></vueCropper>
        </div>
      </div>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="handleConfirm">确认</el-button>
      </div>
    </el-dialog>
  </div>
</template>
 
<script>
export default {
  data() {
    return {
      dialogVisible: false,
      imageUrl: "",
      option: {
        img: "", // 裁剪图片的地址
        info: true, // 裁剪框的大小信息
        outputSize: 0.8, // 裁剪生成图片的质量
        outputType: "jpeg", // 裁剪生成图片的格式
        canScale: false, // 图片是否允许滚轮缩放
        autoCrop: true, // 是否默认生成截图框
        autoCropWidth: 380, // 默认生成截图框宽度
        autoCropHeight: 100, // 默认生成截图框高度
        fixedBox: true, // 固定截图框大小 不允许改变
        fixed: true, // 是否开启截图框宽高固定比例
        fixedNumber: [7, 5], // 截图框的宽高比例
        full: true, // 是否输出原图比例的截图
        canMoveBox: true, // 截图框能否拖动
        original: true, // 上传图片按照原始比例渲染
        centerBox: false, // 截图框是否被限制在图片里面
        infoTrue: true, // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
      },
    };
  },
  methods: {
    beforeAvatarUpload(file) {
      this.filename = file.name;
      this.openCropper(file);
      return false;
    },
    openCropper(file) {
      var _this = this;
      console.log(_this);
      const isJPG =
        file.type === "image/jpeg" ||
        file.type === "image/jpg" ||
        file.type === "image/png" ||
        file.type === "image/PNG" ||
        file.type === "image/JPG";
      if (!isJPG) {
        this.$message.error("上传图片只能为jpg或png格式");
        return;
      }
      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.option.img = data;
        _this.dialogVisible = true;
      };
      // 转化为base64
      reader.readAsDataURL(file);
      // 转化为blob
      // reader.readAsArrayBuffer(file);
    },
    handleConfirm() {
		console.log(this.$refs.cropper);
      this.$refs.cropper.getCropBlob((data) => {
        // if (data.size > 2097152) {
        //   this.showMsg("图片大于2M,请进行裁剪或重新选择");
        // }
        let blob = window.URL.createObjectURL(data);
        this.downImg = blob;
        var base64;
        var img = new Image();
        img.src = blob;
        var _that = this;
        img.onload = function () {
          var that = this;
          //生成比例
          var w = that.width,
            h = that.height,
            scale = w / h;
          h = w / scale;
          //生成canvas
          var canvas = document.createElement("canvas");
          var ctx = canvas.getContext("2d");
          canvas.width = w;
          canvas.height = h;
          ctx.drawImage(that, 0, 0, w, h);
          // 生成base64
          _that.cropperPic = canvas.toDataURL("image/jpeg", 0.8);
          let files = _that.transformToFiles(_that.cropperPic, _that.filename);
          _that.temporaryCloseCropper = true;
 
          // XMLHttpRequest 请求 --最后决定使用 XMLHttpRequest 来进行上传图片
          var xhr = new XMLHttpRequest();
          xhr.timeout = 3000;
          xhr.ontimeout = function (event) {
            console.log("请求超时!");
          };
          let param = new FormData();
          param.append("file", files, _that.filename);
          param.append("deadline", 0); // 按照接口需求情况添加
          param.append("file_size", 512 * 1024); // 按照接口需求情况添加
          xhr.open(
            "POST",
            "上传图片地址"
          );
          xhr.send(param);
          xhr.onreadystatechange = function () {
            if (xhr.readyState == 4 && xhr.status == 200) {
              console.log(JSON.parse(xhr.responseText));
              let res = JSON.parse(xhr.responseText);
			  if(res.code==200){
				_that.imageUrl = res.data;
				console.log(_that.imageUrl);
				_that.dialogVisible = false;
			  }else {
              console.log(res.msg);
            }
            } else {
              console.log(xhr.statusText);
            }
          };
 
          // 生成图片
        //   _that.$refs.upload.$children[0].handleChange({
        //     target: { files: [files] },
        //   });
 
          //  使用此方法 需要在 upload 里 action 设置接口地址
          // _that.$refs.upload.$children[0].post(files)
        };
      });
    },
 
    // base64转成files

    transformToFiles(dataurl, filename) {
      var arr = dataurl.split(","),
        mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]),
        n = bstr.length,
        u8arr = new Uint8Array(n);
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
      }
      return new File([u8arr], filename, { type: mime });
    },
  },
};
</script>
 
<style lang='scss'>
.avatar-uploader .el-upload {
  border: 1px dashed #d9d9d9;
  border-radius: 6px;
  cursor: pointer;
  position: relative;
  overflow: hidden;
}
.avatar-uploader .el-upload:hover {
  border-color: #409eff;
}
.avatar-uploader-icon {
  font-size: 28px;
  color: #8c939d;
  width: 178px;
  height: 178px;
  line-height: 178px;
  text-align: center;
}
.avatar {
  width: 178px;
  height: 178px;
  display: block;
}
</style>

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值