js-手写预览图片对象,兼容手机端

// 图片查看类,支持放大缩小旋转图片

class ViewImg {
  constructor (config = {}) {
    if (new.target !== undefined) {
      this.width = config.width || 300
      this.height = config.height || 300
      this.create()
    } else {
      throw new Error('请通过new 方法调用')
    }
  }
  touchStart (e) {
    e.preventDefault()
    e.stopPropagation()
    // 判断 是否是两指缩放
    if (e.touches.length > 1) {
      this.pinch = true
      this.drag = false
      this.startPoint = {
        x: e.touches[0].screenX,
        y: e.touches[0].screenY,
        x1: e.touches[1].screenX,
        y1: e.touches[1].screenY,
      }
      return
    } else {
      this.drag = true
      this.pinch = false
      this.startPoint = {
        x: e.changedTouches[0].screenX,
        y: e.changedTouches[0].screenY
      }
      this.startDragTime = new Date().getTime()
    }
  }
  touchMove (e) {
    e.preventDefault()
    e.stopPropagation()
    let tnow = Date.now()
    if (this.pinch && tnow - this.startDragTime > 100) {
      // 缩放
      let ts = Math.sqrt(Math.pow(Math.abs(this.startPoint.x - this.startPoint.x1),2), Math.pow(Math.abs(this.startPoint.y - this.startPoint.y1), 2))
      let te = Math.sqrt(Math.pow(Math.abs(e.changedTouches[0].screenX - e.changedTouches[1].screenX), 2), Math.pow(Math.abs(e.changedTouches[0].screenY - e.changedTouches[1].screenY), 2))
      
      this.startPoint = {
        x: e.changedTouches[0].screenX,
        y: e.changedTouches[0].screenY,
        x1: e.changedTouches[1].screenX,
        y1: e.changedTouches[1].screenY,
      }

      if (ts - te > 1.5) {
        if (this.transform.scale > 0.2) {
          this.transform.scale -= 0.1
        } else {
          this.transform.scale = 0.2
        }
      } else if (te - ts > 1.5) {
        if (this.transform.scale < 5) {
          this.transform.scale += 0.1
        } else {
          this.transform.scale = 5
        }
      }
      this.drawIt()
      return
    }
    // 移动
    if (this.drag && tnow - this.startDragTime > 100) {
      this.startDragTime = tnow
      let tobj = [
        e.changedTouches[0].screenX - this.startPoint.x,
        e.changedTouches[0].screenY - this.startPoint.y
      ]
      this.transform.tmptranslate = tobj
      this.drawIt()
    }
  }
  mouseDown (e) {
    e.preventDefault()
    e.stopPropagation()
    this.drag = true
    this.startPoint = {
      x: e.screenX,
      y: e.screenY
    }
    this.startDragTime = new Date().getTime()
  }
  mouseMove (e) {
    e.preventDefault()
    e.stopPropagation()
    let tnow = Date.now()
    if (this.drag && tnow - this.startDragTime > 100) {
      this.startDragTime = tnow
      let tobj = [
        e.screenX - this.startPoint.x,
        e.screenY - this.startPoint.y
      ]
      this.transform.tmptranslate = tobj
      this.drawIt()
    }
  }
  mouseUp (e) {
    if (this.drag) {
      this.drag = false
    
      this.transform.translate = [
        this.transform.translate[0] + this.transform.tmptranslate[0],
        this.transform.translate[1] + this.transform.tmptranslate[1],
      ]
      this.transform.tmptranslate = [0,0]
      
      this.drawIt()
    }
  }
  mouseWheel (e) {
    var tmps=0;
        if(typeof e.wheelDelta=='number'){
            tmps=-e.wheelDelta;
            //普通浏览器有e.wheelDelta值,向上滚是正值120,向下滚是负值-120
        }else{
            tmps=e.detail;
            //火狐浏览器有e.detail值,向上滚是负值-3,向下滚是正值3
        }
    if (tmps < 0) {
      if (this.transform.scale < 5) {
        this.transform.scale += 0.1
      } else {
        this.transform.scale = 5
      }
    } else {
      if (this.transform.scale > 0.5) {
        this.transform.scale -= 0.1
      } else {
        this.transform.scale = 0.5
      }
    }
    this.drawIt()
  }
  create() {
    let tw = window.innerWidth
    let th = window.innerHeight
    this.tw = tw
    this.th = th
    // 查看设置的裁剪比例和设备的宽高比例关系
    let bili = this.width / this.height > tw / th 
    
    this.transform = {
      scale: 1,
      translate: [0,0],
      tmptranslate: [0,0],
      rotate: 0
    }
// 整体裁剪框
    let dik = document.createElement('div')
    this.dik = dik
    dik.setAttribute('style', `
      position:fixed;
      background:rgba(0,0,0,0.6);
      margin:0;
      padding:0;
      left:0;top:0;
      width:100%;height:100%;
      z-index:9998;
    `)
// 按钮
    let close = document.createElement('div')

    close.setAttribute('style', `
      width:80px;
      height:80px;
      position:fixed;
      right:20px;
      top:20px;
      z-index:1001;
      text-align:Center;
      line-height:80px;
      cursor:pointer;
      font-size:60px;
      transform: rotate(45deg);
      border: 1px solid #ffffff44;
      border-radius:50%;
      color:rgba(255,255,255,0.5);
    `)
    close.innerHTML = '+'
    close.addEventListener('click', this.close.bind(this))
    close.addEventListener('touchend', this.close.bind(this))
    dik.append(close)

    let clickHandle = (e) => {
      e.preventDefault()
      e.stopPropagation()
      let title = e.target.getAttribute('title') || e.target.parentNode.getAttribute('title')
      switch(title) {
        case '左转': {
          this.transform.rotate -= 5
          this.drawIt()
          break
        }
        case '右转': {
          this.transform.rotate += 5
          this.drawIt()
          break
        }
        default: {
          this.transform = {
            scale: 1,
            translate: [0,0],
            tmptranslate: [0,0],
            rotate: 0
          }
          this.imgRect = {...this.tmpRect}
          this.drawIt()
          break
        }
      }
    }

    let buts = document.createElement('div')
    buts.setAttribute('style', `
      position: fixed;
      width:350px;
      height: 40px;
      left: 0;
      right: 0;
      display:flex;
      bottom: 80px;
      background:rgba(0,0,0,0.3);
      border-radius: 20px;
      margin: auto;
      border: 1px solid rgba(255,255,255,0.5);
      opacity:0.6;
      z-index: 10001;
    `)
    
    // 子元素
    let but1 = document.createElement('div')
    but1.setAttribute('style', 'width:100%;cursor: pointer;height: 40px;position:relative;')
    but1.setAttribute('title', '左转');
    but1.addEventListener('click', clickHandle)
    but1.addEventListener('touchend', clickHandle)
    let but1_icon1 = document.createElement('div')
    but1_icon1.setAttribute('style', `
      position:absolute;right:0;top:0;bottom:0;left:0;margin:auto;
      width:24px;height:24px;border-radius:50%;
      border-right:2px solid #fff;
      border-top:2px solid #fff;
      `)
    let but1_icon2 = document.createElement('div')
    but1_icon2.setAttribute('style', `
      position:absolute;right:0;top:0;bottom:0;left:-18px;margin:auto;
      width:6px;height:6px;border:6px solid transparent;
      border-top-color:#fff;
      `)
    but1.append(but1_icon1);
    but1.append(but1_icon2);
    
    
    let but2 = document.createElement('div')
    but2.setAttribute('style', 'width:100%;cursor: pointer;height: 40px;position:relative;')
    but2.setAttribute('title', '右转');
    but2.addEventListener('click', clickHandle)
    but2.addEventListener('touchend', clickHandle)
    let but2_icon1 = document.createElement('div')
    but2_icon1.setAttribute('style', `
      position:absolute;right:0;top:0;bottom:0;left:0;margin:auto;
      width:24px;height:24px;border-radius:50%;
      border-left:2px solid #fff;
      border-top:2px solid #fff;
      `)
    let but2_icon2 = document.createElement('div')
    but2_icon2.setAttribute('style', `
      position:absolute;right:-18px;top:0;bottom:0;left:0;margin:auto;
      width:6px;height:6px;border:6px solid transparent;
      border-top-color:#fff;
      `)
    but2.append(but2_icon1);
    but2.append(but2_icon2);
    
    let but3 = document.createElement('div')
    but3.setAttribute('title', '复位');
    but3.setAttribute('style', 'width:100%;cursor: pointer;height: 40px;position:relative;')
    but3.addEventListener('click', clickHandle)
    but3.addEventListener('touchend', clickHandle)
    let but3_icon1 = document.createElement('div')
    but3_icon1.setAttribute('style', `
      position:absolute;right:0;top:0;bottom:0;left:0;margin:auto;
      width:24px;height:24px;border-radius:50%;
      border-bottom:2px solid #fff;
      border-top:2px solid #fff;
      `)
    let but3_icon2 = document.createElement('div')
    but3_icon2.setAttribute('style', `
      position:absolute;right:0;top:0;bottom:0;left:0;margin:auto;
      width:6px;height:6px;border:1px solid #fff;
      `)
    but3.append(but3_icon1);
    but3.append(but3_icon2);

    buts.append(but1)
    buts.append(but2)
    buts.append(but3)
    dik.append(buts)
// 根据不同端绑定事件
    if (tw > 900) {
      // pc端
      dik.addEventListener('mousedown', this.mouseDown.bind(this))
      dik.addEventListener('mousemove', this.mouseMove.bind(this))
      dik.addEventListener('mouseup', this.mouseUp.bind(this))
      if (navigator.userAgent.toLowerCase().indexOf('firefox') > -1) {
        // 火狐浏览器
        dik.addEventListener('DOMMouseScroll', this.mouseWheel.bind(this))
      } else {
        dik.addEventListener('mousewheel', this.mouseWheel.bind(this))
      }
    } else {
      // 手机端事件
      dik.addEventListener('touchstart', this.touchStart.bind(this))
      dik.addEventListener('touchmove', this.touchMove.bind(this))
      // 结束函数与鼠标一致
      dik.addEventListener('touchend', this.mouseUp.bind(this))
    }

    // 算出同比下设备上应该显示的宽或高
    let sh = bili ? (th * this.width/this.height - th*0.1): (th *0.9)  // 高
    sh = sh > this.height ? this.height:sh
    let sw = sh * this.width / this.height // 宽
    let sl = (tw - sw) / 2
    let st = (th - sh) / 2
    
    this.rect = {sl, st, sw, sh} // 缓存当前裁剪框位置

  }
  show (src) {
    // 设置裁剪的图片, 需要传入图片文件
    let img = new Image()
    this.img = img
    // 加入到dom树中
    this.dik.append(this.img)
    document.body.append(this.dik)
    img.onload = () => {
      let timgw = this.rect.sw + 400
      let timgh = img.height / img.width * timgw
      this.imgRect = {
        imgl: this.rect.sl - 200,
        imgt: (this.th - timgh) / 2,
        imgw: timgw,
        imgh: timgh,
      }
      this.tmpRect = Object.assign({}, this.imgRect)
      this.drawIt()
    }
    img.src = src
  }
  drawIt () {
    let arr = this.transform.tmptranslate
    let tranx = this.transform.translate[0]
    let trany = this.transform.translate[1]
    if (arr && arr.length) {
      tranx += arr[0]
      trany += arr[1]
    }
    this.img.setAttribute('style', `
      position: fixed;
      margin:0;
      display:block;
      box-sizing:border-box;
      z-index: 99;
      left: ${this.imgRect.imgl}px;
      top: ${this.imgRect.imgt}px;
      width: ${this.imgRect.imgw}px;
      height: ${this.imgRect.imgh}px;
      transform:rotate(${this.transform.rotate}deg) translate(${tranx}px,${trany}px) scale(${this.transform.scale},${this.transform.scale});
      transform-origin: 50% 50%;
    `)
  }
 
  close () {
    // 关闭
    this.dik.removeChild(this.img)
    document.body.removeChild(this.dik)
    this.transform = {
      scale: 1,
      translate: [0,0],
      tmptranslate: [0,0],
      rotate: 0
    }
    this.img = null
  }
}

// 绑定所有事件
const viewImg = new ViewImg()
const allImages = document.getElementsByTagName('img')
for (let img of allImages) {
  img.onclick = () => {img.src && viewImg.show(img.src)}
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容