欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  web前端

使用vue-cli结合Element-ui在cropper.js基础上封装vue来实现图片裁剪组件功能

程序员文章站 2022-04-25 10:03:20
...
这篇文章主要介绍了vue-cli结合Element-ui基于cropper.js封装vue实现图片裁剪组件功能,本文图文并茂给大家介绍的非常详细,需要的朋友可以参考下

前端工作中,经常需要图片裁剪的场景,cropper.js是一款优秀的前端插件,api十分丰富。

本文是在vue-cli项目下封装图片裁剪插件,效果图如下:

使用vue-cli结合Element-ui在cropper.js基础上封装vue来实现图片裁剪组件功能

话不多说,看步骤吧。

第一步:准备开发环境

cropper.js是基于jquery的,所以要先安装jquery

执行命令:

npm install --save-dev jquery cropper

为webpack配置添加jquery的映射

修改webpack.base.conf.js配置,添加标红的一行

使用vue-cli结合Element-ui在cropper.js基础上封装vue来实现图片裁剪组件功能

第二步:新建图片裁剪组件

使用vue-cli结合Element-ui在cropper.js基础上封装vue来实现图片裁剪组件功能

index.vue内容:

由于用了element-ui,其中布局就引用了element-ui的组件

template:

<template>
 <p class="modal-dialog modal-lg" :id="id">
 <p class="modal-content">
  <form class="avatar-form" enctype="multipart/form-data" method="post">
  <p class="modal-header">
  </p>
  <p class="modal-body">
   <p class="avatar-body">
   <!-- Upload image and data -->
   <p class="avatar-upload">
    <input type="hidden" class="avatar-src" name="avatar_src">
    <input type="hidden" class="avatar-data" name="ci">
    <label for="avatarInput" class="el-button el-button--primary">选择图片</label>
    <input type="file" class="avatar-input " style="visibility: hidden" id="avatarInput" name="file">
   </p>
   <!-- Crop and preview -->
   <el-row>
    <el-col :span="18">
    <p class="avatar-wrapper"></p>
    </el-col>
    <el-col :span="6" style="overflow: hidden;">
    <p style="padding-left: 10px">
     <p class="avatar-preview preview-lg" ></p>
     <p class="avatar-preview avatar-preview-round preview-md"></p>
    <!--<p class="avatar-preview preview-sm"></p>-->
    </p>
    </el-col>
   </el-row>
   <el-row class="avatar-btns">
    <el-col :span="18">
    <el-button-group>
     <button type="primary" class="el-button el-button--primary" data-method="rotate" data-option="-180" title="Rotate -180 degrees">-180deg</button>
     <button type="primary" class="el-button el-button--primary" data-method="rotate" data-option="-90" title="Rotate -90 degrees">-90deg</button>
     <button type="primary" class="el-button el-button--primary" data-method="rotate" data-option="-45" title="Rotate -45 degrees">-45deg</button>
     <button type="primary" class="el-button el-button--primary" data-method="rotate" data-option="45" title="Rotate 45 degrees">45deg</button>
     <button type="primary" class="el-button el-button--primary" data-method="rotate" data-option="90" title="Rotate 90 degrees">90deg</button>
     <button type="primary" class="el-button el-button--primary" data-method="rotate" data-option="180" title="Rotate 180 degrees">180deg</button>
    </el-button-group>
    </el-col>
    <el-col :span="6"></el-col>
   </el-row>
   <el-row>
    <!--<button type="submit" class="btn btn-primary btn-block avatar-save">裁取</button>-->
   </el-row>
   </p>
  </p>
  </form>
 </p>
 </p>
</template>

style:

