Hand on hand to teach you to write vUE cutting preview components

Vue version cutting tool, including preview function

Qiuyaofan.github. IO/vue-corp-de…

Source code address: github.com/qiuyaofan/v…

Step 1: Install the scaffolding with VUe-CLI firstVue – cli’s official website)

// initialize vue-cli vue init webpack my-pluginCopy the code

Step 2: Create the file

New SRC/views/validSlideDemo vue, new VueCrop/SRC/components in the index, js, VueCrop. Vue, the routes/index, js configuration access routing (specific see lot source)Copy the code

The resulting file structure is shown as follows:

Cutting plug-in file structure diagram

Step 3: Register the component

1. Reference all plug-ins: SRC /components/index.js
// Import the plug-in import VueCrop from'./VueCrop/index.js'
const install = function(Vue, opts = {}) {/* Skip */ if installedif (install.installed) returnVue.component(vuecrop. name, VueCrop)} // Register the plug-in globallyif(typeof window ! = ='undefined' && window.Vue) {
  install(window.Vue)
}

export{install, // VueCrop can be removed if main.js is used globally.}Copy the code
2. Global call plugin: SRC /main.js (Vue plugins official documentation explains install)
import Vue from 'vue'
import App from './App'
import router from './router'// add: import import {install} from'src/components/index.js'// Global call, equivalent to calling 'myplugin. install(Vue)' vue.use (install) vue.config.productionTip =false
/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})
Copy the code
3. VueCrop entry file call VueCrop. Vue: SRC/components/VueCrop/index. Js
// Import vue import VueCrop from'./VueCrop.vue'// Vue.js plug-ins should have a public method install. The first argument to this method is the Vue constructor vuecrop.install =functionVue.component(vuecrop.name, VueCrop)}export default VueCrop
Copy the code
Myplugin. install is a method of vue. It is not. It is a public method that we use to construct plugins, which can be interpreted as a method of constructors in native JS:
function MyPlugin(){
  console.info('Constructor')
}
MyPlugin.prototype.install=function(vue,options){
	console.info('Constructor vue:'+vue);
}
Copy the code

Vue.com Ponent ()

So, the process of registering a VUE plug-in is:

1. Call import {install} from in main.js'src/components/index.js'Vue.use (install) 2.index.js Add the install method and call Vue.component to register component 3. Index.js in a component is the same as index.js in all componentsCopy the code

Step 4: Design and develop your own components and build the component structure

Before this, you can first understand the component naming specifications, etc., can refer to the article nuggets: Vue front-end development specifications, which is explained in detail in point 2

First, determine how you call and which parameters you want to expose

<vue-crop
:crop-url="cropUrl1"
:ratio="ratio"
:height="460"
:width="460"
:previewJson="previewJson1"
class="c-crop--preview_right"
@afterCrop="afterCrop"
>
>
Copy the code

Where @afterCrop=”afterCrop” is the clipped callback function, the other is the property configuration

Components in the SRC/components/VueCrop/VueCrop vue, can use this. $emit (‘ afterCrop) trigger afterCrop events in the demo

Component structure is mainly divided into: Cutting the main body part (VueCrop. Vue), marquee components (VueCropTool. Vue), cutting width of box, coordinates calculation (VueCropMove. Js), drag and drop event registration public js (components/utils/draggable. Js)

Current cutting plug-in general idea
  1. The cutting body of the cutting plug-in is composed of picture, marquee and preview structure
  2. The marquee (vuecroptool.vue) is responsible for dragging and dropping to change its size, coordinate position and so on and returning it to vuecrop.vue
  3. Crop –preview (c-crop–preview)
  4. Main trigger calls page (VueCropDemo. Vue) afterCrop events, after returning to pass parameters to cutting url, left, top, bottom, right, x, y, w, h, etc

Note: This component does not have the actual crop function, the final crop is passed to the background, you can extend the afterCrop function based on the coordinates and other information

Next we’ll take a look at each component and JS

