time-format-js
Version:
Localized time formatting library.
393 lines (331 loc) • 9.21 kB
JavaScript
import { getRect, prepend, removeChild } from '../util/dom'
import { ease } from '../util/ease'
import { extend } from '../util/lang'
import { warn } from '../util/debug'
export function snapMixin(BScroll) {
BScroll.prototype._initSnap = function () {
this.currentPage = {}
const snap = this.options.snap
if (snap.loop) {
let children = this.scroller.children
if (children.length > 1) {
prepend(children[children.length - 1].cloneNode(true), this.scroller)
this.scroller.appendChild(children[1].cloneNode(true))
} else {
// Loop does not make any sense if there is only one child.
snap.loop = false
}
}
let el = snap.el
if (typeof el === 'string') {
el = this.scroller.querySelectorAll(el)
}
this.on('refresh', () => {
this.pages = []
if (!this.wrapperWidth || !this.wrapperHeight || !this.scrollerWidth || !this.scrollerHeight) {
return
}
let stepX = snap.stepX || this.wrapperWidth
let stepY = snap.stepY || this.wrapperHeight
let x = 0
let y
let cx
let cy
let i = 0
let l
let m = 0
let n
let rect
if (!el) {
cx = Math.round(stepX / 2)
cy = Math.round(stepY / 2)
while (x > -this.scrollerWidth) {
this.pages[i] = []
l = 0
y = 0
while (y > -this.scrollerHeight) {
this.pages[i][l] = {
x: Math.max(x, this.maxScrollX),
y: Math.max(y, this.maxScrollY),
width: stepX,
height: stepY,
cx: x - cx,
cy: y - cy
}
y -= stepY
l++
}
x -= stepX
i++
}
} else {
l = el.length
n = -1
for (; i < l; i++) {
rect = getRect(el[i])
if (i === 0 || rect.left <= getRect(el[i - 1]).left) {
m = 0
n++
}
if (!this.pages[m]) {
this.pages[m] = []
}
x = Math.max(-rect.left, this.maxScrollX)
y = Math.max(-rect.top, this.maxScrollY)
cx = x - Math.round(rect.width / 2)
cy = y - Math.round(rect.height / 2)
this.pages[m][n] = {
x: x,
y: y,
width: rect.width,
height: rect.height,
cx: cx,
cy: cy
}
if (x > this.maxScrollX) {
m++
}
}
}
this._checkSnapLoop()
let initPageX = snap._loopX ? 1 : 0
let initPageY = snap._loopY ? 1 : 0
this._goToPage(this.currentPage.pageX || initPageX, this.currentPage.pageY || initPageY, 0)
// Update snap threshold if needed.
const snapThreshold = snap.threshold
if (snapThreshold % 1 === 0) {
this.snapThresholdX = snapThreshold
this.snapThresholdY = snapThreshold
} else {
this.snapThresholdX = Math.round(this.pages[this.currentPage.pageX][this.currentPage.pageY].width * snapThreshold)
this.snapThresholdY = Math.round(this.pages[this.currentPage.pageX][this.currentPage.pageY].height * snapThreshold)
}
})
this.on('scrollEnd', () => {
if (snap.loop) {
if (snap._loopX) {
if (this.currentPage.pageX === 0) {
this._goToPage(this.pages.length - 2, this.currentPage.pageY, 0)
}
if (this.currentPage.pageX === this.pages.length - 1) {
this._goToPage(1, this.currentPage.pageY, 0)
}
} else {
if (this.currentPage.pageY === 0) {
this._goToPage(this.currentPage.pageX, this.pages[0].length - 2, 0)
}
if (this.currentPage.pageY === this.pages[0].length - 1) {
this._goToPage(this.currentPage.pageX, 1, 0)
}
}
}
})
if (snap.listenFlick !== false) {
this.on('flick', () => {
let time = snap.speed || Math.max(
Math.max(
Math.min(Math.abs(this.x - this.startX), 1000),
Math.min(Math.abs(this.y - this.startY), 1000)
), 300)
this._goToPage(
this.currentPage.pageX + this.directionX,
this.currentPage.pageY + this.directionY,
time
)
})
}
this.on('destroy', () => {
if (snap.loop) {
let children = this.scroller.children
if (children.length > 2) {
removeChild(this.scroller, children[children.length - 1])
removeChild(this.scroller, children[0])
}
}
})
}
BScroll.prototype._checkSnapLoop = function () {
const snap = this.options.snap
if (!snap.loop || !this.pages || !this.pages.length) {
return
}
if (this.pages.length > 1) {
snap._loopX = true
}
if (this.pages[0] && this.pages[0].length > 1) {
snap._loopY = true
}
if (snap._loopX && snap._loopY) {
warn('Loop does not support two direction at the same time.')
}
}
BScroll.prototype._nearestSnap = function (x, y) {
if (!this.pages.length) {
return {x: 0, y: 0, pageX: 0, pageY: 0}
}
let i = 0
// Check if we exceeded the snap threshold
if (Math.abs(x - this.absStartX) <= this.snapThresholdX &&
Math.abs(y - this.absStartY) <= this.snapThresholdY) {
return this.currentPage
}
if (x > this.minScrollX) {
x = this.minScrollX
} else if (x < this.maxScrollX) {
x = this.maxScrollX
}
if (y > this.minScrollY) {
y = this.minScrollY
} else if (y < this.maxScrollY) {
y = this.maxScrollY
}
let l = this.pages.length
for (; i < l; i++) {
if (x >= this.pages[i][0].cx) {
x = this.pages[i][0].x
break
}
}
l = this.pages[i].length
let m = 0
for (; m < l; m++) {
if (y >= this.pages[0][m].cy) {
y = this.pages[0][m].y
break
}
}
if (i === this.currentPage.pageX) {
i += this.directionX
if (i < 0) {
i = 0
} else if (i >= this.pages.length) {
i = this.pages.length - 1
}
x = this.pages[i][0].x
}
if (m === this.currentPage.pageY) {
m += this.directionY
if (m < 0) {
m = 0
} else if (m >= this.pages[0].length) {
m = this.pages[0].length - 1
}
y = this.pages[0][m].y
}
return {
x,
y,
pageX: i,
pageY: m
}
}
BScroll.prototype._goToPage = function (x, y = 0, time, easing) {
const snap = this.options.snap
if (!snap || !this.pages || !this.pages.length) {
return
}
easing = easing || snap.easing || ease.bounce
if (x >= this.pages.length) {
x = this.pages.length - 1
} else if (x < 0) {
x = 0
}
if (!this.pages[x]) {
return
}
if (y >= this.pages[x].length) {
y = this.pages[x].length - 1
} else if (y < 0) {
y = 0
}
let posX = this.pages[x][y].x
let posY = this.pages[x][y].y
time = time === undefined ? snap.speed || Math.max(
Math.max(
Math.min(Math.abs(posX - this.x), 1000),
Math.min(Math.abs(posY - this.y), 1000)
), 300) : time
this.currentPage = {
x: posX,
y: posY,
pageX: x,
pageY: y
}
this.scrollTo(posX, posY, time, easing)
}
BScroll.prototype.goToPage = function (x, y, time, easing) {
const snap = this.options.snap
if (!snap || !this.pages || !this.pages.length) {
return
}
if (snap.loop) {
let len
if (snap._loopX) {
len = this.pages.length - 2
if (x >= len) {
x = len - 1
} else if (x < 0) {
x = 0
}
x += 1
} else {
len = this.pages[0].length - 2
if (y >= len) {
y = len - 1
} else if (y < 0) {
y = 0
}
y += 1
}
}
this._goToPage(x, y, time, easing)
}
BScroll.prototype.next = function (time, easing) {
const snap = this.options.snap
if (!snap) {
return
}
let x = this.currentPage.pageX
let y = this.currentPage.pageY
x++
if (x >= this.pages.length && this.hasVerticalScroll) {
x = 0
y++
}
this._goToPage(x, y, time, easing)
}
BScroll.prototype.prev = function (time, easing) {
const snap = this.options.snap
if (!snap) {
return
}
let x = this.currentPage.pageX
let y = this.currentPage.pageY
x--
if (x < 0 && this.hasVerticalScroll) {
x = 0
y--
}
this._goToPage(x, y, time, easing)
}
BScroll.prototype.getCurrentPage = function () {
const snap = this.options.snap
if (!snap) {
return null
}
if (snap.loop) {
let currentPage
if (snap._loopX) {
currentPage = extend({}, this.currentPage, {
pageX: this.currentPage.pageX - 1
})
} else {
currentPage = extend({}, this.currentPage, {
pageY: this.currentPage.pageY - 1
})
}
return currentPage
}
return this.currentPage
}
}