@linear_non/stellar-kit 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +69 -0
- package/classes/Component.js +46 -0
- package/classes/Manager.js +73 -0
- package/classes/Scrollbar.js +124 -0
- package/classes/Smooth.js +146 -0
- package/classes/index.js +6 -0
- package/events/Emitter.js +67 -0
- package/events/Mouse.js +60 -0
- package/events/Raf.js +122 -0
- package/events/Resize.js +70 -0
- package/events/Scroll.js +51 -0
- package/events/VirtualScroll.js +196 -0
- package/events/index.js +8 -0
- package/index.js +16 -0
- package/kitStore.js +35 -0
- package/package.json +41 -0
- package/utils/bounds.js +15 -0
- package/utils/index.js +8 -0
- package/utils/listener.js +12 -0
- package/utils/math.js +17 -0
- package/utils/selector.js +5 -0
- package/utils/sniffer.js +133 -0
- package/utils/splitText.js +45 -0
- package/utils/support.js +23 -0
- package/utils/window.js +33 -0
package/events/Resize.js
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Resize.js
|
|
2
|
+
import { sizes, flags, breakpoints } from "../kitStore"
|
|
3
|
+
import emitter, { EVENTS } from "./Emitter"
|
|
4
|
+
import debounce from "lodash.debounce"
|
|
5
|
+
import { sniffer, getWindowSizes, getViewport, setViewportHeight } from "../utils"
|
|
6
|
+
|
|
7
|
+
export default class Resize {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.wasDesktop = sniffer.isDesktop
|
|
10
|
+
this.init()
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
onResize = () => {
|
|
14
|
+
flags.isResizing = true
|
|
15
|
+
|
|
16
|
+
const { width, height } = getViewport()
|
|
17
|
+
const bp = getWindowSizes()
|
|
18
|
+
sniffer.update() // Update sniffer after viewport read
|
|
19
|
+
|
|
20
|
+
Object.assign(sizes, {
|
|
21
|
+
vw: width,
|
|
22
|
+
vh: height,
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
Object.assign(breakpoints, {
|
|
26
|
+
xsmallDown: bp.XS,
|
|
27
|
+
smallDown: bp.S,
|
|
28
|
+
smallUp: bp.S_UP,
|
|
29
|
+
mediumDown: bp.M,
|
|
30
|
+
mediumUp: bp.M_UP,
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
this.updateBodyClasses()
|
|
34
|
+
this.updateVH()
|
|
35
|
+
this.reloadIfBreakpointChanged()
|
|
36
|
+
|
|
37
|
+
emitter.emit(EVENTS.APP_RESIZE)
|
|
38
|
+
|
|
39
|
+
flags.isResizing = false
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
reloadIfBreakpointChanged() {
|
|
43
|
+
const isNowDesktop = sniffer.isDesktop
|
|
44
|
+
if (this.wasDesktop !== isNowDesktop) {
|
|
45
|
+
location.reload()
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
updateVH() {
|
|
50
|
+
setViewportHeight(sizes.vh)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
updateBodyClasses() {
|
|
54
|
+
const { vw, vh } = sizes
|
|
55
|
+
const orientationClass = vw > vh ? "is-landscape" : "is-portrait"
|
|
56
|
+
const deviceClass = sniffer.isDesktop ? "is-desktop" : "is-device"
|
|
57
|
+
|
|
58
|
+
document.body.classList.add(orientationClass)
|
|
59
|
+
document.body.classList.add(deviceClass)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
on() {
|
|
63
|
+
window.addEventListener("resize", debounce(this.onResize, 200))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
init() {
|
|
67
|
+
this.on()
|
|
68
|
+
this.onResize() // Run once on init
|
|
69
|
+
}
|
|
70
|
+
}
|
package/events/Scroll.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// Scroll.js
|
|
2
|
+
import { sizes } from "../kitStore"
|
|
3
|
+
import emitter, { EVENTS } from "./Emitter"
|
|
4
|
+
import VirtualScroll from "./VirtualScroll"
|
|
5
|
+
import { bounds, sniffer } from "../utils"
|
|
6
|
+
|
|
7
|
+
export default class Scroll {
|
|
8
|
+
constructor({ isSmooth = false } = {}) {
|
|
9
|
+
this.isSmooth = isSmooth
|
|
10
|
+
|
|
11
|
+
if (this.isSmooth) {
|
|
12
|
+
document.body.classList.add("is-smooth")
|
|
13
|
+
|
|
14
|
+
this.virtualScroll = new VirtualScroll({
|
|
15
|
+
mouseMultiplier: sniffer.isWindows ? 1.1 : 0.45,
|
|
16
|
+
touchMultiplier: 3.5,
|
|
17
|
+
firefoxMultiplier: sniffer.isWindows ? 40 : 90,
|
|
18
|
+
passive: true,
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
this.virtualScroll.on(this.onVirtualScroll)
|
|
22
|
+
} else {
|
|
23
|
+
this.onScroll = this.onScroll.bind(this)
|
|
24
|
+
document.addEventListener("scroll", this.onScroll, true)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
this.setScrollBounds()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
setScrollBounds() {
|
|
31
|
+
const el = document.querySelector("[data-view]") || document.body
|
|
32
|
+
const height = bounds(el).height
|
|
33
|
+
sizes.fh = height > sizes.vh ? height - sizes.vh : 0
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
onVirtualScroll = e => {
|
|
37
|
+
emitter.emit(EVENTS.APP_SCROLL, { y: e.deltaY * -1 })
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
onScroll(e) {
|
|
41
|
+
emitter.emit(EVENTS.APP_SCROLL, { y: window.scrollY })
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
destroy() {
|
|
45
|
+
if (this.virtualScroll) {
|
|
46
|
+
this.virtualScroll.destroy()
|
|
47
|
+
} else {
|
|
48
|
+
document.removeEventListener("scroll", this.onScroll, true)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
// VirtualScroll.js
|
|
2
|
+
import kitStore from "../kitStore"
|
|
3
|
+
import emitter from "./Emitter"
|
|
4
|
+
import { supportMouseTouch } from "../utils"
|
|
5
|
+
import Lethargy from "lethargy"
|
|
6
|
+
|
|
7
|
+
const EVT_ID = "virtualscroll"
|
|
8
|
+
|
|
9
|
+
export default class VirtualScroll {
|
|
10
|
+
constructor(obj = {}) {
|
|
11
|
+
this.options = {
|
|
12
|
+
mouseMultiplier: 1,
|
|
13
|
+
touchMultiplier: 2,
|
|
14
|
+
firefoxMultiplier: 15,
|
|
15
|
+
keyStep: 120,
|
|
16
|
+
preventTouch: false,
|
|
17
|
+
unpreventTouchClass: "vs-touchmove-allowed",
|
|
18
|
+
limitInertia: false,
|
|
19
|
+
useKeyboard: true,
|
|
20
|
+
useTouch: true,
|
|
21
|
+
...obj,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
this.el = obj.el || window
|
|
25
|
+
this.event = { x: 0, y: 0, deltaX: 0, deltaY: 0 }
|
|
26
|
+
this.touchStartX = null
|
|
27
|
+
this.touchStartY = null
|
|
28
|
+
this.bodyTouchAction = null
|
|
29
|
+
this.focusingInput = false
|
|
30
|
+
|
|
31
|
+
if (this.options.limitInertia) this.lethargy = new Lethargy()
|
|
32
|
+
|
|
33
|
+
if (this.options.passive !== undefined) {
|
|
34
|
+
this.listenerOptions = { passive: this.options.passive }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
document.addEventListener("focusin", () => (this.focusingInput = true))
|
|
38
|
+
document.addEventListener("focusout", () => (this.focusingInput = false))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
notify(e) {
|
|
42
|
+
const evt = this.event
|
|
43
|
+
evt.x += evt.deltaX
|
|
44
|
+
evt.y += evt.deltaY
|
|
45
|
+
|
|
46
|
+
emitter.emit(EVT_ID, {
|
|
47
|
+
x: evt.x,
|
|
48
|
+
y: evt.y,
|
|
49
|
+
deltaX: evt.deltaX,
|
|
50
|
+
deltaY: evt.deltaY,
|
|
51
|
+
originalEvent: e,
|
|
52
|
+
})
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
onWheel = e => {
|
|
56
|
+
if (this.lethargy && !this.lethargy.check(e)) return
|
|
57
|
+
|
|
58
|
+
const evt = this.event
|
|
59
|
+
evt.deltaX = e.wheelDeltaX || e.deltaX * -1
|
|
60
|
+
evt.deltaY = e.wheelDeltaY || e.deltaY * -1
|
|
61
|
+
|
|
62
|
+
if (supportMouseTouch().isFirefox && e.deltaMode === 1) {
|
|
63
|
+
evt.deltaX *= this.options.firefoxMultiplier
|
|
64
|
+
evt.deltaY *= this.options.firefoxMultiplier
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
evt.deltaX *= this.options.mouseMultiplier
|
|
68
|
+
evt.deltaY *= this.options.mouseMultiplier
|
|
69
|
+
|
|
70
|
+
this.notify(e)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
onMouseWheel = e => {
|
|
74
|
+
if (this.options.limitInertia && !this.lethargy.check(e)) return
|
|
75
|
+
|
|
76
|
+
const evt = this.event
|
|
77
|
+
evt.deltaX = e.wheelDeltaX || 0
|
|
78
|
+
evt.deltaY = e.wheelDeltaY || e.wheelDelta
|
|
79
|
+
|
|
80
|
+
this.notify(e)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
onTouchStart = e => {
|
|
84
|
+
const t = e.targetTouches ? e.targetTouches[0] : e
|
|
85
|
+
this.touchStartX = t.pageX
|
|
86
|
+
this.touchStartY = t.pageY
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
onTouchMove = e => {
|
|
90
|
+
if (this.options.preventTouch && !e.target.classList.contains(this.options.unpreventTouchClass)) {
|
|
91
|
+
e.preventDefault()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const evt = this.event
|
|
95
|
+
const t = e.targetTouches ? e.targetTouches[0] : e
|
|
96
|
+
|
|
97
|
+
evt.deltaX = (t.pageX - this.touchStartX) * this.options.touchMultiplier
|
|
98
|
+
evt.deltaY = (t.pageY - this.touchStartY) * this.options.touchMultiplier
|
|
99
|
+
|
|
100
|
+
this.touchStartX = t.pageX
|
|
101
|
+
this.touchStartY = t.pageY
|
|
102
|
+
|
|
103
|
+
this.notify(e)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
onKeyDown = e => {
|
|
107
|
+
const evt = this.event
|
|
108
|
+
evt.deltaX = 0
|
|
109
|
+
evt.deltaY = 0
|
|
110
|
+
|
|
111
|
+
const height = window.innerHeight - 40
|
|
112
|
+
|
|
113
|
+
if (this.focusingInput || kitStore.flags.isFocus) return
|
|
114
|
+
|
|
115
|
+
switch (e.keyCode) {
|
|
116
|
+
case 37: // LEFT
|
|
117
|
+
case 38: // UP
|
|
118
|
+
evt.deltaY = this.options.keyStep
|
|
119
|
+
break
|
|
120
|
+
case 39: // RIGHT
|
|
121
|
+
case 40: // DOWN
|
|
122
|
+
evt.deltaY = -this.options.keyStep
|
|
123
|
+
break
|
|
124
|
+
case 32: // SPACE
|
|
125
|
+
evt.deltaY = e.shiftKey ? height : -height
|
|
126
|
+
break
|
|
127
|
+
default:
|
|
128
|
+
return
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
this.notify(e)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
bind = () => {
|
|
135
|
+
const support = supportMouseTouch()
|
|
136
|
+
|
|
137
|
+
if (support.hasWheelEvent) this.el.addEventListener("wheel", this.onWheel, this.listenerOptions)
|
|
138
|
+
|
|
139
|
+
if (support.hasMouseWheelEvent)
|
|
140
|
+
this.el.addEventListener("mousewheel", this.onMouseWheel, this.listenerOptions)
|
|
141
|
+
|
|
142
|
+
if (support.hasTouch && this.options.useTouch) {
|
|
143
|
+
this.el.addEventListener("touchstart", this.onTouchStart, this.listenerOptions)
|
|
144
|
+
this.el.addEventListener("touchmove", this.onTouchMove, this.listenerOptions)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (support.hasPointer && support.hasTouchWin) {
|
|
148
|
+
this.bodyTouchAction = document.body.style.msTouchAction
|
|
149
|
+
document.body.style.msTouchAction = "none"
|
|
150
|
+
this.el.addEventListener("MSPointerDown", this.onTouchStart, true)
|
|
151
|
+
this.el.addEventListener("MSPointerMove", this.onTouchMove, true)
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (support.hasKeyDown && this.options.useKeyboard) document.addEventListener("keydown", this.onKeyDown)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
unbind = () => {
|
|
158
|
+
const support = supportMouseTouch()
|
|
159
|
+
|
|
160
|
+
if (support.hasWheelEvent) this.el.removeEventListener("wheel", this.onWheel)
|
|
161
|
+
if (support.hasMouseWheelEvent) this.el.removeEventListener("mousewheel", this.onMouseWheel)
|
|
162
|
+
if (support.hasTouch) {
|
|
163
|
+
this.el.removeEventListener("touchstart", this.onTouchStart)
|
|
164
|
+
this.el.removeEventListener("touchmove", this.onTouchMove)
|
|
165
|
+
}
|
|
166
|
+
if (support.hasPointer && support.hasTouchWin) {
|
|
167
|
+
document.body.style.msTouchAction = this.bodyTouchAction
|
|
168
|
+
this.el.removeEventListener("MSPointerDown", this.onTouchStart, true)
|
|
169
|
+
this.el.removeEventListener("MSPointerMove", this.onTouchMove, true)
|
|
170
|
+
}
|
|
171
|
+
if (support.hasKeyDown && this.options.useKeyboard)
|
|
172
|
+
document.removeEventListener("keydown", this.onKeyDown)
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
on = (cb, priority = 0) => {
|
|
176
|
+
emitter.on(EVT_ID, cb, priority)
|
|
177
|
+
const eventCount = emitter.events?.[EVT_ID]?.length || 0
|
|
178
|
+
if (eventCount === 1) this.bind() // Bind only once
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
off = cb => {
|
|
182
|
+
emitter.off(EVT_ID, cb)
|
|
183
|
+
const eventCount = emitter.events?.[EVT_ID]?.length || 0
|
|
184
|
+
if (eventCount === 0) this.unbind() // Unbind when no listeners left
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
reset = () => {
|
|
188
|
+
this.event.x = 0
|
|
189
|
+
this.event.y = 0
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
destroy = () => {
|
|
193
|
+
this.unbind()
|
|
194
|
+
emitter.off(EVT_ID, () => {}) // Clear all
|
|
195
|
+
}
|
|
196
|
+
}
|
package/events/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import emitter, { EVENTS, PRIORITY } from "./Emitter"
|
|
2
|
+
import VirtualScroll from "./VirtualScroll"
|
|
3
|
+
import Scroll from "./Scroll"
|
|
4
|
+
import Resize from "./Resize"
|
|
5
|
+
import Mouse from "./Mouse"
|
|
6
|
+
import Raf from "./Raf"
|
|
7
|
+
|
|
8
|
+
export { emitter, EVENTS, PRIORITY, Resize, VirtualScroll, Scroll, Mouse, Raf }
|
package/index.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// stellar-kit/index.js
|
|
2
|
+
import { Mouse, Resize, Raf, Scroll } from "./events"
|
|
3
|
+
import kitStore from "./kitStore"
|
|
4
|
+
import { sniffer } from "./utils"
|
|
5
|
+
|
|
6
|
+
export function setupKit({ isSmooth = sniffer.isDesktop } = {}) {
|
|
7
|
+
kitStore.flags.isSmooth = isSmooth
|
|
8
|
+
kitStore.flags.isLocked = false
|
|
9
|
+
|
|
10
|
+
kitStore.resize = new Resize()
|
|
11
|
+
kitStore.raf = new Raf()
|
|
12
|
+
kitStore.scroll = new Scroll({ isSmooth })
|
|
13
|
+
kitStore.mouse = new Mouse()
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export { kitStore }
|
package/kitStore.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
//kitStore.js
|
|
2
|
+
export const sizes = {
|
|
3
|
+
vw: 0,
|
|
4
|
+
vh: 0,
|
|
5
|
+
fh: 0,
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const flags = {
|
|
9
|
+
isFocus: false,
|
|
10
|
+
isSmooth: false,
|
|
11
|
+
isResizing: false,
|
|
12
|
+
// ... optionally others
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const breakpoints = {
|
|
16
|
+
xsmallDown: false,
|
|
17
|
+
smallDown: false,
|
|
18
|
+
smallUp: false,
|
|
19
|
+
mediumDown: false,
|
|
20
|
+
mediumUp: false,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export const mouse = {
|
|
24
|
+
el: null,
|
|
25
|
+
x: 0,
|
|
26
|
+
y: 0,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default {
|
|
30
|
+
sizes,
|
|
31
|
+
breakpoints,
|
|
32
|
+
mouse,
|
|
33
|
+
flags,
|
|
34
|
+
pageContent: null, // This should be set to the main content element
|
|
35
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@linear_non/stellar-kit",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Stellar frontend core for Non-Linear Studio projects.",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": "./index.js"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"scripts": {
|
|
11
|
+
"dev": "vite",
|
|
12
|
+
"format": "prettier --write .",
|
|
13
|
+
"check": "vite build --emptyOutDir --watch"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"classes/",
|
|
17
|
+
"events/",
|
|
18
|
+
"utils/",
|
|
19
|
+
"kitStore.js",
|
|
20
|
+
"index.js"
|
|
21
|
+
],
|
|
22
|
+
"keywords": [
|
|
23
|
+
"non-linear",
|
|
24
|
+
"frontend",
|
|
25
|
+
"core",
|
|
26
|
+
"scroll",
|
|
27
|
+
"raf",
|
|
28
|
+
"gsap"
|
|
29
|
+
],
|
|
30
|
+
"author": "Non-Linear Studio",
|
|
31
|
+
"license": "MIT",
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"gsap": "^3.12.5",
|
|
34
|
+
"lethargy": "^1.0.9",
|
|
35
|
+
"lodash.debounce": "^4.0.8"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@linear_non/prettier-config": "^1.0.5",
|
|
39
|
+
"vite": "^5.2.0"
|
|
40
|
+
}
|
|
41
|
+
}
|
package/utils/bounds.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
// bounds.js
|
|
2
|
+
export const bounds = el => {
|
|
3
|
+
const rect = el.getBoundingClientRect()
|
|
4
|
+
|
|
5
|
+
return {
|
|
6
|
+
bottom: rect.bottom,
|
|
7
|
+
left: rect.left,
|
|
8
|
+
height: rect.height,
|
|
9
|
+
right: rect.right,
|
|
10
|
+
top: rect.top,
|
|
11
|
+
width: rect.width,
|
|
12
|
+
x: rect.x,
|
|
13
|
+
y: rect.y,
|
|
14
|
+
}
|
|
15
|
+
}
|
package/utils/index.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { qs, qsa, getid, gettag } from "./selector"
|
|
2
|
+
export { bounds } from "./bounds"
|
|
3
|
+
export { getViewport, getWindowSizes, setViewportHeight } from "./window"
|
|
4
|
+
export { sniffer } from "./sniffer"
|
|
5
|
+
export { lerp, clamp, norm, round } from "./math"
|
|
6
|
+
export { supportWebp, supportMouseTouch } from "./support"
|
|
7
|
+
export { listener } from "./listener"
|
|
8
|
+
export { splitText, reverseSplit } from "./splitText"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// listener.js
|
|
2
|
+
export const listener = (el, action, type, cb, p) => {
|
|
3
|
+
const passive = p === true ? { passive: true } : false
|
|
4
|
+
|
|
5
|
+
if (el.length) {
|
|
6
|
+
for (let i = 0; i < el.length; i++) {
|
|
7
|
+
el[i][action + "EventListener"](type, cb, passive)
|
|
8
|
+
}
|
|
9
|
+
} else {
|
|
10
|
+
el[action + "EventListener"](type, cb, passive)
|
|
11
|
+
}
|
|
12
|
+
}
|
package/utils/math.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// math.js
|
|
2
|
+
export const lerp = (a, b, n) => {
|
|
3
|
+
return a * (1 - n) + b * n
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export const norm = (val, min, max) => {
|
|
7
|
+
return (val - min) / (max - min)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const clamp = (val, min, max) => {
|
|
11
|
+
return Math.min(Math.max(val, min), max)
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const round = (n, p) => {
|
|
15
|
+
const precision = p !== undefined ? Math.pow(10, p) : 1000
|
|
16
|
+
return Math.round(n * precision) / precision
|
|
17
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// selector.js
|
|
2
|
+
export const qs = (s, o = document) => o.querySelector(s)
|
|
3
|
+
export const qsa = (s, o = document) => Array.from(o.querySelectorAll(s))
|
|
4
|
+
export const getid = (s, o = document) => o.getElementById(s)
|
|
5
|
+
export const gettag = (s, o = document) => o.getElementsByTagName(s)
|
package/utils/sniffer.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
// sniffer.js
|
|
2
|
+
export const sniffer = {
|
|
3
|
+
uA: navigator.userAgent.toLowerCase(),
|
|
4
|
+
|
|
5
|
+
get isWindowsMobile() {
|
|
6
|
+
return /windows phone|iemobile|wpdesktop/.test(this.uA)
|
|
7
|
+
},
|
|
8
|
+
|
|
9
|
+
get isMobileOpera() {
|
|
10
|
+
return /opera mini/i.test(this.uA)
|
|
11
|
+
},
|
|
12
|
+
|
|
13
|
+
get isIOS() {
|
|
14
|
+
return /iphone|ipad|ipod/i.test(this.uA)
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
get isIpad() {
|
|
18
|
+
return !this.isWindowsMobile && /ipad/i.test(this.uA) && this.isIOS
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
get isLatestIpad() {
|
|
22
|
+
return /Macintosh/i.test(navigator.userAgent) && navigator.maxTouchPoints && navigator.maxTouchPoints > 1
|
|
23
|
+
},
|
|
24
|
+
|
|
25
|
+
get isIphone() {
|
|
26
|
+
return !this.isWindowsMobile && /iphone/i.test(this.uA) && this.isIOS
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
get isMobileAndroid() {
|
|
30
|
+
return !this.isWindowsMobile && /android.*mobile/.test(this.uA)
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
get isTabletAndroid() {
|
|
34
|
+
return !this.isWindowsMobile && !this.isMobileAndroid && /android/i.test(this.uA)
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
get isAndroid() {
|
|
38
|
+
return this.isMobileAndroid || this.isTabletAndroid
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
get isPhone() {
|
|
42
|
+
return this.isMobileAndroid || (this.isIOS && !this.isIpad) || this.isWindowsMobile
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
get isTablet() {
|
|
46
|
+
return this.isTabletAndroid || this.isIpad || this.isLatestIpad
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
get isDevice() {
|
|
50
|
+
return this.isPhone || this.isTablet
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
get isFirefox() {
|
|
54
|
+
return this.uA.includes("firefox")
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
get isSafari() {
|
|
58
|
+
return !!this.uA.match(/version\/[\d\.]+.*safari/)
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
get isOpera() {
|
|
62
|
+
return this.uA.includes("opr")
|
|
63
|
+
},
|
|
64
|
+
|
|
65
|
+
get isIE11() {
|
|
66
|
+
return !window.ActiveXObject && "ActiveXObject" in window
|
|
67
|
+
},
|
|
68
|
+
|
|
69
|
+
get isIE() {
|
|
70
|
+
return this.uA.includes("msie") || this.isIE11 || this.uA.includes("edge")
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
get isEdge() {
|
|
74
|
+
return this.uA.includes("edge")
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
get isWindows() {
|
|
78
|
+
return ["Win32", "Win64", "Windows", "WinCE"].includes(window.navigator.platform)
|
|
79
|
+
},
|
|
80
|
+
|
|
81
|
+
get isChrome() {
|
|
82
|
+
return (
|
|
83
|
+
window.chrome !== null &&
|
|
84
|
+
window.chrome !== undefined &&
|
|
85
|
+
navigator.vendor.toLowerCase() === "google inc." &&
|
|
86
|
+
!this.isOpera &&
|
|
87
|
+
!this.isEdge
|
|
88
|
+
)
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
get isMac() {
|
|
92
|
+
return navigator.platform.toLowerCase().includes("mac")
|
|
93
|
+
},
|
|
94
|
+
|
|
95
|
+
get isDesktop() {
|
|
96
|
+
return !this.isPhone && !this.isTablet
|
|
97
|
+
},
|
|
98
|
+
|
|
99
|
+
get isTouch() {
|
|
100
|
+
return "ontouchstart" in window
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
get sniff() {
|
|
104
|
+
return {
|
|
105
|
+
isWindowsMobile: this.isWindowsMobile,
|
|
106
|
+
isMobileOpera: this.isMobileOpera,
|
|
107
|
+
isIOS: this.isIOS,
|
|
108
|
+
isIpad: this.isIpad,
|
|
109
|
+
isIphone: this.isIphone,
|
|
110
|
+
isMobileAndroid: this.isMobileAndroid,
|
|
111
|
+
isTabletAndroid: this.isTabletAndroid,
|
|
112
|
+
isAndroid: this.isAndroid,
|
|
113
|
+
isFirefox: this.isFirefox,
|
|
114
|
+
isSafari: this.isSafari,
|
|
115
|
+
isOpera: this.isOpera,
|
|
116
|
+
isIE11: this.isIE11,
|
|
117
|
+
isIE: this.isIE,
|
|
118
|
+
isEdge: this.isEdge,
|
|
119
|
+
isChrome: this.isChrome,
|
|
120
|
+
isMac: this.isMac,
|
|
121
|
+
isPhone: this.isPhone,
|
|
122
|
+
isTablet: this.isTablet,
|
|
123
|
+
isDevice: this.isDevice,
|
|
124
|
+
isDesktop: this.isDesktop,
|
|
125
|
+
isWindows: this.isWindows,
|
|
126
|
+
isTouch: this.isTouch,
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
|
|
130
|
+
update() {
|
|
131
|
+
this.uA = navigator.userAgent.toLowerCase()
|
|
132
|
+
},
|
|
133
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// splitText.js
|
|
2
|
+
import { emitter, EVENTS } from "../events"
|
|
3
|
+
import { SplitText } from "../libraries/gsap"
|
|
4
|
+
|
|
5
|
+
// Emits `APP_SPLITTEXT_READY` once all elements are split
|
|
6
|
+
export const splitText = elements => {
|
|
7
|
+
const splits = []
|
|
8
|
+
let splitted = 0
|
|
9
|
+
const totalSplits = elements.length
|
|
10
|
+
|
|
11
|
+
elements.forEach(el => {
|
|
12
|
+
const dataset = el.dataset.split
|
|
13
|
+
const types = dataset.split(",")
|
|
14
|
+
const classes = {}
|
|
15
|
+
|
|
16
|
+
types.forEach((type, i) => {
|
|
17
|
+
const t = type.trim()
|
|
18
|
+
if (t === "lines") classes.linesClass = `line-${i}`
|
|
19
|
+
if (t === "words") classes.wordsClass = `word-${i}`
|
|
20
|
+
if (t === "chars") classes.charsClass = `char-${i} chr-++`
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
const splitInstance = new SplitText(el, {
|
|
24
|
+
type: dataset.toString(),
|
|
25
|
+
...classes,
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
splits.push(splitInstance)
|
|
29
|
+
splitted++
|
|
30
|
+
|
|
31
|
+
if (splitted === totalSplits) {
|
|
32
|
+
setTimeout(() => {
|
|
33
|
+
emitter.emit(EVENTS.APP_SPLITTEXT_READY, splits)
|
|
34
|
+
}, 0)
|
|
35
|
+
}
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
return splits
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const reverseSplit = instances => {
|
|
42
|
+
instances.forEach(split => {
|
|
43
|
+
split.revert()
|
|
44
|
+
})
|
|
45
|
+
}
|
package/utils/support.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// support.js
|
|
2
|
+
export const supportWebp = () => {
|
|
3
|
+
const el = document.createElement("canvas")
|
|
4
|
+
if (el.getContext && el.getContext("2d")) {
|
|
5
|
+
return el.toDataURL("image/webp").indexOf("data:image/webp") === 0
|
|
6
|
+
}
|
|
7
|
+
return false
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const supportMouseTouch = () => {
|
|
11
|
+
return {
|
|
12
|
+
hasWheelEvent: "onwheel" in document,
|
|
13
|
+
hasMouseWheelEvent: "onmousewheel" in document,
|
|
14
|
+
hasTouch:
|
|
15
|
+
"ontouchstart" in window ||
|
|
16
|
+
window.TouchEvent ||
|
|
17
|
+
(window.DocumentTouch && document instanceof DocumentTouch),
|
|
18
|
+
hasTouchWin: navigator.msMaxTouchPoints && navigator.msMaxTouchPoints > 1,
|
|
19
|
+
hasPointer: !!window.navigator.msPointerEnabled,
|
|
20
|
+
hasKeyDown: "onkeydown" in document,
|
|
21
|
+
isFirefox: navigator.userAgent.indexOf("Firefox") > -1,
|
|
22
|
+
}
|
|
23
|
+
}
|