vue封装原生的可预览裁剪上传图片插件H5,PC端都可以使用

  感谢大家的点赞和转发,欢迎大家关注本人的博客。试用期指导,项目开发,简历优化,毕业设计/论文,欢迎添加本人微信。

 新人作者,欢迎关注和收藏👏🏻👏🏻

思路:1.先做出一个上传的图片的上传区

<!-- 上传区 -->

    <label for="fileUp">

      <div class="upBorder">

        <img src="../assets/add.png" alt="" />

        <input

          ref="fileUp"

          type="file"

          id="fileUp"

          accept="image"

          style="display: none"

          @change="upload()"

        />

      </div>

    </label>

upload() {

    let that = this;

    console.log(this.$refs.fileUp.files);

    if (this.$refs.fileUp.files.length != 0) {

      const reader = new FileReader();

      reader.readAsDataURL(this.$refs.fileUp.files[0]);

      reader.onload = function () {

        const img = new Image();

        img.src = reader.result;

        that.fileList.push(reader.result);

        that.$refs.fileUp.value = null; //上传后重置上传input的value,这样才能同时上传相同的图片

        console.log(reader.result);

      };

      this.upLodaOk = true;

    }

  },

 给上传图片的input绑定上ref属性然后通过FileReader构造函数获取上传的文件。

 2.完成已上传文件的预览区域

<!-- 预览区域 -->

     <div

       class="preView"

       v-for="(i, index) in fileList"

       :key="index"

       ref="preList"

     >

       <div class="fileList" v-if="upLodaOk">

         <img

           src="../assets/remove.png"

           alt=""

           class="remove"

           @click="removeProp(index)"

         />

         <img

           :src="fileList[index]"

           alt=""

           class="img"

           @click="cut(index)"

           ref="imgitem"

         />

       </div>

     </div>

在upload方法中将通过FileReader构造函数获取上传的文件push到fileList数组中然后遍历渲染出已经上传的图片列表,并且给每一个图片绑定ref属性。 

3.完成图片删除的功能

<!-- 删除弹窗 -->
    <div
      class="prop"
      :style="{
        height: this.windowHeight + 'px',
        width: this.windowWidth + 'px',
      }"
      v-if="show"
    >
      <div class="text">
        <img
          src="../assets/remove.png"
          alt=""
          class="close"
          @click="removePropClose()"
        />
        <div>要删除这张照片吗</div>
        <div class="action">
          <button class="btn green" @click="removePropClose()">取消</button>
          <button class="btn blue" @click="remove()">确定</button>
        </div>
      </div>
    </div>

removeProp(index) {

   //v-for循环中的ref是个数组,根据index来取每一个对应的dom元素

   this.removeIndex = index;

   this.show = true;

 },

 removePropClose() {

   this.show = false;

 },

 remove() {

   this.fileList.splice(this.removeIndex, 1);

   this.$refs.fileUp.value = null; //删除后重置上传input的value,这样才能同时上传相同的图片

   console.log(this.$refs.fileUp.value);

   this.show = false;

 },

点击预览图片上的x会触发删除确认弹窗,在removeProp方法中将要删除的图片的Index接收并存储的removeIndex变量中,remove方法中将fileList数组中对应索引的元素去掉并且重置一下上传属性,也可以在每次上传后重置,并且关闭弹窗 

4.完成上传时的剪裁功能

<!-- 裁剪蒙层 -->
    <div
      class="prop center"
      v-if="cutProp"
      :style="{
        height: this.windowHeight + 'px',
        width: this.windowWidth + 'px',
      }"
    >
      <div v-html="pre" ref="preimg" class="imgContent"></div>
      <div class="cutHandler">
        <button class="btn green" @click="cancel()">取消</button>
        <button class="btn blue" @click="qdcut()">剪裁</button>
      </div>
    </div> 

