Vue allows image cropping to zoom in, zoom out, and rotate at the same time.
This article describes how to resize, zoom out, and rotate a cropped image by using vue. The details are as follows:
Effect:
- Crop images in a specified area
- Rotating Images
- Enlarge image
- Output bolb format data to formData object
General principle:
Use the h5 FileReader object to obtain the <input type = "file"/> "file uploaded to the browser". The file format is base64, and base64 is assigned to the canvas context.
Add a pair (mousedown) Listening event to the canvas element. When you press the left mouse button on the canvas:
- Mount the window object mousemove event ---> get the distance between x and y to move the image position in the canvas.
- Mount the window object mouseup event to clear the binding of the mousemove event. (This event will be deleted after being triggered)
The remaining zoom-in, zoom-out, and rotation operations on the canvas object/coordinate system. For details about the api, see the mdn canvas document.
Code
Dom. js
Export const on = ({el, type, fn}) =>{ if (typeof window) {if (window. addEventListener) {el. addEventListener (type, fn, false)} else {el. attachEvent ('on $ {type} ', fn) }}export const off = ({el, type, fn}) =>{ if (typeof window) {if (window. addEventListener) {el. removeEventListener (type, fn)} else {el. detachEvent ('on $ {type} ', fn) }}export const once = ({el, type, fn}) =>{ const hyFn = (Event) =>{ try {fn (event)} finally {off ({el, type, fn: hyFn}) }}on ({el, type, fn: hyFn})} // The Last export const fbTwice = ({fn, time = 300}) ==>{ let [cTime, k] = [null, null] // get the current time const getTime = () => new Date (). getTime () // mixed function const hyFn = () => {const ags = argments return () => {clearTimeout (k) k = cTime = null fn (... ags) }} return () =>{ if (cTime = null) {k = setTimeo Ut (hyFn (... arguments), time) cTime = getTime ()} else {if (getTime ()-cTime <0) {// clear the previous function heap ---- record clearTimeout (k) k = null cTime = getTime () k = setTimeout (hyFn (... arguments), time) }}} export const contains = function (parentNode, childNode) {if (parentNode. contains) {return parentNode! = ChildNode & parentNode. contains (childNode)} else {return !! (ParentNode. compareDocumentPosition (childNode) & 16)} export const addClass = function (el, className) {if (typeof el! = "Object") {console. log ('El is not elem ') return null} let classList = el ['classname'] classList = ''? []: ClassList. split (/\ s +/) if (classList. indexOf (className) ===- 1) {classList. push (className) el. className = classList. join ('')} else {console. warn ('warn className current')} export const removeClass = function (el, className) {let classList = el ['classname'] classList = classList ==== ''? []: ClassList. split (/\ s +/) classList = classList. filter (item => {return item! = ClassName}) el. className = classList. join ('')} export const delay = ({fn, time}) =>{ let oT = null let k = null return () ==>{ // current time let cT = new Date (). getTime () const fixFn = () => {k = oT = null fn ()} if (k = null) {oT = cT k = setTimeout (fixFn, time) return} if (cT-oT <time) {oT = cT clearTimeout (k) k = setTimeout (fixFn, time) }} export const Event = function () {// type th Is. typeList ={}} Event. prototype. on = function ({type, fn}) {if (this. typeList. hasOwnProperty (type) {this. typeList [type]. push (fn)} else {this. typeList [type] = [] this. typeList [type]. push (fn)} Event. prototype. off = function ({type, fn}) {if (this. typeList. hasOwnProperty (type) {let list = this. typeList [type] let index = list. indexOf (fn) if (index! =-1) {list. splice (index, 1) }}else {console. warn ('not has this type')} Event. prototype. once = function ({type, fn}) {const fixFn = () => {fn () this. off ({type, fn: fixFn})} this. on ({type, fn: fixFn})} Event. prototype. trigger = function (type) {if (this. typeList. hasOwnProperty (type) {this. typeList [type]. forEach (fn => {fn ()})}}
Component template
<Template> <div class = "jc-clip-image": style = "{width: '$ {clip. width} '} "> <canvas ref =" ctx ": width =" clip. width ": height =" clip. height "@ mousedown =" handleClip ($ event) "> </canvas> <input type =" file "ref =" file "@ change =" readFileMsg ($ event) "> <div class =" clip-scale-btn "> <a class =" add "@ click =" handleScale (false) "> + </a> <a @ click =" rotate "class =" right-rotate "> conversion </a> <a class =" poor "@ click =" handleScale (tru E) ">-</a> <span >{{ scale }}</span> </div> <div class =" upload-warp "> <a class =" upload- btn "@ click =" dispatchUpload ($ event) "> upload </a> <a class =" upload-cancel "> cancel </a> </div> <div class =" create-canvas "> <a class = "to-send-file" @ click = "outFile" title = "Open the console"> Generate a file </a> </div> </template> <script> import {on, off, once} from '.. /.. /utils/dom 'export default {ctx: null, file: null, x: 0, // click can Vas x mouse address y: 0, // click canvas y mouse address xV: 0, // move the mouse x to yV: 0, // move the mouse y to nX: 0, // Original Coordinate point image x nY: 0, // Original Coordinate point image y img: null, props: {src: {type: String, default: null}, clip: {type: Object, default () {return {width: '200px ', height: '200px' }}}, data () {return {isShow: false, base64: null, scale: 1.5, // zoom in ratio deg: 0 // Rotation Angle }}, computed: {width () {const {clip} = this return parseFloa T (clip. width. replace ('px ', '')}, height () {const {clip} = this return parseFloat (clip. height. replace ('px ', '') }}, mounted () {const {$ options, $ refs, width, height} = this // initialize the canvas file nX nY Object. assign ($ options, {ctx: $ refs. ctx. getContext ('2d '), file: $ refs. file, nX:-width/2, nY:-height/2})}, methods: {// rotation operation rotate () {const {$ options, draw} = this. deg = (this.de G + Math. PI/2) % (Math. PI * 2) draw ($ options. img, $ options. nX + $ options. xV, $ options. nY + $ options. yV, this. scale, this. deg)}, // handle the enlarged handleScale (flag) {const {$ options, draw, deg} = this flag & this. scale> 0.1 & (this. scale = this. scaling-0.1 )! Flag & this. scale <1.9 & (this. scale = this. scale + 0.1) $ options. img & draw ($ options. img, $ options. nX + $ options. xV, $ options. nY + $ options. yV, this. scale, deg)}, // simulate the file Click Event dispatchUpload (e) {this. clearState () const {file} = this. $ options e. preventDefault () file. click ()}, // read the input file information readFileMsg () {const {file} = this. $ options const {draw, createImage, $ options: {nX, nY}, SC Ale, deg} = this const wFile = file. files [0] const reader = new FileReader () reader. onload = (e) => {const img = createImage(e.tar get. result, (img) =>{ draw (img, nX, nY, scale, deg)}) file. value = null} reader. readAsDataURL (wFile)}, // generate the Image createImage (src, cb) {const img = new Image () this. $ el. append (img) img. className = 'base64-den 'img. onload = () => {cb (img)} img. src = src this. $ o Ptions. img = img}, // operation canvas drawing draw (img, x = 0, y = 0, scale = 0.5, deg = Math. PI) {const {ctx} = this. $ options let {width, height} = this // image size let imgW = img. offsetWidth let imgH = img. offsetHeight ctx. save () ctx. clearRect (0, 0, width, height) ctx. translate (width/2, height/2, img) ctx. rotate (deg) ctx. drawImage (img, x, y, imgW * scale, imgH * scale) ctx. restore ()},//... event binding handleCli P (e) {const {handleMove, $ options, deg} = this if (! $ Options. img) {return} Object. assign (this. $ options, {x: e. screenX, y: e. screenY}) on ({el: window, type: 'mousemove ', fn: handleMove}) once ({el: window, type: 'mouseup', fn: (e) ==>{ console. log ('lowdown ') switch (deg) {case 0: {Object. assign ($ options, {nX: $ options. nX + $ options. xV, nY: $ options. nY + $ options. yV, xV: 0, yV: 0}) break;} case Math. PI/2: {Object. assign ($ options, {nX: $ options. nY + $ options. yV, nY: $ options. nX-$ options. xV, xV: 0, yV: 0}) break;} case Math. PI: {Object. assign ($ options, {nX: $ options. nX-$ options. xV, nY: $ options. nY-$ options. yV, xV: 0, yV: 0}) break;} default: {// $ options. nY-$ options. yV, $ options. nX + $ options. xV Object. assign ($ options, {nX: $ options. nY-$ options. yV, nY: $ options. nX + $ options. xV, xV: 0, yV: 0}) }}off ({el: window, type: 'mousemove ', fn: handleMove })}})}, //... handle mouse movement handleMove (e) {e. preventDefault () e. stopPropagation () const {$ options, draw, scale, deg} = this Object. assign ($ options, {xV: e. screenX-$ options. x, yV: e. screenY-$ options. y}) switch (deg) {case 0: {draw ($ options. img, $ options. nX + $ options. xV, $ options. nY + $ options. yV, scale, deg) break;} case Math. PI/2: {draw ($ options. img, $ options. nY + $ options. yV, $ options. nX-$ options. xV, scale, deg) break;} case Math. PI: {draw ($ options. img, $ options. nX-$ options. xV, $ options. nY-$ options. yV, scale, deg) break;} default: {draw ($ options. img, $ options. nY-$ options. yV, $ options. nX + $ options. xV, scale, deg) break ;}}, // clear the clearState () {const {$ options, width, height }= this if ($ options. img) {this. $ el. removeChild ($ options. img) Object. assign ($ options, {x: 0, y: 0, xV: 0, yV: 0, nX:-width/2, nY:-height/2, img: null,}) }}, // output file outFile () {const {$ refs: {ctx }}= this console. log (ctx. toDataURL () ctx. toBlob (blob) => {console. log (blob)}) }}</script> <style> @ component-namespace jc {@ component clip-image {position: relative; width: 100%; canvas {position: relative; width: 100%; height: 100%; cursor: pointer; box-shadow: 0 0 3px #333;} input {display: none ;}. base64-hidden {position: absolute; top: 0; left: 0; display: block; width: 100%; height: auto; z-index:-999; opacity: 0 ;}. clip-scale-btn {position: relative; @ utils-clearfix; margin-bottom: 5px; text-align: center; a {float: left; width: 20px; height: 20px; border-radius: 50%; color: # fff; background: # 49a9ee; text-align: center; cursor: pointer;} &>. poor, &>. right-rotate {float: right ;}&> span {position: absolute; z-index:-9; top: 0; left: 0; display: block; position: relative; width: 100%; text-align: center; height: 20px; line-height: 20px ;}}. upload-warp {@ utils-clearfix ;. upload-btn ,. upload-cancel {float: left; display: inline-block; width: 60px; height: 25px; line-height: 25px; color: # fff; border-radius: 5px; background: # 49a9ee; box-shadow: 0 0 0 #333; text-align: center; top: 0; left: 0; right: 0; bottom: 0; margin: auto; cursor: pointer; margin-top: 5px ;}. upload-cancel {background: gray; float: right ;}}. to-send-file {margin-top: 5px; display: block; width: 50px; height: 25px; line-height: 25px; color: # fff; border-radius: 5px; background: # 49a9ee; cursor: pointer ;}}
Code: https://github.com/L6zt/vuesrr
The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.