当前位置:Gxlcms > JavaScript > 利用vue如何实现裁切图片同时实现放大、缩小、旋转功能(详细教程)

利用vue如何实现裁切图片同时实现放大、缩小、旋转功能(详细教程)

时间:2021-07-01 10:21:17 帮助过:39人阅读

这篇文章主要介绍了vue实现裁切图片同时实现放大、缩小、旋转功能,现在分享给大家,也给大家做个参考。

本篇文章主要介绍了vue实现裁切图片同时实现放大、缩小、旋转功能,分享给大家,具体如下:

实现效果:

  1. 裁切指定区域内的图片

  2. 旋转图片

  3. 放大图片

  4. 输出bolb 格式数据 提供给 formData 对象

效果图







大概原理:

利用h5 FileReader 对象, 获取 <input type="file"/> “上传到浏览器的文件” ,文件形式 为base64形式, 把 base64 赋给canvas的上下文。

然后给canvas 元素上加入对(mousedown)监听事件。 当用户鼠标左键在canvas按下时:

  1. 挂载对 window 对象mousemove事件 ---> 获取 鼠标移动x,y距离.从而操作 canvas里的图像的位置移动。

  2. 挂载对 window 对象mouseup 事件, 清除 mousemove事件的绑定。(同时该事件触发后会被删除)

剩下的 放大、缩小 、 旋转 是对 canvas 对象的操作/坐标体系的操作。具体api详见mdn canvas 文档

代码

dom.js

组件模板

<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 '../../utils/dom'
  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: '200px', height: '200px'}
        }
      }
    },
    data () {
      return {
        isShow: false,
      base64: null,
      scale: 1.5, //放大比例
      deg: 0 //旋转角度
    }
    },
    computed: {
      width () {
       const {clip} = this
     return parseFloat(clip.width.replace('px', ''))
    },
    height () {
     const {clip} = this
     return parseFloat(clip.height.replace('px', ''))
    }
    },
    mounted () {
       const {$options, $refs, width, height} = this
       // 初始化 canvas file nX nY
      Object.assign($options, {
        ctx: $refs.ctx.getContext('2d'),
        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 = 'base64-hidden'
        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: 'mousemove',
          fn: handleMove
        })
        once({
          el: window,
          type: 'mouseup',
          fn: (e) =>{
            console.log('down')
           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
          })
          }
        })
      },
      // ... 处理鼠标移动
      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; } }

上面是我整理给大家的,希望今后会对大家有帮助。

相关文章:

利用vue如何实现图片滚动?

js实现操作二进制数据方法

利用vue2.0中swiper组件实现轮播(详细教程)

以上就是利用vue如何实现裁切图片同时实现放大、缩小、旋转功能(详细教程)的详细内容,更多请关注Gxl网其它相关文章!

人气教程排行