<style rel="stylesheet/scss" lang='scss' scoped>
 /*@import "cropper/dist/cropper.css";*/
 /*!
 * Cropper v3.1.3
 * https://github.com/fengyuanchen/cropper
 *
 * Copyright (c) 2014-2017 Chen Fengyuan
 * Released under the MIT license
 *
 * Date: 2017-10-21T10:03:37.133Z
 */
 .avatar-wrapper{
 width: 100%;
 height: 100%;
 overflow: hidden;
 }
 .cropper-container {
 direction: ltr;
 font-size: 0;
 line-height: 0;
 position: relative;
 -ms-touch-action: none;
 touch-action: none;
 -webkit-user-select: none;
 -moz-user-select: none;
 -ms-user-select: none;
 user-select: none;
 }
 .cropper-container img {/*Avoid margin top issue (Occur only when margin-top <= -height)
 */
 display: block;
 height: 100%;
 image-orientation: 0deg;
 max-height: none !important;
 max-width: none !important;
 min-height: 0 !important;
 min-width: 0 !important;
 width: 100%;
 }
 .cropper-wrap-box,
 .cropper-canvas,
 .cropper-drag-box,
 .cropper-crop-box,
 .cropper-modal {
 bottom: 0;
 left: 0;
 position: absolute;
 right: 0;
 top: 0;
 }
 .cropper-wrap-box,
 .cropper-canvas {
 overflow: hidden;
 }
 .cropper-drag-box {
 background-color: #fff;
 opacity: 0;
 }
 .cropper-modal {
 background-color: #000;
 opacity: .5;
 }
 .cropper-view-box {
 display: block;
 height: 100%;
 outline-color: rgba(51, 153, 255, 0.75);
 outline: 1px solid #39f;
 overflow: hidden;
 width: 100%;
 }
 .cropper-dashed {
 border: 0 dashed #eee;
 display: block;
 opacity: .5;
 position: absolute;
 }
 .cropper-dashed.dashed-h {
 border-bottom-width: 1px;
 border-top-width: 1px;
 height: 33.33333%;
 left: 0;
 top: 33.33333%;
 width: 100%;
 }
 .cropper-dashed.dashed-v {
 border-left-width: 1px;
 border-right-width: 1px;
 height: 100%;
 left: 33.33333%;
 top: 0;
 width: 33.33333%;
 }
 .cropper-center {
 display: block;
 height: 0;
 left: 50%;
 opacity: .75;
 position: absolute;
 top: 50%;
 width: 0;
 }
 .cropper-center:before,
 .cropper-center:after {
 background-color: #eee;
 content: ' ';
 display: block;
 position: absolute;
 }
 .cropper-center:before {
 height: 1px;
 left: -3px;
 top: 0;
 width: 7px;
 }
 .cropper-center:after {
 height: 7px;
 left: 0;
 top: -3px;
 width: 1px;
 }
 .cropper-face,
 .cropper-line,
 .cropper-point {
 display: block;
 height: 100%;
 opacity: .1;
 position: absolute;
 width: 100%;
 }
 .cropper-face {
 background-color: #fff;
 left: 0;
 top: 0;
 }
 .cropper-line {
 background-color: #39f;
 }
 .cropper-line.line-e {
 cursor: e-resize;
 right: -3px;
 top: 0;
 width: 5px;
 }
 .cropper-line.line-n {
 cursor: n-resize;
 height: 5px;
 left: 0;
 top: -3px;
 }
 .cropper-line.line-w {
 cursor: w-resize;
 left: -3px;
 top: 0;
 width: 5px;
 }
 .cropper-line.line-s {
 bottom: -3px;
 cursor: s-resize;
 height: 5px;
 left: 0;
 }
 .cropper-point {
 background-color: #39f;
 height: 5px;
 opacity: .75;
 width: 5px;
 }
 .cropper-point.point-e {
 cursor: e-resize;
 margin-top: -3px;
 right: -3px;
 top: 50%;
 }
 .cropper-point.point-n {
 cursor: n-resize;
 left: 50%;
 margin-left: -3px;
 top: -3px;
 }
 .cropper-point.point-w {
 cursor: w-resize;
 left: -3px;
 margin-top: -3px;
 top: 50%;
 }
 .cropper-point.point-s {
 bottom: -3px;
 cursor: s-resize;
 left: 50%;
 margin-left: -3px;
 }
 .cropper-point.point-ne {
 cursor: ne-resize;
 right: -3px;
 top: -3px;
 }
 .cropper-point.point-nw {
 cursor: nw-resize;
 left: -3px;
 top: -3px;
 }
 .cropper-point.point-sw {
 bottom: -3px;
 cursor: sw-resize;
 left: -3px;
 }
 .cropper-point.point-se {
 bottom: -3px;
 cursor: se-resize;
 height: 20px;
 opacity: 1;
 right: -3px;
 width: 20px;
 }
 @media (min-width: 768px) {
 .cropper-point.point-se {
  height: 15px;
  width: 15px;
 }
 }
 @media (min-width: 992px) {
 .cropper-point.point-se {
  height: 10px;
  width: 10px;
 }
 }
 @media (min-width: 1200px) {
 .cropper-point.point-se {
  height: 5px;
  opacity: .75;
  width: 5px;
 }
 }
 .cropper-point.point-se:before {
 background-color: #39f;
 bottom: -50%;
 content: ' ';
 display: block;
 height: 200%;
 opacity: 0;
 position: absolute;
 right: -50%;
 width: 200%;
 }
 .cropper-invisible {
 opacity: 0;
 }
 .cropper-bg {
 background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAAA3NCSVQICAjb4U/gAAAABlBMVEXMzMz////TjRV2AAAACXBIWXMAAArrAAAK6wGCiw1aAAAAHHRFWHRTb2Z0d2FyZQBBZG9iZSBGaXJld29ya3MgQ1M26LyyjAAAABFJREFUCJlj+M/AgBVhF/0PAH6/D/HkDxOGAAAAAElFTkSuQmCC');
 }
 .cropper-hide {
 display: block;
 height: 0;
 position: absolute;
 width: 0;
 }
 .cropper-hidden {
 display: none !important;
 }
 .cropper-move {
 cursor: move;
 }
 .cropper-crop {
 cursor: crosshair;
 }
 .cropper-disabled .cropper-drag-box,
 .cropper-disabled .cropper-face,
 .cropper-disabled .cropper-line,
 .cropper-disabled .cropper-point {
 cursor: not-allowed;
 }
 .avatar-view {
 display: block;
 margin: 15% auto 5%;
 height: 220px;
 width: 220px;
 border: 3px solid #fff;
 border-radius: 5px;
 box-shadow: 0 0 5px rgba(0,0,0,.15);
 cursor: pointer;
 overflow: hidden;
 }
 .avatar-view img {
 width: 100%;
 }
 .avatar-body {
 padding-right: 15px;
 padding-left: 15px;
 }
 .avatar-upload {
 overflow: hidden;
 }
 .avatar-upload label {
 display: block;
 float: left;
 clear: left;
 width: 100px;
 }
 .avatar-upload input {
 display: block;
 margin-left: 110px;
 }
 .avatar-alert {
 margin-top: 10px;
 margin-bottom: 10px;
 }
 .avatar-wrapper {
 height: 364px;
 width: 100%;
 margin-top: 15px;
 box-shadow: inset 0 0 5px rgba(0,0,0,.25);
 background-color: #fcfcfc;
 overflow: hidden;
 }
 .avatar-wrapper img {
 display: block;
 height: auto;
 max-width: 100%;
 }
 .avatar-preview {
 float: left;
 margin-top: 15px;
 margin-right: 15px;
 border: 1px solid #eee;
 border-radius: 4px;
 background-color: #fff;
 overflow: hidden;
 }
 .avatar-preview:hover {
 border-color: #ccf;
 box-shadow: 0 0 5px rgba(0,0,0,.15);
 }
 .avatar-preview img {
 width: 100%;
 }
 .avatar-preview-round{
 border-radius: 50%;
 }
 .preview-lg {
 height: 184px;
 width: 184px;
 margin-top: 15px;
 }
 .preview-md {
 height: 100px;
 width: 100px;
 }
 .preview-sm {
 height: 50px;
 width: 50px;
 }
 @media (min-width: 992px) {
 .avatar-preview {
  float: none;
 }
 }
 .avatar-btns {
 margin-top: 30px;
 margin-bottom: 15px;
 }
 .avatar-btns .btn-group {
 margin-right: 5px;
 }
