Home Web Front-end JS Tutorial How to use vue to crop images while enlarging, reducing, and rotating them (detailed tutorial)

How to use vue to crop images while enlarging, reducing, and rotating them (detailed tutorial)

Jun 02, 2018 am 09:16 AM
picture accomplish

This article mainly introduces how vue can crop images while enlarging, reducing, and rotating functions. Now I share it with you and give it as a reference.

This article mainly introduces vue to realize cropping pictures while enlarging, reducing, and rotating functions. I would like to share them with you. The details are as follows:

Achievement effect:

  1. Crop the picture within the specified area

  2. Rotate the picture

  3. Enlarge the picture

  4. Output bolb format data is provided to the formData object

Rendering







#General principle:

Use the h5 FileReader object to obtain "File uploaded to the browser", the file format is base64 format, assign base64 to the canvas context.


Then add a (mousedown) listening event to the canvas element. When the user presses the left mouse button on the canvas:

  1. Mount the mousemove event of the window object ---> Get the mouse movement x, y distance to operate the image in the canvas Position moves.

  2. Mount the mouseup event of the window object and clear the binding of the mousemove event. (At the same time, the event will be deleted after it is triggered)

The remaining zoom in, zoom out, and rotation are operations on the canvas object/coordinate system. For details on the specific API, please 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})
  }
  // 最后一个
  export const fbTwice = ({fn, time = 300}) => {
    let [cTime, k] = [null, null]
    // 获取当前时间
    const getTime = () => new Date().getTime()
    // 混合函数
    const hyFn = () => {
      const ags = argments
      return () => {
        clearTimeout(k)
        k = cTime = null
        fn(...ags)
      }
    }
    return () => {
      if (cTime == null) {
        k = setTimeout(hyFn(...arguments), time)
        cTime = getTime()
      } else {
        if ( getTime() - cTime < 0) {
          // 清除之前的函数堆 ---- 重新记录
          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(&#39;el is not elem&#39;)
      return null
    }
    let classList = el[&#39;className&#39;]
    classList = classList === &#39;&#39; ? [] : classList.split(/\s+/)
    if (classList.indexOf(className) === -1) {
      classList.push(className)
      el.className = classList.join(&#39; &#39;)
    } else {
      console.warn(&#39;warn className current&#39;)
    }
  }
  export const removeClass = function (el, className) {
    let classList = el[&#39;className&#39;]
    classList = classList === &#39;&#39; ? [] : classList.split(/\s+/)
    classList = classList.filter(item => {
      return item !== className
    })
    el.className =   classList.join(&#39; &#39;)
  }
  export const delay = ({fn, time}) => {
    let oT = null
    let k = null
    return () => {
      // 当前时间
      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 () {
    // 类型
    this.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(&#39;not has this type&#39;)
    }
  }
  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()
      })
    }
  }
Copy after login

Component template

<template>
  <p 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)">
    <p class="clip-scale-btn">
      <a class="add" @click="handleScale(false)">+</a>
      <a @click="rotate" class="right-rotate">转</a>
      <a class="poor" @click="handleScale(true)">-</a>
      <span>{{scale}}</span>
    </p>
    <p class="upload-warp">
      <a class="upload-btn" @click="dispatchUpload($event)">upload</a>
      <a class="upload-cancel">cancel</a>
    </p>
    <p class="create-canvas">
      <a class="to-send-file" @click="outFile" title="请打开控制台">生成文件</a>
    </p>
  </p>
</template>
<script>
  import {on, off, once} from &#39;../../utils/dom&#39;
  export default {
    ctx: null, 
    file: null, 
    x: 0, // 点击canvas x 鼠标地址
    y: 0,// 点击canvas y 鼠标地址
    xV: 0, // 鼠标移动 x距离
    yV: 0, // 鼠标移动 y距离
    nX: 0, // 原始坐标点 图像 x
    nY: 0,// 原始坐标点 图像 y
    img: null,
    props: {
        src: {
          type: String,
        default: null
      },
      clip: {
          type: Object,
        default () {
         return {width: &#39;200px&#39;, height: &#39;200px&#39;}
        }
      }
    },
    data () {
      return {
        isShow: false,
      base64: null,
      scale: 1.5, //放大比例
      deg: 0 //旋转角度
    }
    },
    computed: {
      width () {
       const {clip} = this
     return parseFloat(clip.width.replace(&#39;px&#39;, &#39;&#39;))
    },
    height () {
     const {clip} = this
     return parseFloat(clip.height.replace(&#39;px&#39;, &#39;&#39;))
    }
    },
    mounted () {
       const {$options, $refs, width, height} = this
       // 初始化 canvas file nX nY
      Object.assign($options, {
        ctx: $refs.ctx.getContext(&#39;2d&#39;),
        file: $refs.file,
        nX: -width / 2,
        nY: -height / 2
      })
    },
    methods: {
    // 旋转操作
      rotate () {
        const {$options, draw} = this
        this.deg = (this.deg + Math.PI /2)% (Math.PI * 2)
        draw($options.img, $options.nX + $options.xV, $options.nY + $options.yV, this.scale, this.deg)
      },
      // 处理放大
        handleScale (flag) {
        const {$options, draw, deg} = this
        flag && this.scale > 0.1 && (this.scale = this.scale - 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)
      },
      // 模拟file 点击事件
      dispatchUpload (e) {
        this.clearState()
        const {file} = this.$options
        e.preventDefault()
        file.click()
      },
      // 读取 input file 信息
      readFileMsg () {
        const {file} = this.$options
        const {draw, createImage, $options: {nX, nY}, scale, deg} = this
        const wFile = file.files[0]
        const reader = new FileReader()
        reader.onload = (e) => {
          const img = createImage(e.target.result, (img) => {
            draw(img, nX, nY, scale, deg)
          })
          file.value = null
        }
        reader.readAsDataURL(wFile)
      },
      // 生成 图像
      createImage (src, cb) {
       const img = new Image()
        this.$el.append(img)
        img.className = &#39;base64-hidden&#39;
        img.onload = () => {
         cb(img)
        }
       img.src = src
       this.$options.img = img
      },
      // 操作画布画图
      draw (img, x = 0, y = 0, scale = 0.5,deg = Math.PI ) {
        const {ctx} = this.$options
        let {width, height} = this
        // 图片尺寸
        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()
      },
      // ... 事件绑定
      handleClip (e) {
        const {handleMove, $options, deg} = this
        if (!$options.img) {
            return
        }
        Object.assign(this.$options, {
          x: e.screenX,
         y: e.screenY
        })
        on({
          el: window,
          type: &#39;mousemove&#39;,
          fn: handleMove
        })
        once({
          el: window,
          type: &#39;mouseup&#39;,
          fn: (e) =>{
            console.log(&#39;down&#39;)
           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: &#39;mousemove&#39;,
            fn: handleMove
          })
          }
        })
      },
      // ... 处理鼠标移动
      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;
          }
        }
      },
      // 清除状态
      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,
        })
      }
      },
      // 输出文件
      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;
      }
    }
Copy after login

The above is what I compiled for everyone. I hope it will be helpful to everyone in the future.

Related articles:

How to implement image scrolling using vue?

js implements the method of operating binary data

Use the swiper component in vue2.0 to implement carousel (detailed tutorial)

The above is the detailed content of How to use vue to crop images while enlarging, reducing, and rotating them (detailed tutorial). For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

Video Face Swap

Video Face Swap

Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

How to solve the problem of automatically saving pictures when publishing on Xiaohongshu? Where is the automatically saved image when posting? How to solve the problem of automatically saving pictures when publishing on Xiaohongshu? Where is the automatically saved image when posting? Mar 22, 2024 am 08:06 AM

With the continuous development of social media, Xiaohongshu has become a platform for more and more young people to share their lives and discover beautiful things. Many users are troubled by auto-save issues when posting images. So, how to solve this problem? 1. How to solve the problem of automatically saving pictures when publishing on Xiaohongshu? 1. Clear the cache First, we can try to clear the cache data of Xiaohongshu. The steps are as follows: (1) Open Xiaohongshu and click the &quot;My&quot; button in the lower right corner; (2) On the personal center page, find &quot;Settings&quot; and click it; (3) Scroll down and find the &quot;Clear Cache&quot; option. Click OK. After clearing the cache, re-enter Xiaohongshu and try to post pictures to see if the automatic saving problem is solved. 2. Update the Xiaohongshu version to ensure that your Xiaohongshu

How to post pictures in TikTok comments? Where is the entrance to the pictures in the comment area? How to post pictures in TikTok comments? Where is the entrance to the pictures in the comment area? Mar 21, 2024 pm 09:12 PM

With the popularity of Douyin short videos, user interactions in the comment area have become more colorful. Some users wish to share images in comments to better express their opinions or emotions. So, how to post pictures in TikTok comments? This article will answer this question in detail and provide you with some related tips and precautions. 1. How to post pictures in Douyin comments? 1. Open Douyin: First, you need to open Douyin APP and log in to your account. 2. Find the comment area: When browsing or posting a short video, find the place where you want to comment and click the &quot;Comment&quot; button. 3. Enter your comment content: Enter your comment content in the comment area. 4. Choose to send a picture: In the interface for entering comment content, you will see a &quot;picture&quot; button or a &quot;+&quot; button, click

6 Ways to Make Pictures Sharper on iPhone 6 Ways to Make Pictures Sharper on iPhone Mar 04, 2024 pm 06:25 PM

Apple's recent iPhones capture memories with crisp detail, saturation and brightness. But sometimes, you may encounter some issues that may cause the image to look less clear. While autofocus on iPhone cameras has come a long way, allowing you to take photos quickly, the camera can mistakenly focus on the wrong subject in certain situations, making the photo blurry in unwanted areas. If your photos on your iPhone look out of focus or lack sharpness overall, the following post should help you make them sharper. How to Make Pictures Clearer on iPhone [6 Methods] You can try using the native Photos app to clean up your photos. If you want more features and options

How to make ppt pictures appear one by one How to make ppt pictures appear one by one Mar 25, 2024 pm 04:00 PM

In PowerPoint, it is a common technique to display pictures one by one, which can be achieved by setting animation effects. This guide details the steps to implement this technique, including basic setup, image insertion, adding animation, and adjusting animation order and timing. Additionally, advanced settings and adjustments are provided, such as using triggers, adjusting animation speed and order, and previewing animation effects. By following these steps and tips, users can easily set up pictures to appear one after another in PowerPoint, thereby enhancing the visual impact of the presentation and grabbing the attention of the audience.

What should I do if the images on the webpage cannot be loaded? 6 solutions What should I do if the images on the webpage cannot be loaded? 6 solutions Mar 15, 2024 am 10:30 AM

Some netizens found that when they opened the browser web page, the pictures on the web page could not be loaded for a long time. What happened? I checked that the network is normal, so where is the problem? The editor below will introduce to you six solutions to the problem that web page images cannot be loaded. Web page images cannot be loaded: 1. Internet speed problem The web page cannot display images. It may be because the computer's Internet speed is relatively slow and there are more softwares opened on the computer. And the images we access are relatively large, which may be due to loading timeout. As a result, the picture cannot be displayed. You can turn off the software that consumes more network speed. You can go to the task manager to check. 2. Too many visitors. If the webpage cannot display pictures, it may be because the webpages we visited were visited at the same time.

How to convert pdf documents into jpg images with Foxit PDF Reader - How to convert pdf documents into jpg images with Foxit PDF Reader How to convert pdf documents into jpg images with Foxit PDF Reader - How to convert pdf documents into jpg images with Foxit PDF Reader Mar 04, 2024 pm 05:49 PM

Are you also using Foxit PDF Reader software? So do you know how Foxit PDF Reader converts pdf documents into jpg images? The following article brings you how Foxit PDF Reader converts pdf documents into jpg images. For those who are interested in the method of converting jpg images, please come and take a look below. First start Foxit PDF Reader, then find "Features" on the top toolbar, and then select the "PDF to Others" function. Next, open a web page called "Foxit PDF Online Conversion". Click the "Login" button on the upper right side of the page to log in, and then turn on the "PDF to Image" function. Then click the upload button and add the pdf file you want to convert into an image. After adding it, click "Start Conversion"

How to arrange two pictures side by side in wps document How to arrange two pictures side by side in wps document Mar 20, 2024 pm 04:00 PM

When using WPS office software, we found that not only one form is used, tables and pictures can be added to the text, pictures can also be added to the table, etc. These are all used together to make the content of the entire document look richer. , if you need to insert two pictures into the document and they need to be arranged side by side. Our next course can solve this problem: how to place two pictures side by side in a wps document. 1. First, you need to open the WPS software and find the picture you want to adjust. Left-click the picture and a menu bar will pop up, select &quot;Page Layout&quot;. 2. Select &quot;Tight wrapping&quot; in text wrapping. 3. After all the pictures you need are confirmed to be set to &quot;Tight text wrapping&quot;, you can drag the pictures to the appropriate position and click on the first picture.

How to implement dual WeChat login on Huawei mobile phones? How to implement dual WeChat login on Huawei mobile phones? Mar 24, 2024 am 11:27 AM

How to implement dual WeChat login on Huawei mobile phones? With the rise of social media, WeChat has become one of the indispensable communication tools in people's daily lives. However, many people may encounter a problem: logging into multiple WeChat accounts at the same time on the same mobile phone. For Huawei mobile phone users, it is not difficult to achieve dual WeChat login. This article will introduce how to achieve dual WeChat login on Huawei mobile phones. First of all, the EMUI system that comes with Huawei mobile phones provides a very convenient function - dual application opening. Through the application dual opening function, users can simultaneously

See all articles