在vue中使用vue-cropper剪辑图片

1.创建vueCropper 子组件

<template>
  <div class="Cropper">
    <div class="cropper-container">
      <div class="cropper-el" :style="geth">
        <vue-cropper
          ref="cropper"
          :img="cropperImg"
          :output-size="option.size"
          :output-type="option.outputType"
          :info="true"
          :can-move="option.canMove"
          :can-move-box="option.canMoveBox"
          :fixed-box="option.fixedBox"
          :auto-crop="option.autoCrop"
          :auto-crop-width="optionWidth"
          :auto-crop-height="optionHeigth"
          :center-box="option.centerBox"
          :high="option.high"
          :info-true="option.infoTrue"
          @realTime="realTime"
          :enlarge="option.enlarge"
          :fixed="option.fixed"
          :fixed-number="option.fixedNumber"
          :limitMinSize="option.limitMinSize"
          @imgLoad="imgLoad"
        />
      </div>
    </div>
  </div>
</template>

<script>
import { VueCropper } from 'vue-cropper';
export default {
  name: 'Cropper',
  components: {
    VueCropper
  },
  props: [
    'cropperImg', //裁剪图片的URL
    'optionWidth', //裁剪框宽
    'optionHeigth', //裁剪框高
    'geth', //样式
    'isDel' //判断是否是裁剪最后一张图
  ],
  data() {
    return {
      previews: {},
      option: {
        img: '', // 裁剪图片的地址
        size: 1, // 裁剪生成图片的质量
        outputType: 'png', // 裁剪生成图片的格式 默认jpg
        canMove: false, // 上传图片是否可以移动
        fixedBox: false, // 固定截图框大小 不允许改变
        canMoveBox: false, // 截图框能否拖动
        autoCrop: true, // 是否默认生成截图框
        // 只有自动截图开启 宽度高度才生效
        autoCropWidth: this.optionWidth, // 默认生成截图框宽度
        autoCropHeight: this.optionHeigth, // 默认生成截图框高度
        centerBox: true, // 截图框是否被限制在图片里面
        high: true, // 是否按照设备的dpr 输出等比例图片
        enlarge: 1, // 图片根据截图框输出比例倍数
        mode: 'contain', // 图片默认渲染方式 //图片默认渲染方式 contain , cover, 100px, 100% auto
        maxImgSize: 1920, // 限制图片最大宽度和高度
        limitMinSize: [10, 10], // 更新裁剪框最小属性
        original: true, //上传图片按照原始比例渲染
        height: true, //是否按照设备的dpr 输出等比例图片
        // limitMinSize: 10, // 更新裁剪框最小属性
        infoTrue: false, // true 为展示真实输出图片宽高 false 展示看到的截图框宽高
        fixed: false, // 是否开启截图框宽高固定比例 (默认:true)
        // fixedNumber: [1, 1] // 截图框的宽高比例 ==>> 这个参数目前没有作用(作者解释的)
        fixedNumber: [1, 1] // 截图框的宽高比例
      }
    };
  },
  methods: {
    // 裁剪时触发的方法,用于实时预览
    realTime(data) {
      // this.previews = data;
      const that = this;
      console.log('', data);
      this.$refs.cropper.getCropBlob(data => {
        // 这里data数据为Blob类型,blobToDataURI方法转换成base64
        console.log(data);
        this.blobToDataURI(data, function(res) {
          console.log(res);
          // that.previewImg = res;
        });
      });
      this.$refs.cropper.clearCrop();
      // console.log(this.previews.url);
      //  console.log(3333)
    },
    // 图片加载的回调 imgLoad 返回结果success, error
    imgLoad(e) {
      console.log('裁剪成功', e);
    },
    blobToDataURI(blob, callback) {
      var reader = new FileReader();
      reader.readAsDataURL(blob);
      reader.onload = function(e) {
        callback(e.target.result);
      };
    }
  }
};
</script>

