mouse-wheel.js 3.55 KB
import { addEvent, removeEvent } from '../util/dom'
import { ease } from '../util/ease'

export function mouseWheelMixin(BScroll) {
  BScroll.prototype._initMouseWheel = function () {
    this._handleMouseWheelEvent(addEvent)

    this.on('destroy', () => {
      clearTimeout(this.mouseWheelTimer)
      clearTimeout(this.mouseWheelEndTimer)
      this._handleMouseWheelEvent(removeEvent)
    })

    this.firstWheelOpreation = true
  }

  BScroll.prototype._handleMouseWheelEvent = function (eventOperation) {
    eventOperation(this.wrapper, 'wheel', this)
    eventOperation(this.wrapper, 'mousewheel', this)
    eventOperation(this.wrapper, 'DOMMouseScroll', this)
  }

  BScroll.prototype._onMouseWheel = function (e) {
    if (!this.enabled) {
      return
    }
    e.preventDefault()

    if (this.options.stopPropagation) {
      e.stopPropagation()
    }

    if (this.firstWheelOpreation) {
      this.trigger('scrollStart')
    }
    this.firstWheelOpreation = false

    const {speed = 20, invert = false, easeTime = 300} = this.options.mouseWheel

    clearTimeout(this.mouseWheelTimer)
    this.mouseWheelTimer = setTimeout(() => {
      if (!this.options.snap && !easeTime) {
        this.trigger('scrollEnd', {
          x: this.x,
          y: this.y
        })
      }
      this.firstWheelOpreation = true
    }, 400)

    let wheelDeltaX
    let wheelDeltaY

    switch (true) {
      case 'deltaX' in e:
        if (e.deltaMode === 1) {
          wheelDeltaX = -e.deltaX * speed
          wheelDeltaY = -e.deltaY * speed
        } else {
          wheelDeltaX = -e.deltaX
          wheelDeltaY = -e.deltaY
        }
        break
      case 'wheelDeltaX' in e:
        wheelDeltaX = e.wheelDeltaX / 120 * speed
        wheelDeltaY = e.wheelDeltaY / 120 * speed
        break
      case 'wheelDelta' in e:
        wheelDeltaX = wheelDeltaY = e.wheelDelta / 120 * speed
        break
      case 'detail' in e:
        wheelDeltaX = wheelDeltaY = -e.detail / 3 * speed
        break
      default:
        return
    }

    let direction = invert ? -1 : 1
    wheelDeltaX *= direction
    wheelDeltaY *= direction

    if (!this.hasVerticalScroll) {
      wheelDeltaX = wheelDeltaY
      wheelDeltaY = 0
    }

    let newX
    let newY
    if (this.options.snap) {
      newX = this.currentPage.pageX
      newY = this.currentPage.pageY

      if (wheelDeltaX > 0) {
        newX--
      } else if (wheelDeltaX < 0) {
        newX++
      }

      if (wheelDeltaY > 0) {
        newY--
      } else if (wheelDeltaY < 0) {
        newY++
      }

      this._goToPage(newX, newY)
      return
    }

    newX = this.x + Math.round(this.hasHorizontalScroll ? wheelDeltaX : 0)
    newY = this.y + Math.round(this.hasVerticalScroll ? wheelDeltaY : 0)

    this.movingDirectionX = this.directionX = wheelDeltaX > 0 ? -1 : wheelDeltaX < 0 ? 1 : 0
    this.movingDirectionY = this.directionY = wheelDeltaY > 0 ? -1 : wheelDeltaY < 0 ? 1 : 0

    if (newX > this.minScrollX) {
      newX = this.minScrollX
    } else if (newX < this.maxScrollX) {
      newX = this.maxScrollX
    }

    if (newY > this.minScrollY) {
      newY = this.minScrollY
    } else if (newY < this.maxScrollY) {
      newY = this.maxScrollY
    }

    const needTriggerEnd = this.y === newY
    this.scrollTo(newX, newY, easeTime, ease.swipe)
    this.trigger('scroll', {
      x: this.x,
      y: this.y
    })
    clearTimeout(this.mouseWheelEndTimer)
    if (needTriggerEnd) {
      this.mouseWheelEndTimer = setTimeout(() => {
        this.trigger('scrollEnd', {
          x: this.x,
          y: this.y
        })
      }, easeTime)
    }
  }
}