rytm-webflow 2.3.0 → 2.3.1
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 +10 -2
- package/.mcp.json +8 -0
- package/package.json +1 -1
- package/scripts/aswap/WebflowListView.js +140 -2
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"permissions": {
|
|
3
3
|
"allow": [
|
|
4
|
-
"Bash(git fetch:*)"
|
|
4
|
+
"Bash(git fetch:*)",
|
|
5
|
+
"mcp__metrum__get_task",
|
|
6
|
+
"Bash(git checkout:*)",
|
|
7
|
+
"mcp__metrum__set_subtask_status",
|
|
8
|
+
"Bash(npm run:*)"
|
|
5
9
|
]
|
|
6
|
-
}
|
|
10
|
+
},
|
|
11
|
+
"enableAllProjectMcpServers": true,
|
|
12
|
+
"enabledMcpjsonServers": [
|
|
13
|
+
"metrum"
|
|
14
|
+
]
|
|
7
15
|
}
|
package/.mcp.json
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
+
import ScrollMagic from 'scrollmagic';
|
|
1
2
|
import gsap from 'gsap';
|
|
2
3
|
import View from './View';
|
|
4
|
+
import scrollController from './../showup/ScrollController';
|
|
3
5
|
import { getWebflowAnimationProps, parseProps } from './../lib/dataTweenParser';
|
|
6
|
+
import { getScrollMagicSceneProps } from './../lib/dataScrollMagicParser';
|
|
7
|
+
import { elementIsVisibleInViewport } from '../lib/helpers';
|
|
4
8
|
import { CLASS_NAME_WEBSCROLL_FIRED } from './WebflowView';
|
|
5
9
|
|
|
6
10
|
// data-webscroll-... (scroll magic)
|
|
@@ -14,6 +18,7 @@ class WebflowListView extends View {
|
|
|
14
18
|
|
|
15
19
|
constructor(id) {
|
|
16
20
|
super(id);
|
|
21
|
+
this.scenes = [];
|
|
17
22
|
}
|
|
18
23
|
/**
|
|
19
24
|
* animate in (show)
|
|
@@ -25,8 +30,15 @@ class WebflowListView extends View {
|
|
|
25
30
|
console.warn("Unknown selector for WebflowListView", this);
|
|
26
31
|
return;
|
|
27
32
|
}
|
|
28
|
-
const
|
|
29
|
-
|
|
33
|
+
const items = [...container.querySelectorAll(this.webset.selector)].filter(this.elementBelongsToView);
|
|
34
|
+
// 1. Set initial state for ALL scroll elements (regardless of viewport)
|
|
35
|
+
this.hideAllScrollElements(items);
|
|
36
|
+
// 2. Stagger-animate in-viewport items (existing behavior)
|
|
37
|
+
items.forEach(this.listElementShow.bind(this));
|
|
38
|
+
// 3. Build ScrollMagic scenes for off-screen items
|
|
39
|
+
this.buildScrollScenesForOffscreenItems(items);
|
|
40
|
+
// 4. Listen for resize to refresh ScrollMagic
|
|
41
|
+
this.addEventListeners();
|
|
30
42
|
}
|
|
31
43
|
/**
|
|
32
44
|
* animate out (hide)
|
|
@@ -40,6 +52,16 @@ class WebflowListView extends View {
|
|
|
40
52
|
}
|
|
41
53
|
const list = [...container.querySelectorAll(this.webset.selector)].filter(this.elementBelongsToView);
|
|
42
54
|
list.forEach(this.listElementHide.bind(this));
|
|
55
|
+
// Also hide off-screen scroll elements that have ScrollMagic scenes
|
|
56
|
+
this.hideOffscreenScrollElements(container);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* hidden (cleanup after hide animation)
|
|
60
|
+
**/
|
|
61
|
+
hidden(container) {
|
|
62
|
+
super.hidden(container);
|
|
63
|
+
this.removeEventListeners();
|
|
64
|
+
this.destroyScenes();
|
|
43
65
|
}
|
|
44
66
|
/**
|
|
45
67
|
* ############
|
|
@@ -106,6 +128,122 @@ class WebflowListView extends View {
|
|
|
106
128
|
});
|
|
107
129
|
}
|
|
108
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* ##################
|
|
133
|
+
* ### DOM events ###
|
|
134
|
+
* ##################
|
|
135
|
+
*/
|
|
136
|
+
handleEvent(e) {
|
|
137
|
+
switch (e.type) {
|
|
138
|
+
case 'resize':
|
|
139
|
+
case 'DOMContentLoaded':
|
|
140
|
+
this.onWindowUpdate();
|
|
141
|
+
break;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
addEventListeners() {
|
|
145
|
+
window.addEventListener('resize', this);
|
|
146
|
+
document.addEventListener('DOMContentLoaded', this);
|
|
147
|
+
}
|
|
148
|
+
removeEventListeners() {
|
|
149
|
+
window.removeEventListener('resize', this);
|
|
150
|
+
document.removeEventListener('DOMContentLoaded', this);
|
|
151
|
+
}
|
|
152
|
+
onWindowUpdate() {
|
|
153
|
+
if (this.scenes.length > 0) {
|
|
154
|
+
scrollController.refresh();
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* #####################
|
|
159
|
+
* ### SCROLL SCENES ###
|
|
160
|
+
* #####################
|
|
161
|
+
*/
|
|
162
|
+
// set initial state for all scroll elements in list items
|
|
163
|
+
hideAllScrollElements(items) {
|
|
164
|
+
items.forEach((el) => {
|
|
165
|
+
const scrollEls = [...el.querySelectorAll('*[data-' + DATA_ATTR_WEBSCROLL_SHOW + ']')]
|
|
166
|
+
.filter(this.elementBelongsToView);
|
|
167
|
+
scrollEls.forEach((scrollEl) => {
|
|
168
|
+
const propsInitial = this.getTweenProps(scrollEl, DATA_ATTR_WEBSCROLL_INIT);
|
|
169
|
+
if (propsInitial) {
|
|
170
|
+
gsap.killTweensOf(scrollEl);
|
|
171
|
+
gsap.set(scrollEl, { ...propsInitial.tween });
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
// build ScrollMagic scenes for off-screen list items
|
|
177
|
+
buildScrollScenesForOffscreenItems(items) {
|
|
178
|
+
items.forEach((el) => {
|
|
179
|
+
if (this.isElementInViewport(el)) return;
|
|
180
|
+
const scrollEls = [...el.querySelectorAll('*[data-' + DATA_ATTR_WEBSCROLL_SHOW + ']')]
|
|
181
|
+
.filter(this.elementBelongsToView);
|
|
182
|
+
scrollEls.forEach((scrollEl) => {
|
|
183
|
+
this.buildScrollmagicScene(scrollEl);
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
// build a single ScrollMagic scene for a scroll element
|
|
188
|
+
buildScrollmagicScene(el) {
|
|
189
|
+
const propsInitial = this.getTweenProps(el, DATA_ATTR_WEBSCROLL_INIT);
|
|
190
|
+
const propsShow = this.getTweenProps(el, DATA_ATTR_WEBSCROLL_SHOW);
|
|
191
|
+
const setup = parseProps(el.dataset[DATA_ATTR_SETUP] || '');
|
|
192
|
+
if (propsInitial && propsShow) {
|
|
193
|
+
const smsp = getScrollMagicSceneProps(el, setup);
|
|
194
|
+
const scene = new ScrollMagic.Scene(smsp);
|
|
195
|
+
scene.on("start", (e) => {
|
|
196
|
+
if (el.classList.contains(CLASS_NAME_WEBSCROLL_FIRED)) return;
|
|
197
|
+
if (e.scrollDirection === "FORWARD") {
|
|
198
|
+
gsap.killTweensOf(el);
|
|
199
|
+
gsap.set(el, { ...propsInitial.tween });
|
|
200
|
+
gsap.to(el, {
|
|
201
|
+
duration: propsShow.time,
|
|
202
|
+
...propsShow.tween,
|
|
203
|
+
onComplete: () => {
|
|
204
|
+
el.classList.add(CLASS_NAME_WEBSCROLL_FIRED);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
scene.on("add", () => {
|
|
210
|
+
setTimeout(() => {
|
|
211
|
+
if (elementIsVisibleInViewport(el, true)) {
|
|
212
|
+
gsap.to(el, {
|
|
213
|
+
duration: propsShow.time,
|
|
214
|
+
...propsShow.tween,
|
|
215
|
+
onComplete: () => {
|
|
216
|
+
el.classList.add(CLASS_NAME_WEBSCROLL_FIRED);
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}, 10);
|
|
221
|
+
});
|
|
222
|
+
scene.addTo(scrollController.get());
|
|
223
|
+
this.scenes.push(scene);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
// hide off-screen scroll elements during view hide
|
|
227
|
+
hideOffscreenScrollElements(container) {
|
|
228
|
+
const scrollEls = [...container.querySelectorAll('*[data-' + DATA_ATTR_WEBSCROLL_HIDE + ']')]
|
|
229
|
+
.filter(this.elementBelongsToView);
|
|
230
|
+
scrollEls.forEach((el) => {
|
|
231
|
+
if (this.isElementInViewport(el.closest(this.webset.selector))) return;
|
|
232
|
+
const propsHide = this.getTweenProps(el, DATA_ATTR_WEBSCROLL_HIDE);
|
|
233
|
+
if (propsHide) {
|
|
234
|
+
gsap.killTweensOf(el);
|
|
235
|
+
gsap.to(el, { duration: propsHide.time, ...propsHide.tween });
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
// destroy all ScrollMagic scenes
|
|
240
|
+
destroyScenes() {
|
|
241
|
+
this.scenes.forEach((scene) => {
|
|
242
|
+
scene.destroy();
|
|
243
|
+
scene = null;
|
|
244
|
+
});
|
|
245
|
+
this.scenes = [];
|
|
246
|
+
}
|
|
109
247
|
/**
|
|
110
248
|
* ###############
|
|
111
249
|
* ### HELPERS ###
|