-
Notifications
You must be signed in to change notification settings - Fork 0
/
ketikin.js
122 lines (98 loc) · 3.76 KB
/
ketikin.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
112
113
114
115
116
117
118
119
120
121
122
const ketikin = (selector, options) => {
const baseTypingSpeed = 5
const maxTypingSpeed = 100
const defaultTimeGap = 1000
const cursor = '<span id="{id}">|</span>'
fenceSpeed = (speed) => {
speed = speed > maxTypingSpeed ? maxTypingSpeed : speed
speed = speed < 0 ? 0 : speed
speed = maxTypingSpeed - speed
return speed
}
getSeq = (element) => {
return element.getAttribute('ketikin-seq')
}
getCursor = (element) => {
return element.querySelector('#' + getSeq(element)) || document.createElement("cursor")
}
animateCursor = (element) => {
getCursor(element).animate([{opacity: 0.5}], {
duration: defaultTimeGap,
iterations: Infinity
})
}
removeCursor = (element) => {
getCursor(element).remove()
}
addCursor = (element) => {
element.innerHTML = element.innerHTML + cursor.replace('{id}', getSeq(element))
}
addTypingChar = (element, char) => {
element.innerHTML = element.innerHTML + char
}
swapTypingText = (element, text) => {
element.innerHTML = text
}
type = (element, char, executionTime) => {
setTimeout(() => removeCursor(element) | addTypingChar(element, char) | addCursor(element), executionTime)
}
backSpace = (element, text, executionTime) => {
setTimeout(() => removeCursor(element) | swapTypingText(element, text) | addCursor(element), executionTime)
return text.substring(0, text.length - 1)
}
arrangeExecutionTime = (lastExecutionTime, speedBaseline) => {
return lastExecutionTime + Math.floor(Math.random() * fenceSpeed(options.speed)) + speedBaseline
}
orchestrate = (element, text, shouldBackSpacing, executionTime) => {
for(const char of text) {
type(element, char, executionTime)
executionTime = arrangeExecutionTime(executionTime, baseTypingSpeed)
}
if(shouldBackSpacing) {
let backSpacingSpeed = baseTypingSpeed
executionTime = executionTime + defaultTimeGap
while(text) {
text = backSpace(element, text, executionTime)
executionTime = arrangeExecutionTime(executionTime, backSpacingSpeed)
backSpacingSpeed = Math.floor(backSpacingSpeed * 0.7)
}
setTimeout(() => swapTypingText(element, text) | addCursor(element), executionTime)
}
return executionTime
}
playOrchestration = (element, texts, opts) => {
let executionTime = 0
let shouldBackSpacing = false
texts.forEach((text, index) => {
shouldBackSpacing = (index < texts.length - 1 && !opts.loop) || opts.loop
executionTime = orchestrate(element, text, shouldBackSpacing, executionTime) + defaultTimeGap
})
if(opts.loop) {
setTimeout(() => playOrchestration(element, texts, opts), executionTime)
} else {
setTimeout(() => animateCursor(element), executionTime)
}
}
setupOptions = (opts) => {
return Object.assign({
texts: null,
speed: 0,
loop: false
}, opts)
}
setupTexts = (element, opts) => {
return (opts.texts || [element.innerText]).filter(text => text)
}
setupElement = (element) => {
element.setAttribute('ketikin-seq', 'seq-' + Math.random().toString(36).substr(2))
element.innerHTML = ""
return element
}
document.querySelectorAll(selector).forEach(element => {
const opts = setupOptions(options)
const texts = setupTexts(element, opts)
if(texts.length > 0) {
playOrchestration(setupElement(element), texts, opts)
}
})
}