</style>

script:

<script>
 import $ from 'jquery'
 import 'cropper/dist/cropper.js'
 export default {
 props:{
  id:String
 },
 data(){
  return {
  $container:null,
  $avatarView:null,
  $avatarModal : null,
  $loading : null,
  $avatarForm : null,
  $avatarUpload : null,
  $avatarSrc : null,
  $avatarData : null,
  $avatarInput : null,
  $avatarSave: null,
  $avatarBtns : null,
  $avatarWrapper : null,
  $avatarPreview: null,
  support: {
  fileList: !!$('<input type="file">').prop('files'),
   blobURLs: !!window.URL && URL.createObjectURL,
   formData: !!window.FormData
  }
  }
 },
 created(){},
 mounted(){
  this.$container = $('#'+this.id);
  this.$avatarForm = this.$container.find('.avatar-form');
  this.$avatarUpload = this.$avatarForm.find('.avatar-upload');
  this.$avatarSrc = this.$avatarForm.find('.avatar-src');
  this.$avatarData = this.$avatarForm.find('.avatar-data');
  this.$avatarInput = this.$avatarForm.find('.avatar-input');
  this.$avatarSave = this.$avatarForm.find('.avatar-save');
  this.$avatarWrapper = this.$container.find('.avatar-wrapper');
  this.$avatarPreview = this.$container.find('.avatar-preview');
  this.$avatarBtns = this.$container.find('.avatar-btns');
  this.$nextTick(function () {
   this.init();
  })
 },
 methods:{
  init: function () {
  this.support.datauri = this.support.fileList && this.support.blobURLs;
  this.addListener();
//  this.startCropper();
  },
  addListener: function () {
  this.$avatarInput.on('change', $.proxy(this.change, this));
  this.$avatarForm.on('submit', $.proxy(this.submit, this));
  this.$avatarBtns.on('click', $.proxy(this.rotate, this));
  },
  initPreview: function () {
  var url = this.$avatar.attr('src');
  this.$avatarPreview.html('<img src="' + url + '">');
  },
  initIframe: function () {
  var target = 'upload-iframe-' + (new Date()).getTime();
  var $iframe = $('<iframe>').attr({
   name: target,
   src: ''
  });
  var _this = this;
  // Ready ifrmae
  $iframe.one('load', function () {
   // respond response
   $iframe.on('load', function () {
   var data;
   try {
    data = $(this).contents().find('body').text();
   } catch (e) {
    console.log(e.message);
   }
   if (data) {
    try {
    data = $.parseJSON(data);
    } catch (e) {
    console.log(e.message);
    }
    _this.submitDone(data);
   } else {
   }
   _this.submitEnd();
   });
  });
  this.$iframe = $iframe;
  this.$avatarForm.attr('target', target).after($iframe.hide());
  },
  click:function () {
  this.initPreview();
  },
  change: function () {
  var files;
  var file;
  if (this.support.datauri) {
   files = this.$avatarInput.prop('files');
   if (files.length > 0) {
   file = files[0];
   if (this.isImageFile(file)) {
    if (this.url) {
    URL.revokeObjectURL(this.url); // Revoke the old one
    }
    this.url = URL.createObjectURL(file);
    this.startCropper();
   }
   }
  } else {
   file = this.$avatarInput.val();
   if (this.isImageFile(file)) {
   this.syncUpload();
   }
  }
  },
  //裁剪提交
  submit: function () {
  if (!this.$avatarSrc.val() && !this.$avatarInput.val()) {
   return false;
  }
  if (this.support.formData) {
   this.ajaxUpload();
   return false;
  }
  },  //旋转事件
  rotate: function (e) {
  var data;
  if (this.active) {
   data = $(e.target).data();
   if (data.method) {
   this.$img.cropper(data.method, data.option);
   }
  }
  },
  isImageFile: function (file) {
  if (file.type) {
   return /^image\/\w+$/.test(file.type);
  } else {
   return /\.(jpg|jpeg|png|gif)$/.test(file);
  }
  },
  startCropper: function () {
  var _this = this;
  if (this.active) {
   this.$img.cropper('replace', this.url);
  } else {
   this.$img = $('<img src="' + this.url + '">');
   this.$avatarWrapper.empty().html(this.$img);
   this.$img.cropper({
   viewMode:1,
   aspectRatio: 1,
   preview: this.$avatarPreview,
   restore:false,
   crop: function (e) {
    var json = [
    '{"x":' + e.x,
    '"y":' + e.y,
    '"height":' + e.height,
    '"width":' + e.width,
    '"rotate":' + e.rotate + '}'
    ].join();
    //裁图参数存起来
    _this.$avatarData.val(json);
   }
   });
   this.active = true;
  }
  },
  stopCropper: function () {
  if (this.active) {
   this.$img.cropper('destroy');
   this.$img.remove();
   this.active = false;
  }
  },
  ajaxUpload: function () {
  var url = '/oss/file/cropping';
  var data = new FormData(this.$avatarForm[0]);
  var _this = this;
  $.ajax(url, {
   type: 'post',
   data: data,
   dataType: 'json',
   processData: false,
   contentType: false,
   success: function (data,textStatus) {
   _this.submitDone(data);
   if(data.success){
    //将返回的数据传给父组件
    _this.$emit('cropper-success',data.data);
    _this.cropDone();
   }
   },
  });
  },
  syncUpload: function () {
  this.$avatarSave.click();
  },
  submitDone: function (data) {
  if ($.isPlainObject(data) && data.state === 200) {
   if (data.result) {
   this.url = data.result;
   if (this.support.datauri || this.uploaded) {
    this.uploaded = false;
    this.cropDone();
   } else {
    this.uploaded = true;
    this.$avatarSrc.val(this.url);
    this.startCropper();
   }
   this.$avatarInput.val('');
   } else if (data.message) {
   }
  } else {
  }
  },
  cropDone: function () {
//  this.$avatarForm.get(0).reset();
//  this.$avatarSrc.prop('src', this.url);
   this.stopCropper();
//  this.$container.hide();
  }
 }
 }
