@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/README.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# stellar-kit
|
|
2
|
+
|
|
3
|
+
A modular frontend utility kit for custom websites. Built for performance, extensibility, and clean developer ergonomics โ with support for smooth scrolling, events, tick updates, mouse tracking, and layout helpers.
|
|
4
|
+
|
|
5
|
+
## โจ Features
|
|
6
|
+
|
|
7
|
+
- RAF-based update loop (`Raf`)
|
|
8
|
+
- Virtual scroll support (`Scroll`)
|
|
9
|
+
- Smooth scrolling with transformable sections (`Smooth`)
|
|
10
|
+
- Viewport-based resize tracking (`Resize`)
|
|
11
|
+
- Custom scrollbar component
|
|
12
|
+
- Centralized event system (`Emitter`)
|
|
13
|
+
- Mouse tracking (`Mouse`)
|
|
14
|
+
- Utility helpers (DOM selection, bounds, clamping, etc.)
|
|
15
|
+
- Designed for use with Astro, GSAP, Three.js, and modular frontend setups
|
|
16
|
+
|
|
17
|
+
## ๐ฆ Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install stellar-kit
|
|
21
|
+
# Or from GitHub:
|
|
22
|
+
npm install nonlinearstudio/stellar-kit
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## ๐งฑ Folder Structure
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
stellar-kit/
|
|
29
|
+
โโโ classes/ # Core logic: Scroll, Smooth, Mouse, Scrollbar, etc.
|
|
30
|
+
โโโ events/ # Emitter system: Raf, Resize, Scroll, Mouse, etc.
|
|
31
|
+
โโโ utils/ # Utilities: bounds, clamp, selectors, etc.
|
|
32
|
+
โโโ kitStore.js # Central store shared across all modules
|
|
33
|
+
โโโ index.js # Entry point for setupKit and kitStore access
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## ๐งช Local Development
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
npm install
|
|
40
|
+
npm run dev
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
> The `dev/` folder provides a Vite playground to test modules like Smooth, Scrollbar, and events. You can import any part of the kit and prototype in isolation.
|
|
44
|
+
|
|
45
|
+
## ๐ ๏ธ Usage Example
|
|
46
|
+
|
|
47
|
+
```js
|
|
48
|
+
// main.js or index.js of your project
|
|
49
|
+
import { kitStore, setupKit } from "stellar-kit"
|
|
50
|
+
|
|
51
|
+
setupKit() // Initializes Resize, Raf, Scroll, Mouse, etc.
|
|
52
|
+
|
|
53
|
+
// Access anywhere
|
|
54
|
+
kitStore.scroll
|
|
55
|
+
kitStore.raf
|
|
56
|
+
kitStore.mouse
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
You can also import from specific paths:
|
|
60
|
+
|
|
61
|
+
```js
|
|
62
|
+
import { bounds, clamp, qs } from "stellar-kit/utils"
|
|
63
|
+
import { Manager } from "stellar-kit/classes"
|
|
64
|
+
import { Raf } from "stellar-kit/events"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
Made with โค๏ธ by [Non-Linear Studio](https://non-linear.studio)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// Component.js
|
|
2
|
+
import { qs, bounds } from "../utils"
|
|
3
|
+
|
|
4
|
+
export default class Component {
|
|
5
|
+
constructor(obj) {
|
|
6
|
+
this.el = obj.el || null
|
|
7
|
+
this.renderer = obj.renderer || null
|
|
8
|
+
const id = obj.index || 0
|
|
9
|
+
const data = obj.data || null
|
|
10
|
+
|
|
11
|
+
this.hasTick = data?.hasTick || false
|
|
12
|
+
this.hasAnimateIn = data?.hasAnimateIn || false
|
|
13
|
+
this.hasAnimateOut = data?.hasAnimateOut || false
|
|
14
|
+
this.hasAnimateOnScroll = data?.hasAnimateOnScroll || false
|
|
15
|
+
|
|
16
|
+
if (this.el) this.rect = bounds(this.el)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
setup() {
|
|
20
|
+
if (!this.name) return
|
|
21
|
+
this.el = qs(`[data-${this.name}]`)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
animateIn() {}
|
|
25
|
+
animateOut() {}
|
|
26
|
+
animateOnScroll() {}
|
|
27
|
+
|
|
28
|
+
tick(obj) {}
|
|
29
|
+
resize() {}
|
|
30
|
+
smoothResize() {}
|
|
31
|
+
|
|
32
|
+
on() {}
|
|
33
|
+
off() {}
|
|
34
|
+
|
|
35
|
+
destroy() {
|
|
36
|
+
this.off()
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
setWebgl() {}
|
|
40
|
+
|
|
41
|
+
render() {
|
|
42
|
+
this.setup()
|
|
43
|
+
this.on()
|
|
44
|
+
if (this.hasAnimateOnScroll) this.animateOnScroll()
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// Manager.js
|
|
2
|
+
import { qsa } from "../utils"
|
|
3
|
+
|
|
4
|
+
export default class Manager {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.components = []
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
addComponent(obj, renderer) {
|
|
10
|
+
const { name, instance } = obj
|
|
11
|
+
const els = qsa(`[data-${name}]`)
|
|
12
|
+
const data = {}
|
|
13
|
+
if (els.length === 0) return
|
|
14
|
+
|
|
15
|
+
data.hasTick = obj.hasTick || false
|
|
16
|
+
data.hasAnimateIn = obj.hasAnimateIn || false
|
|
17
|
+
data.hasAnimateOut = obj.hasAnimateOut || false
|
|
18
|
+
data.hasAnimateOnScroll = obj.hasAnimateOnScroll || false
|
|
19
|
+
|
|
20
|
+
els.forEach((el, index) => {
|
|
21
|
+
const component = new instance({ index, el, data, renderer })
|
|
22
|
+
this.components.push({ name, component })
|
|
23
|
+
})
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
initializeComponents() {
|
|
27
|
+
this.components.forEach(({ component }) => {
|
|
28
|
+
component?.render()
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
animateIn() {
|
|
33
|
+
this.components.forEach(({ component }) => {
|
|
34
|
+
component.hasAnimateIn && component.animateIn()
|
|
35
|
+
})
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
animateOut() {
|
|
39
|
+
this.components.forEach(({ component }) => {
|
|
40
|
+
component.hasAnimateOut && component.animateOut()
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
animateOnScroll() {
|
|
45
|
+
this.components.forEach(({ component }) => {
|
|
46
|
+
component.hasAnimateOnScroll && component.animateOnScroll()
|
|
47
|
+
})
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
update(obj) {
|
|
51
|
+
this.components.forEach(({ component }) => {
|
|
52
|
+
component.hasTick && component.tick(obj)
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
resize() {
|
|
57
|
+
this.components.forEach(({ component }) => {
|
|
58
|
+
component?.resize()
|
|
59
|
+
})
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
smoothResize() {
|
|
63
|
+
this.components.forEach(({ component }) => {
|
|
64
|
+
component?.smoothResize()
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
destroy() {
|
|
69
|
+
this.components.forEach(({ component }) => {
|
|
70
|
+
component?.destroy()
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import kitStore from "../kitStore"
|
|
2
|
+
import { emitter, EVENTS, Raf } from "../events"
|
|
3
|
+
|
|
4
|
+
export default class Scrollbar {
|
|
5
|
+
constructor(obj) {
|
|
6
|
+
this.container = obj.container || document.body
|
|
7
|
+
this.el = null
|
|
8
|
+
this.handle = null
|
|
9
|
+
this.touching = false
|
|
10
|
+
|
|
11
|
+
this.state = {
|
|
12
|
+
clicked: false,
|
|
13
|
+
scale: 0,
|
|
14
|
+
current: 0,
|
|
15
|
+
}
|
|
16
|
+
this.height = 0
|
|
17
|
+
this.init()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
init() {
|
|
21
|
+
this.create()
|
|
22
|
+
this.setBounds()
|
|
23
|
+
this.on()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
on() {
|
|
27
|
+
emitter.on(EVENTS.APP_TICK, this.tick)
|
|
28
|
+
emitter.on(EVENTS.APP_RESIZE, this.resize)
|
|
29
|
+
this.handle.addEventListener("mousedown", this.mouseDown)
|
|
30
|
+
window.addEventListener("mouseup", this.mouseUp)
|
|
31
|
+
window.addEventListener("mousemove", this.mouseMove)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
off() {
|
|
35
|
+
emitter.off(EVENTS.APP_TICK, this.tick)
|
|
36
|
+
emitter.off(EVENTS.APP_RESIZE, this.resize)
|
|
37
|
+
this.handle.removeEventListener("mousedown", this.mouseDown)
|
|
38
|
+
window.removeEventListener("mouseup", this.mouseUp)
|
|
39
|
+
window.removeEventListener("mousemove", this.mouseMove)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
mouseDown = () => {
|
|
43
|
+
this.touching = true
|
|
44
|
+
document.body.style.userSelect = "none"
|
|
45
|
+
document.body.style.webkitUserSelect = "none"
|
|
46
|
+
document.body.style.pointerEvents = "none"
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
mouseUp = () => {
|
|
50
|
+
this.touching = false
|
|
51
|
+
document.body.style.userSelect = "inherit"
|
|
52
|
+
document.body.style.webkitUserSelect = "inherit"
|
|
53
|
+
document.body.style.pointerEvents = "inherit"
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
mouseMove = e => {
|
|
57
|
+
if (this.touching === true) {
|
|
58
|
+
const { isLocked } = kitStore.flags
|
|
59
|
+
|
|
60
|
+
if (isLocked) return
|
|
61
|
+
|
|
62
|
+
kitStore.raf.scroll.target = e.clientY * this.state.scale
|
|
63
|
+
kitStore.raf.clamp()
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
setBounds() {
|
|
68
|
+
const { vh, fh } = kitStore.sizes
|
|
69
|
+
const scrollLimit = fh
|
|
70
|
+
this.state.scale = (scrollLimit + vh) / vh
|
|
71
|
+
this.handle.style.height = `${vh / this.state.scale}px`
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
tick = ({ current }) => {
|
|
75
|
+
const scroll = current / this.state.scale
|
|
76
|
+
this.state.current = scroll
|
|
77
|
+
this.handle.style.transform = `translate3d(0, ${this.state.current}px, 0)`
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
resize = () => {
|
|
81
|
+
this.setBounds()
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
calcScroll(e) {
|
|
85
|
+
const delta = e.clientY * this.state.scale
|
|
86
|
+
Raf.target = delta
|
|
87
|
+
Raf.clampTarget()
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
create() {
|
|
91
|
+
this.el = document.createElement("div")
|
|
92
|
+
this.handle = document.createElement("div")
|
|
93
|
+
this.el.classList.add("scrollbar")
|
|
94
|
+
this.handle.classList.add("handle")
|
|
95
|
+
|
|
96
|
+
Object.assign(this.el.style, {
|
|
97
|
+
position: "fixed",
|
|
98
|
+
top: 0,
|
|
99
|
+
right: 0,
|
|
100
|
+
height: "100%",
|
|
101
|
+
pointerEvents: "all",
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
Object.assign(this.handle.style, {
|
|
105
|
+
position: "absolute",
|
|
106
|
+
top: 0,
|
|
107
|
+
left: 0,
|
|
108
|
+
width: "100%",
|
|
109
|
+
cursor: "pointer",
|
|
110
|
+
zIndex: 101,
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
this.container.appendChild(this.el)
|
|
114
|
+
this.el.appendChild(this.handle)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
update() {
|
|
118
|
+
this.setBounds()
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
destroy() {
|
|
122
|
+
this.off()
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// Smooth.js
|
|
2
|
+
import kitStore from "../kitStore"
|
|
3
|
+
import Scrollbar from "./Scrollbar"
|
|
4
|
+
import { emitter, EVENTS } from "../events"
|
|
5
|
+
import { qs, qsa, bounds } from "../utils"
|
|
6
|
+
|
|
7
|
+
export default class Smooth {
|
|
8
|
+
constructor(obj) {
|
|
9
|
+
this.scroll = obj.scroll
|
|
10
|
+
this.el = qs("[data-view]")
|
|
11
|
+
this.elems = null
|
|
12
|
+
this.current = 0
|
|
13
|
+
this.threshold = 100
|
|
14
|
+
this.sections = null
|
|
15
|
+
this.scrollbar = null
|
|
16
|
+
this.init()
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
getSections() {
|
|
20
|
+
const { isSmooth } = kitStore.flags
|
|
21
|
+
if (!this.elems || !isSmooth) return
|
|
22
|
+
|
|
23
|
+
this.sections = []
|
|
24
|
+
this.elems.forEach(el => {
|
|
25
|
+
el.style.transform = "translate3d(0, 0, 0)"
|
|
26
|
+
const speed = el.dataset.speed || 1
|
|
27
|
+
const { top, bottom, offset } = this.getVars(el, speed)
|
|
28
|
+
let parent = el.parentNode.closest("[data-smooth]")
|
|
29
|
+
|
|
30
|
+
if (parent) {
|
|
31
|
+
this.sections.some(obj => {
|
|
32
|
+
if (obj.el === parent) parent = obj
|
|
33
|
+
})
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
this.sections.push({
|
|
37
|
+
el,
|
|
38
|
+
parent,
|
|
39
|
+
top,
|
|
40
|
+
bottom,
|
|
41
|
+
offset,
|
|
42
|
+
speed,
|
|
43
|
+
out: true,
|
|
44
|
+
transform: 0,
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
tick = ({ current }) => {
|
|
50
|
+
this.current = current
|
|
51
|
+
this.transformSections()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
transformSections() {
|
|
55
|
+
const { isSmooth, isResizing } = kitStore.flags
|
|
56
|
+
if (!this.sections || !isSmooth) return
|
|
57
|
+
|
|
58
|
+
this.sections.forEach(section => {
|
|
59
|
+
const { isVisible, transform } = this.isVisible(section)
|
|
60
|
+
|
|
61
|
+
if (isVisible || isResizing || !section.out) {
|
|
62
|
+
section.out = !isVisible
|
|
63
|
+
section.transform = transform
|
|
64
|
+
section.el.style.transform = this.getTransform(transform)
|
|
65
|
+
}
|
|
66
|
+
})
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
isVisible(section) {
|
|
70
|
+
const { vh } = kitStore.sizes
|
|
71
|
+
const { top, bottom, offset, speed, parent } = section
|
|
72
|
+
|
|
73
|
+
const extra = (parent && parent.transform) || 0
|
|
74
|
+
const translate = this.current * speed
|
|
75
|
+
const transform = translate - offset - extra
|
|
76
|
+
const start = top - translate
|
|
77
|
+
const end = bottom - translate
|
|
78
|
+
const isVisible = start < this.threshold + vh && end > -this.threshold
|
|
79
|
+
|
|
80
|
+
return { isVisible, transform }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
getTransform(transform) {
|
|
84
|
+
return `translate3d(0, ${-transform}px, 0)`
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
getVars(el, speed) {
|
|
88
|
+
const { vh } = kitStore.sizes
|
|
89
|
+
const rect = bounds(el)
|
|
90
|
+
const centering = vh / 2 - rect.height / 2
|
|
91
|
+
const offset = rect.top < vh ? 0 : (rect.top - centering) * speed - (rect.top - centering)
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
top: rect.top + offset,
|
|
95
|
+
bottom: rect.bottom + offset,
|
|
96
|
+
offset,
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
resize = () => {
|
|
101
|
+
if (!this.sections) return
|
|
102
|
+
|
|
103
|
+
this.sections.forEach(section => {
|
|
104
|
+
section.el.style.transform = "translate3d(0, 0, 0)"
|
|
105
|
+
const { top, bottom, offset } = this.getVars(section.el, section.speed)
|
|
106
|
+
Object.assign(section, { top, bottom, offset })
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
emitter.emit(EVENTS.APP_SMOOTH_RESIZE)
|
|
110
|
+
this.transformSections()
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
update(elems) {
|
|
114
|
+
kitStore.flags.isResizing = true
|
|
115
|
+
|
|
116
|
+
this.scroll.setScrollBounds()
|
|
117
|
+
this.elems = elems || qsa("[data-smooth]")
|
|
118
|
+
this.scrollbar.update()
|
|
119
|
+
this.getSections()
|
|
120
|
+
this.transformSections()
|
|
121
|
+
|
|
122
|
+
kitStore.flags.isResizing = false
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
clean() {
|
|
126
|
+
this.elems = this.sections = null
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
on() {
|
|
130
|
+
emitter.on(EVENTS.APP_TICK, this.tick)
|
|
131
|
+
emitter.on(EVENTS.APP_RESIZE, this.resize)
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
off() {
|
|
135
|
+
emitter.off(EVENTS.APP_TICK, this.tick)
|
|
136
|
+
emitter.off(EVENTS.APP_RESIZE, this.resize)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
init(elems) {
|
|
140
|
+
this.elems = elems || qsa("[data-smooth]")
|
|
141
|
+
this.getSections()
|
|
142
|
+
this.scrollbar = new Scrollbar(this.current)
|
|
143
|
+
this.on()
|
|
144
|
+
this.update()
|
|
145
|
+
}
|
|
146
|
+
}
|
package/classes/index.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Emitter.js
|
|
2
|
+
class Emitter {
|
|
3
|
+
constructor() {
|
|
4
|
+
this.events = {}
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
emit(event, ...args) {
|
|
8
|
+
const callbacks = this.events[event] || []
|
|
9
|
+
for (let i = 0, { length } = callbacks; i < length; i++) {
|
|
10
|
+
callbacks[i].cb(...args)
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
on(event, cb, priority = 0) {
|
|
15
|
+
const data = { cb, priority }
|
|
16
|
+
this.events[event]?.push(data) || (this.events[event] = [data])
|
|
17
|
+
this.events[event].sort((a, b) => a.priority - b.priority)
|
|
18
|
+
return () => {
|
|
19
|
+
this.events[event] = this.events[event]?.filter(v => cb !== v.cb)
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
off(event, callback) {
|
|
24
|
+
this.events[event] = this.events[event]?.filter(({ cb }) => callback !== cb)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
once(event, cb, priority = 0) {
|
|
28
|
+
const onceCallback = (...args) => {
|
|
29
|
+
cb(...args)
|
|
30
|
+
this.off(event, onceCallback)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
this.on(event, onceCallback, priority)
|
|
34
|
+
|
|
35
|
+
return () => {
|
|
36
|
+
this.off(event, cb)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
destroy() {
|
|
41
|
+
this.events = {}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const emitter = new Emitter()
|
|
46
|
+
|
|
47
|
+
const EVENTS = {
|
|
48
|
+
APP_TICK: "tick",
|
|
49
|
+
APP_RESIZE: "resize",
|
|
50
|
+
APP_SMOOTH_RESIZE: "smooth:resize",
|
|
51
|
+
APP_SCROLL: "scroll",
|
|
52
|
+
APP_MOUSEMOVE: "mousemove",
|
|
53
|
+
APP_MOUSEDOWN: "mousedown",
|
|
54
|
+
APP_MOUSEUP: "mouseup",
|
|
55
|
+
APP_SPLITTEXT_READY: "splittext:ready",
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const PRIORITY = {
|
|
59
|
+
first: -10,
|
|
60
|
+
instant: 0,
|
|
61
|
+
high: 10,
|
|
62
|
+
mid: 20,
|
|
63
|
+
low: 30,
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export { EVENTS, PRIORITY }
|
|
67
|
+
export default emitter
|
package/events/Mouse.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Mouse.js
|
|
2
|
+
import emitter, { EVENTS } from "./Emitter"
|
|
3
|
+
import { sniffer } from "../utils"
|
|
4
|
+
|
|
5
|
+
export default class Mouse {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.state = {
|
|
8
|
+
on: 0,
|
|
9
|
+
off: 0,
|
|
10
|
+
coords: { x: 0, y: 0 },
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
this.events = {
|
|
14
|
+
move: sniffer.isDevice ? "touchmove" : "mousemove",
|
|
15
|
+
down: sniffer.isDevice ? "touchstart" : "mousedown",
|
|
16
|
+
up: sniffer.isDevice ? "touchend" : "mouseup",
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
this.on()
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
on() {
|
|
23
|
+
const { move, down, up } = this.events
|
|
24
|
+
window.addEventListener(move, this.onMove)
|
|
25
|
+
window.addEventListener(down, this.onDown)
|
|
26
|
+
window.addEventListener(up, this.onUp)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
off() {
|
|
30
|
+
const { move, down, up } = this.events
|
|
31
|
+
window.removeEventListener(move, this.onMove)
|
|
32
|
+
window.removeEventListener(down, this.onDown)
|
|
33
|
+
window.removeEventListener(up, this.onUp)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
getPos(e) {
|
|
37
|
+
const x = e.changedTouches ? e.changedTouches[0].clientX : e.clientX
|
|
38
|
+
const y = e.changedTouches ? e.changedTouches[0].clientY : e.clientY
|
|
39
|
+
const target = e.target
|
|
40
|
+
return { x, y, target }
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
onMove = e => {
|
|
44
|
+
const { x, y, target } = this.getPos(e)
|
|
45
|
+
emitter.emit(EVENTS.APP_MOUSEMOVE, { x, y, target, e })
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
onDown = e => {
|
|
49
|
+
const { x, y, target } = this.getPos(e)
|
|
50
|
+
this.state.on = x
|
|
51
|
+
emitter.emit(EVENTS.APP_MOUSEDOWN, { x, y, target })
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
onUp = e => {
|
|
55
|
+
const { x, target } = this.getPos(e)
|
|
56
|
+
this.state.off = x
|
|
57
|
+
const isClick = Math.abs(this.state.on - this.state.off) > 10
|
|
58
|
+
emitter.emit(EVENTS.APP_MOUSEUP, { x, target, isClick })
|
|
59
|
+
}
|
|
60
|
+
}
|
package/events/Raf.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
// Raf.js
|
|
2
|
+
import kitStore from "../kitStore"
|
|
3
|
+
import emitter, { EVENTS } from "./Emitter"
|
|
4
|
+
import { sniffer, lerp } from "../utils"
|
|
5
|
+
import { gsap, ScrollTrigger } from "../libraries/gsap"
|
|
6
|
+
|
|
7
|
+
export default class Raf {
|
|
8
|
+
constructor() {
|
|
9
|
+
this.scroll = {
|
|
10
|
+
target: 0,
|
|
11
|
+
current: 0,
|
|
12
|
+
rounded: 0,
|
|
13
|
+
ease: 0.115,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
this.mouse = {
|
|
17
|
+
x: 0,
|
|
18
|
+
y: 0,
|
|
19
|
+
target: null,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
this.setScrollTrigger()
|
|
23
|
+
this.on()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
tick = () => {
|
|
27
|
+
const { target, current, ease } = this.scroll
|
|
28
|
+
|
|
29
|
+
this.scroll.current = lerp(current, target, ease)
|
|
30
|
+
this.scroll.rounded = Math.round(this.scroll.current * 100) / 100
|
|
31
|
+
this.diff = (target - this.scroll.current) * 0.005
|
|
32
|
+
|
|
33
|
+
emitter.emit(EVENTS.APP_TICK, {
|
|
34
|
+
target,
|
|
35
|
+
current: this.getScroll(),
|
|
36
|
+
mouse: this.mouse,
|
|
37
|
+
diff: this.diff,
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
ScrollTrigger.update()
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getScroll() {
|
|
44
|
+
const { pageContent } = kitStore
|
|
45
|
+
return sniffer.isDevice ? pageContent.parentNode.scrollTop : this.scroll.rounded
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
clamp() {
|
|
49
|
+
const { fh } = kitStore.sizes
|
|
50
|
+
this.scroll.target = Math.min(Math.max(this.scroll.target, 0), fh)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
onScroll = ({ y }) => {
|
|
54
|
+
if (kitStore.flags.isLocked) return
|
|
55
|
+
this.scroll.target += y
|
|
56
|
+
this.clamp()
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
onMouseMove = ({ x, y, target }) => {
|
|
60
|
+
this.mouse.x = x
|
|
61
|
+
this.mouse.y = y
|
|
62
|
+
this.mouse.target = target
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
scrollTo(offset) {
|
|
66
|
+
if (kitStore.flags.isSmooth) {
|
|
67
|
+
gsap.to(this.scroll, {
|
|
68
|
+
target: offset,
|
|
69
|
+
duration: 1,
|
|
70
|
+
ease: "expo.inOut",
|
|
71
|
+
})
|
|
72
|
+
} else {
|
|
73
|
+
window.scrollTo({ top: offset, left: 0, behavior: "smooth" })
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
setScroll(offset) {
|
|
78
|
+
if (kitStore.flags.isSmooth) {
|
|
79
|
+
gsap.set(this.scroll, {
|
|
80
|
+
target: offset,
|
|
81
|
+
current: offset,
|
|
82
|
+
rounded: offset,
|
|
83
|
+
})
|
|
84
|
+
} else {
|
|
85
|
+
window.scrollTo({ top: offset, left: 0 })
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
setScrollTrigger() {
|
|
90
|
+
const { pageContent } = kitStore
|
|
91
|
+
|
|
92
|
+
ScrollTrigger.defaults({
|
|
93
|
+
scroller: pageContent,
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
ScrollTrigger.scrollerProxy(pageContent, {
|
|
97
|
+
scrollTop: () => {
|
|
98
|
+
return this.smooth()
|
|
99
|
+
},
|
|
100
|
+
getBoundingClientRect() {
|
|
101
|
+
// Important that width and height are dynamic
|
|
102
|
+
return { top: 0, left: 0, width: window.innerWidth, height: window.innerHeight }
|
|
103
|
+
},
|
|
104
|
+
})
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
on() {
|
|
108
|
+
gsap.ticker.add(this.tick)
|
|
109
|
+
emitter.on(EVENTS.APP_SCROLL, this.onScroll)
|
|
110
|
+
emitter.on(EVENTS.APP_MOUSEMOVE, this.onMouseMove)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
off() {
|
|
114
|
+
gsap.ticker.remove(this.tick)
|
|
115
|
+
emitter.off(EVENTS.APP_SCROLL, this.onScroll)
|
|
116
|
+
emitter.off(EVENTS.APP_MOUSEMOVE, this.onMouseMove)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
destroy() {
|
|
120
|
+
this.off()
|
|
121
|
+
}
|
|
122
|
+
}
|