cut(index) {

   this.selIndex = index;

   this.pre = `<img

         src="${this.fileList[index]}"

         alt=""

         class='cutImg'

       />`;

   this.cutProp = true;

   console.log(this.$refs);

   this.$nextTick(function () {

     console.log(this.$refs.preimg.firstChild); //使用nextTick,dom更新完成后才能获取到子节点

     this.myCropper = new Cropper(this.$refs.preimg.firstChild, {

       aspectRatio: 1 / 1,

       dragMode: "move",

       outputType: "png", //防止图片背景变黑

       crop(event) {

         console.log(event.detail.x);

         console.log(event.detail.y);

         console.log(event.detail.width);

         console.log(event.detail.height);

         console.log(event.detail.rotate);

         console.log(event.detail.scaleX);

         console.log(event.detail.scaleY);

       },

     });

   });

 },

 qdcut() {

   let cropBox = this.myCropper.getCropBoxData();

   console.log(this.myCropper.getCropBoxData()); //打印裁剪数据

   let cropCanvas = this.myCropper.getCroppedCanvas({

     width: cropBox.width,

     height: cropBox.height,

   }); //使用画布画出裁剪后的图片

   let imgData = cropCanvas.toDataURL(); //导出裁剪后图片的数据

   console.log(imgData);

   this.fileList.splice(this.selIndex, 1, imgData);

   console.log(this.fileList);

   this.cutProp = false;

 }, //确定裁剪

 cancel() {

   this.cutProp = false;

 }, //取消裁剪

 因为本次封装的是预览时裁剪的功能,所以裁剪的是点击预览列表中的文件触发的,cut方法将选择的图片的index存储selIndex变量中,然后通过v-html指令在剪裁弹窗中加载出对应的图片来进行裁剪,裁剪使用cropper.js来进行的,注意使用时要在this.$nextTick方法的回调中来进行剪裁函数的初始化,这样才能获取到通过v-html指令插入的图片。

  选择合适的裁剪尺寸后点击确认才加调用qdcut方法,通过cropper.js的内置方法getCropBoxData()获取剪裁的数据,通过getCroppedCanvas()传入对应的数据然后导出剪裁后的图片,将fileList中对应的元素替换即可完成

6.下面附上整个代码,可以直接拿去使用: 

<template>
  <div>
    <!-- 裁剪蒙层 -->
    <div
      class="prop center"
      v-if="cutProp"
      :style="{
        height: this.windowHeight + 'px',
        width: this.windowWidth + 'px',
      }"
    >
      <div v-html="pre" ref="preimg" class="imgContent"></div>
      <div class="cutHandler">
        <button class="btn green" @click="cancel()">取消</button>
        <button class="btn blue" @click="qdcut()">剪裁</button>
      </div>
    </div>
    <!-- 删除弹窗 -->
    <div
      class="prop"
      :style="{
        height: this.windowHeight + 'px',
        width: this.windowWidth + 'px',
      }"
      v-if="show"
    >
      <div class="text">
        <img
          src="../assets/remove.png"
          alt=""
          class="close"
          @click="removePropClose()"
        />
        <div>要删除这张照片吗</div>
        <div class="action">
          <button class="btn green" @click="removePropClose()">取消</button>
          <button class="btn blue" @click="remove()">确定</button>
        </div>
      </div>
    </div>
    <!-- 上传区域 -->
    <div class="upContent">
      <!-- 预览区域 -->
      <div
        class="preView"
        v-for="(i, index) in fileList"
        :key="index"
        ref="preList"
      >
        <div class="fileList" v-if="upLodaOk">
          <img
            src="../assets/remove.png"
            alt=""
            class="remove"
            @click="removeProp(index)"
          />
          <img
            :src="fileList[index]"
            alt=""
            class="img"
            @click="cut(index)"
            ref="imgitem"
          />
        </div>
      </div>
      <!-- 上传区 -->
      <label for="fileUp">
        <div class="upBorder">
          <img src="../assets/add.png" alt="" />
          <input
            ref="fileUp"
            type="file"
            id="fileUp"
            accept="image"
            style="display: none"
            @change="upload()"
          />
        </div>
      </label>
    </div>
  </div>