<style lang="scss" scoped>
.Cropper {
  .cropper-el {
    flex-shrink: 0;
  }
  .cropper-container {
    display: block;
    justify-content: space-between;
    .prive-el {
      flex: 1;
      align-self: center;
      text-align: center;
      .prive-style {
        margin: 0 auto;
        flex: 1;
        -webkit-flex: 1;
        display: flex;
        display: -webkit-flex;
        justify-content: center;
        -webkit-justify-content: center;
        overflow: hidden;
        background: #ededed;
        margin-left: 40px;
      }
      .preview {
        overflow: hidden;
      }
      .el-button {
        margin-top: 20px;
      }
    }
  }
}
</style>
<style lang="scss">
.cropper-box-canvas img {
  width: 100% !important;
  height: 100% !important;
}
</style>

2.在父组件中使用

<template>
  <div>
    <el-dialog
      :title="type == 'add' ? '添加' : '编辑'"
      :visible.sync="dialogVisible"
      width="764px"
      :close-on-click-modal="false"
    >
      <div>
        <el-form
          :model="ruleForm"
          :rules="rules"
          ref="ruleForm"
          label-width="100px"
          class="demo-ruleForm"
        >
          <el-form-item label="姓名" prop="visitorName">
            <el-input v-model="ruleForm.visitorName"></el-input>
          </el-form-item>
          <el-form-item label="手机号" prop="phone">
            <el-input v-model.number="ruleForm.phone"></el-input>
          </el-form-item>
          <el-form-item label="所属单位" prop="companyId">
            <el-select
              v-model="ruleForm.companyId"
              placeholder="单位名称"
              @change="toSelect"
            >
              <el-option
                v-for="item in options"
                :key="item.id"
                :label="item.companyName"
                :value="item.id"
              >
              </el-option>
            </el-select>
          </el-form-item>
          <el-form-item label="身份信息" prop="idCard">
            <el-input v-model="ruleForm.idCard"></el-input>
          </el-form-item>
          <el-form-item prop="cardPhoto">
            <div style="display:flex;">
              <img-upload
                :url="ruleForm.cardPhoto"
                @setImg="setImg"
              ></img-upload>

              <div class="imgbox" v-if="resImg">
                <el-image :src="resImg" :preview-src-list="[resImg]">
                </el-image>
              </div>
            </div>

            <div
              class="cropper"
              :style="{
                width: `${imgObj.width}px`,
                height: `${imgObj.height}px`
              }"
            >
              <vueCropper
                ref="cropper"
                :img="option.img"
                :outputSize="option.outputSize"
                :outputType="option.outputType"
                :canScale="option.canScale"
                :autoCrop="option.autoCrop"
                :autoCropWidth="option.autoCropWidth"
                :autoCropHeight="option.autoCropHeight"
                :canMoveBox="option.canMoveBox"
                :canMove="option.canMove"
                :centerBox="option.centerBox"
                :info="option.info"
                :fixedBox="option.fixedBox"
                @realTime="realTime"
              ></vueCropper>
            </div>
          </el-form-item>
        </el-form>
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button @click="resetForm('ruleForm')">取 消</el-button>
        <el-button type="primary" @click="submitForm('ruleForm')"
          >确 定</el-button
        >
      </span>
    </el-dialog>
  </div>
