vue 는 그림 을 자 르 는 동시에 확대,축소,회전 기능 을 실현 한다.

17627 단어 vue재단한다그림.
이 글 은 vue 가 재단 사진 을 실현 하 는 동시에 확대,축소,회전 기능 을 실현 하여 여러분 에 게 공유 하 는 것 을 소 개 했 습 니 다.구체 적 으로 다음 과 같 습 니 다.
구현 효과:
지 정 된 구역 내의 그림 을 재단 합 니 다회전 사진
확대 사진
출력 bolb 형식 데이터 제공 formData 대상효과 도







대략적인 원리:
h5 FileReader 대상 을 이용 하여"브 라 우 저 에 올 린 파일"을 가 져 옵 니 다.파일 형식 은 base 64 형식 으로 base 64 를 canvas 에 부여 하 는 컨 텍스트 입 니 다.
그리고 canvas 요소 에 대(mousedown)감청 사건 을 추가 합 니 다.사용자 가 canvas 에서 왼쪽 단 추 를 눌 렀 을 때:
  • window 대상 mousemove 이벤트 마 운 트->마우스 이동 x,y 거 리 를 가 져 와 canvas 의 그림 위 치 를 이동 합 니 다
  • window 대상 mouseup 이 벤트 를 마 운 트 하고 mousemove 이벤트 의 바 인 딩 을 제거 합 니 다.(동시에 이 이벤트 가 발생 하면 삭 제 됩 니 다)
  • 나머지 확대,축소,회전 은 canvas 대상 에 대한 조작/좌표 시스템 의 조작 입 니 다.자세 한 api 는 mdn canvas 문서 참조
    코드
    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('el is not elem')
          return null
        }
        let classList = el['className']
        classList = 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 () => {
          //     
          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('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()
          })
        }
      }
    모듈 템 플 릿
    
    <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"> </a>
          <a class="poor" @click="handleScale(true)">-</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="      ">    </a>
        </div>
      </div>
    </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;
          }
        }
    프로젝트 코드:https://github.com/L6zt/vuesrr
    이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.

    좋은 웹페이지 즐겨찾기