@hortonstudio/main 1.0.0 → 1.1.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/.claude/settings.local.json +18 -0
- package/CLAUDE.md +45 -0
- package/animations/hero.js +530 -0
- package/animations/text.js +305 -0
- package/animations/{modules/transition.js → transition.js} +2 -1
- package/autoInit/smooth-scroll.js +89 -0
- package/index.js +116 -48
- package/package.json +1 -1
- package/styles.css +17 -0
- package/utils/navbar.js +203 -0
- package/utils/scroll-progress.js +29 -0
- package/utils/toc.js +54 -0
- package/animations/READ +0 -0
- package/animations/modules/hero.js +0 -154
- package/animations/modules/text.js +0 -165
- package/utils/READ +0 -0
- package/utils/modules/toc.js +0 -0
@@ -0,0 +1,305 @@
|
|
1
|
+
const API_NAME = 'hsmain';
|
2
|
+
|
3
|
+
const config = {
|
4
|
+
global: {
|
5
|
+
animationDelay: 0
|
6
|
+
},
|
7
|
+
wordSplit: {
|
8
|
+
duration: 1.5,
|
9
|
+
stagger: 0.075,
|
10
|
+
yPercent: 110,
|
11
|
+
ease: "power4.out",
|
12
|
+
start: "top 97%"
|
13
|
+
},
|
14
|
+
lineSplit: {
|
15
|
+
duration: 1.5,
|
16
|
+
stagger: 0.1,
|
17
|
+
yPercent: 110,
|
18
|
+
ease: "power4.out",
|
19
|
+
start: "top 97%"
|
20
|
+
},
|
21
|
+
charSplit: {
|
22
|
+
duration: 1.2,
|
23
|
+
stagger: 0.03,
|
24
|
+
yPercent: 110,
|
25
|
+
ease: "power4.out",
|
26
|
+
start: "top 97%"
|
27
|
+
},
|
28
|
+
appear: {
|
29
|
+
y: 50,
|
30
|
+
duration: 1.5,
|
31
|
+
ease: "power3.out",
|
32
|
+
start: "top 97%"
|
33
|
+
}
|
34
|
+
};
|
35
|
+
|
36
|
+
function updateConfig(newConfig) {
|
37
|
+
function deepMerge(target, source) {
|
38
|
+
for (const key in source) {
|
39
|
+
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
40
|
+
target[key] = target[key] || {};
|
41
|
+
deepMerge(target[key], source[key]);
|
42
|
+
} else {
|
43
|
+
target[key] = source[key];
|
44
|
+
}
|
45
|
+
}
|
46
|
+
return target;
|
47
|
+
}
|
48
|
+
|
49
|
+
deepMerge(config, newConfig);
|
50
|
+
}
|
51
|
+
|
52
|
+
function killTextAnimations() {
|
53
|
+
activeAnimations.forEach(({ timeline, element }) => {
|
54
|
+
if (timeline) {
|
55
|
+
timeline.kill();
|
56
|
+
}
|
57
|
+
if (element?.splitTextInstance) {
|
58
|
+
element.splitTextInstance.revert();
|
59
|
+
}
|
60
|
+
});
|
61
|
+
activeAnimations.length = 0;
|
62
|
+
}
|
63
|
+
|
64
|
+
function startTextAnimations() {
|
65
|
+
setInitialStates().then(() => {
|
66
|
+
initAnimations();
|
67
|
+
});
|
68
|
+
}
|
69
|
+
|
70
|
+
let activeAnimations = [];
|
71
|
+
|
72
|
+
function waitForFonts() {
|
73
|
+
return document.fonts.ready;
|
74
|
+
}
|
75
|
+
|
76
|
+
const CharSplitAnimations = {
|
77
|
+
async initial() {
|
78
|
+
await waitForFonts();
|
79
|
+
|
80
|
+
const elements = document.querySelectorAll(".a-char-split > *:first-child");
|
81
|
+
|
82
|
+
elements.forEach((textElement, index) => {
|
83
|
+
|
84
|
+
const split = SplitText.create(textElement, {
|
85
|
+
type: "chars",
|
86
|
+
mask: "chars",
|
87
|
+
charsClass: "char",
|
88
|
+
});
|
89
|
+
textElement.splitTextInstance = split;
|
90
|
+
|
91
|
+
gsap.set(split.chars, {
|
92
|
+
yPercent: config.charSplit.yPercent
|
93
|
+
});
|
94
|
+
gsap.set(textElement, { autoAlpha: 1 });
|
95
|
+
});
|
96
|
+
},
|
97
|
+
|
98
|
+
async animate() {
|
99
|
+
await waitForFonts();
|
100
|
+
|
101
|
+
document.querySelectorAll(".a-char-split > *:first-child").forEach((textElement) => {
|
102
|
+
const chars = textElement.querySelectorAll('.char');
|
103
|
+
const tl = gsap.timeline({
|
104
|
+
scrollTrigger: {
|
105
|
+
trigger: textElement,
|
106
|
+
start: config.charSplit.start,
|
107
|
+
invalidateOnRefresh: true,
|
108
|
+
},
|
109
|
+
onComplete: () => {
|
110
|
+
textElement.splitTextInstance.revert();
|
111
|
+
}
|
112
|
+
});
|
113
|
+
|
114
|
+
tl.to(chars, {
|
115
|
+
yPercent: 0,
|
116
|
+
duration: config.charSplit.duration,
|
117
|
+
stagger: config.charSplit.stagger,
|
118
|
+
ease: config.charSplit.ease,
|
119
|
+
});
|
120
|
+
|
121
|
+
activeAnimations.push({ timeline: tl, element: textElement });
|
122
|
+
});
|
123
|
+
}
|
124
|
+
};
|
125
|
+
|
126
|
+
const WordSplitAnimations = {
|
127
|
+
async initial() {
|
128
|
+
await waitForFonts();
|
129
|
+
|
130
|
+
const elements = document.querySelectorAll(".a-word-split > *:first-child");
|
131
|
+
|
132
|
+
elements.forEach((textElement, index) => {
|
133
|
+
|
134
|
+
const split = SplitText.create(textElement, {
|
135
|
+
type: "words",
|
136
|
+
mask: "words",
|
137
|
+
wordsClass: "word",
|
138
|
+
});
|
139
|
+
textElement.splitTextInstance = split;
|
140
|
+
|
141
|
+
gsap.set(split.words, {
|
142
|
+
yPercent: config.wordSplit.yPercent
|
143
|
+
});
|
144
|
+
gsap.set(textElement, { autoAlpha: 1 });
|
145
|
+
});
|
146
|
+
},
|
147
|
+
|
148
|
+
async animate() {
|
149
|
+
await waitForFonts();
|
150
|
+
|
151
|
+
document.querySelectorAll(".a-word-split > *:first-child").forEach((textElement) => {
|
152
|
+
const words = textElement.querySelectorAll('.word');
|
153
|
+
const tl = gsap.timeline({
|
154
|
+
scrollTrigger: {
|
155
|
+
trigger: textElement,
|
156
|
+
start: config.wordSplit.start,
|
157
|
+
invalidateOnRefresh: true,
|
158
|
+
},
|
159
|
+
onComplete: () => {
|
160
|
+
textElement.splitTextInstance.revert();
|
161
|
+
}
|
162
|
+
});
|
163
|
+
|
164
|
+
tl.to(words, {
|
165
|
+
yPercent: 0,
|
166
|
+
duration: config.wordSplit.duration,
|
167
|
+
stagger: config.wordSplit.stagger,
|
168
|
+
ease: config.wordSplit.ease,
|
169
|
+
});
|
170
|
+
|
171
|
+
activeAnimations.push({ timeline: tl, element: textElement });
|
172
|
+
});
|
173
|
+
}
|
174
|
+
};
|
175
|
+
|
176
|
+
const LineSplitAnimations = {
|
177
|
+
async initial() {
|
178
|
+
await waitForFonts();
|
179
|
+
|
180
|
+
const elements = document.querySelectorAll(".a-line-split > *:first-child");
|
181
|
+
|
182
|
+
elements.forEach((textElement, index) => {
|
183
|
+
|
184
|
+
const split = SplitText.create(textElement, {
|
185
|
+
type: "lines",
|
186
|
+
mask: "lines",
|
187
|
+
linesClass: "line",
|
188
|
+
});
|
189
|
+
textElement.splitTextInstance = split;
|
190
|
+
|
191
|
+
gsap.set(split.lines, {
|
192
|
+
yPercent: config.lineSplit.yPercent
|
193
|
+
});
|
194
|
+
gsap.set(textElement, { autoAlpha: 1 });
|
195
|
+
});
|
196
|
+
},
|
197
|
+
|
198
|
+
async animate() {
|
199
|
+
await waitForFonts();
|
200
|
+
|
201
|
+
document.querySelectorAll(".a-line-split > *:first-child").forEach((textElement) => {
|
202
|
+
const lines = textElement.querySelectorAll('.line');
|
203
|
+
const tl = gsap.timeline({
|
204
|
+
scrollTrigger: {
|
205
|
+
trigger: textElement,
|
206
|
+
start: config.lineSplit.start,
|
207
|
+
invalidateOnRefresh: true,
|
208
|
+
},
|
209
|
+
onComplete: () => {
|
210
|
+
textElement.splitTextInstance.revert();
|
211
|
+
}
|
212
|
+
});
|
213
|
+
|
214
|
+
tl.to(lines, {
|
215
|
+
yPercent: 0,
|
216
|
+
duration: config.lineSplit.duration,
|
217
|
+
stagger: config.lineSplit.stagger,
|
218
|
+
ease: config.lineSplit.ease,
|
219
|
+
});
|
220
|
+
|
221
|
+
activeAnimations.push({ timeline: tl, element: textElement });
|
222
|
+
});
|
223
|
+
}
|
224
|
+
};
|
225
|
+
|
226
|
+
const AppearAnimations = {
|
227
|
+
async initial() {
|
228
|
+
await waitForFonts();
|
229
|
+
|
230
|
+
const elements = document.querySelectorAll('.a-appear');
|
231
|
+
elements.forEach(element => {
|
232
|
+
gsap.set(element, {
|
233
|
+
y: config.appear.y,
|
234
|
+
opacity: 0
|
235
|
+
});
|
236
|
+
});
|
237
|
+
},
|
238
|
+
|
239
|
+
async animate() {
|
240
|
+
await waitForFonts();
|
241
|
+
|
242
|
+
document.querySelectorAll('.a-appear').forEach(element => {
|
243
|
+
const tl = gsap.timeline({
|
244
|
+
scrollTrigger: {
|
245
|
+
trigger: element,
|
246
|
+
start: config.appear.start,
|
247
|
+
invalidateOnRefresh: true,
|
248
|
+
}
|
249
|
+
});
|
250
|
+
|
251
|
+
tl.to(element, {
|
252
|
+
y: 0,
|
253
|
+
opacity: 1,
|
254
|
+
duration: config.appear.duration,
|
255
|
+
ease: config.appear.ease
|
256
|
+
});
|
257
|
+
|
258
|
+
activeAnimations.push({ timeline: tl, element: element });
|
259
|
+
});
|
260
|
+
|
261
|
+
}
|
262
|
+
};
|
263
|
+
|
264
|
+
async function setInitialStates() {
|
265
|
+
await Promise.all([
|
266
|
+
CharSplitAnimations.initial(),
|
267
|
+
WordSplitAnimations.initial(),
|
268
|
+
LineSplitAnimations.initial(),
|
269
|
+
AppearAnimations.initial()
|
270
|
+
]);
|
271
|
+
}
|
272
|
+
|
273
|
+
async function initAnimations() {
|
274
|
+
if (config.global.animationDelay > 0) {
|
275
|
+
await new Promise(resolve => setTimeout(resolve, config.global.animationDelay * 1000));
|
276
|
+
}
|
277
|
+
|
278
|
+
await Promise.all([
|
279
|
+
CharSplitAnimations.animate(),
|
280
|
+
WordSplitAnimations.animate(),
|
281
|
+
LineSplitAnimations.animate(),
|
282
|
+
AppearAnimations.animate()
|
283
|
+
]);
|
284
|
+
}
|
285
|
+
|
286
|
+
export async function init() {
|
287
|
+
await setInitialStates();
|
288
|
+
initAnimations();
|
289
|
+
|
290
|
+
window.addEventListener('resize', ScrollTrigger.refresh());
|
291
|
+
|
292
|
+
const api = window[API_NAME] || {};
|
293
|
+
api.textAnimations = {
|
294
|
+
config: config,
|
295
|
+
updateConfig: updateConfig,
|
296
|
+
start: startTextAnimations,
|
297
|
+
kill: killTextAnimations,
|
298
|
+
restart: () => {
|
299
|
+
killTextAnimations();
|
300
|
+
startTextAnimations();
|
301
|
+
}
|
302
|
+
};
|
303
|
+
|
304
|
+
return { result: 'anim-text initialized' };
|
305
|
+
}
|
@@ -1,4 +1,5 @@
|
|
1
1
|
// Page Transition Module
|
2
|
+
const API_NAME = 'hsmain';
|
2
3
|
export async function init() {
|
3
4
|
|
4
5
|
// Your original code with minimal changes
|
@@ -36,5 +37,5 @@ export async function init() {
|
|
36
37
|
setTimeout(() => {$(".transition").css("display", "none");}, 50);});
|
37
38
|
}, introDurationMS);
|
38
39
|
|
39
|
-
return { result: 'transition
|
40
|
+
return { result: 'anim-transition initialized' };
|
40
41
|
}
|
@@ -0,0 +1,89 @@
|
|
1
|
+
const API_NAME = 'hsmain';
|
2
|
+
|
3
|
+
export async function init() {
|
4
|
+
const api = window[API_NAME];
|
5
|
+
api.afterWebflowReady(() => {
|
6
|
+
if (typeof $ !== 'undefined') {
|
7
|
+
$(document).off('click.wf-scroll');
|
8
|
+
}
|
9
|
+
});
|
10
|
+
|
11
|
+
// Disable CSS smooth scrolling
|
12
|
+
document.documentElement.style.scrollBehavior = 'auto';
|
13
|
+
document.body.style.scrollBehavior = 'auto';
|
14
|
+
|
15
|
+
// Check if user prefers reduced motion
|
16
|
+
function prefersReducedMotion() {
|
17
|
+
return window.matchMedia('(prefers-reduced-motion: reduce)').matches;
|
18
|
+
}
|
19
|
+
|
20
|
+
function getScrollOffset() {
|
21
|
+
const offsetValue = getComputedStyle(document.documentElement)
|
22
|
+
.getPropertyValue('--misc--scroll-offset').trim();
|
23
|
+
return parseInt(offsetValue) || 0;
|
24
|
+
}
|
25
|
+
|
26
|
+
// Smooth scroll to element with offset
|
27
|
+
function scrollToElement(target, offset = 0) {
|
28
|
+
if (!target) return;
|
29
|
+
|
30
|
+
// Skip animation if user prefers reduced motion
|
31
|
+
if (prefersReducedMotion()) {
|
32
|
+
const targetPosition = target.getBoundingClientRect().top + window.scrollY - offset;
|
33
|
+
window.scrollTo(0, targetPosition);
|
34
|
+
target.setAttribute('tabindex', '-1');
|
35
|
+
target.focus({ preventScroll: true });
|
36
|
+
return;
|
37
|
+
}
|
38
|
+
|
39
|
+
gsap.to(window, {
|
40
|
+
duration: 1,
|
41
|
+
scrollTo: {
|
42
|
+
y: target,
|
43
|
+
offsetY: offset
|
44
|
+
},
|
45
|
+
ease: "power2.out",
|
46
|
+
onComplete: function() {
|
47
|
+
target.setAttribute('tabindex', '-1');
|
48
|
+
target.focus({ preventScroll: true });
|
49
|
+
}
|
50
|
+
});
|
51
|
+
}
|
52
|
+
|
53
|
+
// Handle anchor link clicks and keyboard activation
|
54
|
+
function handleAnchorClicks() {
|
55
|
+
document.addEventListener('click', handleAnchorActivation);
|
56
|
+
document.addEventListener('keydown', function(e) {
|
57
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
58
|
+
handleAnchorActivation(e);
|
59
|
+
}
|
60
|
+
});
|
61
|
+
}
|
62
|
+
|
63
|
+
function handleAnchorActivation(e) {
|
64
|
+
const link = e.target.closest('a[href^="#"]');
|
65
|
+
if (!link) return;
|
66
|
+
|
67
|
+
const href = link.getAttribute('href');
|
68
|
+
if (!href || href === '#') return;
|
69
|
+
|
70
|
+
const targetId = href.substring(1);
|
71
|
+
const targetElement = document.getElementById(targetId);
|
72
|
+
|
73
|
+
if (targetElement) {
|
74
|
+
e.preventDefault();
|
75
|
+
if (history.replaceState) {
|
76
|
+
history.replaceState(null, null, `#${targetElement.id}`);
|
77
|
+
}
|
78
|
+
const offset = getScrollOffset();
|
79
|
+
scrollToElement(targetElement, offset);
|
80
|
+
}
|
81
|
+
}
|
82
|
+
|
83
|
+
// Initialize anchor link handling
|
84
|
+
handleAnchorClicks();
|
85
|
+
|
86
|
+
return {
|
87
|
+
result: 'autoInit-smooth-scroll initialized'
|
88
|
+
};
|
89
|
+
}
|
package/index.js
CHANGED
@@ -1,18 +1,38 @@
|
|
1
|
-
|
1
|
+
// Configuration: Change this variable to rename the API
|
2
|
+
const API_NAME = 'hsmain';
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
]);
|
4
|
+
window[API_NAME] = window[API_NAME] || {};
|
5
|
+
window[API_NAME].loaded = false
|
6
6
|
|
7
|
-
const
|
8
|
-
"hs-
|
9
|
-
"hs-
|
10
|
-
"hs-
|
11
|
-
"hs-a-transition": "./animations/modules/transition.js"
|
7
|
+
const animationModules = {
|
8
|
+
"data-hs-anim-text": "./animations/text.js",
|
9
|
+
"data-hs-anim-hero": "./animations/hero.js",
|
10
|
+
"data-hs-anim-transition": "./animations/transition.js"
|
12
11
|
};
|
13
12
|
|
13
|
+
const utilityModules = {
|
14
|
+
"data-hs-util-toc": "./utils/toc.js",
|
15
|
+
"data-hs-util-progress": "./utils/scroll-progress.js",
|
16
|
+
"data-hs-util-navbar": "./utils/navbar.js"
|
17
|
+
};
|
18
|
+
|
19
|
+
// Modules that auto-initialize
|
20
|
+
const autoInitModules = {
|
21
|
+
"smooth-scroll": "./autoInit/smooth-scroll.js"
|
22
|
+
};
|
23
|
+
|
24
|
+
// Store callbacks to run after Webflow.ready()
|
25
|
+
const postWebflowCallbacks = [];
|
26
|
+
|
14
27
|
const loadModule = (moduleName) => {
|
15
|
-
|
28
|
+
// Check manual modules first
|
29
|
+
let modulePath = animationModules[moduleName] || utilityModules[moduleName];
|
30
|
+
|
31
|
+
// Then check auto-init modules
|
32
|
+
if (!modulePath) {
|
33
|
+
modulePath = autoInitModules[moduleName];
|
34
|
+
}
|
35
|
+
|
16
36
|
if (!modulePath) {
|
17
37
|
throw new Error(`HortonStudio module "${moduleName}" is not supported.`);
|
18
38
|
}
|
@@ -20,81 +40,129 @@ const loadModule = (moduleName) => {
|
|
20
40
|
};
|
21
41
|
|
22
42
|
const findCurrentScriptTag = () => {
|
23
|
-
const scriptTag = document.querySelector('script[hs-main]');
|
43
|
+
const scriptTag = document.querySelector('script[data-hs-main]');
|
24
44
|
return scriptTag || null;
|
25
45
|
};
|
26
46
|
|
27
|
-
const processModules = (scriptTag) => {
|
28
|
-
|
29
|
-
return;
|
30
|
-
}
|
47
|
+
const processModules = async (scriptTag) => {
|
48
|
+
const modulePromises = [];
|
31
49
|
|
32
|
-
|
33
|
-
|
34
|
-
|
50
|
+
// Load manual modules based on attributes
|
51
|
+
for (const moduleName of Object.keys({ ...animationModules, ...utilityModules })) {
|
52
|
+
if (scriptTag && scriptTag.hasAttribute(moduleName)) {
|
53
|
+
modulePromises.push(loadHsModule(moduleName));
|
35
54
|
}
|
36
55
|
}
|
37
|
-
};
|
38
|
-
|
39
|
-
const loadHsModule = async (moduleName) => {
|
40
|
-
const { hsMain } = window;
|
41
56
|
|
42
|
-
|
43
|
-
|
57
|
+
// Load auto-init modules
|
58
|
+
for (const moduleName of Object.keys(autoInitModules)) {
|
59
|
+
modulePromises.push(loadHsModule(moduleName));
|
44
60
|
}
|
45
61
|
|
46
|
-
|
62
|
+
// Wait for ALL modules to finish loading
|
63
|
+
await Promise.all(modulePromises);
|
47
64
|
|
48
|
-
|
49
|
-
|
65
|
+
// Always refresh Webflow after all modules are loaded
|
66
|
+
refreshWebflow();
|
67
|
+
};
|
68
|
+
|
69
|
+
const refreshWebflow = () => {
|
50
70
|
|
71
|
+
setTimeout(() => {
|
72
|
+
if (window.Webflow && window.Webflow.ready) {
|
73
|
+
window.Webflow.ready();
|
74
|
+
|
75
|
+
// Run all registered post-Webflow callbacks
|
76
|
+
setTimeout(() => {
|
77
|
+
postWebflowCallbacks.forEach(callback => {
|
78
|
+
try {
|
79
|
+
callback();
|
80
|
+
} catch (error) {
|
81
|
+
}
|
82
|
+
});
|
83
|
+
window[API_NAME].loaded = true;
|
84
|
+
},);
|
85
|
+
}
|
86
|
+
},);
|
87
|
+
};
|
88
|
+
|
89
|
+
const loadHsModule = async (moduleName) => {
|
90
|
+
const apiInstance = window[API_NAME];
|
91
|
+
|
92
|
+
if (apiInstance.process.has(moduleName)) {
|
93
|
+
return apiInstance.modules[moduleName]?.loading;
|
94
|
+
}
|
95
|
+
|
96
|
+
apiInstance.process.add(moduleName);
|
97
|
+
|
98
|
+
const moduleObj = apiInstance.modules[moduleName] || {};
|
99
|
+
apiInstance.modules[moduleName] = moduleObj;
|
100
|
+
|
51
101
|
moduleObj.loading = new Promise((resolve, reject) => {
|
52
102
|
moduleObj.resolve = resolve;
|
53
103
|
moduleObj.reject = reject;
|
54
104
|
});
|
55
|
-
|
105
|
+
|
56
106
|
try {
|
57
107
|
const { init, version } = await loadModule(moduleName);
|
58
108
|
const initResult = await init();
|
59
109
|
const { result } = initResult || {};
|
60
|
-
|
110
|
+
|
61
111
|
moduleObj.version = version;
|
62
112
|
moduleObj.restart = () => {
|
63
|
-
|
113
|
+
apiInstance.process.delete(moduleName);
|
64
114
|
return loadHsModule(moduleName);
|
65
115
|
};
|
66
|
-
|
116
|
+
|
67
117
|
moduleObj.resolve?.(result);
|
68
118
|
delete moduleObj.resolve;
|
69
119
|
delete moduleObj.reject;
|
70
|
-
|
120
|
+
|
71
121
|
return result;
|
72
|
-
|
122
|
+
|
73
123
|
} catch (error) {
|
74
124
|
moduleObj.reject?.(error);
|
75
|
-
|
125
|
+
apiInstance.process.delete(moduleName);
|
76
126
|
throw error;
|
77
127
|
}
|
78
128
|
};
|
79
129
|
|
80
|
-
const
|
81
|
-
const
|
82
|
-
|
83
|
-
const existingRequests = Array.isArray(
|
130
|
+
const initializeAPI = () => {
|
131
|
+
const apiInstance = window[API_NAME];
|
132
|
+
|
133
|
+
const existingRequests = Array.isArray(apiInstance) ? apiInstance : [];
|
84
134
|
const scriptTag = findCurrentScriptTag();
|
85
|
-
|
86
|
-
|
135
|
+
const richTextBlocks = document.querySelectorAll('.w-richtext');
|
136
|
+
richTextBlocks.forEach(block => {
|
137
|
+
const images = block.querySelectorAll('img');
|
138
|
+
images.forEach(img => {
|
139
|
+
img.loading = 'eager';
|
140
|
+
});
|
141
|
+
});
|
142
|
+
|
143
|
+
window[API_NAME] = {
|
87
144
|
scriptTag,
|
88
145
|
modules: {},
|
89
146
|
process: new Set(),
|
90
|
-
|
147
|
+
|
91
148
|
load: loadHsModule,
|
92
|
-
|
149
|
+
|
150
|
+
// API function for scripts to register post-Webflow callbacks
|
151
|
+
afterWebflowReady: (callback) => {
|
152
|
+
if (typeof callback === 'function') {
|
153
|
+
postWebflowCallbacks.push(callback);
|
154
|
+
} else {
|
155
|
+
}
|
156
|
+
},
|
157
|
+
|
93
158
|
status(moduleName) {
|
94
159
|
if (!moduleName) {
|
95
160
|
return {
|
96
161
|
loaded: Object.keys(this.modules),
|
97
|
-
loading: [...this.process]
|
162
|
+
loading: [...this.process],
|
163
|
+
animations: Object.keys(animationModules),
|
164
|
+
utilities: Object.keys(utilityModules),
|
165
|
+
autoInit: Object.keys(autoInitModules)
|
98
166
|
};
|
99
167
|
}
|
100
168
|
return {
|
@@ -103,20 +171,20 @@ const initializeHsMain = () => {
|
|
103
171
|
};
|
104
172
|
}
|
105
173
|
};
|
106
|
-
|
174
|
+
|
107
175
|
processModules(scriptTag);
|
108
|
-
|
176
|
+
|
109
177
|
if (existingRequests.length > 0) {
|
110
178
|
existingRequests.forEach(request => {
|
111
179
|
if (typeof request === 'string') {
|
112
|
-
window.
|
180
|
+
window[API_NAME].load(request);
|
113
181
|
}
|
114
182
|
});
|
115
183
|
}
|
116
184
|
};
|
117
185
|
|
118
186
|
if (document.readyState === 'loading') {
|
119
|
-
document.addEventListener('DOMContentLoaded',
|
187
|
+
document.addEventListener('DOMContentLoaded', initializeAPI);
|
120
188
|
} else {
|
121
|
-
|
189
|
+
initializeAPI();
|
122
190
|
}
|
package/package.json
CHANGED
package/styles.css
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
/* transition */
|
2
|
+
body .transition {display: block}
|
3
|
+
.w-editor .transition {display: none;}
|
4
|
+
.no-scroll-transition {overflow: hidden; position: relative;}
|
5
|
+
|
6
|
+
/* splittext */
|
7
|
+
.line-mask, .word-mask, .char-mask {
|
8
|
+
padding-bottom: .1em;
|
9
|
+
margin-bottom: -.1em;
|
10
|
+
padding-inline: .1em;
|
11
|
+
margin-inline: -.1em;
|
12
|
+
}
|
13
|
+
/* scroll cleanliness */
|
14
|
+
html, body {
|
15
|
+
overscroll-behavior: none;
|
16
|
+
scrollbar-gutter: stable;
|
17
|
+
}
|