</script>

第三步:父组件引用子组件

用了element-ui中的 el-dialog组件,此时el-dialog组件为父组件

在父组件中引入子组件

import cropper from '@/components/Cropper/index'

template:

<template>
 <p class="app-main-content" >
 <el-dialog :visible.sync="showCropper" title="封面裁图" width="70%">
  <cropper id="avatarCrop" ref="cropper" @cropper-success="cropperSuccessHandle"></cropper>
  <span slot="footer" class="dialog-footer">
  <el-button @click="cancelCropper">取 消</el-button>
  <el-button type="primary" @click="toCropper">确 定</el-button>
  </span>
 </el-dialog>
 </p>

script:

import cropper from '@/components/Cropper/index'
export default {
 name: 'addNews',
 components:{
  cropper
 },
 data(){
  return {
  avatarUrl2: null,
  showCropper:false
  }
 },
 methods:{
  //隐藏裁剪框
  cancelCropper(){
  this.showCropper = false
  this.$refs.cropper.cropDone();
  },
  //父组件调用子组件裁剪方法
  toCropper(){
   this.$refs.cropper.submit();
  },
  //子组件裁剪方法成功执行后与父组件通信
  cropperSuccessHandle(data){
   //返回data
  this.showCropper = false
  this.avatarUrl2 = data.url
  }
 }
 }

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

解决Vue 通过下表修改数组,页面不渲染的问题

Vue.js实现图片的随意拖动方法

JS字符串去除连续或全部重复字符的实例

以上就是使用vue-cli结合Element-ui在cropper.js基础上封装vue来实现图片裁剪组件功能的详细内容,更多请关注其它相关文章!