rytm-webflow 1.1.1 → 1.1.5
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 +171 -14
- package/package.json +10 -4
- package/scripts/aswap/ASwap.js +1 -0
- package/scripts/aswap/ASwapDispatcher.js +10 -2
- package/scripts/bootstrap/v5/Bootstrap5.js +45 -0
- package/scripts/index.js +17 -2
- package/scripts/lib/dataScrollMagicParser.js +80 -0
- package/scripts/lib/dataTweenParser.js +209 -0
- package/scripts/showup/ScrollController.js +43 -0
- package/scripts/showup/ShowUp.js +126 -0
package/README.md
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
# rytm-webflow
|
|
2
2
|
Rytm webflow pack is used to make websites rendered on the server (using a PHP or other back-end) more dynamic.
|
|
3
|
-
A static website can be transformed into something we call a *pseudo single page application* (SPA). The pack includes **ASwap** - a framework programmed by Rytm Digital arround 2016 which uses *AJAX* to first load and then *swap* the page's content in user's browser
|
|
3
|
+
A static website can be transformed into something we call a *pseudo single page application* (SPA). The pack includes **[ASwap](#markdown-header-aswap)** - a framework programmed by Rytm Digital arround 2016 which uses *AJAX* to first load and then *swap* the page's content in user's browser.
|
|
4
|
+
Another future rytm-webflow includes is **[ShowUp](#markdown-header-showup)** - a ScrollMagic.js wrapper which can be integrated with ASwap.
|
|
5
|
+
|
|
4
6
|
|
|
5
7
|
**Install using NPM:**
|
|
6
8
|
```
|
|
7
9
|
$ npm install rytm-webflow --save
|
|
8
10
|
```
|
|
11
|
+
## Dependencies
|
|
12
|
+
- gsap
|
|
13
|
+
- imagesloaded
|
|
14
|
+
- jquery
|
|
15
|
+
- scrollmagic
|
|
16
|
+
- scrollmagic-plugin-gsap
|
|
9
17
|
|
|
10
|
-
##
|
|
18
|
+
## ASwap
|
|
19
|
+
### Basic usage example
|
|
11
20
|
In your HTML for each request you neeed a wrapper (default wrapper selector: ```#stage```)
|
|
12
21
|
Inside a wrapper you can have multiple view instances. Each view uses a name defined in ```data-as-view``` and an unique id defined in ```data-as-id```
|
|
13
22
|
```html
|
|
@@ -25,12 +34,8 @@ import RytmWebflow from 'rytm-webflow'
|
|
|
25
34
|
RytmWebflow.aswap.init()
|
|
26
35
|
```
|
|
27
36
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
- jquery
|
|
31
|
-
|
|
32
|
-
## More examples
|
|
33
|
-
### Controllers
|
|
37
|
+
### More examples
|
|
38
|
+
#### Controllers
|
|
34
39
|
To programm some interaction within your ASwap views create a controller
|
|
35
40
|
|
|
36
41
|
```js
|
|
@@ -83,7 +88,7 @@ If you are using the ```ControllerImgLoad``` - to hide the view before the show
|
|
|
83
88
|
```
|
|
84
89
|
|
|
85
90
|
|
|
86
|
-
|
|
91
|
+
#### Views
|
|
87
92
|
If you need some fancy animations to show / hide your views you can create view instances.
|
|
88
93
|
```js
|
|
89
94
|
import RytmWebflow from 'rytm-webflow'
|
|
@@ -114,7 +119,7 @@ RytmWebflow.aswap.init({
|
|
|
114
119
|
})
|
|
115
120
|
```
|
|
116
121
|
Read more about it -> [View class](#markdown-header-view-class)
|
|
117
|
-
|
|
122
|
+
#### Sample params
|
|
118
123
|
```js
|
|
119
124
|
import RytmWebflow from 'rytm-webflow'
|
|
120
125
|
import { trace } from 'rytm-helpers'
|
|
@@ -130,7 +135,7 @@ RytmWebflow.aswap.init({
|
|
|
130
135
|
```
|
|
131
136
|
|
|
132
137
|
|
|
133
|
-
|
|
138
|
+
### Params
|
|
134
139
|
|
|
135
140
|
| param | default | info |
|
|
136
141
|
|---|---|---|
|
|
@@ -149,9 +154,10 @@ RytmWebflow.aswap.init({
|
|
|
149
154
|
| ```refreshOnSameUrl``` | ```true``` | When ```true``` each request on same URL will refresh browser's window |
|
|
150
155
|
| ```routes``` | ```{}``` | The routes object where you can define controllers and views |
|
|
151
156
|
| ```swapSelector``` | ```'#stage'``` | ASwap wrapper selector |
|
|
157
|
+
| ```defaultDocumentScrollBehavior``` | ```'smooth'``` | Default document scroll behaviour |
|
|
152
158
|
| ```trace``` | ```(message) => {}``` | A function to log stuff, eg in console |
|
|
153
159
|
|
|
154
|
-
|
|
160
|
+
### Controller class
|
|
155
161
|
|
|
156
162
|
| method | info |
|
|
157
163
|
|---|---|
|
|
@@ -167,7 +173,7 @@ RytmWebflow.aswap.init({
|
|
|
167
173
|
| ```onViewShow()``` | read more -> [global events](#markdown-header-global-events) |
|
|
168
174
|
| ```onViewShown()``` | read more -> [global events](#markdown-header-global-events) |
|
|
169
175
|
|
|
170
|
-
|
|
176
|
+
### View class
|
|
171
177
|
| method | parameters | info |
|
|
172
178
|
|---|---|---|
|
|
173
179
|
| ```hide()``` | ```container``` | this method is called by the ```Controller``` instance in the ```onViewShow()``` method |
|
|
@@ -176,7 +182,7 @@ RytmWebflow.aswap.init({
|
|
|
176
182
|
| ```loadImagesStart()``` | ```container``` | this method is called by the ```ControllerImgLoad``` instance on image load start |
|
|
177
183
|
| ```loadImagesComplete()``` | ```container``` | this method is called by the ```ControllerImgLoad``` instance on image load complete |
|
|
178
184
|
|
|
179
|
-
|
|
185
|
+
### Global events
|
|
180
186
|
You also can pass custom methods to the global ASwap instance events, eg:
|
|
181
187
|
```js
|
|
182
188
|
import RytmWebflow from 'rytm-webflow'
|
|
@@ -198,3 +204,154 @@ RytmWebflow.aswap.init(params, {
|
|
|
198
204
|
| ```onViewSwapComplete()``` | fired just after the scene's content is being replaced |
|
|
199
205
|
| ```onViewShow()``` | fired when the new scene is loaded and the show transition is being started |
|
|
200
206
|
| ```onViewShown()``` | fired within ```animationTimeShow``` ms from show transition start |
|
|
207
|
+
|
|
208
|
+
### Bootstrap integration ###
|
|
209
|
+
#### Bootstrap v5 ####
|
|
210
|
+
A common situation is to hide all Bootstrap offcanvases or modals when an ASwap page transition is in made. To do so just call the `RytmWebflow.bootsrap5.hideFlyovers()` method. You can also hide offcanvases or modals separatly by calling the `RytmWebflow.bootsrap5.hideOffcanvases()` or `RytmWebflow.bootsrap5.hideModals()` methods.
|
|
211
|
+
A basic usage example:
|
|
212
|
+
```js
|
|
213
|
+
import RytmWebflow from 'rytm-webflow'
|
|
214
|
+
|
|
215
|
+
RytmWebflow.aswap.init(params, {
|
|
216
|
+
onViewHide: () => {
|
|
217
|
+
RytmWebflow.bootsrap5.hideFlyovers()
|
|
218
|
+
},
|
|
219
|
+
})
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
## ShowUp ##
|
|
223
|
+
ShowUp makes creating gsap scroll-triggered-animations much faster. It can be used to show content while user scrolls down the page. It can also be used for parallax like animations. ShowUp comes with the **Webflow syntax** - a minified way to describe an animation, it's duration (time), easing, delay or the trigger. Eg. `y:100,o:0` will animate an element from: `y:100px` and `opacity: 0` to it's current state. Go to [Webflow syntax](#markdown-header-webflow-syntax) for more.
|
|
224
|
+
### Webflow usage example
|
|
225
|
+
To enable ShowUp call the init method in your JS:
|
|
226
|
+
```js
|
|
227
|
+
import RytmWebflow from 'rytm-webflow'
|
|
228
|
+
|
|
229
|
+
RytmWebflow.showUp.init()
|
|
230
|
+
```
|
|
231
|
+
If you are working on a SPA or using ASwap the only thing you need to do is call the rebuild method on each view load. Check out the ASwap example:
|
|
232
|
+
```js
|
|
233
|
+
import RytmWebflow from 'rytm-webflow'
|
|
234
|
+
|
|
235
|
+
const events = {
|
|
236
|
+
onViewSwapComplete: () => {
|
|
237
|
+
RytmWebflow.showUp.rebuild()
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
RytmWebflow.aswap.init({}, events)
|
|
241
|
+
```
|
|
242
|
+
To implement ShowUp on your HTML add a `data-webflow` or `data-webpara` attribute to DOM elements which will describe the animation
|
|
243
|
+
```html
|
|
244
|
+
<!-- From opacity: 0 in .4s -->
|
|
245
|
+
<div data-webflow="o:0,t:.4">...</div>
|
|
246
|
+
```
|
|
247
|
+
Add easing for your animations
|
|
248
|
+
```html
|
|
249
|
+
<!-- gsap easing -->
|
|
250
|
+
<div data-webflow="x:-10vw,t:.5,e:Expo.easeOut">...</div>
|
|
251
|
+
```
|
|
252
|
+
Delay animations
|
|
253
|
+
```html
|
|
254
|
+
<!-- delay by .3s -->
|
|
255
|
+
<div data-webflow="x:-20,t:.5,d:.3">...</div>
|
|
256
|
+
```
|
|
257
|
+
#### Trigger element
|
|
258
|
+
By default the ScrollMagic.Scene is triggered by the element itself. Also it is triggered once the element becomes visible (at the window bottom).
|
|
259
|
+
You can override these defaults using the `data-webset` attribute.
|
|
260
|
+
To specyfy an offset:
|
|
261
|
+
```html
|
|
262
|
+
<!-- offset: 100px from bottom up -->
|
|
263
|
+
<div data-webflow="o:0" data-webset="offset:100">...</div>
|
|
264
|
+
|
|
265
|
+
<!-- offset: window center -->
|
|
266
|
+
<div data-webflow="o:0" data-webset="offset:center">...</div>
|
|
267
|
+
|
|
268
|
+
<!-- offset: window top -->
|
|
269
|
+
<div data-webflow="o:0" data-webset="offset:top">...</div>
|
|
270
|
+
```
|
|
271
|
+
To specyfy a trigger element:
|
|
272
|
+
```html
|
|
273
|
+
<!-- target -->
|
|
274
|
+
<div data-webflow="o:0" data-webset="trigger:#myTarget">...</div>
|
|
275
|
+
```
|
|
276
|
+
#### *Hide* animation
|
|
277
|
+
You can specify a *hide* transition (eg. for ASwap View to hide elements)
|
|
278
|
+
Separate *show* and *hide* animation using `;`
|
|
279
|
+
```html
|
|
280
|
+
<!-- show, hide -->
|
|
281
|
+
<div data-webflow="x:-30,t:.5;x:15,t:.3">...</div>
|
|
282
|
+
```
|
|
283
|
+
To start the hide animation on each dom element call the `hide()` method
|
|
284
|
+
```js
|
|
285
|
+
RytmWebflow.showUp.hide()
|
|
286
|
+
```
|
|
287
|
+
An ASwap example implementing ShowUp along with the *hide* animation:
|
|
288
|
+
```js
|
|
289
|
+
import RytmWebflow from 'rytm-webflow'
|
|
290
|
+
|
|
291
|
+
const events = {
|
|
292
|
+
onViewSwapComplete: () => {
|
|
293
|
+
RytmWebflow.showUp.rebuild()
|
|
294
|
+
},
|
|
295
|
+
onViewHide: () => {
|
|
296
|
+
RytmWebflow.showUp.hide()
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
RytmWebflow.aswap.init({}, events)
|
|
300
|
+
```
|
|
301
|
+
### Parallax usage example
|
|
302
|
+
Parallax like animation:
|
|
303
|
+
```html
|
|
304
|
+
<!-- parallax duration 100% - by container height -->
|
|
305
|
+
<div data-webpara="x:100" data-webset="duration:100%">...</div>
|
|
306
|
+
```
|
|
307
|
+
Duration can be relative to viewport:
|
|
308
|
+
```html
|
|
309
|
+
<!-- parallax duration 30vh - by viewport height -->
|
|
310
|
+
<div data-webpara="s:1.2" data-webset="duration:30vh">...</div>
|
|
311
|
+
```
|
|
312
|
+
When moving elements in the Y axis you can use the `parent` trigger hook:
|
|
313
|
+
```html
|
|
314
|
+
<!-- parallax triggered by parent Node -->
|
|
315
|
+
<div>
|
|
316
|
+
<div data-webpara="y:-100" data-webset="duration:100%,trigger:parent"></div>
|
|
317
|
+
</div>
|
|
318
|
+
```
|
|
319
|
+
### Webflow syntax ##
|
|
320
|
+
| short code | prameter | example |
|
|
321
|
+
|---|---|---|
|
|
322
|
+
| ```d``` | delay | ```"d:.4"``` |
|
|
323
|
+
| ```e``` | ease | ```"e:Bounce:easeOut"``` |
|
|
324
|
+
| ```h``` | height | ```"h:10"``` |
|
|
325
|
+
| ```o``` | opacity | ```"o:0"``` |
|
|
326
|
+
| ```r``` | rotation | ```"r:180"``` |
|
|
327
|
+
| ```s``` | scale | ```"s:.1"``` |
|
|
328
|
+
| ```t``` | time | ```"t:.35"``` |
|
|
329
|
+
| ```w``` | width | ```"w:1200"```| |
|
|
330
|
+
|
|
331
|
+
### More examples
|
|
332
|
+
Examples:
|
|
333
|
+
```html
|
|
334
|
+
<h1>Webflow show up</h1>
|
|
335
|
+
<div data-webflow="t:.4,o:0,y:10;t:.4,o:0,y:-30">Webflow Simple ALPHA no delay</div>
|
|
336
|
+
<div data-webflow="t:.4,o:0,s:.4;t:.4,o:0,s:1.5">Webflow Scale</div>
|
|
337
|
+
<div data-webflow="t:1.4,x:-300,e:Expo.easeOut;t:.4,d:0,x:-100,e:Expo.easeIn" data-webset="offset:100">Webflow Offset: 100, Ease EXPO</div>
|
|
338
|
+
<div data-webflow="t:1.4,x:-100vw;t:.4,d:0,x:-250" data-webset="offset:center">Webflow Offset: center</div>
|
|
339
|
+
<div data-webflow="t:1.4,x:-250;t:.4,d:0,x:-250" data-webset="offset:top">Webflow offset: TOP</div>
|
|
340
|
+
<div data-webflow="t:1.4,x:-250,w:10;t:.4,d:0,x:-250" data-webset="trigger:#boom">Webflow Triggered by next BLOCK</div>
|
|
341
|
+
<div id="boom" >Trigger</div>
|
|
342
|
+
<!-- ### Webflow Parallax ### -->
|
|
343
|
+
<h1>Webflow parallax</h1>
|
|
344
|
+
<div data-webpara="x:400" data-webset="duration:100%">Parallax duration 100%</div>
|
|
345
|
+
<div data-webpara="x:400,e:Back.easeOut" data-webset="duration:40vh">Parallax duration 40vh, ease: Back</div>
|
|
346
|
+
<div >parent TRIGGER
|
|
347
|
+
<div data-webpara="y:-500,r:180" data-webset="duration:100%,trigger:parent">Parallax Y</div>
|
|
348
|
+
</div>
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
You can use the parser functions to implement your own view related methods using the Webflow syntax:
|
|
352
|
+
|
|
353
|
+
| function | descr |
|
|
354
|
+
|---|---|
|
|
355
|
+
| ```RytmWebflow.getWebflowProps(str)``` | converts a webflow syntax string into an webflow parameters object: ```{show: {tween: {}, time: X}, hide: {tween: {}, time: X}``` |
|
|
356
|
+
| ```RytmWebflow.getParallaxProps(str)``` | converts a webflow syntax string into an webflow parameters object: ```{parallax: {tween: {}}}``` |
|
|
357
|
+
|
package/package.json
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rytm-webflow",
|
|
3
|
-
"version": "1.1.
|
|
4
|
-
"description": "rytm webflow pack - ASwap
|
|
3
|
+
"version": "1.1.5",
|
|
4
|
+
"description": "rytm webflow pack - ASwap, ShowUp",
|
|
5
5
|
"main": "scripts/index.js",
|
|
6
|
-
"scripts": {
|
|
6
|
+
"scripts": {
|
|
7
|
+
"update": "git pull",
|
|
8
|
+
"push": "git add . && git commit -a -m \"$m\" && git push"
|
|
9
|
+
},
|
|
7
10
|
"repository": {
|
|
8
11
|
"type": "git",
|
|
9
12
|
"url": "git+ssh://git@bitbucket.org/rytm/rytm-webflow.git"
|
|
@@ -13,7 +16,10 @@
|
|
|
13
16
|
"homepage": "https://bitbucket.org/rytm/rytm-webflow#readme",
|
|
14
17
|
"devDependencies": {},
|
|
15
18
|
"dependencies": {
|
|
19
|
+
"gsap": "^3.5.1",
|
|
16
20
|
"imagesloaded": "^4.1.4",
|
|
17
|
-
"jquery": "^3.1.1"
|
|
21
|
+
"jquery": "^3.1.1",
|
|
22
|
+
"scrollmagic": "^2.0.7",
|
|
23
|
+
"scrollmagic-plugin-gsap": "^1.0.4"
|
|
18
24
|
}
|
|
19
25
|
}
|
package/scripts/aswap/ASwap.js
CHANGED
|
@@ -25,6 +25,7 @@ class ASwap {
|
|
|
25
25
|
refreshOnSameUrl: true, // refresh window if same URL is requested
|
|
26
26
|
routes: {},
|
|
27
27
|
swapSelector: '#stage', // swap wrapper selector
|
|
28
|
+
defaultDocumentScrollBehavior: 'smooth',
|
|
28
29
|
trace: () => {}, // a function to trace progress in console
|
|
29
30
|
}
|
|
30
31
|
// public events
|
|
@@ -231,8 +231,16 @@ class ASwapDispatcher extends EventDispatcher {
|
|
|
231
231
|
* scroll back up
|
|
232
232
|
**/
|
|
233
233
|
resetScrollPosition() {
|
|
234
|
-
//
|
|
235
|
-
|
|
234
|
+
// set scrollBehavior to auto
|
|
235
|
+
document.documentElement.style.scrollBehavior = 'auto';
|
|
236
|
+
// scroll without animation
|
|
237
|
+
setTimeout(() => {
|
|
238
|
+
window.scrollTo(0, 0)
|
|
239
|
+
}, 5)
|
|
240
|
+
// set scrollBehavior to it's default value
|
|
241
|
+
setTimeout(() => {
|
|
242
|
+
document.documentElement.style.scrollBehavior = this.defaultDocumentScrollBehavior ? this.defaultDocumentScrollBehavior : 'smooth';
|
|
243
|
+
}, 5)
|
|
236
244
|
}
|
|
237
245
|
/**
|
|
238
246
|
* Preload URL
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Offcanvas, Modal } from 'bootstrap'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Bootstrap v5 helpers
|
|
5
|
+
*/
|
|
6
|
+
class Bootstrap5 {
|
|
7
|
+
/**
|
|
8
|
+
* @constructor
|
|
9
|
+
**/
|
|
10
|
+
constructor() {
|
|
11
|
+
}
|
|
12
|
+
// # Hide all modals, offcanvases, etc.
|
|
13
|
+
hideFlyovers() {
|
|
14
|
+
this.hideModals()
|
|
15
|
+
this.hideOffcanvases()
|
|
16
|
+
}
|
|
17
|
+
// # Hide all modals #
|
|
18
|
+
hideModals() {
|
|
19
|
+
const list = [...document.querySelectorAll(".modal")]
|
|
20
|
+
list.forEach(this.hdieModal.bind(this))
|
|
21
|
+
}
|
|
22
|
+
// # Hide offcanvas element
|
|
23
|
+
hdieModal(el) {
|
|
24
|
+
const modal = Modal.getInstance(el)
|
|
25
|
+
if (modal) {
|
|
26
|
+
modal.hide()
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// # Hide all offcanvases #
|
|
30
|
+
hideOffcanvases() {
|
|
31
|
+
const list = [...document.querySelectorAll(".offcanvas")]
|
|
32
|
+
list.forEach(this.hdieOffcanvas.bind(this))
|
|
33
|
+
}
|
|
34
|
+
// # Hide offcanvas element
|
|
35
|
+
hdieOffcanvas(el) {
|
|
36
|
+
const offcanvas = Offcanvas.getInstance(el)
|
|
37
|
+
if (offcanvas) {
|
|
38
|
+
offcanvas.hide()
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const bootstrap5 = new Bootstrap5();
|
|
45
|
+
export default bootstrap5;
|
package/scripts/index.js
CHANGED
|
@@ -2,11 +2,26 @@ import aswapInstance from './aswap/ASwap'
|
|
|
2
2
|
import Controller from './aswap/Controller'
|
|
3
3
|
import ControllerImgLoad from './aswap/ControllerImgLoad'
|
|
4
4
|
import View from './aswap/View'
|
|
5
|
-
|
|
5
|
+
// Bootstrap v5 helper
|
|
6
|
+
import bootsrap5 from './bootstrap/v5/Bootstrap5'
|
|
7
|
+
// showup
|
|
8
|
+
import scrollController from './showup/ScrollController'
|
|
9
|
+
import showUp from './showup/ShowUp'
|
|
10
|
+
import { getWebflowProps, getParallaxProps } from './lib/dataTweenParser'
|
|
11
|
+
import { getScrollMagicSceneProps } from './lib/dataScrollMagicParser'
|
|
6
12
|
|
|
7
13
|
export default {
|
|
8
14
|
aswap: aswapInstance,
|
|
9
15
|
Controller,
|
|
10
16
|
ControllerImgLoad,
|
|
11
|
-
View
|
|
17
|
+
View,
|
|
18
|
+
// boottstrap
|
|
19
|
+
bootsrap5,
|
|
20
|
+
// showup
|
|
21
|
+
scrollController,
|
|
22
|
+
showUp,
|
|
23
|
+
// lib
|
|
24
|
+
getWebflowProps,
|
|
25
|
+
getParallaxProps,
|
|
26
|
+
getScrollMagicSceneProps
|
|
12
27
|
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* get ScrollMagic.Scene props
|
|
3
|
+
* @param {Object} wfProps
|
|
4
|
+
* @param {Object} setup
|
|
5
|
+
* @return {Object}
|
|
6
|
+
*/
|
|
7
|
+
export const getScrollMagicSceneProps = (el, setup) => {
|
|
8
|
+
const triggerElement = getTriggerFromProps(setup, el)
|
|
9
|
+
const duration = getDurationFromProps(setup)
|
|
10
|
+
const offset = getOffsetFromProps(setup)
|
|
11
|
+
return {
|
|
12
|
+
triggerElement,
|
|
13
|
+
duration,
|
|
14
|
+
offset
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const getTriggerFromProps = (setup, el) => {
|
|
18
|
+
let trigger = el
|
|
19
|
+
if (setup.trigger) {
|
|
20
|
+
switch (setup.trigger) {
|
|
21
|
+
case "parent":
|
|
22
|
+
trigger = el.parentNode
|
|
23
|
+
break;
|
|
24
|
+
default:
|
|
25
|
+
trigger = setup.trigger
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return trigger
|
|
30
|
+
}
|
|
31
|
+
const getDurationFromProps = (setup) => {
|
|
32
|
+
let duration = 0
|
|
33
|
+
if (setup.duration) {
|
|
34
|
+
duration = isNaN(setup.duration) ? translateNonNumericValue("duration", setup.duration) : parseFloat(setup.duration)
|
|
35
|
+
}
|
|
36
|
+
return duration
|
|
37
|
+
}
|
|
38
|
+
const getOffsetFromProps = (setup) => {
|
|
39
|
+
const bottomOffset = setup.offset ? setup.offset : 0
|
|
40
|
+
let offset
|
|
41
|
+
switch (bottomOffset) {
|
|
42
|
+
case "center":
|
|
43
|
+
offset = 0
|
|
44
|
+
break;
|
|
45
|
+
case "top":
|
|
46
|
+
offset = window.innerHeight * .5
|
|
47
|
+
break;
|
|
48
|
+
default:
|
|
49
|
+
offset = bottomOffset - window.innerHeight * .5
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
return offset
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Translate non numeric value strings...
|
|
57
|
+
* @param {string} key
|
|
58
|
+
* @param {string} valStr
|
|
59
|
+
* @return {*}
|
|
60
|
+
*/
|
|
61
|
+
const translateNonNumericValue = (key, valStr) => {
|
|
62
|
+
let val
|
|
63
|
+
switch (key) {
|
|
64
|
+
case 'duration':
|
|
65
|
+
// calculate viewport vw & vh
|
|
66
|
+
if (valStr.includes("vw")) {
|
|
67
|
+
val = parseInt(valStr.replace("vw", "")) * .01 * window.innerWidth
|
|
68
|
+
} else if (valStr.includes("vh")) {
|
|
69
|
+
val = parseInt(valStr.replace("vh", "")) * .01 * window.innerHeight
|
|
70
|
+
} else {
|
|
71
|
+
val = valStr
|
|
72
|
+
}
|
|
73
|
+
break;
|
|
74
|
+
default:
|
|
75
|
+
// by default don't change anything (eg. '100%', '50vw')
|
|
76
|
+
val = valStr
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
return val
|
|
80
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
import { Power0, Power1, Power2, Power3, Power4, Back, Bounce, Elastic, Expo, Sine } from 'gsap'
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
const DEFAULT_ANIMATION_TIME = .4
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Split string into props
|
|
8
|
+
* @param {string} gstr (eg. y:100,o:0)
|
|
9
|
+
* @return {Object}
|
|
10
|
+
*/
|
|
11
|
+
export const parseProps = (gstr) => {
|
|
12
|
+
let props = {}
|
|
13
|
+
if (gstr) {
|
|
14
|
+
const arr = gstr.split(',')
|
|
15
|
+
arr.forEach((s, index) => {
|
|
16
|
+
let p = parseWebflowKeyValue(s)
|
|
17
|
+
props = {...props, ...p}
|
|
18
|
+
})
|
|
19
|
+
}
|
|
20
|
+
return props
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* get Parallax Props
|
|
24
|
+
* @param {String} dataStr
|
|
25
|
+
* @return {Object}
|
|
26
|
+
* {parallax: {tween: {}}}
|
|
27
|
+
*/
|
|
28
|
+
export const getParallaxProps = (dataStr) => {
|
|
29
|
+
let params = {}
|
|
30
|
+
if (dataStr) {
|
|
31
|
+
const groups = dataStr.split(';')
|
|
32
|
+
params.parallax = getWebflowAnimationProps(groups[0])
|
|
33
|
+
}
|
|
34
|
+
return params
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* get Webflow Props
|
|
39
|
+
* @param {String} dataStr
|
|
40
|
+
* @return {Object}
|
|
41
|
+
* {show: {tween: {}, time: X}, hide: {tween: {}, time: X}
|
|
42
|
+
*/
|
|
43
|
+
export const getWebflowProps = (dataStr) => {
|
|
44
|
+
let params = {}
|
|
45
|
+
if (dataStr) {
|
|
46
|
+
const groups = dataStr.split(';')
|
|
47
|
+
params.show = getWebflowAnimationProps(groups[0])
|
|
48
|
+
// hide (optional)
|
|
49
|
+
params.hide = groups.length > 1 ? getWebflowAnimationProps(groups[1]) : false
|
|
50
|
+
}
|
|
51
|
+
return params
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* group string to JS object
|
|
55
|
+
* @param {string} gstr (eg. y:100,o:0)
|
|
56
|
+
* @return {Object}
|
|
57
|
+
*/
|
|
58
|
+
const getWebflowAnimationProps = (gstr) => {
|
|
59
|
+
let propsFromData = parseProps(gstr)
|
|
60
|
+
return getValidWebflowAnimationProps(propsFromData)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* divide properties for TweenMax use: time (int) vs tween (obj)
|
|
65
|
+
* @param {Object} propsFromData
|
|
66
|
+
* @return {Object}
|
|
67
|
+
*/
|
|
68
|
+
const getValidWebflowAnimationProps = (propsFromData) => {
|
|
69
|
+
let ap = {
|
|
70
|
+
time: DEFAULT_ANIMATION_TIME,
|
|
71
|
+
tween: {}
|
|
72
|
+
}
|
|
73
|
+
for (let key in propsFromData) {
|
|
74
|
+
switch (key) {
|
|
75
|
+
case 'time':
|
|
76
|
+
ap.time = propsFromData[key]
|
|
77
|
+
break;
|
|
78
|
+
default:
|
|
79
|
+
ap.tween[key] = propsFromData[key]
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return ap
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* key:value string to JS object
|
|
87
|
+
* @param {string} s (eg. y:100)
|
|
88
|
+
* @return {*}
|
|
89
|
+
*/
|
|
90
|
+
const parseWebflowKeyValue = (s) => {
|
|
91
|
+
let p = {}
|
|
92
|
+
const arr = s.split(':')
|
|
93
|
+
if (arr.length > 1) {
|
|
94
|
+
const key = translateKey(arr[0])
|
|
95
|
+
const val = isNaN(arr[1]) ? translateNonNumericValue(key, arr[1]) : parseFloat(arr[1])
|
|
96
|
+
p[key] = val
|
|
97
|
+
}
|
|
98
|
+
return p
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Translate key (eg: 'o' = 'opacity')
|
|
102
|
+
* @param {string} keyStr
|
|
103
|
+
* @return string
|
|
104
|
+
*/
|
|
105
|
+
const translateKey = (keyStr) => {
|
|
106
|
+
let key = ''
|
|
107
|
+
switch (keyStr) {
|
|
108
|
+
case 'd':
|
|
109
|
+
key = 'delay'
|
|
110
|
+
break
|
|
111
|
+
case 'e':
|
|
112
|
+
key = 'ease'
|
|
113
|
+
break
|
|
114
|
+
case 'h':
|
|
115
|
+
key = 'height'
|
|
116
|
+
break
|
|
117
|
+
case 'o':
|
|
118
|
+
key = 'opacity'
|
|
119
|
+
break
|
|
120
|
+
case 'r':
|
|
121
|
+
key = 'rotation'
|
|
122
|
+
break
|
|
123
|
+
case 's':
|
|
124
|
+
key = 'scale'
|
|
125
|
+
break
|
|
126
|
+
case 't':
|
|
127
|
+
key = 'time'
|
|
128
|
+
break
|
|
129
|
+
case 'w':
|
|
130
|
+
key = 'width'
|
|
131
|
+
break
|
|
132
|
+
default:
|
|
133
|
+
key = keyStr
|
|
134
|
+
break
|
|
135
|
+
}
|
|
136
|
+
return key
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Translate non numeric value strings...
|
|
140
|
+
* @param {string} key
|
|
141
|
+
* @param {string} valStr
|
|
142
|
+
* @return {*}
|
|
143
|
+
*/
|
|
144
|
+
const translateNonNumericValue = (key, valStr) => {
|
|
145
|
+
let val
|
|
146
|
+
switch (key) {
|
|
147
|
+
case 'ease':
|
|
148
|
+
val = getWebflowEasing(valStr)
|
|
149
|
+
break;
|
|
150
|
+
default:
|
|
151
|
+
// by default don't change anything (eg. '100%', '50vw')
|
|
152
|
+
val = valStr
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
return val
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Get the TweenMax Easing function
|
|
159
|
+
* @param {string} str
|
|
160
|
+
* @return {Ease}
|
|
161
|
+
*/
|
|
162
|
+
const getWebflowEasing = (str) => {
|
|
163
|
+
let easing
|
|
164
|
+
let arr = str.split('.')
|
|
165
|
+
if (arr.length == 2) {
|
|
166
|
+
switch (arr[0]) {
|
|
167
|
+
case 'Back':
|
|
168
|
+
easing = Back[arr[1]]
|
|
169
|
+
break;
|
|
170
|
+
case 'Bounce':
|
|
171
|
+
easing = Bounce[arr[1]]
|
|
172
|
+
break;
|
|
173
|
+
case 'Circ':
|
|
174
|
+
easing = Circ[arr[1]]
|
|
175
|
+
break;
|
|
176
|
+
case 'Elastic':
|
|
177
|
+
easing = Elastic[arr[1]]
|
|
178
|
+
break;
|
|
179
|
+
case 'Expo':
|
|
180
|
+
easing = Expo[arr[1]]
|
|
181
|
+
break;
|
|
182
|
+
case 'Power0':
|
|
183
|
+
easing = Power0[arr[1]]
|
|
184
|
+
break;
|
|
185
|
+
case 'Power1':
|
|
186
|
+
easing = Power1[arr[1]]
|
|
187
|
+
break;
|
|
188
|
+
case 'Power2':
|
|
189
|
+
easing = Power2[arr[1]]
|
|
190
|
+
break;
|
|
191
|
+
case 'Power3':
|
|
192
|
+
easing = Power3[arr[1]]
|
|
193
|
+
break;
|
|
194
|
+
case 'Power4':
|
|
195
|
+
easing = Power4[arr[1]]
|
|
196
|
+
break;
|
|
197
|
+
case 'SlowMo':
|
|
198
|
+
easing = SlowMo[arr[1]]
|
|
199
|
+
break;
|
|
200
|
+
case 'Stepped':
|
|
201
|
+
easing = SteppedEase[arr[1]]
|
|
202
|
+
break;
|
|
203
|
+
default:
|
|
204
|
+
easing = Sine[arr[1]]
|
|
205
|
+
break;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
return easing
|
|
209
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// # configure scroll magic and gsap
|
|
2
|
+
import ScrollMagic from 'scrollmagic'
|
|
3
|
+
import { ScrollMagicPluginGsap } from "scrollmagic-plugin-gsap";
|
|
4
|
+
import { gsap } from 'gsap'
|
|
5
|
+
// GSAP plugins
|
|
6
|
+
ScrollMagicPluginGsap(ScrollMagic, gsap)
|
|
7
|
+
|
|
8
|
+
class ScrollController {
|
|
9
|
+
/**
|
|
10
|
+
* @constructor
|
|
11
|
+
**/
|
|
12
|
+
constructor() {
|
|
13
|
+
this.controller = null
|
|
14
|
+
}
|
|
15
|
+
// Build ScrollMagic Controller
|
|
16
|
+
_build() {
|
|
17
|
+
this.destroy()
|
|
18
|
+
this.controller = new ScrollMagic.Controller({
|
|
19
|
+
loglevel: 0
|
|
20
|
+
})
|
|
21
|
+
}
|
|
22
|
+
get() {
|
|
23
|
+
if (!this.controller) {
|
|
24
|
+
this._build()
|
|
25
|
+
}
|
|
26
|
+
return this.controller
|
|
27
|
+
}
|
|
28
|
+
destroy() {
|
|
29
|
+
if (this.controller) {
|
|
30
|
+
this.controller.destroy()
|
|
31
|
+
this.controller = null
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
refresh(immediately = false) {
|
|
35
|
+
if (this.controller) {
|
|
36
|
+
this.controller.update(immediately)
|
|
37
|
+
} else {
|
|
38
|
+
console.warn("Can't refresh ScrollMagic.Controller. Controller was not build")
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const scrollController = new ScrollController();
|
|
43
|
+
export default scrollController;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// # configure scroll magic and gsap
|
|
2
|
+
import ScrollMagic from 'scrollmagic'
|
|
3
|
+
import { TweenMax } from 'gsap'
|
|
4
|
+
import scrollController from './ScrollController'
|
|
5
|
+
import { parseProps, getWebflowProps, getParallaxProps } from './../lib/dataTweenParser'
|
|
6
|
+
import { getScrollMagicSceneProps } from './../lib/dataScrollMagicParser'
|
|
7
|
+
|
|
8
|
+
const DATA_ATTR_WEBFLOW = "webflow"
|
|
9
|
+
const DATA_ATTR_SETUP = "webset"
|
|
10
|
+
const DATA_ATTR_PARALLAX = "webpara"
|
|
11
|
+
|
|
12
|
+
class ShowUp {
|
|
13
|
+
/**
|
|
14
|
+
* @constructor
|
|
15
|
+
**/
|
|
16
|
+
constructor() {
|
|
17
|
+
this.scenes = []
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Init all scenes
|
|
21
|
+
*/
|
|
22
|
+
init() {
|
|
23
|
+
this.buildWebflowScenes()
|
|
24
|
+
this.buildParallaxScenes()
|
|
25
|
+
this.addEventListeners()
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Add event listeners
|
|
29
|
+
*/
|
|
30
|
+
addEventListeners() {
|
|
31
|
+
$(document).on('DOMContentLoaded load', $.proxy(this.onWindowUpdate, this))
|
|
32
|
+
$(window).on('resize', $.proxy(this.onWindowUpdate, this))
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Remove event listeners
|
|
36
|
+
*/
|
|
37
|
+
removeEventListeners() {
|
|
38
|
+
$(document).off('DOMContentLoaded load', $.proxy(this.onWindowUpdate, this));
|
|
39
|
+
$(window).off('resize', $.proxy(this.onWindowUpdate, this));
|
|
40
|
+
}
|
|
41
|
+
onWindowUpdate(e) {
|
|
42
|
+
if (this.scenes.length > 0) {
|
|
43
|
+
scrollController.refresh()
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Bild scenes for data-webflow elements
|
|
48
|
+
*/
|
|
49
|
+
buildWebflowScenes() {
|
|
50
|
+
const list = [...document.querySelectorAll('*[data-' + DATA_ATTR_WEBFLOW + ']')]
|
|
51
|
+
list.forEach(this.buildWebflowScene.bind(this))
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Build scene
|
|
55
|
+
* @param {DOM} el
|
|
56
|
+
* @param {Int} index
|
|
57
|
+
*/
|
|
58
|
+
buildWebflowScene(el, index) {
|
|
59
|
+
const wfp = getWebflowProps(el.dataset[DATA_ATTR_WEBFLOW])
|
|
60
|
+
const setup = parseProps(el.dataset[DATA_ATTR_SETUP])
|
|
61
|
+
if (wfp.show) {
|
|
62
|
+
const smsp = getScrollMagicSceneProps(el, setup)
|
|
63
|
+
const scene = new ScrollMagic.Scene(smsp)
|
|
64
|
+
scene.setTween(TweenMax.from(el, wfp.show.time, wfp.show.tween))
|
|
65
|
+
scene.addTo(scrollController.get())
|
|
66
|
+
this.scenes.push(scene)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Build Parallax scenes
|
|
71
|
+
*/
|
|
72
|
+
buildParallaxScenes() {
|
|
73
|
+
const list = [...document.querySelectorAll('*[data-' + DATA_ATTR_PARALLAX + ']')]
|
|
74
|
+
list.forEach(this.buildParallaxScene.bind(this))
|
|
75
|
+
}
|
|
76
|
+
buildParallaxScene(el, index) {
|
|
77
|
+
const pp = getParallaxProps(el.dataset[DATA_ATTR_PARALLAX])
|
|
78
|
+
const setup = parseProps(el.dataset[DATA_ATTR_SETUP])
|
|
79
|
+
if (pp) {
|
|
80
|
+
const smsp = getScrollMagicSceneProps(el, setup)
|
|
81
|
+
const scene = new ScrollMagic.Scene(smsp)
|
|
82
|
+
scene.setTween(TweenMax.to(el, 1, pp.parallax.tween))
|
|
83
|
+
scene.addTo(scrollController.get())
|
|
84
|
+
this.scenes.push(scene)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Hide webflow elements
|
|
89
|
+
*/
|
|
90
|
+
hide() {
|
|
91
|
+
const list = [...document.querySelectorAll('*[data-' + DATA_ATTR_WEBFLOW + ']')]
|
|
92
|
+
list.forEach(this.hideWebflowElement.bind(this))
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Hide element
|
|
96
|
+
* @param {DOM} el
|
|
97
|
+
* @param {Int} index
|
|
98
|
+
*/
|
|
99
|
+
hideWebflowElement(el, index) {
|
|
100
|
+
const wfp = getWebflowProps(el.dataset[DATA_ATTR_WEBFLOW])
|
|
101
|
+
if (wfp.hide) {
|
|
102
|
+
TweenMax.to(el, wfp.hide.time, wfp.hide.tween)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Destroy all scenes
|
|
107
|
+
*/
|
|
108
|
+
destroy() {
|
|
109
|
+
this.removeEventListeners()
|
|
110
|
+
this.scenes.forEach((scene) => {
|
|
111
|
+
scene.destroy()
|
|
112
|
+
scene = null
|
|
113
|
+
})
|
|
114
|
+
this.scenes = []
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Rebuild / refresh
|
|
118
|
+
*/
|
|
119
|
+
rebuild() {
|
|
120
|
+
this.destroy()
|
|
121
|
+
this.init()
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
}
|
|
125
|
+
const showUp = new ShowUp();
|
|
126
|
+
export default showUp;
|