1. Draggabable. Js is a reference to element, modified part of the source code is as follows
export default function (element, options) {
  const moveFn = function (event) {
    if (options.drag) {
      options.drag(event)
    }
  }
  // mousedown fn
  const downFn = function (event) {
    ifOptions. start(event)}} // mouseup fn const upFn =function (event) {
    document.removeEventListener('mousemove', moveFn)
    document.removeEventListener('mouseup', upFn)
    document.onselectstart = null
    document.ondragstart = null

    if(options.end) {// Call the end function options.end(event)}} // bind the event element.adDeventListener ('mousedown', event => {
    if (options.stop && options.stop(event, element) === false) {
      return false
    }
    document.onselectstart = function () {
      return false
    }
    document.ondragstart = function () {
      return false
    }
    document.addEventListener('mousedown', downFn)
    document.addEventListener('mousemove', moveFn)
    document.addEventListener('mouseup', upFn)
  })
}

Copy the code
Vuecroptool. vue can be used as follows
draggable(this.$el.querySelector('.c-crop--drap_screen'), {
	start: (event) => {
	  this.startPos = [event.x, event.y]
	},
	drag: (event) => {
	  this.handleDragLocation(event)
	},
	end: (event) => {
	  this.handleDragLocation(event)
	}
})
Copy the code
2. Cut the main part (Vuecrop. vue full source link)
<script> import VueCropTool from'./VueCropTool.vue'
export default {
  name: 'VueCrop'.data () {
    return{// previewImgSize: null, // originImgSize: null, // elWidth: 0, // Top cursorTop: 0, // left cursorLeft: 0, // imgH: 0, imgW: 0, // image url url: this.cropUrl, // To adapt to the current container of the original image scale: 1, // based on the current selection and the original image before scaling, to obtain the final crop size coord: CropJson: {CW: null, ch: null, w: null, h: null, r: null}}}, // The exposed parameter is props: {cropUrl: String, // ratio: {type: null,
      default: false
    },
    width: null,
    height: null,
    coordWidth: null,
    coordHeight: null,
    previewJson: {
      type: Array,
      default: function () {
        return []
      }
    }
  },
  components: {
    VueCropTool
  },
  createdCropUrl (val) {this.url = val //setTimeout is for compatibility when the size cannot be retrieved immediatelysetTimeout(() => { this.setSize() }, 200) } }, methods: {// Update the drag size, DrapSizeUpdate (w, h, t,) is triggered mostly by the Crop box component via @updatesize. {this.elwidth = w this.elheight = h this.cursorTop = t this.cursorLeft = l // GetCoord = this.getCoord(l, t, w) getCoord = this.getCoord(l, t, w) getCoord = this.getCoord(l, t, w) H) // Update the preview size this.setpreviewSize (this.coord)}, // cut backafterCrop () {
      this.$emit('afterCrop', this.coord, this.url)}, // Set the preview sizesetPreviewSize (coord) {
      if(! this.previewJson.length) {return false
      }
      letResult = this.previewjson. map(data => {// calculate the scaling ratiolet scale = data.width / coord.w
        return{ scale, l: -scale * coord.l, t: -scale * coord.t, w: scale * this.originImgSize.w, h: Scale * this.originimgsize.h}}) this.previewimgSize = result}, // Set the size of the image to be trimmed and store the scale value asyncsetSize () {
      if(! this.url) {return
      }
      let imgSize = await this.getSize(this.url)
      this.originImgSize = imgSize
      this.setCoordRange()
      this.scale = imgSize.w / this.imgW
      this.cursorTop = 0
      this.cursorLeft = 0
      letjson = {... This. CropJson} json.w = this.imgW json.h = this.imgH //if (this.ratio) {
        json.r = this.ratio
        if (json.w > json.h) {
          let r = json.h * this.ratio / json.w
          if (r > 1) {
            json.ch = json.h / r
            json.cw = json.ch * this.ratio
          } else {
            json.ch = json.h
            json.cw = json.ch * this.ratio
          }
        } else {
          let r = json.w / this.ratio / json.h
          if (r > 1) {
            json.cw = json.w / r
            json.ch = json.cw / this.ratio
          } else {
            json.cw = json.w
            json.ch = json.cw / this.ratio
          }
        }
      } else{// unproportional json.cw = json.w json.ch = json.h} This. ElWidth = json.cw / 2 this.elHeight = json.ch / 2 this.cursorTop = json.ch / 4 this.cursorLeft = json.cw / 4 this.cropJson = {... json} this.drapSizeUpdate(this.elWidth, this.elHeight, this.cursorTop, this.cursorLeft) }, // Set the current display area image size according to the original image size ratio and the width and height of the size passed in by the usersetCoordRange() { var size = {... this.originImgSize} var ratio1 = this.width / this.height var ratio2 = size.rif (ratio2 > ratio1) {
        this.imgW = this.width
        this.imgH = this.width / size.r
      } else{this.imgH = this.height this.imgW = this.height * size.r}}, getCoord (l, t, w) {this.imgH = this.height this.imgW = this.height * size.r}, h) { l = this.scale * l t = this.scale * t w = this.scale * w h = this.scale * hreturn{ p0: [l, t], p1: [l + w, t], p2: [l + w, t + h], p3: [l, t + h], w: w, h: h, l: l, t: T}}, getSize (SRC) {let _this = this
      let img = this.$el.querySelector('#c-crop--hide_img')
      return new Promise(resolve => {
        if (src && img) {
          img.onload = function () {
            const size = _this.getSizeImg(img)
            resolve(size)
          }
          img.src = src
        } else{resolve({w: 0, h: 0, r: 0})}}, // getSizeImg (img) {let w = img.width
      let h = img.height
      let r = w === 0 && h === 0 ? 0 : w / h
      return {
        w: w,
        h: h,
        r: r
      }
    }

  },
  mounted () {
    this.setSize()
  }

}

