JavaScript平移图片或div内容

/**
 * 矩阵类
 * @class Matrix
 * @description 用于处理矩阵的类
 */
class Matrix {
  /**
   * 构造方法
   *
   * @param {Number} row 行数
   * @param {Number} column 列数
   * @param {Array} value 值
   */
  constructor(row, column, value) {
    this.r = row
    this.c = column
    for (let i = 0; i < row; i++) {
      this[i] = []
    }
    if (value) {
      for (let i = 0; i < this.r; i++) {
        for (let j = 0; j < this.c; j++) {
          this[i][j] = value[i][j] ?? this[i][j]
        }
      }
    }
  }

  /**
   * 矩阵相乘
   *
   * @param {Object} other 矩阵
   * @return {Matrix} 结果
   * */
  multiplyD(other) {
    const result = new Matrix(this.r, other.c, null)
    const n = this.c
    for (let i = 0; i < result.r; i++) {
      for (let j = 0; j < result.c; j++) {
        let value = 0
        for (let k = 0; k < n; k++) {
          value += this[i][k] * other[k][j]
        }
        result[i][j] = value
      }
    }
    return result
  }
}

/**
 * 屏幕移动类
 * @class ScreenMove
 * @description 用于处理屏幕鼠标平移缩放的类
 */
class ScreenMove {
  /**
   * 构造方法
   * @description 父容器需提前指定宽高
   * @param {string} fatherContainerId 父容器id
   * @param {string} sonContainerId 子容器id
   * @param {Boolean} translate 平移
   * @param {Boolean} scale 缩放
   * @param {Boolean} exceedScreen 超出屏幕
   * @param {Number} translateSpeed 平移速度
   * @param {Number} scaleSpeed 缩放速度
   */
  constructor(fatherContainerId, sonContainerId,
      translate = true, scale = true, exceedScreen = false,
      translateSpeed = 2, scaleSpeed = 1) {
    this._config = {
      translate: true,
      scale: true,
      translateSpeed: 2,
      scaleSpeed: 1,
      sonContainerId: '',
      fatherContainerId: '',
      exceedScreen: false,
    }
    this._x = 0
    this._y = 0
    this._s = 1
    this._$translate = null
    this._$scale = null
    this._moveDelta = 0

    // 获取父元素,给元素添加指定样式
    const $container = document.getElementById(fatherContainerId)
    if ($container === null) {
      throw new Error('查找不到父容器id')
    }
    this._config.fatherContainerId = fatherContainerId
    $container.style.overflow = 'hidden'
    $container.style.position = 'relative'

    // 指定父容器事件监听
    $container.addEventListener('mousemove', this.#handle_move.bind(this))
    $container.addEventListener('click', this.#handle_click.bind(this), true)
    $container.addEventListener('mousewheel', this.#handle_wheel.bind(this))

    const _$translate = document.createElement('div')
    _$translate.style.transformOrigin = '0 0'

    const _$scale = document.createElement('div')
    _$scale.style.transformOrigin = '0 0'

    // 子容器
    const $content = document.getElementById(sonContainerId)
    if ($content === null) {
      throw new Error('查找不到子容器id')
    }
    this._config.sonContainerId = sonContainerId
    $content.style.width = 'max-content'
    $content.style.height = 'max-content'

    $container.appendChild(_$translate)
    _$translate.appendChild(_$scale)
    _$scale.appendChild($content)

    this._$translate = _$translate
    this._$scale = _$scale
    this._config.translate = translate
    this._config.scale = scale
    this._config.translateSpeed = translateSpeed
    this._config.scaleSpeed = scaleSpeed
    this._config.exceedScreen = exceedScreen

    this.#adaptive($content)
  }

  /**
   * son 自适应页面大小
   *
   * @param {Object} $content 元素
   * */
  #adaptive($content) {
    if ($content.offsetWidth < document.getElementById(this._config.fatherContainerId).offsetWidth &&
        $content.offsetHeight < document.getElementById(this._config.fatherContainerId).offsetHeight) {
      this._s = Math.max(document.getElementById(this._config.fatherContainerId).offsetWidth /
          $content.offsetWidth, document.getElementById(this._config.fatherContainerId).offsetHeight / $content.offsetHeight)
      this.#scale()
    }

    // 自适应son缩放控制
    const w = this._s < document.getElementById(this._config.fatherContainerId).offsetWidth / document.getElementById(this._config.sonContainerId).offsetWidth
    const h = this._s < document.getElementById(this._config.fatherContainerId).offsetHeight / document.getElementById(this._config.sonContainerId).offsetHeight
    if (w === false && h === true) {
      while (this._s < document.getElementById(this._config.fatherContainerId).offsetHeight / document.getElementById(this._config.sonContainerId).offsetHeight) {
        const delta = this.#computeDeltaY(-100)
        this.#origin(delta, 0, 0)
        this.#scale()
      }
      const delta = this.#computeDeltaY(-100)
      this.#exceedScreen_scale()
      this.#origin(delta, 0, 0)
      this.#scale()
      return
    }
    if (w === false && h === false) {
      while (!(this._s < document.getElementById(this._config.fatherContainerId).offsetHeight / document.getElementById(this._config.sonContainerId).offsetHeight)) {
        const delta = this.#computeDeltaY(100)
        this.#origin(delta, 0, 0)
        this.#scale()
      }
      const delta = this.#computeDeltaY(100)
      this.#exceedScreen_scale()
      this.#origin(delta, 0, 0)
      this.#scale()
    }
  }

  /**
     * 处理鼠标拖动
     * @param {Object} ev 事件对象
     */
  #handle_move(ev) {
    if (this._config.translate) {
      if (ev.buttons === 1) {
        this._x += (ev.movementX / this._s) * this._config.translateSpeed
        this._y += (ev.movementY / this._s) * this._config.translateSpeed
        this._moveDelta += Math.abs(ev.movementX + ev.movementY)
        this.#exceedScreen_move()
        this.#translate()
      }
    }
  }

  /**
     * 处理鼠标抬起
     * @description 阻止拖动时点击
     * @param {Object} ev 事件对象
     */
  #handle_click(ev) {
    if (this._moveDelta > 10) {
      ev.preventDefault()
      ev.stopPropagation()
    }
    this._moveDelta = 0
  }

