-
Notifications
You must be signed in to change notification settings - Fork 60
/
number-flip.js
111 lines (105 loc) · 3.1 KB
/
number-flip.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import { g } from 'gelerator'
const maxLenNum = (aNum, bNum) => (aNum > bNum ? aNum : bNum).toString().length
const num2PadNumArr = (num, len) => {
const padLeftStr = (rawStr, lenNum) => (rawStr.length < lenNum
? padLeftStr('0' + rawStr, lenNum)
: rawStr)
const str2NumArr = rawStr => rawStr.split('').map(Number)
return str2NumArr(padLeftStr(num.toString(), len)).reverse()
}
export class Flip {
constructor({
node,
from = 0,
to,
duration = .5,
delay,
easeFn = (pos => (pos /= .5) < 1
? .5 * Math.pow(pos, 3)
: .5 * (Math.pow((pos - 2), 3) + 2)),
systemArr = [...Array(10).keys()],
direct = true
}) {
this.beforeArr = []
this.afterArr = []
this.ctnrArr = []
this.duration = duration * 1000
this.systemArr = systemArr
this.easeFn = easeFn
this.from = from
this.to = to || 0
this.node = node
this.direct = direct
this._initHTML(maxLenNum(this.from, this.to))
if (to === undefined) return
if (delay) setTimeout(() => this.flipTo({to: this.to, direct}), delay * 1000)
else this.flipTo({to: this.to, direct})
}
_initHTML(digits) {
this.node.classList.add('number-flip')
this.node.style.position = 'relative'
this.node.style.overflow = 'hidden'
;[...Array(digits).keys()].forEach(i => {
const ctnr = g(`ctnr ctnr${i}`)(
...this.systemArr.map(i => g('digit')(i)),
g('digit')(this.systemArr[0])
)
ctnr.style.position = 'relative'
ctnr.style.display = 'inline-block'
this.ctnrArr.unshift(ctnr)
this.node.appendChild(ctnr)
this.beforeArr.push(0)
})
this.height = this.ctnrArr[0].clientHeight / (this.systemArr.length + 1)
this.node.style.height = this.height + 'px'
for (let d = 0, len = this.ctnrArr.length; d < len; d += 1)
this._draw({
digit: d,
per: 1,
alter: ~~(this.from / Math.pow(10, d))
})
}
_draw({per, alter, digit}) {
const from = this.beforeArr[digit]
const modNum = ((per * alter + from) % 10 + 10) % 10
const translateY = `translateY(${- modNum * this.height}px)`
this.ctnrArr[digit].style.webkitTransform = translateY
this.ctnrArr[digit].style.transform = translateY
}
flipTo({
to,
duration,
easeFn,
direct = true
}) {
const len = this.ctnrArr.length
this.beforeArr = num2PadNumArr(this.from, len)
this.afterArr = num2PadNumArr(to, len)
const draw = per => {
let temp = 0
for (let d = this.ctnrArr.length - 1; d >= 0; d -= 1) {
let alter = this.afterArr[d] - this.beforeArr[d]
temp += alter
const fn = easeFn || this.easeFn
this._draw({
digit: d,
per: fn(per),
alter: direct ? alter : temp
})
temp *= 10
}
}
const start = performance.now()
const dur = (duration * 1000) || this.duration
const tick = now => {
let elapsed = now - start
draw(elapsed / dur)
if (elapsed < dur) requestAnimationFrame(tick)
else {
this.from = to
draw(1)
}
}
requestAnimationFrame(tick)
}
}