</script>


Copy the code
3. Cut the frame part (Vuecroptool. vue full source link)
// add js import draggable from'.. /utils/draggable'Js import movePos from'./VueCropMove'Const dragEle = [const dragEle = [const dragEle = [const dragEle = [const dragEle = [const dragEle = [const dragEle = ['.c-crop--drap_eline'.'.c-crop--drap_sline'.'.c-crop--drap_wline'.'.c-crop--drap_nline'.'.c-crop--drap_e'.'.c-crop--drap_s'.'.c-crop--drap_w'.'.c-crop--drap_n'.'.c-crop--drap_ne'.'.c-crop--drap_se'.'.c-crop--drap_sw'.'.c-crop--drap_nw']

export default {
  data () {
    return{ width: this.elWidth, height: this.elHeight, top: this.cursorTop, left: // Crop: [0, 0], crop: [], // crop: [] StartSize: null}}, props: ['elWidth'.'elHeight'.'cursorTop'.'cursorLeft'.'cropJson'].created() {}, watch: { elWidth (val) { this.width = val }, elHeight (val) { this.height = val }, cursorTop (val) { this.top = val }, CursorLeft (val) {this.left = val}}, methods: {let x = event.clientX
      lety = event.clientY this.left = x - this.startPos[0] + this.left this.top = y - this.startPos[1] + this.top this.startPos = [x, y] this.handlesize () // Update size this.$emit('updateSize', this.width, this.height, this.top, this.left)
      clearTimeout(this.cropTimer)
      // setTimeout is called afterCrop this.cropTimer = for drag to completesetTimeout(() => {// Call callback this.$emit('afterCrop'}, 200)}, // Drag to change position: bind eventdragCallLocation () {
      draggable(this.$el.querySelector('.c-crop--drap_screen'), { start: (event) => { this.startPos = [event.x, event.y] }, drag: (event) => { this.handleDragLocation(event) }, end: (event) => {this.handleDragLocation(event)}})}, // getParentElement (p, className) { const classNames = p.classNameif (classNames.indexOf(className) === -1) {
        p = p.parentNode
        return this.getParentElement(p, className)
      } else {
        returnP}}, // getDragSize (event) {const el = this.$el
      const screen = this.$cropArea.getBoundingClientRect()
      const rect = el.getBoundingClientRect()
      let json = {
        x: event.clientX,
        y: event.clientY,
        t: rect.top,
        b: rect.bottom,
        l: rect.left,
        r: rect.right,
        w: rect.width,
        h: rect.height,
        screen: screen
      }
      json.ratio = json.w / json.h
      returnJson}, // drag to change size handleDrag (event, I) {// get coordinates // console.info('move', i)
      const json = this.getDragSize(event)
      movePos[i](this, json, this.startSize)
      this.handleSize(true)
      this.$emit('updateSize', this.width, this.height, this.top, this.left)
      clearTimeout(this.cropTimer)
      this.cropTimer = setTimeout(() => {// Call callback this.$emit('afterCrop'}, 200)}, // Drag to change size: bind event dragCall (I) {let target = this.$el.querySelector(dragEle[i]) draggable(target, { start: Json this.startSize = this.getDragSize(event)}, drag: (event) => { this.handleDrag(event, i) }, end: HandleSize (isSize) {this.left = range2(this.left, this.left) = range2(this.left, this.left) this.width, this.cropJson.w) this.top = range2(this.top, this.height, this.cropJson.h)if (isSize) {
        let d1 = this.cropJson.w - this.left
        letD2 = this.cropjson. h - this.top // Cut to scaleif (this.cropJson.r) {
          if (d1 < this.width) {
            this.width = d1
            this.height = this.width / this.cropJson.r
          } else if (d2 < this.height) {
            this.height = d2
            this.width = this.height * this.cropJson.r
          }
        } else{// Do not cut to scaleif (d1 < this.width) {
            this.width = d1
          }
          if (d2 < this.height) {
            this.height = d2
          }
        }
      }
    }

  },
  mounted () {
    this.$cropArea = this.getParentElement(this.$el.parentNode, 'c-crop--area') // Initialize drag to change sizefor(var i = 0; i < dragEle.length; I ++) {this.dragCall(I)} // initialize drag to change position this.dragCallLocation()}} // calculate the allowed rangefunction range2 (pos, val, mainW) {
  return pos <= 0 ? 0 : pos > mainW - val ? mainW - val : pos
}