</template>
<script>
import imgUpload from './imgUpload.vue';
import {
  companyList,
  addAgreementVisitor,
  agreementVisitorDetail,
  editorAgreementVisitor
} from '@/api/visitor/protocol.js';
import { OBSuploads, OBSupload, base64toFile } from '@/utils/upload';
import { VueCropper } from 'vue-cropper';
import { number } from 'echarts';
export default {
  name: 'addVisitorModel',
  props: ['type', 'editId'],
  components: { imgUpload, VueCropper },
  data() {
    var validateidCard = (rule, value, callback) => {
      if (value === '') {
        callback(new Error('请输入身份证号'));
      } else {
        let reg = /(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/;
        if (!reg.test(this.ruleForm.idCard)) {
          callback(new Error('请输入正确的身份证号'));
          // this.$refs.ruleForm.validateField('checkPass');
        }
        callback();
      }
    };
    return {
      dialogVisible: false,
      mode: 'view',
      options: [],
      ruleForm: {
        visitorName: '',
        phone: '',
        companyName: '',
        companyId: '',
        idCard: '',
        cardPhoto: '',
        cutPhoto: ''
      },
      option: {
        img: '', // 裁剪图片地址,这里可以本地图片或者链接,链接不用require
        outputSize: 1, // 裁剪生成图片质量
        outputType: 'png', // 裁剪生成图片格式
        canScale: true, // 图片是否允许滚轮播放
        autoCrop: true, // 是否默认生成截图框 false
        info: false, // 是否展示截图框信息
        autoCropWidth: 70, // 生成截图框的宽度
        autoCropHeight: 90, // 生成截图框的高度
        canMoveBox: true, // 截图框是否可以拖动
        fixedBox: false, // 固定截图框的大小
        canMove: true, // 上传图片是否可拖动
        centerBox: true // 截图框限制在图片里面
      },
      resImg: null, //截图后图片
      previewImg: null, // 预览后的图片
      // previewObj: {
      //   width: 60,
      //   height: 80
      // },
      imgObj: {
        width: 182,
        height: 100
      },
      rules: {
        visitorName: [
          { required: true, message: '请输入活动名称', trigger: 'blur' }
        ],
        phone: [
          {
            required: true,
            message: '请输入手机号'
          },
          {
            //通过正则校验
            pattern: /^1[3|5|7|8|9]\d{9}$/,
            //不通过的提示信息
            message: '请输入正确的手机号'
          }
        ],
        idCard: [
          // { required: true, message: '请输入身份信息', trigger: 'blur' }
          { validator: validateidCard, trigger: 'blur' }
        ],
        companyId: [
          { required: true, message: '请选择活动区域', trigger: 'change' }
        ],
        cardPhoto: [{ required: true, message: '请上传身份证' }]
      }
    };
  },
  watch: {
    dialogVisible(val) {
      this.resImg = '';
      this.companyList();
      if (this.type == 'edit' && this.editId) {
        this.agreementVisitorDetail();
      } else {
        this.reset();
      }
    },
    // 'ruleForm.cutPhoto': {
    //   handler(newValue, oldValue) {
    //     this.resImg = newValue;
    //     console.log('22222', this.resImg);
    //   }
    // },
    'ruleForm.idCard': {
      handler(newValue, oldValue) {
        this.ruleForm.idCard = newValue.replace(
          /[\u4e00-\u9fa5/\s+/]|[^a-zA-Z0-9\u4E00-\u9FA5]/g,
          ''
        );
      }
    },
    'option.img': {
      handler: function(val) {
        const that = this;
        const img = new Image();
        img.src = val;
        img.onload = function() {
          that.imgObj.width = 180;
          that.imgObj.height = 100;
        };
      },
      immediate: true
    }
  },

  methods: {
    agreementVisitorDetail() {
      agreementVisitorDetail({ id: this.editId }).then(res => {
        this.reset();
        this.ruleForm = { ...res.data };
        this.resImg = res.data.cutPhoto;
        //为了避免剪辑框显示的图片跨域,座椅将图片重现转换成base64,再进行展示
        this.setAvatarBase64(res.data.cardPhoto, base64 => {
          this.option.img = base64;
        });
        // this.option.img = res.data.cardPhoto;
      });
    },
    toSelect() {
      let obj = {};
      obj = this.options.find(item => {
        return item.id === this.ruleForm.companyId;
      });
      this.ruleForm.companyName = obj.companyName;
    },
    companyList() {
      companyList().then(res => {
        this.options = res.data;
      });
    },
    setAvatarBase64(src, callback) {
      let _this = this;
      let image = new Image();
      // 处理缓存
      image.src = src + '?v=' + Math.random();
      // 支持跨域图片
      image.crossOrigin = '*';
      image.onload = function() {
        let base64 = _this.transBase64FromImage(image);
        callback && callback(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');
    },
    realTime(data) {
      const that = this;
      // this.$refs.cropper.goAutoCrop(); // 重新生成裁剪框
      this.$refs.cropper.getCropBlob(data => {
        let name = `.${data.type.split('/')[1]}`;
        // 这里data数据为Blob类型,blobToDataURI方法转换成base64
        // this.blobToDataURI(data, function(res) {
        //   that.previewImg = res;
        // });
        this.$nextTick(() => {
          // 设置自动裁剪框的宽高和位置
          this.$refs.cropper.cropOffsertX = 89; // x1
          this.$refs.cropper.cropOffsertY = 0; // y1
        });
        this.$refs.cropper.getCropData(data => {
          this.resImg = data;
          let formData = new FormData();
          formData.append('file', data);
          OBSupload({
            file: base64toFile(data),
            fileName: name
          })
            .then(res => {
              // this.resImg = res;
              this.ruleForm.cutPhoto = res;
              // this.$emit('setImg', res);
            })
            .catch(e => {});
          // this.handleDownload(data);
        });
      });
    },
    // handleClick() {
    //   this.$refs.cropper.getCropData(data => {
    //     // console.log('2222', data);
    //     this.resImg = data;
    //     // this.handleDownload(data);
    //   });
    // },
    handleDownload(url) {
      var a = document.createElement('a'); // 生成一个a元素
      var event = new MouseEvent('click'); // 创建一个单击事件
      a.download = 'photo'; // 设置图片名称, 这里可以自定义,也可以获取图片名称进行修改
      a.href = url; // 将生成的URL设置为a.href属性
      a.dispatchEvent(event); // 触发a的单击事件
    },
    blobToDataURI(blob, callback) {
      var reader = new FileReader();
      reader.readAsDataURL(blob);
      reader.onload = function(e) {
        callback(e.target.result);
      };
    },
    reset() {
      this.ruleForm = {
        visitorName: '',
        phone: '',
        companyName: '',
        companyId: '',
        idCard: '',
        cardPhoto: '',
        cutPhoto: ''
      };
      this.resImg = '';
      this.option.img = '';
    },
    setImg(data) {
      this.ruleForm.cardPhoto = data;
      this.option.img = data;
    },
    submitForm(formName) {
      console.log(this.ruleForm);
      this.$refs[formName].validate(valid => {
        if (valid) {
          if (this.type == 'add') {
            const loading = this.$loading({
              lock: true,
              text: '加载中',
              spinner: 'el-icon-loading',
              background: 'rgba(0, 0, 0, 0.7)'
            });
            addAgreementVisitor(this.ruleForm)
              .then(res => {
                this.dialogVisible = false;
                this.$emit('success', true);
                this.reset();
                loading.close();
              })
              .catch(e => {
                loading.close();
                this.dialogVisible = true;
              });
          } else {
            editorAgreementVisitor(this.ruleForm).then(res => {
              this.dialogVisible = false;
              this.$emit('success', true);
            });
          }
        } else {
          return false;
        }
      });
    },
    resetForm(formName) {
      this.$refs[formName].resetFields();
      this.dialogVisible = false;
    },
    show() {
      this.dialogVisible = true;
      this.$nextTick(() => {
        this.$refs['ruleForm'].clearValidate();
      });
    },
    hide() {
      this.dialogVisible = false;
    }
  }
};
</script>
<style lang="less" scoped>
.imgbox {
  width: 60px;
  height: 80px;
  text-align: center;
  margin-top: 20px;
  margin-left: 20px;
}
.cropper {
  margin-top: 10px;
}
.previewImg {
  width: 100%;
  height: 100%;
  object-fit: cover;
  // border-radius: 50%;
}
</style>

  • 有更多需要的可以去官网看具体的配置
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值