  /**
   * 处理鼠标滚轮
   * @param {Object} ev 事件对象
   */
  #handle_wheel(ev) {
    if (this._config.scale) {
      const delta = this.#computeDeltaY(ev.deltaY)
      if (this._config.exceedScreen) {
        this.#origin(delta, ev.clientX, ev.clientY)
        this.#scale()
        return
      }
      if (this._s < document.getElementById(this._config.fatherContainerId).offsetWidth / document.getElementById(this._config.sonContainerId).offsetWidth ||
          this._s < document.getElementById(this._config.fatherContainerId).offsetHeight / document.getElementById(this._config.sonContainerId).offsetHeight) {
        this.#exceedScreen_scale()
      } else {
        this.#origin(delta, ev.clientX, ev.clientY)
        this.#scale()
      }
    }
  }

  /**
   * 计算滚动距离
   * @param {Number} deltaY 滚动距离
   * @return {Number} 滚动距离
   * */
  #computeDeltaY(deltaY) {
    const delta = -(deltaY / 2000) * this._config.scaleSpeed
    this._s *= 1 + delta
    return delta
  }

  /**
   * 超出屏幕时的拖动
   * */
  #exceedScreen_move() {
    if (this._config.exceedScreen) {
      return
    }
    if (this._x > 0) {
      this._x = 0
    }
    if (this._y > 0) {
      this._y = 0
    }
    if (this._x < document.getElementById(this._config.fatherContainerId).offsetWidth - document.getElementById(this._config.sonContainerId).offsetWidth * this._s) {
      this._x = document.getElementById(this._config.fatherContainerId).offsetWidth - document.getElementById(this._config.sonContainerId).offsetWidth * this._s
    }
    if (this._y < document.getElementById(this._config.fatherContainerId).offsetHeight - document.getElementById(this._config.sonContainerId).offsetHeight * this._s) {
      this._y = document.getElementById(this._config.fatherContainerId).offsetHeight - document.getElementById(this._config.sonContainerId).offsetHeight * this._s
    }
  }

  /**
   * 超出屏幕时的缩放
   * */
  #exceedScreen_scale() {
    if (this._config.exceedScreen) {
      return
    }
    if (this._s < document.getElementById(this._config.fatherContainerId).offsetWidth / document.getElementById(this._config.sonContainerId).offsetWidth) {
      this._s = document.getElementById(this._config.fatherContainerId).offsetWidth / document.getElementById(this._config.sonContainerId).offsetWidth
    }
    if (this._s < document.getElementById(this._config.fatherContainerId).offsetHeight / document.getElementById(this._config.sonContainerId).offsetHeight) {
      this._s = document.getElementById(this._config.fatherContainerId).offsetHeight / document.getElementById(this._config.sonContainerId).offsetHeight
    }
    this.#exceedScreen_move()
    this.#translate()
  }

  /**
   * 缩放原点
   * @param {Number} delta 缩放系数变化量
   * @param {Number} ox 缩放中心横坐标
   * @param {Number} oy 缩放中心纵坐标
   */
  #origin(delta, ox, oy) {
    const v = new Matrix(1, 3, [[this._x, this._y, 1]])
    const tf = new Matrix(3, 3, [
      [1, 0, 0],
      [0, 1, 0],
      [-ox, -oy, 1],
    ])
    const sc = new Matrix(3, 3, [
      [1 + delta, 0, 0],
      [0, 1 + delta, 0],
      [0, 0, 1],
    ])
    const tb = new Matrix(3, 3, [
      [1, 0, 0],
      [0, 1, 0],
      [ox, oy, 1],
    ])
    const r = v.multiplyD(tf).multiplyD(sc).multiplyD(tb)
    this._x = r[0][0]
    this._y = r[0][1]
    this.#translate()
  }

  /**
     * 平移
     */
  #translate() {
    this._$translate.style.transform = `translate(${this._x}px, ${this._y}px)`
  }

  /**
   * 缩放
   */
  #scale() {
    this._$scale.style.transform = `scale(${this._s})`
  }
}

export default ScreenMove

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值