</script>
Copy the code
4. Calculate the js of cutting frame (Vuecropmove.js full source link)
// 12 shapes, four sides, midpoint of side, four corners of side. E: east, w: west, N: north, S: South, NE: southeast const movePos = {0: e, 4: e, 1: s, 5: s, 2: w, 6: w, 3: n, 7: n, 8: NE, 9: se, 10: sw, 11: nw }letWidth, height, result, ratio // Gets the maximum width or height of a form typefunction getMaxSize (json, startJson, dire, type) {
  if (type= = ='w') {
    switch (dire) {
      case 'e':
      case 's':
      case 'n':
      case 'ne':
      case 'se':
        return json.screen.right - json.l
      case 'w':
      case 'nw':
      case 'sw':
        return startJson.r - json.screen.left
    }
  } else if (type= = ='h') {
    switch (dire) {
      case 'n':
      case 'nw':
      case 'ne':
        return startJson.b - json.screen.top
      case 's':
      case 'w':
      case 'e':
      case 'sw':
      case 'se':
        returnJson.screen. bottom-startjson. t}}} // Check whether there is ratio and return the modified sizefunction setRatioSize (type, json, startJson, ratio, width, height) {
  if (ratio) {
    if (width / ratio >= height) {
      var maxHeight = getMaxSize(json, startJson, type.'h')
      height = width / ratio
      if (height > maxHeight) {
        height = maxHeight
        width = height * ratio
      }
    } else {
      var maxWidth = getMaxSize(json, startJson, type.'w')
      width = height * ratio
      if (width > maxWidth) {
        width = maxWidth
        height = width / ratio
      }
    }
  }
  return{width: width, height: height}function e (_this, json, startJson) {
  ratio = _this.cropJson.r
  width = range(getWidth(json, startJson, 'e'), getMaxSize(json, startJson, 'e'.'w'))
  ifHeight = range(width/ratio, getMaxSize(json, startJson,'e'.'h'))
    result = setRatioSize('e', json, startJson, ratio, width, height)
    setSize(_this, result)
  } else {
    _this.width = width
  }
  return_this} // Drag the south side, the width is the same, unless there is a proportional dragfunction s (_this, json, startJson) {
  ratio = _this.cropJson.r
  height = range(getHeight(json, startJson, 's'), getMaxSize(json, startJson, 's'.'h'))
  ifWidth = range(height * ratio, getMaxSize(json, startJson);'s'.'w'))
    result = setRatioSize('s', json, startJson, ratio, width, height)
    setSize(_this, result)
  } else {
    _this.height = height
  }

  return_this} // same as above, and so onfunction w (_this, json, startJson) {
  ratio = _this.cropJson.r
  width = range(getWidth(json, startJson, 'w'), getMaxSize(json, startJson, 'w'.'w'))
  if (ratio) {
    height = range(width / ratio, getMaxSize(json, startJson, 'w'.'h'))
    result = setRatioSize('w', json, startJson, ratio, width, height)
    setSize(_this, result)
    _this.left = getLeft(_this, json, startJson)
  } else {
    _this.width = width
    _this.left = rangeMax(json.x - json.screen.left, startJson.r)
  }
  return _this
}
function n (_this, json, startJson) {
  ratio = _this.cropJson.r
  height = range(getHeight(json, startJson, 'n'), getMaxSize(json, startJson, 'n'.'h'))
  if (ratio) {
    width = range(height * ratio, getMaxSize(json, startJson, 'n'.'w'))
    result = setRatioSize('n', json, startJson, ratio, width, height)
    setSize(_this, result)
    _this.top = getTop(_this, json, startJson)
  } else {
    _this.height = height
    _this.top = rangeMax(json.y - json.screen.top, startJson.b)
  }
  return _this
}

