Vue-cli and Element-ui encapsulate vue Based on cropper. js to implement the image cropping component function. vue-clielement-ui
In front-end work, image cropping is often required,cropper.js
Is an excellent front-end plug-in with a wide range of APIs.
This article encapsulates the image cropping plug-in the vue-cli project, as follows:
Let's take a look at the steps.
Step 1: Prepare the development environment
Cropper. js is based on jquery, so you must install jquery first.
Run the following command:
npm install --save-dev jquery cropper
Add jquery ing for webpack Configuration
Modifywebpack.base.conf.js
Configuration, add a red line
Step 2: create an image cropping component
Index. vue content:
Element-ui is used, and the layout references the element-ui component.
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 -- primary"> select an image </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-lg "> </div> <div class =" avatar-preview-round preview-md "> </ div> <! -- <Div class = "avatar-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 -- primary" data-method = "rotate" data -option = "-180" title = "Rotate-180 degrees">-180deg </button> <button type = "primary" class = "el-button -- primary "data-method =" rotate "data-option ="-90 "title =" Rotat E-90 degrees ">-90deg </button> <button type =" primary "class =" el-button -- primary "data-method =" rotate "data- option = "-45" title = "Rotate-45 degrees">-45deg </button> <button type = "primary" class = "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-op Tion = "90" title = "Rotate 90 degrees"> 90deg </button> <button type = "primary" class = "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> <! -- <Button type = "submit" class = "btn-primary btn-block avatar-save"> cut </button> --> </el-row> </div> </div> </form> </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;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. $ av AtarForm. 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. startCropp Er () ;}, 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 ('') ;}, initIframe: function () {var target = 'upload-iframe-'+ (new Da Te ()). 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. submitDo Ne (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 () ;}}, // crop and submit: function () {if (! This. $ avatarSrc. val ()&&! This. $ avatarInput. val () {return false;} if (this. support. formData) {this. ajaxUpload (); return false ;}}, // rotation event rotate: function (e) {var data; if (this. active) {data = tables (e.tar get ). 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 = $ (''); 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 (); // Save the cut parameter _ this. $ avatarData. val (json) ;}}); this. active = true ;}, stopCropper: function () {if (this. active) {this. $ img. cropper ('deststroy'); 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) {// send the returned data to the parent component _ 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>
Step 3: parent component references child components
The el-dialog component in element-ui is used. The el-dialog component is the parent component.
Introduce child components into Parent Components
import cropper from '@/components/Cropper/index'
Template:
<Template> <div class = "app-main-content"> <el-dialog: visible. sync = "showCropper" title = "cover Cutting Image" width = "70%"> <cropper id = "avatarCrop" ref = "cropper" @ cropper-success = "cropperSuccessHandle"> </cropper> <span slot = "footer" class = "dialog-footer"> <el-button @ click = "cancelCropper"> cancel </el-button> <el- button type = "primary" @ click = "toCropper"> confirm </el-button> </span> </el-dialog> </div>
Script:
Import cropper from '@/components/Cropper/Index' export default {name: 'addnew', components: {cropper}, data () {return {avatarUrl2: null, showCropper: false }}, methods: {// hide the cancelCropper () {this. showCropper = false this. $ refs. cropper. cropDone () ;}, // The parent component calls the toCropper () {this. $ refs. cropper. submit () ;}, // The cropperSuccessHandle (data) {// return data this. showCropper = false this. avatarUrl2 = data. url }}}
This document combines element-ui, vue-cli, jquery, and cropper. js to encapsulate the cut graph component. Write it here first. If it is helpful to you, please give me a thumbs up!