rytm-webflow 2.3.1 → 2.3.2

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.md CHANGED
@@ -3,7 +3,7 @@
3
3
  AJAX view-swapping engine and scroll-triggered animation framework for tuki CMS projects.
4
4
  Built on GSAP + ScrollMagic. Transforms server-rendered pages into pseudo-SPAs.
5
5
 
6
- **Version**: 2.2.4 | **Entry**: `scripts/index.js` | **Deps**: gsap, imagesloaded, scrollmagic
6
+ **Version**: 2.3.2 | **Entry**: `scripts/index.js` | **Deps**: gsap, imagesloaded, scrollmagic
7
7
 
8
8
  ## Exports
9
9
 
@@ -361,3 +361,4 @@ RytmWebflow.scrollController.destroy() // Clean up
361
361
  7. **Stagger not working** — requires `data-webset="selector:..."` attribute
362
362
  8. **Easing not applied** — use GSAP v3 syntax: `e:power3.out` (not `e:Power3.easeOut`)
363
363
  9. **Cache causes stale content** — add `no-as-cache` class to forms or dynamic links
364
+ 10. **Webscroll elements and viewport growth** — since 2.3.2 `webscroll` elements that enter the viewport via a resize / devtools toggle / background-tab activation (no scroll) are revealed automatically; earlier versions only fired on a `FORWARD` scroll crossing
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rytm-webflow",
3
- "version": "2.3.1",
3
+ "version": "2.3.2",
4
4
  "description": "rytm webflow pack - ASwap, ShowUp",
5
5
  "main": "scripts/index.js",
6
6
  "types": "scripts/index.d.ts",