</template>
<script>
import Cropper from "cropperjs";
import "cropperjs/dist/cropper.css";
export default {
  name: "upload",
  data() {
    return {
      cutProp: false,
      pre: "", //准备剪裁的图片
      selIndex: "", //选择照片的索引
      removeIndex: "", //准备删除的照片的索引
      show: false, //删除弹出层
      myCropper: null,
      afterImg: "",
      ingData: null,
      upLodaOk: false, //是否展示预览列表
      fileList: [], //已经上传图片的列表
    };
  },
  methods: {
    upload() {
      let that = this;
      console.log(this.$refs.fileUp.files);
      if (this.$refs.fileUp.files.length != 0) {
        const reader = new FileReader();
        reader.readAsDataURL(this.$refs.fileUp.files[0]);
        reader.onload = function () {
          const img = new Image();
          img.src = reader.result;
          that.fileList.push(reader.result);
          that.$refs.fileUp.value = null; //上传后重置上传input的value,这样才能同时上传相同的图片
          console.log(reader.result);
        };
        this.upLodaOk = true;
      }
    },
    removeProp(index) {
      //v-for循环中的ref是个数组,根据index来取每一个对应的dom元素
      this.removeIndex = index;
      this.show = true;
    },
    removePropClose() {
      this.show = false;
    },
    remove() {
      this.fileList.splice(this.removeIndex, 1);
      this.$refs.fileUp.value = null; //删除后重置上传input的value,这样才能同时上传相同的图片
      console.log(this.$refs.fileUp.value);
      this.show = false;
    },
    cut(index) {
      this.selIndex = index;
      this.pre = `<img
            src="${this.fileList[index]}"
            alt=""
            class='cutImg'
          />`;
      this.cutProp = true;
      console.log(this.$refs);
      this.$nextTick(function () {
        console.log(this.$refs.preimg.firstChild); //使用nextTick,dom更新完成后才能获取到子节点
        this.myCropper = new Cropper(this.$refs.preimg.firstChild, {
          aspectRatio: 1 / 1,
          dragMode: "move",
          outputType: "png", //防止图片背景变黑
          crop(event) {
            console.log(event.detail.x);
            console.log(event.detail.y);
            console.log(event.detail.width);
            console.log(event.detail.height);
            console.log(event.detail.rotate);
            console.log(event.detail.scaleX);
            console.log(event.detail.scaleY);
          },
        });
      });
    },
    qdcut() {
      let cropBox = this.myCropper.getCropBoxData();
      console.log(this.myCropper.getCropBoxData()); //打印裁剪数据
      let cropCanvas = this.myCropper.getCroppedCanvas({
        width: cropBox.width,
        height: cropBox.height,
      }); //使用画布画出裁剪后的图片
      let imgData = cropCanvas.toDataURL(); //导出裁剪后图片的数据
      console.log(imgData);
      this.fileList.splice(this.selIndex, 1, imgData);
      console.log(this.fileList);
      this.cutProp = false;
    }, //确定裁剪
    cancel() {
      this.cutProp = false;
    }, //取消裁剪
  },
  mounted() {},
  computed: {
    windowWidth() {
      return document.documentElement.clientWidth;
    },
    windowHeight() {
      return document.documentElement.clientHeight;
    },
  }, //监听屏幕的宽度和高度
};
</script>
<style>
.upBorder {
  width: 8rem;
  height: 8rem;
  border: 1px silver dashed;
  display: flex;
  justify-content: center;
  align-items: center;
}
.upContent {
  display: flex;
  justify-content: center;
  align-items: center;
}
.img {
  width: 8rem;
  height: 8rem;
}

.fileList {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}
.remove {
  position: absolute;
  width: 1rem;
  height: 1rem;
  top: 0rem;
  right: 0rem;
  cursor: pointer;
}
.prop {
  vertical-align: middle;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 999;
  background-color: rgba(0, 0, 0, 0.7);
}
.text {
  border-radius: 0.2rem;
  top: 50%;
  left: 50%;
  -webkit-transform: translate3d(-50%, -50%, 0);
  transform: translate3d(-50%, -50%, 0);
  position: fixed;
  z-index: 1000;
  color: black;
  text-align: center;
  background-color: #fff;
  padding: 2rem 4rem;
  white-space: nowrap;
}
.close {
  position: absolute;
  top: 0.3rem;
  right: 0.3rem;
  width: 1rem;
  height: 1rem;
}
.action {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-top: 1rem;
}
.btn {
  font-size: 0.12rem;
  color: #fff;
  padding: 0.2rem 0.8rem;
}
.blue {
  background-color: #1989fa;
  border: 1px solid #1989fa;
}
.green {
  background-color: #07c160;
  border: 1px solid #07c160;
}
.cropper-point.point-se {
  width: 5px;
  height: 5px;
}
.cropper {
  position: fixed;
  top: 0;
  z-index: 999;
}

/* .cropper-container{
   top: 50%;
  left: 50%;
  -webkit-transform: translate3d(-50%, -50%, 0);
  transform: translate3d(-50%, -50%, 0);
} */
.imgContent {
  width: 16rem;
  height: 16rem;
  display: inline-block;
  /* top: 50%;
  left: 50%;
  -webkit-transform: translate3d(-50%, -50%, 0);
  transform: translate3d(-50%, -50%, 0); */
}
.cutImg {
  display: block;
  max-width: 100%;
}
.center {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}
.cropper-bg {
  background: none;
}
.cutHandler {
  margin-top: 2rem;
  width: 16rem;
  text-align: center;
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.cropper-modal {
  background: rgba(0, 0, 0, 0);
}
</style>

运行截图:

 H5,PC端都可以使用

  感谢大家的点赞和转发,欢迎大家关注本人的博客。试用期指导,项目开发,简历优化,毕业设计/论文,欢迎添加本人微信。

 新人作者,欢迎关注和收藏👏🏻👏🏻

 觉得作者写的不错或者心情愉悦的老板也可以投币打赏,感谢观看,希望能给大家带来帮助 

 

  • 33
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值