前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >vue+vue-cropper实现上传剪裁图片以及上传时压缩图片

vue+vue-cropper实现上传剪裁图片以及上传时压缩图片

原创
作者头像
conanma
修改于 2021-11-03 04:38:39
修改于 2021-11-03 04:38:39
3.6K00
代码可运行
举报
文章被收录于专栏:正则正则
运行总次数:0
代码可运行

第一次做上传剪裁图片,找了许多框架,最后找到一个优雅的图片裁剪插件vue-cropper,很方便新手入手

安装

npm install vue-cropper

使用

import VueCropper from vue-cropper

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import axios from 'axios'
const host = 'xxx';

//预上传
export function uploadBefore(md5, size, ext) {
    const url = host + "/file/upload/before";
    return axios.post(url, {
        md5: md5,
        size: size,
        ext: ext
    }).then((res) => {
        return Promise.resolve(res)
    })
}
//保存文件
export function saveImage(key) {
    const url = host + "/file/save";
    return axios.put(url, {
        key: key
    }).then((res) => {
        return Promise.resolve(res)
    })
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import * as qiniu from 'qiniu-js'
import browserMD5File from 'browser-md5-file'
import { uploadBefore as preUploadFile, saveImage as saveFile } from 'api/uploadImage'

export function uploadFile(file) {
  return new Promise((resolve, reject) => {
    function checkError(res) {
      if (res.data.code !== 0) {
        reject(new Error(res.data.msg))
      }
    }

    // 预上传
    new Promise(preResolve => {
      // 生成文件 MD5 以便校验服务器上是否存在该文件
      browserMD5File(file, function (err, md5) {
        // size 转换成 kb
        const size = file.size / 1024
        const ext = file.name.substr(file.name.lastIndexOf('.') + 1)

        preUploadFile(md5, size, ext).then(res => {
          checkError(res)

          // 当 file_id 存在时表示文件已经上传过,所以进行秒传处理
          if (res.data.data.file_id) {
            resolve(res.data.data)
          } else {
            preResolve(res.data.data)
          }
        })
      })
    }).then(res => {
      let observable = qiniu.upload(file, res.key, res.token)
      return new Promise((uploadResolve, uploadReject) => {
        observable.subscribe({
          next(res) {
          },
          error(err) {
            uploadReject(err)
          },
          complete(res) {
            uploadResolve(res)
          }
        })
      })
    }).then(res => {
      const key = res.key

      return saveFile(key)
    }).then(res => {
      checkError(res)

      resolve(res.data.data)
    }).catch(e => {
      console.log(e)
      this.$message.warning(e.message)
    })
  })
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<template>
  <div>
     <!-- element 上传图片按钮 -->
      <el-upload class="uploader"
                       action
                       :show-file-list="false"
                       :on-success="handleUploadSuccess"
                       :http-request="customUpload">
              <img v-if="fileinfo.url"
                   :src="fileinfo.url"
                   class="upload">
              <i v-else
                 class="el-icon-plus uploader-icon" />
            </el-upload>
......
    <!-- vueCropper 剪裁图片实现-->
    <div class="vue-cropper-box"
         v-if="isShowCropper">
      <div class="vue-cropper-content">
        <vueCropper ref="cropper"
                    :img="option.img"
                    :outputSize="option.outputSize"
                    :outputType="option.outputType"
                    :info="option.info"
                    :canScale="option.canScale"
                    :autoCrop="option.autoCrop"
                    :autoCropWidth="option.autoCropWidth"
                    :autoCropHeight="option.autoCropHeight"
                    :fixed="option.fixed"
                    :fixedNumber="option.fixedNumber"></vueCropper>
      </div>
      <el-button v-if="isShowCropper"
                 type="danger"
                 @click="onCubeImg">确定裁剪图片</el-button>

    </div>
  </div>
</template>

<script>
import VueCropper from "vue-cropper"
import { uploadFile } from './uploadFile'

export default {
  components: {
    VueCropper
  },
  data() {
    return {
     //裁剪组件的基础配置option
      option: {
        img: '',                         //裁剪图片的地址
        info: true,                      //裁剪框的大小信息
        outputSize: 1,                   // 裁剪生成图片的质量
        outputType: 'jpeg',              //裁剪生成图片的格式
        canScale: false,                 // 图片是否允许滚轮缩放
        autoCrop: true,                  // 是否默认生成截图框
        autoCropWidth: 150,              // 默认生成截图框宽度
        autoCropHeight: 150,             // 默认生成截图框高度
        fixed: false,                    //是否开启截图框宽高固定比例
        fixedNumber: [4, 4]              //截图框的宽高比例
      },
      isShowCropper: false,            //是否显示截图框
      fileUpload: null,
      fileinfo: {},
      form: {},
    }
},
  methods: {
    //上传按钮上传成功执行事件
    handleUploadSuccess(response, file, fileList) {
      this.log('Upload response is %o', response)
      this.fileinfo = response

      this.fileUpload = file;

      //上传成功后将图片地址赋值给裁剪框显示图片
      this.$nextTick(() => {
        this.option.img = file.url;
        this.isShowCropper = true
      })
    },

    // 确定裁剪图片
    onCubeImg() {
      // 获取cropper的截图的base64 数据
      this.$refs.cropper.getCropData(data => {
        this.fileinfo.url = data
        this.isShowCropper = false

       //先将显示图片地址清空,防止重复显示
        this.option.img = ''

       //将剪裁后base64的图片转化为file格式
        let file = this.convertBase64UrlToBlob(data)
        file.name = this.fileUpload.name

        //将剪裁后的图片执行上传
        this.uploadFile(file).then(res => {
          this.form.content = res.file_id    //将上传的文件id赋值给表单from的content
        })

      })
    },

    // 将base64的图片转换为file文件
    convertBase64UrlToBlob(urlData) {
      let bytes = window.atob(urlData.split(',')[1]);//去掉url的头,并转换为byte
      //处理异常,将ascii码小于0的转换为大于0
      let ab = new ArrayBuffer(bytes.length);
      let ia = new Uint8Array(ab);
      for (var i = 0; i < bytes.length; i++) {
        ia[i] = bytes.charCodeAt(i);
      }
      return new Blob([ab], { type: 'image/jpeg' });
    },

代码暂时都是从项目中抽出来的,只适合借鉴参考,等有时间再单独将这些功能单独写项目,欢迎大家提供更好用的方法或指出不足之处,一起进步。

2019-11-07最新更新,在elementUI封装成组件(在PC端)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//使用方式: 
...
     <upload-cover-item :url="form.url"
                           @change="changeAvatar" />
...
   // 图片
    changeAvatar (data) {
      this.form.url= data
    },

上传之前

上传之后

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//upload-cover-item
<template>
  <div class="upload-box">
    <div class="avatar-uploader-box">
      <!-- 方框样式 -->
      <el-upload ref="avatarUploader"
                 class="avatar-uploader"
                 :action="actionUrl"
                 :show-file-list="false"
                 :on-change="handleAvatarChange"
                 :auto-upload="false"
                 accept="image/*"
                 v-if="type==='avatar' && !imageUrl">
        <span v-loading="loading"
              element-loading-text="上传中"
              element-loading-spinner="el-icon-loading"
              element-loading-background="rgba(0, 0, 0, 0.8)"
              style="line-height:30px;"
              v-if="!imageUrl">
          <i class="el-icon-plus avatar-uploader-icon"></i></span>
      </el-upload>

      <!-- 上传的图片 -->
      <img v-if="imageUrl"
           v-lazy="imageUrl"
           :src="imageUrl"
           class="avatar"
           @mouseover.stop="isShowPopup = true" />

      <!-- 显示查看和删除的按钮弹窗 -->
      <div class="avatar-uploader-popup"
           v-show="imageUrl && isShowPopup"
           @mouseleave="isShowPopup = false">
        <i class="el-icon-zoom-in"
           @click="dialogVisible = true"></i>
        <i class="el-icon-delete"
           @click="delImageUrl"></i>
      </div>
    </div>

    <!-- 查看大图 -->
    <el-dialog title="图片查看"
               center
               :visible.sync="dialogVisible"
               append-to-body>
      <img width="100%"
           :src="imageUrl"
           alt />
    </el-dialog>

    <!-- 裁剪图片 start-->
    <vue-cropper-item ref="VueCropperItem"
                      @confirm="confirmCropper" />
    <!-- 裁剪图片 end-->
  </div>
</template>

<script>
import * as url from '@/api/httpConfig'
import { uploadOss } from '@/api/modules/system' 
 // 上传图片 export const uploadOss = params => http.upload(`/sys/oss/upload`, params)

export default {
  name: 'upload-item',
  components: {
    VueCropper,
  },
  props: {
    type: {
      type: String,
      default: 'avatar'
    },
    url: {
      type: String,
      default: ''
    },
    autoCropWidth: {
      type: Number,
      default: 275
    },
    autoCropHeight: {
      type: Number,
      default: 206
    },
    fixedBox: {
      type: Boolean,
      default: false
    },
    isCompress: {  //是否压缩
      type: Boolean,
      default: true
    },
    compress: {  //压缩率
      type: String,
      default: '0.8'
    },
  },
  data () {
    return {
      loading: false,
      isShowPopup: false,
      dialogVisible: false,
      isProgress: false,
      percentage: 0,
      imageUrl: '',
      actionUrl: `${url.default.apiURL}/sys/oss/upload`,
    }
  },
  created () {
    this.imageUrl = this.url;
  },
  methods: {
    // 上传之前
    beforeAvatarUpload (file) {
      return new Promise((resolve, reject) => {
        if (!/\.(jpg|jpeg|png|JPG|PNG)$/.test(file.name)) {
          return reject('上传图片只能是JPG或PNG格式!');
        }

        if (file.size > 5 * 1024 * 1024) {
          return reject('上传图片大小不能超过5M!');
        }

        resolve("符合表單規則");
      });
    },

    // 上传改变
    async handleAvatarChange (file, fileList) {
      try {
        await this.beforeAvatarUpload(file)
        // console.log('上传改变', file)
        this.showVueCropperItem(file)

      } catch (e) {
        this.$message.warning(JSON.stringify(e))
      }
    },

    // 上传图片接口
    async uploadOssApi (data) {
      try {
        let params = new FormData()
        params.append('file', data)

        const res = await uploadOss(params)

        this.imageUrl = res.data.url;
        this.isProgress = false
        this.$emit('change', res.data.url)
      } catch (err) {
        this.$message.error('上传失败')
        this.imageUrl = ''
        this.isProgress = false
        this.$emit('change', '')
      }
    },

    // 删除图片
    delImageUrl () {
      this.imageUrl = ''
      this.$emit('change', '')
    },

    // 显示裁剪框
    showVueCropperItem (file) {
      this.$refs.VueCropperItem.init()
      this.$refs.VueCropperItem.fileName = file.name;

      this.$refs.VueCropperItem.options.img = URL.createObjectURL(file.raw);
      this.setVueCropperOptions()
    },

    // 确认裁剪后上传
    async confirmCropper (file) {
      this.loading = true
      await this.uploadOssApi(file)
      this.loading = false
    },

    // 设置裁剪的配置
    setVueCropperOptions () {
        this.$refs.VueCropperItem.options.autoCropWidth = this.autoCropWidth;
        this.$refs.VueCropperItem.options.autoCropHeight = this.autoCropHeight;
        this.$refs.VueCropperItem.options.fixedBox = this.fixedBox;

      this.$refs.VueCropperItem.isCompress = this.isCompress; //是否压缩图片
      this.$refs.VueCropperItem.compress = this.compress //压缩率
    },

  },
  watch: {
    url () {
      this.imageUrl = this.url;
    }
  }
}
</script>

<style lang="scss" scoped>
.upload-box {
  .avatar-uploader-box {
    position: relative;
    line-height: 0;
    width: fit-content; //  收缩与包裹,收缩到合适
    max-width: 400px;
    .avatar-uploader {
      /deep/ .el-upload {
        border: 1px dashed #d9d9d9;
        border-radius: 6px;
        cursor: pointer;
        position: relative;
        overflow: hidden;
        &:hover {
          border-color: #409eff;
        }
      }

      .avatar-uploader-icon {
        font-size: 28px;
        color: #8c939d;
        min-width: 178px;
        height: 178px;
        line-height: 178px;
        text-align: center;
      }
      .avatar {
        min-width: 178px;
        display: block;
      }
    }
    .progress-box {
      position: absolute;
      top: 0;
      left: 0;
      z-index: 2;
      width: 99%;
      height: 99%;
      background: #fff;
      display: flex;
      justify-content: center;
      align-items: center;
    }

    .avatar-uploader-popup {
      position: absolute;
      top: 0;
      left: 0;
      z-index: 2;
      width: 100%;
      height: 100%;
      background: rgba($color: #000000, $alpha: 0.5);
      display: flex;
      justify-content: center;
      align-items: center;
      color: #fff;
      font-size: 20px;
      border-radius: 6px;
      i {
        width: 30%;
        text-align: center;
        padding: 0 5%;
        font-size: 24px;
      }
    }
  }
}
</style>

裁剪框

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//vue-cropper-item

<template>
  <!-- 裁剪图片 start-->
  <el-dialog title="裁剪图片"
             width="700px"
             :close-on-click-modal="false"
             :close-on-press-escape="false"
             :show-close="false"
             :append-to-body="true"
             :visible.sync="isShowCropper">
    <!-- 主区域 start -->
    <div class="vue-cropper-box">
      <!-- 裁剪框 start -->
      <div class="vue-cropper-content">
        <div class="show-cropper"
             v-loading="loading"
             element-loading-text="加载中"
             element-loading-spinner="el-icon-loading"
             element-loading-background="rgba(0, 0, 0, 0.8)">
          <VueCropper ref="cropper"
                      :img="options.img"
                      :outputSize="options.size"
                      :outputType="options.outputType"
                      :info="options.info"
                      :centerBox="options.centerBox"
                      :canScale="options.canScale"
                      :autoCrop="options.autoCrop"
                      :autoCropWidth="options.autoCropWidth"
                      :autoCropHeight="options.autoCropHeight"
                      :fixedBox="options.fixedBox"
                      :fixed="options.fixed"
                      :fixedNumber="options.fixedNumber"
                      :original="options.original"
                      :full="options.full"
                      @realTime="realTime"
                      @imgLoad="imgLoad"></VueCropper>
        </div>
      </div>
      <!-- 裁剪框 end -->

      <!--底部 设置按钮 start-->
      <div class="vue-cropper-bottom">
        <p class="vue-cropper-bottom-item">
          自定义尺寸:<el-switch v-model="options.fixedBox"
                     active-color="#13ce66"
                     inactive-color="#ff4949"
                     :active-value="false"
                     :inactive-value="true"> </el-switch>
        </p>
        <p class="vue-cropper-bottom-item">
          <el-tooltip class="item"
                      effect="dark"
                      content="放大图片"
                      placement="bottom">
            <i class="el-icon-zoom-in scale"
               @click="changeScale(1)"></i>
          </el-tooltip>
        </p>
        <p class="vue-cropper-bottom-item">
          <el-tooltip class="item"
                      effect="dark"
                      content="缩小图片"
                      placement="bottom">
            <i class="el-icon-zoom-out scale"
               @click="changeScale(-1)"></i>
          </el-tooltip>
        </p>
        <p class="vue-cropper-bottom-item">
          <el-tooltip class="item"
                      effect="dark"
                      content="向左旋转90°"
                      placement="bottom">
            <i class="el-icon-refresh-left scale"
               @click="rotateLeft"></i>
          </el-tooltip>
        </p>
      </div>
      <!--底部 设置按钮 end-->
    </div>
    <!-- 主区域 end -->

    <span slot="footer"
          class="dialog-footer">
      <el-button v-if="isShowCropper"
                 @click="cancelCubeImg">取消</el-button>
      <el-button v-if="isShowCropper"
                 type="danger"
                 :disabled="loading"
                 :loading="isLoading"
                 @click="onCubeImg">确定</el-button>
    </span>
  </el-dialog>
  <!-- 裁剪图片 end-->
</template>

<script>
import { VueCropper } from "vue-cropper"
export default {
  name: 'vue-cropper-item',
  components: {
    VueCropper,
  },
  data () {
    return {
      isCompress: true, //是否压缩
      compress: 0.8, //压缩率
      isShowCropper: false,
      options: {
        img: '',
        info: true,
        size: 1,
        outputType: 'jpeg', //裁剪生成图片的格式
        centerBox: true, //截图框是否被限制在图片里面
        canScale: true, //图片是否允许滚轮缩放

        autoCrop: true,  // 是否默认生成截图框, 只有自动截图开启 宽度高度才生效
        autoCropWidth: 320,
        autoCropHeight: 200,
        fixedBox: false, //  固定截图框大小 不允许改变

        fixed: false,  // 开启宽度和高度比例
        fixedNumber: [4, 4],

        original: false,  // 上传图片按照原始比例渲染
        full: true  // 是否输出原图比例的截图
      },
      fileName: null,

      loading: false,
      isLoading: false,
      previews: {},
    }
  },
  methods: {
    // 初始化
    init () {
      this.isShowCropper = true
      this.loading = true
    },

    // 图片加载完成
    imgLoad (data) {
      this.loading = false
      this.isLoading = false
    },

    // 裁剪图放大或缩小
    changeScale (val) {
      this.$refs.cropper.changeScale(val)
    },

    // 向左旋转
    rotateLeft () {
      this.$refs.cropper.rotateLeft()
    },

    // 裁剪图片上传
    onCubeImg (file) {
      this.isLoading = true
      this.$refs.cropper.getCropData(data => {
        this.isLoading = false
        this.isShowCropper = false
        this.options.img = ''

        let file = null
        if (this.isCompress) {  //是否压缩
          let img = new Image()
          img.src = data
          img.onload = () => {
            let _data = this.onImgCompression(img)
            file = this.dataURLtoFile(_data, this.fileName)
            console.log('图片大小-压缩过:', (file.size / 1024).toFixed(2), 'kb,', '压缩率:', this.compress)
            this.$emit('confirm', file)
          }
        } else {
          file = this.dataURLtoFile(data, this.fileName)
          console.log('图片大小-未压缩:', (file.size / 1024).toFixed(2), 'kb')
          this.$emit('confirm', file)
        }
      })
    },

    // 将裁剪base64的图片转换为file文件
    dataURLtoFile (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 });
    },


    // 压缩图片 
    onImgCompression (img) {
      let canvas = document.createElement("canvas")
      let ctx = canvas.getContext("2d")
      let initSize = img.src.length
      let width = img.width
      let height = img.height
      canvas.width = width
      canvas.height = height
      // 铺底色 
      ctx.fillStyle = "#fff"
      ctx.fillRect(0, 0, canvas.width, canvas.height)
      ctx.drawImage(img, 0, 0, width, height)
      //进行压缩 
      let compress = this.compress || 0.8  //压缩率
      return canvas.toDataURL("image/jpeg", compress)
    },

    // 取消裁剪
    cancelCubeImg () {
      this.isShowCropper = false
      this.$message.info('取消裁剪图片!')
    },
  },
  watch: {
  }
}
</script>

<style lang="scss" scoped>
.vue-cropper-box {
  width: 100%;
  height: 450px;
  .vue-cropper-content {
    width: 100%;
    height: 90%;
    padding-bottom: 20px;
    text-align: center;
    display: flex;
    justify-content: space-between;

    .show-cropper {
      // width: 50%;
      width: 100%;
      height: 100%;
    }
    .show-preview {
      width: 50%;
      height: 100%;
      margin: 5px;
      overflow: hidden;
    }
  }

  .vue-cropper-bottom {
    display: flex;
    flex-flow: wrap row;
    // justify-content: space-between;
    align-items: center;
    &-item {
      margin-right: 15px;
    }
    .scale {
      font-size: 24px;
      color: dodgerblue;
      vertical-align: middle;
      &:hover {
        color: mediumseagreen;
        box-shadow: 0 0 2px 1px rgba(0, 140, 186, 0.5);
      }
    }
  }
}
</style>

2019-11-07最新更新,在Vant封装成组件 (移动端)

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//使用方式: 
...
     <upload-cover-item :url="form.url"
                           @change="changeAvatar" />
...
   // 图片
    changeAvatar (data) {
      this.form.url= data
    },

上传之前

上传之后

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//upload-cover-item
<template>
  <div class="upload-box">
    <div class="avatar-uploader-box">
      <van-uploader :before-read="beforeRead"
                    v-if="!imageUrl">
        <!-- 背景图,可更换 -->
        <img src="../../assets/img/common/product-cover-upload.png" />
      </van-uploader>

     <!-- 上传后的图片显示 -->
      <div class="avatar-img-box"
           v-if="imageUrl">
        <img v-lazy="imageUrl"
             :src="imageUrl"
             class="avatar-img"
             @click="openPreview" />

        <van-icon name="clear"
                  class="delete-icon"
                  @click="delImageUrl" />
      </div>
    </div>

    <!-- 预览图片 -->
    <van-image-preview v-model="isShowPreview"
                       :images="previewImgs">
      <template v-slot:index>预览图片</template>
    </van-image-preview>

    <!-- 裁剪图片 -->
    <vue-cropper-item ref="VueCropperItem"
                      @confirm="confirmCropper" />

  </div>
</template>

<script>
import { uploadOss } from '@/api/index'
 // 上传图片 export const uploadOss = params => http.upload(`/sys/oss/upload`, params)

export default {
  name: 'upload-item',
  props: {
    type: {
      type: String,
      default: 'avatar'
    },
    url: {
      type: String,
      default: ''
    },
    autoCropWidth: {
      type: Number,
      default: 275
    },
    autoCropHeight: {
      type: Number,
      default: 206
    },
    fixedBox: {
      type: Boolean,
      default: false
    },
    isCompress: {  //是否压缩
      type: Boolean,
      default: true
    },
    compress: {  //压缩率
      type: String,
      default: '0.8'
    },
  },
  data () {
    return {
      isShowPreview: false,
      imageUrl: '', //require('../../assets/img/logo.png'),
      previewImgs: []
    }
  },
  created () {
    this.imageUrl = this.url;
  },
  methods: {
    // 预览图片
    openPreview () {
      this.isShowPreview = true;
      this.previewImgs[0] = this.imageUrl
    },

    // 上传之前
    beforeRead (file) {
      if (!/\.(jpg|jpeg|png|JPG|PNG)$/.test(file.name)) {
        return this.$toast('上传图片只能是JPG或PNG格式!');
      }

      if (file.size > 5 * 1024 * 1024) {
        return this.$toast('上传图片大小不能超过5M!');
      }

      // console.log('选择完成显示裁剪框', file)
      this.showVueCropperItem(file)
    },

    // 显示裁剪框
    showVueCropperItem (file) {
      this.$refs.VueCropperItem.init()
      this.$refs.VueCropperItem.fileName = file.name;
      this.$refs.VueCropperItem.options.img = URL.createObjectURL(file);
      this.setVueCropperOptions()
    },

    // 设置裁剪的配置
    setVueCropperOptions () {
        this.$refs.VueCropperItem.options.autoCropWidth = this.autoCropWidth;
        this.$refs.VueCropperItem.options.autoCropHeight = this.autoCropHeight;
        this.$refs.VueCropperItem.options.fixedBox = this.fixedBox;

      this.$refs.VueCropperItem.isCompress = this.isCompress; //是否压缩图片
      this.$refs.VueCropperItem.compress = this.compress //压缩率
    },

    // 确认裁剪后上传
    async confirmCropper (file) {
      await this.uploadOssApi(file)
    },

    // 上传图片接口
    async uploadOssApi (data) {
      this.$showLoading('上传中...');
      try {
        let params = new FormData()
        params.append('file', data)

        const res = await uploadOss(params)

        this.imageUrl = res.data.url;

        this.$emit('change', res.data.url)
      } catch (err) {
        this.$toast.fail('上传失败')
        this.imageUrl = ''

        this.$emit('change', '')
      }
      this.$toast.clear()
    },

    // 删除图片
    delImageUrl () {
      this.imageUrl = ''
      this.$emit('change', '')
    }
  },
  watch: {
    url () {
      this.imageUrl = this.url;
    }
  }
}
</script>

<style lang="scss" scoped>
.upload-box {
  /deep/ .van-uploader__wrapper {
    width: 80px;
    height: 60px;
    background: #f3f3f5;
    border: 1px solid #e1e1e3;
    border-radius: 8px;
    display: flex;
    justify-content: center;
  }

  .avatar-img-box {
    position: relative;
    width: 80px;
    height: 60px;
    border: 1px solid #f3f3f5;
    border-radius: 8px;
    .avatar-img {
      width: 100%;
      height: 100%;
      overflow: hidden;
    }
    .delete-icon {
      position: absolute;
      top: -10px;
      right: -10px;
      font-size: 22px;
    }
  }
}
</style>

裁剪图片框

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//vue-cropper-item
<template>
  <!-- 裁剪图片 start-->
  <div class="vue-cropper-container"
       v-if="isShowCropper">

    <header class="header-nav">裁剪图片</header>

    <!-- 主区域 start -->
    <div class="vue-cropper-box">
      <!-- 裁剪框 start -->
      <div class="vue-cropper-content">
        <div class="show-cropper">
          <VueCropper ref="cropper"
                      :img="options.img"
                      :outputSize="options.size"
                      :outputType="options.outputType"
                      :info="options.info"
                      :centerBox="options.centerBox"
                      :canScale="options.canScale"
                      :autoCrop="options.autoCrop"
                      :autoCropWidth="options.autoCropWidth"
                      :autoCropHeight="options.autoCropHeight"
                      :fixedBox="options.fixedBox"
                      :fixed="options.fixed"
                      :fixedNumber="options.fixedNumber"
                      :original="options.original"
                      :full="options.full"
                      @imgLoad="imgLoad"></VueCropper>
        </div>
      </div>
      <!-- 裁剪框 end -->
    </div>
    <!-- 主区域 end -->

    <div class="vue-cropper-footer">
      <span @click="cancelCubeImg">{{$t('btn.cancel')}}</span>
      <span @click="onCubeImg">{{$t('btn.confirm')}}</span>
    </div>
  </div>
  <!-- 裁剪图片 end-->
</template>

<script>
import { VueCropper } from "vue-cropper"
export default {
  name: 'vue-cropper-item',
  components: {
    VueCropper,
  },
  data () {
    return {
      isCompress: true, //是否压缩
      compress: 0.8, //压缩率
      isShowCropper: false,
      options: {
        img: require('../../assets/img/logo.png'),
        info: true,
        size: 1,
        outputType: 'jpeg', //裁剪生成图片的格式
        centerBox: true, //截图框是否被限制在图片里面
        canScale: true, //图片是否允许滚轮缩放

        autoCrop: true,  // 是否默认生成截图框, 只有自动截图开启 宽度高度才生效
        autoCropWidth: 275,
        autoCropHeight: 206,
        fixedBox: true, //  固定截图框大小 不允许改变

        fixed: false,  // 开启宽度和高度比例
        fixedNumber: [4, 4],

        original: false,  // 上传图片按照原始比例渲染
        full: true  // 是否输出原图比例的截图
      },
      fileName: null,
    }
  },
  methods: {
    // 初始化
    init () {
      this.isShowCropper = true
      this.$showLoading(this.$t('loading'));
    },

    // 图片加载完成
    imgLoad (data) {
      this.$toast.clear()
    },

    // 裁剪图片上传
    onCubeImg (file) {
      if (!this.isShowCropper) return

      this.$showLoading(this.$t('confirm'));
      this.$refs.cropper.getCropData(data => {
        this.$toast.clear()
        this.isShowCropper = false
        this.options.img = ''

        let file = null
        if (this.isCompress) {  //是否压缩
          let img = new Image()
          img.src = data
          img.onload = () => {
            let _data = this.onImgCompression(img)
            file = this.dataURLtoFile(_data, this.fileName)
            console.log('图片大小-压缩过:', (file.size / 1024).toFixed(2), 'kb,', '压缩率:', this.compress)
            this.$emit('confirm', file)
          }
        } else {
          file = this.dataURLtoFile(data, this.fileName)
          console.log('图片大小-未压缩:', (file.size / 1024).toFixed(2), 'kb')
          this.$emit('confirm', file)
        }
      })
    },

    // 将裁剪base64的图片转换为file文件
    dataURLtoFile (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 });
    },

    // 压缩图片 
    onImgCompression (img) {
      let canvas = document.createElement("canvas")
      let ctx = canvas.getContext("2d")
      let initSize = img.src.length
      let width = img.width
      let height = img.height
      canvas.width = width
      canvas.height = height
      // 铺底色 
      ctx.fillStyle = "#fff"
      ctx.fillRect(0, 0, canvas.width, canvas.height)
      ctx.drawImage(img, 0, 0, width, height)
      //进行压缩 
      let compress = this.compress || 0.8  //压缩率
      return canvas.toDataURL("image/jpeg", compress)
    },

    // 取消裁剪
    cancelCubeImg () {
      this.isShowCropper = false
      this.$toast(this.$t('tips.cancelText'))
    }
  },
  watch: {
  }
}
</script>

<style lang="scss" scoped>
.vue-cropper-container {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 99;
  width: 100%;
  height: 100%;
  background: #000;

  .vue-cropper-box {
    width: 100%;
    height: 70%;
    margin: 10% 0;
    .vue-cropper-content {
      width: 100%;
      height: 90%;
      padding-bottom: 20px;
      text-align: center;
      display: flex;
      justify-content: space-between;

      .show-cropper {
        width: 100%;
        height: 100%;
      }
    }
  }
}

.header-nav {
  width: 100%;
  height: 50px;
  line-height: 50px;
  text-align: center;
  color: #fff;
  font-size: 16px;
}

.vue-cropper-footer {
  position: absolute;
  bottom: 0;
  width: 100%;
  height: 50px;
  line-height: 40px;
  text-align: center;
  padding: 5px 0;
  color: #fff;
  border-top: 1px solid rgba(255, 255, 255, 0.4);
  span {
    width: 50%;
    &:first-child {
      border-right: 1px solid rgba(255, 255, 255, 0.4);
    }
  }
}
</style>

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
[R语言]数据可视化的最佳解决方案:ggplot2
ggplot是一个拥有一套完备语法且容易上手的绘图系统,在Python和R中都能引入并使用,在数据分析可视化领域拥有极为广泛的应用。本篇从R的角度介绍如何使用ggplot2包,首先给几个我觉得最值得推荐的理由:
TOMOCAT
2020/06/10
2.8K0
ggplot2|详解八大基本绘图要素
ggplot2是由Hadley Wickham创建的一个十分强大的可视化R包。按照ggplot2的绘图理念,Plot(图)= data(数据集)+ Aesthetics(美学映射)+ Geometry(几何对象)。本文将从ggplot2的八大基本要素逐步介绍这个强大的R可视化包。
生信补给站
2020/08/05
7.2K0
ggplot2|详解八大基本绘图要素
ggplot2修改坐标轴详细介绍
ggplot2的每个细节都是可以修改的,非常推荐大家系统学习一下,用到再学确实是一种不错的方式,但是如果要提高进阶,还是有必要系统学习的。
医学和生信笔记
2022/11/15
12K0
ggplot2修改坐标轴详细介绍
跟我一起ggplot2(1)
ggplot2 R的作图工具包,可以使用非常简单的语句实现非常复杂漂亮的效果。 qplot 加载qplot library(ggplot2) # 测试数据集,ggplot2内置的钻石数据 qplot(carat, price, data = diamonds) dsmall <- diamonds[sample(nrow(diamonds), 100), ] #对diamonds数据集进行抽样 #1. 按color,size,shape的基本分类可视化      #1.1 简单的散点图(利用color分
cloudskyme
2018/03/20
2.2K0
跟我一起ggplot2(1)
(数据科学学习手札37)ggplot2基本绘图语法介绍
  ggplot2是R语言中四大著名绘图框架之一,且因为其极高的参数设置自由度和图像的美学感,即使其绘图速度不是很快,但丝毫不影响其成为R中最受欢迎的绘图框架;ggplot2的作者是现任Rstudio首席科学家的Hadley Wickham,ggplot2基于Leland Wilkinson在Grammar of Graphics(图形的语法)中提出的理论,取首字母缩写再加上plot,于是得名ggplot,末尾的2是因为Hadley写包的一个习惯——对先前的版本不满意便写一个新版本的名称不变仅在末尾加上2,如reshape2等;
Feffery
2018/05/26
7.1K0
R语言绘图之ggplot2包「建议收藏」
6月份一直在忙期末考试,今天来迅速的学习下ggplot2包的简单绘图。 R的基础包里面也有很多画图函数,例如plot();barplot();qqplot(); 但是还有大名鼎鼎的ggplot2包,用这个包的函数画出的图比较漂亮,而且使用灵活。
全栈程序员站长
2022/07/23
2.2K0
R语言绘图之ggplot2包「建议收藏」
独特的箱型图版式,你学会了吗?
ggeconodist是开发者受Economist杂志独特风格的启发,开发的一款与普通绘制的箱型图不同风格的R包。
作图丫
2022/03/29
9250
独特的箱型图版式,你学会了吗?
R语言笔记-5
ggplot2是与base r语言不同的作图语法,最少元素包括:指定数据、美学映射、几何对象
Jon_L
2023/05/21
6100
R语言 基础作图
*ggplot2中通过不同的geom函数生成图层,从前往后覆盖,因此需要考虑函数书写的顺序
Magnolia
2023/01/06
1.4K0
R语言基础笔记-03(ggplot2)
<GEOM_FUNCTION>(mapping =aes(<MAPPINGS>))
我不知道
2023/03/14
8300
十一、画图(ggplot2、ggpubr)及图片保存
Q1 :能不能自行指定映射的具体颜色? 想要自行指定映射的颜色,就必须要有映射。
叮当猫DDM
2023/02/09
2.3K0
R绘图-ggplot2(1)
small <- diamonds[sample(nrow(diamonds), 1000), ]
生信补给站
2020/08/06
1.1K0
R语言入门(二)之ggplot作图
group1 = rep(gl(2, 5, labels = c("a", "b")), 2),
生信real
2020/08/26
2.9K0
R语言入门(二)之ggplot作图
R语言绘制靓图--ggthemr、gsci、tvthemes主题包
一般情况下只会设置palette 参数,指定主题名称,其它均为默认参数【即每种主题的字号,磅值,图边距等均可修改】。
拴小林
2021/10/11
6200
R语言绘制靓图--ggthemr、gsci、tvthemes主题包
56-R可视化-5-ggplot2基石三部曲之基础二
几何对象的本质,也就是画面上的不同图层。当我们通过 ggplot(data=example) 后,便相当于设定了默认的ggplot2 设定的背景图层,接着依靠 +geom_point() , +geom_bar() 等等,便可以实现图层的添加。
北野茶缸子
2021/12/17
2K0
56-R可视化-5-ggplot2基石三部曲之基础二
生信技能树学习笔记-day6
---title: "生信技能树学习笔记"author: "天空"引用自生信技能树date: "2023-01-04"output: html_document---R语言作图1. 常用可视化R包图片2. R基础包、ggplot2和ggpubr之间的绘图差别图片图片#作图分三类#1.基础包 略显陈旧 了解一下plot(iris[,1],iris[,3],col = iris[,5]) text(6.5,4, labels = 'hello')图片# dev.off() #关闭画板#2.ggplot2 中坚力
天空UP
2023/01/04
5890
跟小洁老师学习R语言的第六天
图片 图片 常用可视化R包 作图 base ggplot2(特殊语法:列名不带引号,行末写加号) 图片 颜色:color 大小:size 形状:shape 图片 透明度:alpha 填充颜色:fill(既有边框又有内心的,才需要color和fill两个参数) 映射和手动设置的区别 图片 自行指定映射的具体颜色 ggplot(data = iris)+ geom_point(mapping = aes(x = Sepal.Length, y = Pe
贝诺酯
2023/03/16
5420
(数据科学学习手札38)ggplot2基本图形简述
  上一篇中我们介绍了ggplot2的基本语法规则,为了生成各种复杂的叠加图层,需要了解ggplot2中一些基本的几何图形的构造规则,本文便就常见的基础几何图形进行说明;
Feffery
2018/05/30
5.3K10
R科研绘图调色板—ggsci
ggsci是R中的一个包,提供了一系列颜色给ggplot2调色。里面包括了一些知名杂志期刊或者软件(甚至是知名科幻电影、动画等)的经典配色风格,对于科研绘图是相当有帮助的。下边介绍一下这个包的用法和内容,如果想要自己查看说明,可以在R中输入
生信编程日常
2020/04/01
1.7K0
R科研绘图调色板—ggsci
R语言作图基础20230206
ps:高级绘图函数是指可以绘制出一张图,而低价绘图函数是指在图中添加的“零部件”,低级绘图函数必须在高级绘图函数的基础上才能绘制,二者都是base包的内容
顾卿岚
2023/02/08
1.5K0
相关推荐
[R语言]数据可视化的最佳解决方案:ggplot2
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档