您好,欢迎来到汇智旅游网。
搜索
您的当前位置:首页vue-cli结合Element-ui基于cropper.js封装vue实现图片裁剪组件功能

vue-cli结合Element-ui基于cropper.js封装vue实现图片裁剪组件功能

来源:汇智旅游网

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

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

话不多说,看步骤吧。

第一步:准备开发环境

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

执行命令:

  npm  install --save-dev jquery cropper

 为webpack配置添加jquery的映射

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

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

index.vue内容:

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

 template:

<template>
 <div class="modal-dialog modal-lg" :id="id">
 <div class="modal-content">
 <form class="avatar-form" enctype="multipart/form-data" method="post">
 <div class="modal-header">
 </div>
 <div class="modal-body">
 <div class="avatar-body">
 <!-- Upload image and data -->
 <div 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">
 </div>
 <!-- Crop and preview -->
 <el-row>
 <el-col :span="18">
 <div class="avatar-wrapper"></div>
 </el-col>
 <el-col :span="6" style="overflow: hidden;">
 <div style="padding-left: 10px">
 <div class="avatar-preview preview-lg" ></div>
 <div class="avatar-preview avatar-preview-round preview-md"></div>
 <!--<div class="avatar-preview preview-sm"></div>-->
 </div>
 </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>
 </div>
 </div>
 </form>
 </div>
 </div>
</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;base,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: 3px;
 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>
 <div 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>
 </div>

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
 }
 }
 }

本文结合element-ui,vue-cli,jquery,cropper.js,实现裁图组件的封装,先写到这啦,如果对你有帮助,还请点个赞噢!

Copyright © 2019- hzar.cn 版权所有 赣ICP备2024042791号-5

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务