@@ -19,6 +19,7 @@ class WebflowListView extends View {
19
19
  constructor(id) {
20
20
  super(id);
21
21
  this.scenes = [];
22
+ this._revealTimer = null;
22
23
  }
23
24
  /**
24
25
  * animate in (show)
@@ -136,6 +137,7 @@ class WebflowListView extends View {
136
137
  handleEvent(e) {
137
138
  switch (e.type) {
138
139
  case 'resize':
140
+ case 'pageshow':
139
141
  case 'DOMContentLoaded':
140
142
  this.onWindowUpdate();
141
143
  break;
@@ -143,17 +145,58 @@ class WebflowListView extends View {
143
145
  }
144
146
  addEventListeners() {
145
147
  window.addEventListener('resize', this);
148
+ window.addEventListener('pageshow', this);
146
149
  document.addEventListener('DOMContentLoaded', this);
147
150
  }
148
151
  removeEventListeners() {
149
152
  window.removeEventListener('resize', this);
153
+ window.removeEventListener('pageshow', this);
150
154
  document.removeEventListener('DOMContentLoaded', this);
155
+ clearTimeout(this._revealTimer);
151
156
  }
152
157
  onWindowUpdate() {
153
158
  if (this.scenes.length > 0) {
154
159
  scrollController.refresh();
160
+ // viewport size changed - recompute viewport-dependent scene offsets
161
+ // (they are baked from window.innerHeight at scene-build time)
162
+ this.refreshSceneOffsets();
163
+ // reveal elements that entered the viewport without a scroll (debounced -
164
+ // resize fires in bursts while dragging / toggling devtools)
165
+ clearTimeout(this._revealTimer);
166
+ this._revealTimer = setTimeout(() => this.revealVisibleScrollElements(), 150);
155
167
  }
156
168
  }
169
+ /**
170
+ * Recompute viewport-dependent scene offsets after a viewport change
171
+ */
172
+ refreshSceneOffsets() {
173
+ this.scenes.forEach(({ scene, el, setup }) => {
174
+ scene.offset(getScrollMagicSceneProps(el, setup).offset);
175
+ });
176
+ }
177
+ /**
178
+ * Reveal scene elements that entered the viewport without a scroll
179
+ * (devtools toggle, window resize, background-tab activation) -
180
+ * resize-driven scene updates carry scrollDirection "PAUSED", so the
181
+ * FORWARD guard in the scene start handler never fires for them
182
+ */
183
+ revealVisibleScrollElements() {
184
+ this.scenes.forEach(({ el, propsShow }) => {
185
+ if (el.classList.contains(CLASS_NAME_WEBSCROLL_FIRED)) {
186
+ return;
187
+ }
188
+ if (!elementIsVisibleInViewport(el, true)) {
189
+ return;
190
+ }
191
+ gsap.to(el, {
192
+ duration: propsShow.time,
193
+ ...propsShow.tween,
194
+ onComplete: () => {
195
+ el.classList.add(CLASS_NAME_WEBSCROLL_FIRED);
196
+ }
197
+ });
198
+ });
199
+ }
157
200
  /**
158
201
  * #####################
159
202
  * ### SCROLL SCENES ###
@@ -220,7 +263,7 @@ class WebflowListView extends View {
220
263
  }, 10);
221
264
  });
222
265
  scene.addTo(scrollController.get());
223
- this.scenes.push(scene);
266
+ this.scenes.push({ scene, el, propsShow, setup });
224
267
  }
225
268
  }
226
269
  // hide off-screen scroll elements during view hide
@@ -238,9 +281,8 @@ class WebflowListView extends View {
238
281
  }
239
282
  // destroy all ScrollMagic scenes
240
283
  destroyScenes() {
241
- this.scenes.forEach((scene) => {
284
+ this.scenes.forEach(({ scene }) => {
242
285
  scene.destroy();
243
- scene = null;
244
286
  });
245
287
  this.scenes = [];
246
288
  }
@@ -28,6 +28,7 @@ class WebflowView extends View {
28
28
  constructor(id) {
29
29
  super(id);
30
30
  this.scenes = [];
31
+ this._revealTimer = null;
31
32
  }
32
33
  /**
33
34
  * prepare (before show)
@@ -85,6 +86,9 @@ class WebflowView extends View {
85
86
  case 'resize':
86
87
  this.onWindowUpdate(e);
87
88
  break;
89
+ case 'pageshow':
90
+ this.onWindowUpdate(e);
91
+ break;
88
92
  case 'DOMContentLoaded':
89
93
  this.onWindowUpdate(e);
90
94
  break;
@@ -95,6 +99,7 @@ class WebflowView extends View {
95
99
  */
96
100
  addEventListeners() {
97
101
  window.addEventListener('resize', this);
102
+ window.addEventListener('pageshow', this);
98
103
  document.addEventListener('DOMContentLoaded', this);
99
104
  }
100
105
  /**
@@ -102,13 +107,53 @@ class WebflowView extends View {
102
107
  */
103
108
  removeEventListeners() {
104
109
  window.removeEventListener('resize', this);
110
+ window.removeEventListener('pageshow', this);
105
111
  document.removeEventListener('DOMContentLoaded', this);
112
+ clearTimeout(this._revealTimer);
106
113
  }
107
114
  onWindowUpdate(e) {
108
115
  if (this.scenes.length > 0) {
109
116
  scrollController.refresh();
117
+ // viewport size changed - recompute viewport-dependent scene offsets
118
+ // (they are baked from window.innerHeight at scene-build time)
119
+ this.refreshSceneOffsets();
120
+ // reveal elements that entered the viewport without a scroll (debounced -
121
+ // resize fires in bursts while dragging / toggling devtools)
122
+ clearTimeout(this._revealTimer);
123
+ this._revealTimer = setTimeout(() => this.revealVisibleScrollElements(), 150);
110
124
  }
111
125
  }
126
+ /**
127
+ * Recompute viewport-dependent scene offsets after a viewport change
128
+ */
129
+ refreshSceneOffsets() {
130
+ this.scenes.forEach(({ scene, el, setup }) => {
131
+ scene.offset(getScrollMagicSceneProps(el, setup).offset);
132
+ });
133
+ }
134
+ /**
135
+ * Reveal scene elements that entered the viewport without a scroll
136
+ * (devtools toggle, window resize, background-tab activation) -
137
+ * resize-driven scene updates carry scrollDirection "PAUSED", so the
138
+ * FORWARD guard in the scene start handler never fires for them
139
+ */
140
+ revealVisibleScrollElements() {
141
+ this.scenes.forEach(({ el, propsShow }) => {
142
+ if (el.classList.contains(CLASS_NAME_WEBSCROLL_FIRED)) {
143
+ return;
144
+ }
145
+ if (!elementIsVisibleInViewport(el, true)) {
146
+ return;
147
+ }
148
+ gsap.to(el, {
149
+ duration: propsShow.time,
150
+ ...propsShow.tween,
151
+ onComplete: () => {
152
+ el.classList.add(CLASS_NAME_WEBSCROLL_FIRED);
153
+ }
154
+ });
155
+ });
156
+ }
112
157
  /**
113
158
  * ############
114
159
  * ### SHOW ###
@@ -234,7 +279,7 @@ class WebflowView extends View {
234
279
  }, 10);
235
280
  });
236
281
  scene.addTo(scrollController.get());
237
- this.scenes.push(scene);
282
+ this.scenes.push({ scene, el, propsShow, setup });
238
283
  }
239
284
  }
240
285
  /**
@@ -310,9 +355,8 @@ class WebflowView extends View {
310
355
  }
311
356
  // ## Destroy scrollmagic scenes
312
357
  destroyScenes() {
313
- this.scenes.forEach((scene) => {
358
+ this.scenes.forEach(({ scene }) => {
314
359
  scene.destroy();
315
- scene = null;
316
360
  })
317
361
  this.scenes = [];
318
362
  }