function ne (_this, json, startJson) {
  height = range(getHeight(json, startJson, 'n'), getMaxSize(json, startJson, 'ne'.'h'))
  width = range(getWidth(json, startJson, 'e'), getMaxSize(json, startJson, 'ne'.'w'))
  result = setRatioSize('ne', json, startJson, _this.cropJson.r, width, height)
  setSize(_this, result)
  _this.top = getTop(_this, json, startJson)
  return _this
}
function se (_this, json, startJson) {
  height = range(getHeight(json, startJson, 's'), getMaxSize(json, startJson, 'se'.'h'))
  width = range(getWidth(json, startJson, 'e'), getMaxSize(json, startJson, 'se'.'w'))
  result = setRatioSize('se', json, startJson, _this.cropJson.r, width, height)
  setSize(_this, result)
  return _this
}
function sw (_this, json, startJson) {
  width = range(getWidth(json, startJson, 'w'), getMaxSize(json, startJson, 'sw'.'w'))
  height = range(getHeight(json, startJson, 's'), getMaxSize(json, startJson, 'sw'.'h'))
  result = setRatioSize('sw', json, startJson, _this.cropJson.r, width, height)
  setSize(_this, result)
  _this.left = getLeft(_this, json, startJson)
  return _this
}
function nw (_this, json, startJson) {
  width = range(getWidth(json, startJson, 'w'), getMaxSize(json, startJson, 'nw'.'w'))
  height = range(getHeight(json, startJson, 'n'), getMaxSize(json, startJson, 'nw'.'h'))
  result = setRatioSize('nw', json, startJson, _this.cropJson.r, width, height)
  setSize(_this, result)
  _this.left = getLeft(_this, json, startJson)
  _this.top = getTop(_this, json, startJson)
  return_this} // Match rangefunction range (value, max) {
  value = value > max ? max : value
  returnvalue < 20 ? 20: value} // Maximum valuefunction rangeMax (value, max) {
  return value > max ? max : value
}
// top
function getTop (_this, json, startJson) {
  return rangeMax(startJson.b - _this.height - json.screen.top, startJson.b)
}
// left
function getLeft (_this, json, startJson) {
  returnRangeMax (startJson. R - _this. Width - json. Screen. The left, startJson. R)} / / height: only exists in s | | n typefunction getHeight (json, startJson, type) {
  return type= = ='n'? StartJson. B - json. Y: json. Y - startJson. T} / / width: only exists in w | | e typefunction getWidth (json, startJson, type) {
  return type= = ='w' ? startJson.r - json.x : json.x - startJson.l
}
// setSize
function setSize (_this, result) {
  _this.width = result.width
  _this.height = result.height
}

export default movePos

Copy the code

If you like this plugin, go to Github star