Vue allows image cropping to zoom in, zoom out, and rotate at the same time.

Source: Internet
Author: User
Tags hasownproperty

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:

  1. Crop images in a specified area
  2. Rotating Images
  3. Enlarge image
  4. 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:

  1. Mount the window object mousemove event ---> get the distance between x and y to move the image position in the canvas.
  2. 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.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.