Compare commits
159 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5d6d4c3772 | ||
|
|
3f3284b3a5 | ||
|
|
81a2232ba8 | ||
|
|
4e5185ddde | ||
|
|
7a7a1a8260 | ||
|
|
4468069d8f | ||
|
|
dc69c3226f | ||
|
|
94f9cf896f | ||
|
|
c950ec4ce4 | ||
|
|
72f5c19808 | ||
|
|
c9e213c766 | ||
|
|
a67afd4671 | ||
|
|
4a484c91d8 | ||
|
|
4d778193d9 | ||
|
|
75f8c2df7f | ||
|
|
7aa04ec24c | ||
|
|
c3007562a8 | ||
|
|
5abe2aaed5 | ||
|
|
1a65068e7e | ||
|
|
607c580c92 | ||
|
|
9f2a396600 | ||
|
|
26496247c2 | ||
|
|
684a50a795 | ||
|
|
42dd4d8e50 | ||
|
|
6b5fce714a | ||
|
|
6a6eedf156 | ||
|
|
c27f2d1208 | ||
|
|
bdf8c186a0 | ||
|
|
c84a79ba67 | ||
|
|
47ac9f4531 | ||
|
|
6715763440 | ||
|
|
18679a9920 | ||
|
|
b4c109d20f | ||
|
|
2d38405398 | ||
|
|
362595b5db | ||
|
|
239bc9cb86 | ||
|
|
552314fa53 | ||
|
|
b36213454b | ||
|
|
cf35933a15 | ||
|
|
e0d20568d5 | ||
|
|
1508393a65 | ||
|
|
9dd8c42a69 | ||
|
|
a54cbc3bfe | ||
|
|
de025733a2 | ||
|
|
5db870e838 | ||
|
|
ef9943ca21 | ||
|
|
6e350824ff | ||
|
|
dac3a9c8d2 | ||
|
|
bc345a7385 | ||
|
|
b6134e4f33 | ||
|
|
94c89a54eb | ||
|
|
47463a5220 | ||
|
|
537ce7f36b | ||
|
|
dd21a86b6c | ||
|
|
702f26f3d3 | ||
|
|
7ca20b3114 | ||
|
|
2f4e2804d5 | ||
|
|
322ee2726b | ||
|
|
5c3ab529bf | ||
|
|
7790246185 | ||
|
|
0beada6c6c | ||
|
|
0edfbdad76 | ||
|
|
e8be532dfb | ||
|
|
6b6cf6aae7 | ||
|
|
d7ab09c1da | ||
|
|
c790a0319e | ||
|
|
853e8a1a17 | ||
|
|
edb04e2542 | ||
|
|
ee2df22519 | ||
|
|
ee27e5e559 | ||
|
|
d804f6f51e | ||
|
|
44ed7346d8 | ||
|
|
51dbff484c | ||
|
|
4ba449be29 | ||
|
|
0b31b92a3d | ||
|
|
66b790b063 | ||
|
|
1b545d270e | ||
|
|
61544135a8 | ||
|
|
99ac0356f7 | ||
|
|
dc9df50ffa | ||
|
|
38eddc7ab8 | ||
|
|
6d8b4ca9b3 | ||
|
|
938b1be1b8 | ||
|
|
69edd64243 | ||
|
|
1e1067f632 | ||
|
|
19df0b70c1 | ||
|
|
08bdf99fdd | ||
|
|
2ab2c5392c | ||
|
|
374a0933d0 | ||
|
|
21f8ab2ca5 | ||
|
|
b4d0cfc30d | ||
|
|
30109c0d9f | ||
|
|
3c37dc966e | ||
|
|
ee8c120e9b | ||
|
|
5d9adbfc9f | ||
|
|
98fa5f9a63 | ||
|
|
47d3e8caa1 | ||
|
|
7a8473be34 | ||
|
|
4cd6de77d9 | ||
|
|
9f9ac92b9d | ||
|
|
2383abd408 | ||
|
|
1b98233010 | ||
|
|
e3ace92004 | ||
|
|
2c54a68027 | ||
|
|
1a2205bfdf | ||
|
|
5c70d40a35 | ||
|
|
8a98a9781c | ||
|
|
e630079794 | ||
|
|
f68690aca8 | ||
|
|
33757cf7fa | ||
|
|
e44b8c7ddb | ||
|
|
57d6726326 | ||
|
|
6437b229f9 | ||
|
|
359d19e8af | ||
|
|
e6a322bbac | ||
|
|
b670d2fd12 | ||
|
|
21aced1fab | ||
|
|
e1ed428663 | ||
|
|
877bb3ec1a | ||
|
|
b7d32ace26 | ||
|
|
3813e8d486 | ||
|
|
b4cfc8d6b4 | ||
|
|
f2d3dc0977 | ||
|
|
a9a5d6a216 | ||
|
|
f035230a56 | ||
|
|
fddfeaabeb | ||
|
|
51dac49553 | ||
|
|
eb0a1b4230 | ||
|
|
7965234d87 | ||
|
|
ea813af50b | ||
|
|
278168414b | ||
|
|
25ac2b5497 | ||
|
|
9c9038a3ea | ||
|
|
bb6133ea2b | ||
|
|
0d36e901d7 | ||
|
|
49c45d8e4f | ||
|
|
1247463303 | ||
|
|
a587813c1e | ||
|
|
08dd1ceb6e | ||
|
|
f9074ee5e6 | ||
|
|
8684fb8ee3 | ||
|
|
9589627ed7 | ||
|
|
692ff93731 | ||
|
|
cf77d95f0e | ||
|
|
1dd3bebbdd | ||
|
|
33bd72d825 | ||
|
|
b461e7b1cf | ||
|
|
fee90b3a82 | ||
|
|
7431ff3840 | ||
|
|
07899d3443 | ||
|
|
b99389f388 | ||
|
|
027772e364 | ||
|
|
802fa7592c | ||
|
|
f9395c6c27 | ||
|
|
e8b68c246a | ||
|
|
e2a30f53b5 | ||
|
|
c96ff53460 | ||
|
|
9790b42b2e | ||
|
|
b50e688197 |
9
.babelrc
@@ -1,3 +1,8 @@
|
||||
{
|
||||
"presets": ["@babel/preset-env"]
|
||||
}
|
||||
"presets": [
|
||||
"@babel/preset-env"
|
||||
],
|
||||
"plugins": [
|
||||
"@babel/plugin-proposal-class-properties"
|
||||
]
|
||||
}
|
||||
17
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**lax.js version**
|
||||
Please note, only bugs in v2.0 or later will be fixed.
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**Code**
|
||||
Please provide a link to a repo or code example of the issue in question.
|
||||
20
.github/ISSUE_TEMPLATE/feature_request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
node_modules/
|
||||
.DS_Store
|
||||
LaxLogo.sketch
|
||||
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2019 Alex Fox
|
||||
Copyright (c) 2020 Alex Fox
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
504
README.md
@@ -1,205 +1,383 @@
|
||||
# Archive Notice
|
||||
Due to other commitments I am unable to continue maintaining this project. As far as I know it still works, but there are likely better, more up to date alterantives out there.
|
||||
|
||||
# lax.js
|
||||
|
||||
Simple & light weight (2kb minified & zipped) vanilla javascript plugin to create *smooth* & beautiful animations when you scrolllll! Harness the power of the most intuitive interaction and make your websites come alive!
|
||||
Simple & lightweight (<4kb gzipped) vanilla JavaScript library to create smooth & beautiful animations when you scroll.
|
||||
|
||||
[>>> DEMO <<<](https://alexfox.dev/laxxx/)
|
||||

|
||||
|
||||

|
||||
[>> DEMO <<](https://alexfox.dev/lax.js/)
|
||||
|
||||
## Getting started
|
||||
---
|
||||
## What's new with Lax.js 2.0
|
||||
Lax.js 2.0 has been completely re-written with a focus on modularity and flexibility giving you more tools to create awesome animations.
|
||||
- New javascript animation syntax, allowing for more advanced effect combos
|
||||
- Use any value to drive animations, for example mouse position, time of day .. and of course scroll!
|
||||
- Animations can be given inertia when scrolling
|
||||
- Create custom CSS bindings
|
||||
- Animation easings
|
||||
- And much more..
|
||||
|
||||
### NPM setup
|
||||
## Examples
|
||||
- [Scroll effect](https://alexfox.dev/lax.js/examples/scroll)
|
||||
- [Horizontal snap scroll](https://alexfox.dev/lax.js/examples/snap-scroll)
|
||||
- [Inertia](https://alexfox.dev/lax.js/examples/inertia)
|
||||
- [Video/Gif playback](https://alexfox.dev/lax.js/examples/sprite)
|
||||
- [Cursor position](https://alexfox.dev/lax.js/examples/cursor)
|
||||
- [Text input](https://alexfox.dev/lax.js/examples/input)
|
||||
- [Update HTML content](https://alexfox.dev/lax.js/examples/on-update)
|
||||
- [Preset Explorer](https://alexfox.dev/lax.js/preset-explorer)
|
||||
|
||||
|
||||
# Documentation
|
||||
|
||||
### 1. Getting started
|
||||
|
||||
- [Setup](#setup)
|
||||
- [Using presets](#using-presets)
|
||||
- [Usage with UI frameworks](#dom-behavior-and-usage-with-frameworks)
|
||||
- [Adding drivers](#adding-drivers)
|
||||
- [Adding elements](#adding-elements)
|
||||
|
||||
### 2. Going deeper
|
||||
|
||||
- [Custom animations](#custom-animations)
|
||||
- [Optimising performance](#optimising-performance)
|
||||
|
||||
### 3. Glossary
|
||||
|
||||
- [CSS properties](#css-properties)
|
||||
- [Special values](#special-values)
|
||||
- [Supported easings](#supported-easings)
|
||||
|
||||
# Getting started
|
||||
|
||||
### NPM Setup
|
||||
|
||||
```bash
|
||||
# https://www.npmjs.com/package/lax.js
|
||||
|
||||
npm install lax.js
|
||||
yarn add lax.js
|
||||
```
|
||||
```js
|
||||
import lax from 'lax.js'
|
||||
```
|
||||
|
||||
### Basic browser setup
|
||||
1) Add lax.js to your html
|
||||
### HTML setup
|
||||
|
||||
```html
|
||||
<script src="lib/lax.min.js" >
|
||||
<script src="path-to-lax.min.js"></script>
|
||||
<!-- or via CDN -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/lax.js" ></script>
|
||||
```
|
||||
|
||||
2) Initialize the plugin
|
||||
|
||||
## Setup
|
||||
|
||||
To implement lax you need to create at least one _driver_, to provide values for animations, as well as the element animation bindings. Below is a simple example:
|
||||
|
||||
```html
|
||||
<!-- JS -->
|
||||
<script>
|
||||
window.onload = function () {
|
||||
lax.init()
|
||||
|
||||
// Add a driver that we use to control our animations
|
||||
lax.addDriver('scrollY', function () {
|
||||
return window.scrollY
|
||||
})
|
||||
|
||||
// Add animation bindings to elements
|
||||
lax.addElements('.selector', {
|
||||
scrollY: {
|
||||
translateX: [
|
||||
["elInY", "elCenterY", "elOutY"],
|
||||
[0, 'screenWidth/2', 'screenWidth'],
|
||||
]
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- HTML -->
|
||||
<div class="selector">Hello</div>
|
||||
```
|
||||
|
||||
## Using presets
|
||||
|
||||
The easiest way to get started is to use presets via html classes. For example:
|
||||
|
||||
```html
|
||||
<div class="lax lax_preset_fadeIn:50:100 lax_preset_spin"></div>
|
||||
```
|
||||
|
||||
Multiple presets can be chained together and they can be customised to suit your needs. Use the [preset explorer](https://alexfox.dev/lax.js/preset-explorer) to explore effects and see a simple example [here](https://alexfox.dev/lax.js/examples/html-inline).
|
||||
|
||||
## DOM behavior and usage with Frameworks
|
||||
|
||||
To increase performance, `lax.js` indexes the list of elements to animate when the page loads. If you're using a library like React, Vue or EmberJS, it is likely that you are adding elements after the initial window.onload. Because of this you will need to call `lax.addElements` when you add components to the DOM that you want to animate, and `lax.removeElements` when the component unmounts.
|
||||
|
||||
Please find a React example [here](https://codesandbox.io/s/laxjs-react-example-nc4h7). Other examples will be available soon for Vue.js and Angular.
|
||||
|
||||
## Adding drivers
|
||||
|
||||
Drivers provide the values that _drive_ your animations. To set up a driver just call `lax.addDriver` with a name and a function which returns a number. This method is called every frame to calculate the animations so keep the method as computationally _light_ as possible. The example below will be the most common use case for lax which returns the scrollY position of the window.
|
||||
|
||||
```javascript
|
||||
window.onload = function() {
|
||||
lax.setup({ /* opts */ }) // init
|
||||
|
||||
document.addEventListener('scroll', function(e) {
|
||||
lax.update(window.scrollY) // update every scroll
|
||||
}, false)
|
||||
lax.addDriver(
|
||||
'scrollY', // Driver name
|
||||
function(laxFrame) {
|
||||
return window.scrollY // Value method
|
||||
},
|
||||
{ } // Options
|
||||
)
|
||||
```
|
||||
|
||||
lax.update(window.scrollY) // set initial positions
|
||||
### Driver options
|
||||
|
||||
#### `inertiaEnabled: boolean = false`
|
||||
|
||||
If enabled, the driver will calculate the speed at which its value is changing. Used to add inertia to elements using the [inertia element option](#inertia-number).
|
||||
|
||||
See this in action in the [here](https://alexfox.dev/lax.js/examples/inertia).
|
||||
|
||||
#### `frameStep: number = 1`
|
||||
|
||||
By default each driver updates its value every animation frame, around ~60 times per second. You can use the `frameStep` to reduce frequency of the driver value updating. For example a value of `2` would only update ~30 times per second and a value of `60` would only update about once per second.
|
||||
|
||||
## Adding elements
|
||||
|
||||
You can add lax animations to an element using the `addElements` method:
|
||||
|
||||
```javascript
|
||||
lax.addElements(
|
||||
'.selector', // Element selector rule
|
||||
{ // Animation data
|
||||
scrollY: {
|
||||
opacity: [
|
||||
[0, 100],
|
||||
[1, 0]
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
style: {} // Element options
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
### Element options
|
||||
|
||||
#### `style: StyleObject`
|
||||
|
||||
Add static CSS to each element, for example:
|
||||
|
||||
```css
|
||||
{
|
||||
transform: '200ms scale ease-in-out';
|
||||
}
|
||||
```
|
||||
|
||||
#### `elements: Array<DOM nodes>`
|
||||
|
||||
3) Add attributes to the HTML tags you want to animate e.g.
|
||||
```html
|
||||
<p data-lax-preset="spin fadeInOut">Look at me goooooo!</p>
|
||||
Pass references to raw DOM elements to allow for more flexible selection patterns. In this case, a unique `selector` must still be passed as the first argument, however it does _not_ need to be a valid DOM selector.
|
||||
|
||||
This allows the library to tag the elements for removal later. Example:
|
||||
|
||||
```js
|
||||
const myDomElements = $('.selector')
|
||||
|
||||
{
|
||||
elements: myDomElements
|
||||
}
|
||||
```
|
||||
|
||||
4) Scroll and enjoy!
|
||||
#### `onUpdate: (driverValues: Object, domElement: DomElement) => void`
|
||||
A method called every frame with the current driverValues and domElement. This could be used to toggle classes on an element or set innerHTML. See it in action [here](https://alexfox.dev/lax.js/examples/on-update).
|
||||
|
||||
### Dealing with DOM changes
|
||||
lax builds a list of all elements it needs to control when the page loads so if they are added to the DOM subsequently they won't be updated on page scroll. If you're using a library like React or vue.js, it is likely that not all elements are in the dom on page load. Because of this you will need to call `lax.populateElements()` when you add elements to the DOM that you want to animate.
|
||||
|
||||
For example `componentDidMount() // React` or `created() // vue.js`.
|
||||
|
||||
## Presets
|
||||
|
||||
The easiest way to get started is to use the presets via the `data-lax-preset` attribute. You can chain multiple presetes together for e.g. `data-lax-preset="blurOut fadeOut spin"`. Some presets also support an optional strength e.g. `data-lax-preset="blurOut-50"`
|
||||
|
||||
See the list of [Supported Presets](#supported-presets) for details.
|
||||
|
||||
## Custom Animations
|
||||
|
||||
You can easily create your own effects. Just add an attribute to your HTML tag (see [Supported Attribute Keys](#supported-attribute-keys)) with an array of values. These arrays take the format of `scrollPos val, scrollPos val, ...` e.g:
|
||||
```html
|
||||
<p data-lax-opacity="0 1, 100 1, 200 0">
|
||||
I start to fade out after the window scrolls 100px
|
||||
and then I'm gone by 200px!
|
||||
</p>
|
||||
The driver values are formatted as follows:
|
||||
```js
|
||||
{
|
||||
scrollY: [ // Driver name
|
||||
100, // Driver value
|
||||
0 // Driver inertia
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
By default the `scrollPos` is `window.scrollY` but you can use an element distance from the top of the screen instead. You can either pass in a selector `data-lax-anchor="#bio"` or set it to use itself `data-lax-anchor="self"` (this is the default for all presets) e.g.
|
||||
```html
|
||||
<p data-lax-opacity="200 1, 100 1, 0 0" data-lax-anchor="self">
|
||||
I start to fade out after I'm 100px away from the top of the window
|
||||
and then I'm gone by the time I reach the top!
|
||||
</p>
|
||||
```
|
||||
# Going deeper
|
||||
|
||||
There are also some shortcuts for useful values:
|
||||
## Custom animations
|
||||
Custom animations are defined using an object.
|
||||
|
||||
| Key | Value |
|
||||
| ------------- | ------------- |
|
||||
| vw | window.innerWidth |
|
||||
| vh | window.innerHeight |
|
||||
| elw | targetElement.clientHeight |
|
||||
| elh | targetElement.clientWidth |
|
||||
|
||||
You can use these instead of integer values for the scrollPos e.g.
|
||||
```html
|
||||
<p data-lax-opacity="0 1, vh 0">
|
||||
I fade out as the page scrolls down and
|
||||
I'm gone when the page has scrolled the view port height!
|
||||
</p>
|
||||
```
|
||||
|
||||
You can also use vanilla JS within `( )` for calculations and access to more variables e.g.
|
||||
```html
|
||||
<p data-lax-opacity="0 1, (document.body.scrollHeight*0.5) 0">
|
||||
I fade out as the page scrolls down and
|
||||
I'm gone when the page has scrolled 50%
|
||||
down the entire page height!
|
||||
</p>
|
||||
```
|
||||
## Supported Presets
|
||||
|
||||
| Preset | Default Strength |
|
||||
| ------------- | ------------- |
|
||||
| linger | n/a |
|
||||
| lazy | 100 |
|
||||
| eager | 100 |
|
||||
| lazy | 100 |
|
||||
| slalom | 50 |
|
||||
| crazy | n/a |
|
||||
| spin | 360 |
|
||||
| spinRev | 360 |
|
||||
| spinIn | 360 |
|
||||
| spinOut | 360 |
|
||||
| blurInOut | 40 |
|
||||
| blurIn | 40 |
|
||||
| blurOut | 40 |
|
||||
| fadeInOut | n/a |
|
||||
| fadeIn | n/a |
|
||||
| fadeOut | n/a |
|
||||
| driftLeft | 100 |
|
||||
| driftRight | 100 |
|
||||
| leftToRight | 1 |
|
||||
| rightToLeft | 1 |
|
||||
| zoomInOut | 0.2 |
|
||||
| zoomIn | 0.2 |
|
||||
| zoomOut | 0.2 |
|
||||
|
||||
|
||||
## Supported Attribute Keys
|
||||
|
||||
Transforms
|
||||
|
||||
| Transform | Key |
|
||||
| ------------- | ------------- |
|
||||
| opacity | data-lax-opacity |
|
||||
| translate | data-lax-translate |
|
||||
| translateX | data-lax-translate-x |
|
||||
| translateY | data-lax-translate-y |
|
||||
| scale | data-lax-scale |
|
||||
| scaleX | data-lax-scale-x |
|
||||
| scaleY | data-lax-scale-y |
|
||||
| skew | data-lax-skew |
|
||||
| skewX | data-lax-skew-x |
|
||||
| skewY | data-lax-skew-y |
|
||||
| rotate | data-lax-rotate |
|
||||
|
||||
Filters (note - these may be unperformant on low powered machines)
|
||||
|
||||
| Filter | Key |
|
||||
| ------------- | ------------- |
|
||||
| brightness | data-lax-brightness |
|
||||
| contrast | data-lax-contrast |
|
||||
| hue-rotate | data-lax-hue-rotate |
|
||||
| blur | data-lax-blur |
|
||||
| invert | data-lax-invert |
|
||||
| saturate | data-lax-saturate |
|
||||
| grayscale | data-lax-grayscale |
|
||||
|
||||
## Custom Presets
|
||||
To avoid duplicate code you can define your own presets with a list of attributes e.g.
|
||||
```javascript
|
||||
lax.addPreset("myCoolPreset", function() {
|
||||
return {
|
||||
"data-lax-opacity": "(-vh*0.8) 40, (-vh*0.6) 0",
|
||||
"data-lax-rotate": "(-vh*2) 1000, (-vh*0.5) 0"
|
||||
}
|
||||
})
|
||||
```
|
||||
You can then access this preset like this:
|
||||
```html
|
||||
<p data-lax-preset="myCoolPreset">
|
||||
I'm the coolest preset in the world 😎
|
||||
</p>
|
||||
// Animation data
|
||||
{
|
||||
scrollY: { // Driver name
|
||||
translateX: [ // CSS property
|
||||
['elInY', 'elOutY'], // Driver value map
|
||||
[0, 'screenWidth'], // Animation value map
|
||||
{
|
||||
inertia: 10 // Options
|
||||
}
|
||||
],
|
||||
opacity: [
|
||||
// etc
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Notes
|
||||
### Driver name
|
||||
The name of the driver you want to use as a source of values to map to your animation, for example, the document's scrollY position. Read about adding drivers [here](#adding-drivers).
|
||||
|
||||
### Performance
|
||||
By default `-webkit-backface-visibility: hidden;` is added to your elements style to encourage the browser to render that object as a layer on the GPU and increase performance. To turn this off add `lax-optimize="false"` to your element.
|
||||
### CSS property
|
||||
The name of the CSS property you want to animate, for example `opacity` or `rotate`. See a list of supported properties [here](#css-properties).
|
||||
|
||||
### Screen rotating & resizing
|
||||
As some values (vh, vw, elh, elw) are calculated on load, when the screen size changes or rotates you might want to recalculate these. E.g.
|
||||
> Some CSS properties, for example `box-shadow`, require a custom function to build the style string. To do this use the [cssFn](#cssfn-value-number--string) element option.
|
||||
|
||||
### Value maps
|
||||
The value maps are used to interpolate the driver value and output a value for your CSS property. For example:
|
||||
|
||||
```javascript
|
||||
[0, 200, 800] // Driver value map
|
||||
[0, 10, 20] // Animation value map
|
||||
|
||||
// Result
|
||||
|
||||
| In | Out |
|
||||
| --- | --- |
|
||||
| 0 | 0 |
|
||||
| 100 | 5 |
|
||||
| 200 | 10 |
|
||||
| 500 | 15 |
|
||||
| 800 | 20 |
|
||||
```
|
||||
window.addEventListener("resize", function() {
|
||||
lax.populateElements()
|
||||
});
|
||||
|
||||
Within the maps you can use strings for simple formulas as well as use special values. e.g:
|
||||
|
||||
```javascript
|
||||
['elInY', 'elCenterY-200', 'elCenterY',
|
||||
```
|
||||
Be warned, on mobile, a resize event is fired when you scroll and the toolbar is hidden so you might want to check if the width or orientation has changed.
|
||||
|
||||
### Scroll Wheels
|
||||
Scroll wheels only icrement the scroll position in steps which can cause the animations to look janky. You can use the SmoothScroll (http://www.smoothscroll.net/) plugin to smooth this out, however there maybe performance implications that need investigating.
|
||||
See a list of available values [here](#special-values).
|
||||
|
||||
## To Do / Ideas
|
||||
* Re-calculate values on rotate / change window size
|
||||
* Add debug mode
|
||||
* Elastic bouncing values
|
||||
* Optimise elements that go off screen
|
||||
* Implement a tween for scroll wheels to remove dependency on smoothscroll
|
||||
* Better error reporting
|
||||
* Add "momentum" as option for anchor & presets
|
||||
* Move presets to lax-presets.js to reduce base library size
|
||||
You can also use mobile breakpoints within driver value maps and animation maps for more flexibility.
|
||||
|
||||
```javascript
|
||||
scrollY: {
|
||||
translateX: [
|
||||
['elInY', 'elCenterY', 'elOutY'],
|
||||
{
|
||||
500: [10, 20, 50], // Screen width < 500
|
||||
900: [30, 40, 60], // Screen width > 500 and < 900
|
||||
1400: [30, 40, 60], // Screen width > 900
|
||||
},
|
||||
];
|
||||
}
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
#### `modValue: number | undefined`
|
||||
Set this option to modulus the value from the driver, for example if you want to loop the animation value as the driver value continues to increase.
|
||||
|
||||
#### `frameStep: number = 1`
|
||||
By default each animation updates its value every animation frame, around ~60 times per second. You can use the `frameStep` to reduce frequency of the animation updating. For example a value of `2` would only update ~30 times per second and a value of `60` would only update about once per second.
|
||||
|
||||
#### `inertia: number`
|
||||
Use to add inertia to your animations. Use in combination with the [inertiaEnabled](#inertiaenabled-boolean--false) driver option.
|
||||
|
||||
See inertia in action [here](https://alexfox.dev/lax.js/examples/inertia).
|
||||
|
||||
|
||||
#### `inertiaMode: "normal" | "absolute"`
|
||||
Use in combination with `inertia`. If set to `absolute` the inertia value will always be a positive number via the `Math.abs` operator.
|
||||
|
||||
#### `cssUnit: string = ""`
|
||||
Define the unit to be appended to the end of the value, for example
|
||||
For example `px` `deg`
|
||||
|
||||
#### `cssFn: (value: number, domElement: DomElement) => number | string`
|
||||
Some CSS properties require more complex strings as values. For example, `box-shadow` has multiple values that could be modified by a lax animation.
|
||||
|
||||
```javascript
|
||||
// Box-shadow example
|
||||
(val) => {
|
||||
return `${val}px ${val}px ${val}px rgba(0,0,0,0.5)`;
|
||||
};
|
||||
```
|
||||
|
||||
#### `easing: string`
|
||||
See a list of available values [here](#supported-easings).
|
||||
|
||||
## Optimising performance
|
||||
Lax.js has been designed to be performant but there are a few things to bare in mind when creating your websites.
|
||||
- Smaller elements perform better.
|
||||
- Postion `fixed` and `absolute` elements perform best as they do not trigger a layout change when updated.
|
||||
- Off-screen elements do not need to be updated so consider that when creating your animation value maps.
|
||||
- The css properties `blur`, `hue-rotate` and `brightness` are graphically intensive and do not run as smoothly as the other available properties.
|
||||
|
||||
# Glossary
|
||||
|
||||
## CSS properties
|
||||
|
||||
| name |
|
||||
| ---------- |
|
||||
| opacity |
|
||||
| scaleX |
|
||||
| scaleY |
|
||||
| scale |
|
||||
| skewX |
|
||||
| skewY |
|
||||
| skew |
|
||||
| rotateX |
|
||||
| rotateY |
|
||||
| rotate |
|
||||
| translateX |
|
||||
| translateY |
|
||||
| translateZ |
|
||||
| blur |
|
||||
| hue-rotate |
|
||||
| brightness |
|
||||
|
||||
## Special values
|
||||
|
||||
| key | value |
|
||||
| ------------ | -------------------------------------------------------------------------------- |
|
||||
| screenWidth | current width of the screen |
|
||||
| screenHeight | current height of the screen |
|
||||
| pageWidth | width of the document |
|
||||
| pageHeight | height of the document |
|
||||
| elWidth | width of the element |
|
||||
| elHeight | height of the element |
|
||||
| elInY | window scrollY position when element will appear at the bottom of the screen |
|
||||
| elOutY | window scrollY position when element will disappear at the top of the screen |
|
||||
| elCenterY | window scrollY position when element will be centered vertically on the screen |
|
||||
| elInX | window scrollX position when element will appear at the right of the screen |
|
||||
| elOutX | window scrollX position when element will disappear at the left of the screen |
|
||||
| elCenterX | window scrollX position when element will be centered horizontally on the screen |
|
||||
| index | index of the element when added using `lax.addElements` |
|
||||
|
||||
## Supported easings
|
||||
|
||||
| name |
|
||||
| -------------- |
|
||||
| easeInQuad |
|
||||
| easeOutQuad |
|
||||
| easeInOutQuad |
|
||||
| easeInCubic |
|
||||
| easeOutCubic |
|
||||
| easeInOutCubic |
|
||||
| easeInQuart |
|
||||
| easeOutQuart |
|
||||
| easeInOutQuart |
|
||||
| easeInQuint |
|
||||
| easeOutQuint |
|
||||
| easeInOutQuint |
|
||||
| easeOutBounce |
|
||||
| easeInBounce |
|
||||
| easeOutBack |
|
||||
| easeInBack |
|
||||
|
||||
BIN
docs/assets/a.png
Normal file
|
After Width: | Height: | Size: 7.3 KiB |
BIN
docs/assets/bg1.jpg
Normal file
|
After Width: | Height: | Size: 50 KiB |
BIN
docs/assets/bg2.jpg
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
docs/assets/bg3.jpg
Normal file
|
After Width: | Height: | Size: 129 KiB |
BIN
docs/assets/button-bg.jpg
Normal file
|
After Width: | Height: | Size: 122 KiB |
BIN
docs/assets/downarrow.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
docs/assets/l.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
docs/assets/lax.png
Normal file
|
After Width: | Height: | Size: 55 KiB |
BIN
docs/assets/pink-zag.png
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
BIN
docs/assets/purple-zag.png
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
BIN
docs/assets/shoe1.png
Normal file
|
After Width: | Height: | Size: 540 KiB |
BIN
docs/assets/shoe2.png
Normal file
|
After Width: | Height: | Size: 540 KiB |
BIN
docs/assets/shoe3.png
Normal file
|
After Width: | Height: | Size: 576 KiB |
BIN
docs/assets/spritesheet.jpg
Normal file
|
After Width: | Height: | Size: 418 KiB |
BIN
docs/assets/teal-zag.png
Normal file
|
After Width: | Height: | Size: 6.0 KiB |
BIN
docs/assets/x.png
Normal file
|
After Width: | Height: | Size: 20 KiB |
143
docs/examples/cursor.html
Normal file
@@ -0,0 +1,143 @@
|
||||
<head>
|
||||
<script type="application/javascript" src="../lib/lax.min.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
window.onload = function () {
|
||||
lax.init()
|
||||
|
||||
// Setup mouse move listener
|
||||
document.addEventListener('mousemove', function (e) {
|
||||
lax.__cursorX = e.clientX
|
||||
lax.__cursorY = e.clientY
|
||||
}, false)
|
||||
|
||||
// Add lax driver for cursorX
|
||||
lax.addDriver('cursorX', function () {
|
||||
return lax.__cursorX || 0
|
||||
})
|
||||
|
||||
// Add lax driver for cursorY
|
||||
lax.addDriver('cursorY', function () {
|
||||
return lax.__cursorY || 0
|
||||
})
|
||||
|
||||
// Add lax driver for cursorXY
|
||||
lax.addDriver('cursorDistanceFromCenter', function () {
|
||||
var pageHeight = document.body.scrollHeight
|
||||
var pageWidth = document.body.scrollWidth
|
||||
|
||||
var pageCenterX = pageWidth / 2
|
||||
var pageCenterY = pageHeight / 2
|
||||
|
||||
var absDistanceFromCenterY = Math.abs((lax.__cursorY || 0) - pageCenterY) / pageCenterY
|
||||
var absDistanceFromCenterX = Math.abs((lax.__cursorX || 0) - pageCenterX) / pageCenterX
|
||||
|
||||
return absDistanceFromCenterX + absDistanceFromCenterY
|
||||
})
|
||||
|
||||
lax.addElements(".text", {
|
||||
'cursorX': {
|
||||
"translateX": [
|
||||
[0, 'screenWidth'],
|
||||
['index * 10', 'index * -10'],
|
||||
],
|
||||
},
|
||||
'cursorY': {
|
||||
"translateY": [
|
||||
[0, 'screenHeight'],
|
||||
['index * 10', 'index * -10'],
|
||||
],
|
||||
},
|
||||
'cursorDistanceFromCenter': {
|
||||
"scale": [
|
||||
[0, 1],
|
||||
[1, '1 + (index * 0.05 )'],
|
||||
],
|
||||
}
|
||||
})
|
||||
|
||||
lax.addElements(".container", {
|
||||
'cursorX': {
|
||||
"filter": [
|
||||
[0, 'screenWidth'],
|
||||
[0, 'screenWidth/2'],
|
||||
{
|
||||
"cssFn": (val) => {
|
||||
return `hue-rotate(${val % 360}deg)`
|
||||
}
|
||||
}
|
||||
],
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<style>
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
.text {
|
||||
width: 100vw;
|
||||
text-align: center;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
margin-top: calc(50vh - 40px);
|
||||
z-index: 1000;
|
||||
font-size: 100px;
|
||||
transform-origin: 50% 50%;
|
||||
}
|
||||
|
||||
.text.a {
|
||||
color: #a94fe4;
|
||||
}
|
||||
|
||||
.text.b {
|
||||
color: #68e4f1;
|
||||
}
|
||||
|
||||
.text.c {
|
||||
color: #ffe773;
|
||||
}
|
||||
|
||||
.text.d {
|
||||
color: #f544ad;
|
||||
}
|
||||
|
||||
.container {
|
||||
background-color: #f5922c;
|
||||
position: fixed;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1 class='text a'>Lax.js</h1>
|
||||
<h1 class='text b'>Lax.js</h1>
|
||||
<h1 class='text c'>Lax.js</h1>
|
||||
<h1 class='text d'>Lax.js</h1>
|
||||
<h1 class='text a'>Lax.js</h1>
|
||||
<h1 class='text b'>Lax.js</h1>
|
||||
<h1 class='text c'>Lax.js</h1>
|
||||
<h1 class='text d'>Lax.js</h1>
|
||||
<h1 class='text a'>Lax.js</h1>
|
||||
<h1 class='text b'>Lax.js</h1>
|
||||
<h1 class='text c'>Lax.js</h1>
|
||||
<h1 class='text d'>Lax.js</h1>
|
||||
<h1 class='text a'>Lax.js</h1>
|
||||
<h1 class='text b'>Lax.js</h1>
|
||||
<h1 class='text c'>Lax.js</h1>
|
||||
<h1 class='text d'>Lax.js</h1>
|
||||
<h1 class='text a'>Lax.js</h1>
|
||||
<h1 class='text b'>Lax.js</h1>
|
||||
<h1 class='text c'>Lax.js</h1>
|
||||
<h1 class='text d'>Lax.js</h1>
|
||||
</div>
|
||||
</body>
|
||||
41
docs/examples/html-inline.html
Normal file
@@ -0,0 +1,41 @@
|
||||
<head>
|
||||
<script type="application/javascript" src="../lib/lax.min.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
window.onload = function () {
|
||||
lax.init()
|
||||
|
||||
lax.addDriver('scrollY', function () {
|
||||
return window.scrollY
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<style>
|
||||
.square {
|
||||
height: 200px;
|
||||
width: 200px;
|
||||
background-color: #a26ddc;
|
||||
margin-bottom: 0px;
|
||||
margin-left: -100px;
|
||||
margin-top: -100px;
|
||||
left: 50vw;
|
||||
top: 50vh;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 0;
|
||||
background-color: #dedbde;
|
||||
background-image: linear-gradient(rgba(255, 255, 255, .2) 50%, transparent 50%, transparent);
|
||||
margin: 0;
|
||||
background-size: 700px 700px;
|
||||
height: 1000000px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body>
|
||||
<div class="square lax lax_preset_spin:400:360 lax_preset_flipX"></div>
|
||||
</body>
|
||||
82
docs/examples/inertia.html
Normal file
@@ -0,0 +1,82 @@
|
||||
<head>
|
||||
<script type="application/javascript" src="../lib/lax.min.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
window.onload = function () {
|
||||
lax.init()
|
||||
|
||||
lax.addDriver('scrollY', function () {
|
||||
return window.scrollY
|
||||
}, { inertiaEnabled: true })
|
||||
|
||||
lax.addElements(".circle", {
|
||||
scrollY: {
|
||||
perspective: [
|
||||
[0],
|
||||
[1000],
|
||||
],
|
||||
rotateX: [
|
||||
[0],
|
||||
[0],
|
||||
{
|
||||
inertia: -1
|
||||
}
|
||||
],
|
||||
"box-shadow": [
|
||||
[0],
|
||||
[0],
|
||||
{
|
||||
inertia: -1,
|
||||
cssFn: (val) => {
|
||||
return `0px ${Math.abs(val)}px 30px rgba(0,0,0,0.2)`
|
||||
}
|
||||
}
|
||||
],
|
||||
translateY: [
|
||||
[0],
|
||||
[0],
|
||||
{
|
||||
inertia: -1
|
||||
}
|
||||
],
|
||||
brightness: [
|
||||
[0],
|
||||
[1],
|
||||
{
|
||||
inertia: -0.01
|
||||
}
|
||||
]
|
||||
},
|
||||
})
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<style>
|
||||
.circle {
|
||||
height: 200px;
|
||||
width: 200px;
|
||||
background-color: #a26ddc;
|
||||
margin-bottom: 0px;
|
||||
margin-left: -100px;
|
||||
margin-top: -100px;
|
||||
border-radius: 20px;
|
||||
left: 50vw;
|
||||
top: 50vh;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 0;
|
||||
background-color: #dedbde;
|
||||
background-image: linear-gradient(rgba(255, 255, 255, .2) 50%, transparent 50%, transparent);
|
||||
margin: 0;
|
||||
background-size: 700px 700px;
|
||||
height: 1000000px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body>
|
||||
<div class="circle">
|
||||
</div>
|
||||
</body>
|
||||
55
docs/examples/input.html
Normal file
@@ -0,0 +1,55 @@
|
||||
<head>
|
||||
<script type="application/javascript" src="../lib/lax.min.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
window.onload = function () {
|
||||
lax.init()
|
||||
|
||||
const input = document.getElementById('input')
|
||||
|
||||
// Add lax driver for inputLength
|
||||
lax.addDriver('inputLength', function () {
|
||||
return input.value.length
|
||||
})
|
||||
|
||||
lax.addElements("#input", {
|
||||
'inputLength': {
|
||||
"rotate": [
|
||||
[0, 100],
|
||||
[0, 360],
|
||||
],
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<style>
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
|
||||
#input {
|
||||
text-align: center;
|
||||
width: calc(100vw - 200px);
|
||||
transform-origin: center;
|
||||
margin-left: 100px;
|
||||
margin-top: calc(50vh - 50px);
|
||||
position: fixed;
|
||||
font-size: 40px;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
background-color: #f544ad;
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
color: white;
|
||||
border-radius: 50px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body>
|
||||
<input id='input' placeholder="type something..." autocomplete="false" autofocus />
|
||||
</body>
|
||||
61
docs/examples/on-update.html
Normal file
@@ -0,0 +1,61 @@
|
||||
<head>
|
||||
<script type="application/javascript" src="../lib/lax.min.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
window.onload = function () {
|
||||
lax.init()
|
||||
|
||||
lax.addDriver('scrollY', function () {
|
||||
return window.scrollY
|
||||
})
|
||||
|
||||
lax.addElements("#text", {}, {
|
||||
onUpdate: function (driverValues, domElement) {
|
||||
const scrollY = driverValues.scrollY[0]
|
||||
|
||||
const oCount = Math.floor((scrollY / 10) + 1)
|
||||
const oString = Array.from({ length: oCount }, (v, i) => "o").join("")
|
||||
domElement.innerHTML = "scr" + oString + "ll"
|
||||
|
||||
if (scrollY > 1000) {
|
||||
domElement.classList.add('pink')
|
||||
} else {
|
||||
domElement.classList.remove('pink')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<style>
|
||||
#text {
|
||||
width: calc(100vw - 40px);
|
||||
left: 20;
|
||||
top: 20;
|
||||
position: fixed;
|
||||
font-size: 60px;
|
||||
font-family: sans-serif;
|
||||
color: #a26ddc;
|
||||
overflow-wrap: anywhere;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#text.pink {
|
||||
color: #ff568c;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 0;
|
||||
background-color: #dedbde;
|
||||
background-image: linear-gradient(rgba(255, 255, 255, .2) 50%, transparent 50%, transparent);
|
||||
margin: 0;
|
||||
background-size: 700px 700px;
|
||||
height: 1000000px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body>
|
||||
<div id="text">
|
||||
</div>
|
||||
</body>
|
||||
91
docs/examples/scroll.html
Normal file
@@ -0,0 +1,91 @@
|
||||
<head>
|
||||
<script type="application/javascript" src="../lib/lax.min.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
window.onload = function () {
|
||||
lax.init()
|
||||
|
||||
lax.addDriver('scrollY', function () {
|
||||
return window.scrollY
|
||||
})
|
||||
|
||||
const container = document.querySelector('.container')
|
||||
const count = 100
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
const el = document.createElement('div')
|
||||
el.className = "circle"
|
||||
container.appendChild(el)
|
||||
}
|
||||
|
||||
lax.addElements(".circle", {
|
||||
scrollY: {
|
||||
translateX: [
|
||||
["elInY", "elCenterY", "elOutY"],
|
||||
[0, 'screenWidth/2', 'screenWidth'],
|
||||
{
|
||||
easing: 'easeInOutQuart',
|
||||
}
|
||||
],
|
||||
opacity: [
|
||||
["elInY", "elCenterY", "elOutY"],
|
||||
[0, 1, 0],
|
||||
{
|
||||
easing: 'easeInOutCubic'
|
||||
}
|
||||
],
|
||||
"border-radius": [
|
||||
["elInY+200", "elCenterY", "elOutY-200"],
|
||||
[0, 100, 0],
|
||||
{
|
||||
easing: 'easeInOutQuint',
|
||||
}
|
||||
],
|
||||
"box-shadow": [
|
||||
["elInY+200", "elCenterY", "elOutY-200"],
|
||||
[50, 0, 50],
|
||||
{
|
||||
easing: 'easeInOutQuint',
|
||||
cssFn: (val) => {
|
||||
return `${val}px ${val}px ${val}px rgba(0,0,0,0.5)`
|
||||
}
|
||||
}
|
||||
],
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<style>
|
||||
.circle {
|
||||
height: 100px;
|
||||
width: 100px;
|
||||
background-color: #a26ddc;
|
||||
margin-bottom: 0px;
|
||||
margin-left: -50px;
|
||||
margin-bottom: 40px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding-top: 50vh;
|
||||
width: 100vw;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #dedbde;
|
||||
background-image: linear-gradient(rgba(255, 255, 255, .2) 50%, transparent 50%, transparent);
|
||||
margin: 0;
|
||||
background-size: 700px 700px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
</div>
|
||||
</body>
|
||||
228
docs/examples/snap-scroll.html
Normal file
@@ -0,0 +1,228 @@
|
||||
<head>
|
||||
<script type="application/javascript" src="../lib/lax.min.js"></script>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.8, maximum-scale=0.8, minimum-scale=0.8">
|
||||
|
||||
<link href="https://fonts.googleapis.com/css2?family=Comfortaa:wght@300;700&display=swap" rel="stylesheet">
|
||||
<script type="application/javascript">
|
||||
|
||||
window.onload = function () {
|
||||
lax.init()
|
||||
|
||||
const container = document.querySelector('.container')
|
||||
|
||||
lax.addDriver('containerScrollX', function () {
|
||||
return container.scrollLeft
|
||||
})
|
||||
|
||||
lax.addElements(".bg", {
|
||||
containerScrollX: {
|
||||
"opacity": [
|
||||
["screenWidth * (index-1)", "screenWidth * index", "screenWidth * (index+1)"],
|
||||
[0, 1, 0],
|
||||
],
|
||||
}
|
||||
})
|
||||
|
||||
const imageAnimationMap = ["elCenterX-elWidth", "elCenterX", "elCenterX+elWidth"]
|
||||
const textAnimationMap = ["elCenterX-(elWidth/3)", "elCenterX", "elCenterX+(elWidth/3)"]
|
||||
|
||||
lax.addElements(".page h1", {
|
||||
containerScrollX: {
|
||||
translateY: [
|
||||
textAnimationMap,
|
||||
[200, 0, 200],
|
||||
{
|
||||
easing: 'easeInOutQuad',
|
||||
}
|
||||
],
|
||||
opacity: [
|
||||
textAnimationMap,
|
||||
[0, 1, 0],
|
||||
],
|
||||
}
|
||||
})
|
||||
|
||||
lax.addElements(".page p", {
|
||||
containerScrollX: {
|
||||
translateY: [
|
||||
textAnimationMap,
|
||||
[500, 0, 500],
|
||||
{
|
||||
easing: 'easeInOutQuad',
|
||||
}
|
||||
],
|
||||
opacity: [
|
||||
textAnimationMap,
|
||||
[0, 1, 0],
|
||||
],
|
||||
}
|
||||
})
|
||||
|
||||
lax.addElements(".page .image", {
|
||||
containerScrollX: {
|
||||
translateY: [
|
||||
imageAnimationMap,
|
||||
[-100, 0, -100],
|
||||
{
|
||||
easing: 'easeInOutQuad',
|
||||
}
|
||||
],
|
||||
scale: [
|
||||
imageAnimationMap,
|
||||
[0.8, 1, 0.8],
|
||||
{
|
||||
easing: 'easeInOutQuad',
|
||||
}
|
||||
],
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<style>
|
||||
.background {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.page {
|
||||
flex-shrink: 0;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
scroll-snap-align: center;
|
||||
scroll-snap-stop: always;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.bg {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: fixed;
|
||||
background-size: cover;
|
||||
}
|
||||
|
||||
.image {
|
||||
height: 60vh;
|
||||
width: 80%;
|
||||
left: 10%;
|
||||
position: absolute;
|
||||
transform-origin: center top;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
background-size: contain;
|
||||
top: 5vh;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
scroll-snap-type: x mandatory;
|
||||
direction: ltr;
|
||||
width: 100vw;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
h1 {
|
||||
top: 67vh;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
font-weight: 100;
|
||||
text-align: center;
|
||||
color: white;
|
||||
font-weight: 800;
|
||||
font-size: 60px;
|
||||
}
|
||||
|
||||
p {
|
||||
top: 70vh;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
font-weight: 100;
|
||||
text-align: center;
|
||||
color: white;
|
||||
font-size: 18px;
|
||||
line-height: 28px;
|
||||
padding: 10vh;
|
||||
padding-left: 200px;
|
||||
padding-right: 200px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 600px) {
|
||||
p {
|
||||
padding: 50px;
|
||||
top: 55vh;
|
||||
}
|
||||
|
||||
h1 {
|
||||
top: 48vh;
|
||||
}
|
||||
|
||||
.image {
|
||||
top: -2vh;
|
||||
}
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background-color: black;
|
||||
overflow-y: hidden;
|
||||
overflow-x: hidden;
|
||||
font-family: 'Comfortaa', arial;
|
||||
}
|
||||
|
||||
html {
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#controls {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body>
|
||||
<div class="background">
|
||||
<div class="bg" style="background-image: url('../assets/bg3.jpg');"></div>
|
||||
<div class="bg" style="background-image: url('../assets/bg1.jpg');"></div>
|
||||
<div class="bg" style="background-image: url('../assets/bg2.jpg');"></div>
|
||||
</div>
|
||||
|
||||
<div class="container" id="scroller">
|
||||
<div class="page">
|
||||
<div class="image" style="background-image: url('../assets/shoe3.png');"></div>
|
||||
<h1>Superstar</h1>
|
||||
<p>
|
||||
Classics never go out of style. An instant icon since their debut, adidas Superstar Shoes first rose to fame on
|
||||
the basketball courts of the '70s and haven't slowed their roll since.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="page"">
|
||||
<div class=" image" style="background-image: url('../assets/shoe1.png');"></div>
|
||||
<h1>Retrorun</h1>
|
||||
<p>
|
||||
Retro design with a modern twist. Check out a busy side street or stroll to the corner store in these adidas
|
||||
running-inspired shoes. Suede overlays and contrast 3-Stripes give the flexible upper a sleek, sporty finish.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="page">
|
||||
<div class="image" style="background-image: url('../assets/shoe2.png');"></div>
|
||||
<h1>Grand Court</h1>
|
||||
<p>
|
||||
A '70s style reborn. These shoes take inspiration from iconic sport styles of the past and move them into the
|
||||
future. The shoes craft an everyday look with a leather-like upper.
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
59
docs/examples/sprite.html
Normal file
@@ -0,0 +1,59 @@
|
||||
<head>
|
||||
<script type="application/javascript" src="../lib/lax.min.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
window.onload = function () {
|
||||
lax.init()
|
||||
|
||||
lax.addDriver('scrollY', function () {
|
||||
return window.scrollY
|
||||
})
|
||||
|
||||
const frameWidth = 370
|
||||
const frameCount = 12
|
||||
|
||||
lax.addElements(".sprite", {
|
||||
scrollY: {
|
||||
"background-position": [
|
||||
[0, 1e9],
|
||||
[0, 1e9],
|
||||
{
|
||||
cssFn: function (val) {
|
||||
const frame = Math.floor(val / 10) % frameCount
|
||||
const x = frame * frameWidth
|
||||
|
||||
return `-${x}px 0px`
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<style>
|
||||
.sprite {
|
||||
height: 370px;
|
||||
width: 370px;
|
||||
background-image: url('../assets/spritesheet.jpg');
|
||||
background-repeat: no-repeat;
|
||||
position: fixed;
|
||||
left: calc(50vw - 185px);
|
||||
top: calc(50vh - 185px);
|
||||
}
|
||||
|
||||
body {
|
||||
padding: 0;
|
||||
background-color: #dedbde;
|
||||
background-image: linear-gradient(rgba(255, 255, 255, .2) 50%, transparent 50%, transparent);
|
||||
margin: 0;
|
||||
background-size: 700px 700px;
|
||||
height: 1000000px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body>
|
||||
<div class="sprite">
|
||||
</div>
|
||||
</body>
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 52.22 46.64"><defs><style>.cls-1{fill:#f3f4f5;}</style></defs><title>a</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M1,32.73H6.43C2.55,29.23,0,24.41,0,17.41,0,7.85,5.49,0,15.52,0h.18C26.77,0,31.88,8.42,31.88,20.43a36.09,36.09,0,0,1-2.08,12.4h.85c6,0,9.27-3.69,9.27-10.88A37.31,37.31,0,0,0,37.18,8l11-3.6A44.74,44.74,0,0,1,52.22,24c0,15.7-8.13,22.61-21.85,22.61H1ZM21.38,33a22.24,22.24,0,0,0,1.89-9.18c0-6.15-2.46-9.93-7-9.93h-.19c-3.88,0-6.15,3.21-6.15,7.85,0,6.72,3.69,11.26,8.9,11.26Z"/></g></g></svg>
|
||||
|
Before Width: | Height: | Size: 615 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 69.06 14.38"><defs><style>.cls-1{fill:#f3f4f5;}</style></defs><title>l</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M0,0H69.06V14.38H0Z"/></g></g></svg>
|
||||
|
Before Width: | Height: | Size: 264 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50.71 50.9"><defs><style>.cls-1{fill:#f3f4f5;}</style></defs><title>x</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M0,35.48,15.8,25.26,0,15V0L25.73,17.79,50.71.66V16.08L36,25.54,50.71,35.1v15L26,33,0,50.9Z"/></g></g></svg>
|
||||
|
Before Width: | Height: | Size: 334 B |
764
docs/index.html
@@ -1,360 +1,472 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.75, maximum-scale=0.75, minimum-scale=0.75">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5">
|
||||
|
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,600,800" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.7.2/css/all.css" integrity="sha384-fnmOCqbTlWIlj8LyTjo7mOUStjsKC4pOpQbqyi7RrhN7udi9RwhKkMHpvLbHG9Sr" crossorigin="anonymous">
|
||||
|
||||
<script src="./lib/lax.min.js?v=1.0.4"></script>
|
||||
<!-- <script src="../src/lax.js"></script> -->
|
||||
<script src="./lib/lax.min.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.onload = function() {
|
||||
document.getElementById("main").classList.add("loaded")
|
||||
window.scrollTo(0, 0)
|
||||
|
||||
lax.setup()
|
||||
|
||||
document.addEventListener('scroll', function(x) {
|
||||
lax.update(window.scrollY)
|
||||
}, false)
|
||||
window.onload = function () {
|
||||
|
||||
let w = window.innerWidth
|
||||
window.addEventListener("resize", function() {
|
||||
if(w !== window.innerWidth) {
|
||||
lax.populateElements()
|
||||
lax.init()
|
||||
|
||||
lax.addDriver(
|
||||
"scrollY",
|
||||
function () {
|
||||
return document.documentElement.scrollTop;
|
||||
},
|
||||
{ frameStep: 1 }
|
||||
);
|
||||
|
||||
lax.addElements(".letter-x", {
|
||||
scrollY: {
|
||||
translateY: [[-400, 0, 100], [300, 0, 100]],
|
||||
scale: [[100, "screenHeight"], [0.25, 10]],
|
||||
opacity: [[0, "screenHeight/2", "screenHeight"], [1, 1, 0]],
|
||||
}
|
||||
});
|
||||
|
||||
lax.update(window.scrollY)
|
||||
lax.addElements(".letter-l", {
|
||||
scrollY: {
|
||||
translateY: [[-400, 0], [100, 0]],
|
||||
translateX: [[0, "screenHeight"], [0, 400]],
|
||||
opacity: [[0, "screenHeight/2"], [1, 0]]
|
||||
}
|
||||
});
|
||||
|
||||
lax.addElements(".letter-a", {
|
||||
scrollY: {
|
||||
translateY: [[-400, 0], [200, 0]],
|
||||
translateX: [[0, "screenHeight"], [0, -400]],
|
||||
opacity: [[0, "screenHeight/2"], [1, 0]]
|
||||
}
|
||||
});
|
||||
|
||||
lax.addElements(".scrolldown", {
|
||||
scrollY: {
|
||||
"letter-spacing": [
|
||||
[0, "screenHeight"],
|
||||
[0, 150],
|
||||
{
|
||||
cssUnit: "px"
|
||||
}
|
||||
],
|
||||
opacity: [["screenHeight*0.25", "screenHeight*0.75"], [1, 0]],
|
||||
translateX: [[0, "screenHeight"], [0, 80]],
|
||||
}
|
||||
});
|
||||
|
||||
lax.addElements(".oooh", {
|
||||
scrollY: {
|
||||
translateX: [["elInY", "elOutY"], [0, "screenWidth-200"]]
|
||||
}
|
||||
});
|
||||
|
||||
lax.addElements(".aaah", {
|
||||
scrollY: {
|
||||
translateX: [["elInY", "elOutY"], [0, "-screenWidth-200"]]
|
||||
}
|
||||
});
|
||||
|
||||
lax.addElements(".wheee", {
|
||||
scrollY: {
|
||||
translateX: [["elInY", "elOutY"], [-400, "screenWidth+100"]],
|
||||
skewX: [["elInY", "elOutY"], [40, -40]],
|
||||
}
|
||||
});
|
||||
|
||||
lax.addElements(".bubble", {
|
||||
scrollY: {
|
||||
translateY: [
|
||||
["screenHeight/4", "screenHeight * 3"],
|
||||
["Math.random()*screenHeight", "Math.random()*screenHeight*3"]
|
||||
],
|
||||
opacity: [
|
||||
["screenHeight/4", "screenHeight/2"],
|
||||
[0, 1]
|
||||
],
|
||||
scale: [[0], ["(Math.random()*0.8)+0.2"]],
|
||||
translateX: [[0], ["index*(screenWidth/25)-50"]],
|
||||
transform: [
|
||||
[0, 4000],
|
||||
[0, "(Math.random() + 0.8) * 1000"],
|
||||
{
|
||||
cssFn: function (val) {
|
||||
return `rotateX(${val % 360}deg)`
|
||||
}
|
||||
}
|
||||
],
|
||||
rotate: [
|
||||
[0, 4000],
|
||||
[0, "(Math.random() - 0.5) * 1000"],
|
||||
],
|
||||
}
|
||||
});
|
||||
|
||||
lax.addElements('#pinkZag', {
|
||||
scrollY: {
|
||||
translateY: [
|
||||
["elInY", "elOutY"],
|
||||
[0, -300]
|
||||
],
|
||||
}
|
||||
})
|
||||
|
||||
lax.addElements('#tealZag', {
|
||||
scrollY: {
|
||||
translateY: [
|
||||
["elInY", "elOutY"],
|
||||
[0, 200]
|
||||
],
|
||||
}
|
||||
})
|
||||
|
||||
lax.addElements('#purpleZag', {
|
||||
scrollY: {
|
||||
translateY: [
|
||||
["elInY", "elOutY"],
|
||||
[0, 700]
|
||||
],
|
||||
}
|
||||
})
|
||||
|
||||
lax.addElements(".downarrows img", {
|
||||
scrollY: {
|
||||
translateY: [
|
||||
[0, 200],
|
||||
[0, 200]
|
||||
],
|
||||
opacity: [
|
||||
[0, "screenHeight"],
|
||||
[1, 0]
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
lax.addElements(".bottombutton", {
|
||||
scrollY: {
|
||||
"background-position": [
|
||||
["elInY", "elOutY"],
|
||||
[0, 400],
|
||||
{
|
||||
cssFn: function (val) {
|
||||
return `${val}px 0`
|
||||
}
|
||||
}
|
||||
],
|
||||
scale: [
|
||||
["elInY", "elCenterY"],
|
||||
[3, 1],
|
||||
]
|
||||
},
|
||||
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
body,html {
|
||||
margin:0;
|
||||
padding:0;
|
||||
color: #F3F4F5;
|
||||
background: #191818;
|
||||
html {
|
||||
overflow-x: hidden;
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#main {
|
||||
opacity: 0;
|
||||
transition: opacity 200ms;
|
||||
}
|
||||
|
||||
#main.loaded {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 100vw;
|
||||
z-index: 2;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.bg {
|
||||
position: fixed;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#header img {
|
||||
width: 78pt;
|
||||
margin-top: 12pt;
|
||||
}
|
||||
|
||||
#header h2 {
|
||||
margin-top: 60pt;
|
||||
font-size: 24pt;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#header h4 {
|
||||
font-size: 16pt;
|
||||
font-weight: 300;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#header i {
|
||||
font-size: 14pt;
|
||||
background: white;
|
||||
color: black;
|
||||
width: 30pt;
|
||||
text-align: center;
|
||||
height: 30pt;
|
||||
line-height: 30pt;
|
||||
border-radius: 15pt;
|
||||
margin-top: -10pt;
|
||||
}
|
||||
|
||||
.left {
|
||||
position: absolute;
|
||||
left: 80pt;
|
||||
}
|
||||
|
||||
.right {
|
||||
position: absolute;
|
||||
right: 80pt;
|
||||
}
|
||||
|
||||
.chunkyText {
|
||||
font-size: 80pt;
|
||||
font-weight: 1000;
|
||||
}
|
||||
|
||||
.crazyText {
|
||||
font-size: 40pt;
|
||||
font-weight: 1000;
|
||||
color: #5ee5d5;
|
||||
position: absolute;
|
||||
width: 100vw;
|
||||
text-align: center;
|
||||
margin-top: 500pt;
|
||||
}
|
||||
|
||||
.bubble {
|
||||
position: absolute;
|
||||
margin-top: 100pt;
|
||||
}
|
||||
|
||||
.block {
|
||||
position: absolute;
|
||||
width: 60pt;
|
||||
height: 60pt;
|
||||
}
|
||||
|
||||
.bubble.a {
|
||||
width: 100pt;
|
||||
height: 100pt;
|
||||
border-radius: 50pt
|
||||
}
|
||||
|
||||
.bubble.b {
|
||||
width: 130pt;
|
||||
height: 130pt;
|
||||
border-radius: 65pt
|
||||
}
|
||||
|
||||
.bubble.c {
|
||||
width: 50pt;
|
||||
height: 50pt;
|
||||
border-radius: 25pt
|
||||
}
|
||||
|
||||
.section {
|
||||
z-index: 2;
|
||||
display: block;
|
||||
body {
|
||||
padding: 0;
|
||||
overflow-x: hidden;
|
||||
width: 100%;
|
||||
background-color: #242224;
|
||||
margin: 0;
|
||||
height: 480vh;
|
||||
color: white;
|
||||
font-family: "Montserrat", sans-serif;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#section1 {
|
||||
height: 750pt;
|
||||
}
|
||||
|
||||
#section2 {
|
||||
height: 200pt;
|
||||
}
|
||||
|
||||
#section3 {
|
||||
padding: 0 20pt;
|
||||
font-size: 20pt;
|
||||
line-height: 30pt;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 85vh;
|
||||
}
|
||||
|
||||
p {
|
||||
max-width: 500pt;
|
||||
.bottombutton {
|
||||
background-image: url(./assets/button-bg.jpg);
|
||||
width: 250px;
|
||||
height: 70px;
|
||||
background-size: 160px;
|
||||
color: white;
|
||||
font-weight: 800;
|
||||
text-align: center;
|
||||
margin-bottom: 100pt;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.button {
|
||||
padding: 10pt 20pt;
|
||||
background: #35D5E5;
|
||||
border-radius: 10pt;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
a {
|
||||
line-height: 70px;
|
||||
font-size: 30px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background: white;
|
||||
color: #35D5E5 !important;
|
||||
position: absolute;
|
||||
top: 425vh;
|
||||
border-radius: 20px;
|
||||
left: 50vw;
|
||||
margin-left: -125px;
|
||||
z-index: 100;
|
||||
pointer-events: all;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.bottombg {
|
||||
background-color: #8d77ed;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
top: 380vh;
|
||||
z-index: 50;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.letter-l {
|
||||
margin-top: 100px;
|
||||
width: 200px;
|
||||
left: 50vw;
|
||||
margin-left: -75px;
|
||||
position: fixed;
|
||||
left: 50vw;
|
||||
margin-left: -75px;
|
||||
}
|
||||
|
||||
.letter-a {
|
||||
margin-top: 158px;
|
||||
position: fixed;
|
||||
left: 50vw;
|
||||
margin-left: -77px;
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.letter-x {
|
||||
margin-top: 85px;
|
||||
position: fixed;
|
||||
left: 50vw;
|
||||
margin-left: -300px;
|
||||
transform: scale(0.25);
|
||||
transform-origin: 50% 50%;
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
.letter-x img {
|
||||
width: 600px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.scrolldown {
|
||||
bottom: 90px;
|
||||
height: 40px;
|
||||
position: fixed;
|
||||
width: 300vw;
|
||||
left: -100vw;
|
||||
font-size: 40px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.oooh {
|
||||
font-size: 150px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 140vh;
|
||||
}
|
||||
|
||||
.aaah {
|
||||
font-size: 150px;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 170vh;
|
||||
}
|
||||
|
||||
.wheee {
|
||||
top: 230vh;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
height: 50px;
|
||||
font-size: 100px;
|
||||
}
|
||||
|
||||
.downarrows {
|
||||
bottom: 60px;
|
||||
position: fixed;
|
||||
left: 50vw;
|
||||
width: 70px;
|
||||
margin-left: -35px;
|
||||
height: 26px;
|
||||
}
|
||||
|
||||
.downarrows img {
|
||||
width: 70px;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.bubbles {
|
||||
top: -100vh;
|
||||
position: fixed;
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.bubble {
|
||||
width: 140px;
|
||||
height: 140px;
|
||||
opacity: 1;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.bubble.red {
|
||||
background: #a94fe4;
|
||||
}
|
||||
|
||||
.bubble.blue {
|
||||
background: #68e4f1;
|
||||
}
|
||||
|
||||
.bubble.yellow {
|
||||
background: #ffe773;
|
||||
}
|
||||
|
||||
.zags {
|
||||
margin-top: 250vh;
|
||||
z-index: 100;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
height: 150vh;
|
||||
}
|
||||
|
||||
.zag {
|
||||
width: 100vw;
|
||||
height: 150vh;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
#pinkZag {
|
||||
background-image: url(./assets/pink-zag.png);
|
||||
background-size: 200px;
|
||||
background-repeat: repeat-x;
|
||||
background-position-y: bottom;
|
||||
}
|
||||
|
||||
#tealZag {
|
||||
background-image: url(./assets/teal-zag.png);
|
||||
background-size: 200px;
|
||||
background-repeat: repeat-x;
|
||||
background-position-y: bottom;
|
||||
}
|
||||
|
||||
#purpleZag {
|
||||
background-image: url(./assets/purple-zag.png);
|
||||
background-size: 200px;
|
||||
background-repeat: repeat-x;
|
||||
background-position-y: bottom;
|
||||
}
|
||||
|
||||
.bottom {
|
||||
margin-top: 400vh;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
|
||||
<body>
|
||||
<div id="main">
|
||||
<div id="header" class="section" data-lax-opacity="0 1, (0.8*vh) 0">
|
||||
<img src="./img/l.svg" style="width: 103pt; margin-left: 26pt; margin-bottom: -4pt;" data-lax-translate-x="0 0, vh 200" />
|
||||
<img src="./img/a.svg" data-lax-translate-x="0 0, vh -200" />
|
||||
<img src="./img/x.svg" />
|
||||
<img src="./img/x.svg" style="margin-top: -79pt" data-lax-translate-y="0 0, vh 200" />
|
||||
<img src="./img/x.svg" style="margin-top: -79pt" data-lax-translate-y="0 0, vh 400" />
|
||||
<img src="./img/x.svg" style="margin-top: -79pt" data-lax-translate-y="0 0, vh 600" />
|
||||
<img src="./img/x.svg" style="margin-top: -79pt" data-lax-translate-y="0 0, vh 800" />
|
||||
<img src="./img/x.svg" style="margin-top: -79pt" data-lax-translate-y="0 0, vh 1000" />
|
||||
<h2 data-lax-scale="0 1, vh 0.2" data-lax-translate-y="0 0, vh 1200" data-lax-opacity="0 1, (vh*0.5) 0">awesum scroll effects</h2>
|
||||
|
||||
<h4 data-lax-opacity="0 1, (vh*0.05) 0">scroll down</h4>
|
||||
<i class="fas fa-chevron-down"
|
||||
data-lax-opacity="0 1, (vh*0.1) 0"
|
||||
data-lax-translate-y="0 0, 200 100">
|
||||
</i>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="section1" class="section">
|
||||
<div class="left">
|
||||
<div class="bubble a"
|
||||
style="background: #EDD943"
|
||||
data-lax-preset="lazy-250"
|
||||
></div>
|
||||
|
||||
<div class="bubble c"
|
||||
style="background: #ED2471; margin-left: 80pt"
|
||||
data-lax-preset="lazy-100"
|
||||
></div>
|
||||
|
||||
<div class="bubble b"
|
||||
style="background: #35D5E5; margin-left: 160pt"
|
||||
data-lax-preset="lazy-300"
|
||||
></div>
|
||||
|
||||
<h3 data-lax-preset="driftRight" class="chunkyText" style="color: #35D5E5;">oooh!</h3>
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
<div class="bubble a"
|
||||
style="background: #35D5E5; margin-left: 120pt"
|
||||
data-lax-preset="lazy-200"
|
||||
></div>
|
||||
|
||||
<div class="bubble c"
|
||||
style="background: #EDD943; margin-left: -20pt"
|
||||
data-lax-preset="lazy-150"
|
||||
></div>
|
||||
|
||||
<div class="bubble b"
|
||||
style="background: #ED2471; margin-left: 20pt; margin-top: 200pt"
|
||||
data-lax-preset="lazy-350"
|
||||
></div>
|
||||
<h3 data-lax-preset="driftLeft" class="chunkyText" style="color: #ED2471; margin-top: 200pt;">aaah!</h3>
|
||||
</div>
|
||||
|
||||
<h3 data-lax-preset="crazy zoomInOut" class="crazyText">sooo crazy</h3>
|
||||
</div>
|
||||
|
||||
<div id="section2" class="section">
|
||||
<div class="blockContainer" data-lax-preset="leftToRight-1.1 fadeInOut">
|
||||
<div class="block"
|
||||
style="background: #35D5E5;"
|
||||
data-lax-preset="spin"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div class="blockContainer" data-lax-preset="leftToRight-1.2 fadeInOut">
|
||||
<div class="block"
|
||||
style="background: #EDD943; margin-top: -50pt; margin-left: -50pt; width: 40pt; height: 40pt;"
|
||||
data-lax-preset="spinRev-500"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div class="blockContainer" data-lax-preset="leftToRight-1.4 fadeInOut">
|
||||
<div class="block"
|
||||
style="background: #ED2471; margin-top: -90pt; margin-left: -0pt;"
|
||||
data-lax-preset="spin-500"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div class="blockContainer" data-lax-preset="leftToRight-1.5 fadeInOut">
|
||||
<div class="block"
|
||||
style="background: #EDD943; margin-top: 70pt; margin-left: -150pt; width: 40pt; height: 40pt;"
|
||||
data-lax-preset="spinRev-500"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div class="blockContainer" data-lax-preset="leftToRight-1.3 fadeInOut">
|
||||
<div class="block"
|
||||
style="background: #EDD943; margin-top: 100pt; margin-left: -60pt; width: 25pt; height: 25pt;"
|
||||
data-lax-preset="spin-500"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div class="blockContainer" data-lax-preset="leftToRight-1.05 fadeInOut">
|
||||
<div class="block"
|
||||
style="background: #ED2471; margin-top: -30pt; margin-left: -70pt;"
|
||||
data-lax-preset="spin"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<h3 data-lax-preset="leftToRight-0.8 fadeInOut" class="chunkyText" style="
|
||||
color: #white; position: absolute; margin-top: -20pt; margin-left: -100pt">
|
||||
wheee!
|
||||
</h3>
|
||||
|
||||
<div class="blockContainer" data-lax-preset="leftToRight-1.15 fadeInOut">
|
||||
<div class="block"
|
||||
style="background: #35D5E5; margin-top: -70pt; margin-left: -20pt; width: 40pt; height: 40pt;"
|
||||
data-lax-preset="spinRev-500"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div class="blockContainer" data-lax-preset="leftToRight-1.45 fadeInOut">
|
||||
<div class="block"
|
||||
style="background: #ED2471; margin-top: -50pt; margin-left: -50pt; width: 25pt; height: 25pt;"
|
||||
data-lax-preset="spin-500"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div class="blockContainer" data-lax-preset="leftToRight-1.5 fadeInOut">
|
||||
<div class="block"
|
||||
style="background: #35D5E5; margin-top: 30pt; margin-left: -20pt;"
|
||||
data-lax-preset="spinRev-500"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div class="blockContainer" data-lax-preset="leftToRight-1.25 fadeInOut">
|
||||
<div class="block"
|
||||
style="background: #ED2471; margin-top: 80pt; margin-left: -10pt;"
|
||||
data-lax-preset="spin-500"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="section3" class="section">
|
||||
<p data-lax-preset="linger">
|
||||
Harness the power of scrolling and make your websites come alive!
|
||||
</p>
|
||||
<a class="button" data-lax-preset="linger" href="https://github.com/alexfoxy/laxxx">
|
||||
Get lax.js
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<img src="./assets/l.png" class="letter-l" />
|
||||
<img src="./assets/a.png" class="letter-a" />
|
||||
<div class="letter-x">
|
||||
<img src="./assets/x.png" />
|
||||
</div>
|
||||
|
||||
</body>
|
||||
<h2 class="scrolldown">scroll down</h2>
|
||||
|
||||
<div class="downarrows">
|
||||
<img src="./assets/downarrow.png" />
|
||||
</div>
|
||||
|
||||
<div class="zags">
|
||||
<div id="pinkZag" class="zag"></div>
|
||||
<div id="tealZag" class="zag"></div>
|
||||
<div id="purpleZag" class="zag"></div>
|
||||
</div>
|
||||
|
||||
<div class="bubbles">
|
||||
<div class="bubble blue"></div>
|
||||
<div class="bubble red"></div>
|
||||
<div class="bubble yellow"></div>
|
||||
<div class="bubble red"></div>
|
||||
<div class="bubble blue"></div>
|
||||
<div class="bubble yellow"></div>
|
||||
<div class="bubble blue"></div>
|
||||
<div class="bubble red"></div>
|
||||
<div class="bubble yellow"></div>
|
||||
<div class="bubble red"></div>
|
||||
<div class="bubble blue"></div>
|
||||
<div class="bubble blue"></div>
|
||||
<div class="bubble red"></div>
|
||||
<div class="bubble yellow"></div>
|
||||
<div class="bubble red"></div>
|
||||
<div class="bubble blue"></div>
|
||||
<div class="bubble yellow"></div>
|
||||
<div class="bubble blue"></div>
|
||||
<div class="bubble red"></div>
|
||||
<div class="bubble yellow"></div>
|
||||
<div class="bubble red"></div>
|
||||
<div class="bubble blue"></div>
|
||||
<div class="bubble yellow"></div>
|
||||
<div class="bubble blue"></div>
|
||||
<div class="bubble red"></div>
|
||||
</div>
|
||||
|
||||
<h1 class="oooh">oooh</h1>
|
||||
<h1 class="aaah">aaah</h1>
|
||||
|
||||
<h1 class="wheee">wheee!</h1>
|
||||
|
||||
<div class="bottombg"></div>
|
||||
|
||||
<a href="https://github.com/alexfoxy/lax.js">
|
||||
<div class="bottombutton">
|
||||
Get lax.js
|
||||
</div>
|
||||
</a>
|
||||
|
||||
|
||||
<a href="https://github.com/alexfoxy/lax.js" class="github-corner" aria-label="View source on GitHub"><svg width="80"
|
||||
height="80" viewBox="0 0 250 250"
|
||||
style="fill:#fff; color:#151513; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true">
|
||||
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
|
||||
<path
|
||||
d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
|
||||
fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
|
||||
<path
|
||||
d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
|
||||
fill="currentColor" class="octo-body"></path>
|
||||
</svg></a>
|
||||
<style>
|
||||
.github-corner:hover .octo-arm {
|
||||
animation: octocat-wave 560ms ease-in-out
|
||||
}
|
||||
|
||||
@keyframes octocat-wave {
|
||||
|
||||
0%,
|
||||
100% {
|
||||
transform: rotate(0)
|
||||
}
|
||||
|
||||
20%,
|
||||
60% {
|
||||
transform: rotate(-25deg)
|
||||
}
|
||||
|
||||
40%,
|
||||
80% {
|
||||
transform: rotate(10deg)
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width:500px) {
|
||||
.github-corner:hover .octo-arm {
|
||||
animation: none
|
||||
}
|
||||
|
||||
.github-corner .octo-arm {
|
||||
animation: octocat-wave 560ms ease-in-out
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</body>
|
||||
2
docs/lib/lax.min.js
vendored
488
docs/preset-explorer.html
Normal file
@@ -0,0 +1,488 @@
|
||||
<head>
|
||||
<script src="https://cdn.jsdelivr.net/gh/google/code-prettify@master/loader/run_prettify.js"></script>
|
||||
|
||||
<script type="application/javascript" src="./lib/lax.min.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
window.copyPreset = function () {
|
||||
const input = document.getElementById("presetString")
|
||||
|
||||
input.select()
|
||||
input.setSelectionRange(0, 99999)
|
||||
|
||||
document.execCommand("copy")
|
||||
|
||||
alert("Copied the text: " + input.value)
|
||||
}
|
||||
|
||||
window.onload = function () {
|
||||
|
||||
/*
|
||||
Setup control data
|
||||
*/
|
||||
|
||||
const baseSettings = {
|
||||
fade: [
|
||||
{
|
||||
name: 'Distance',
|
||||
limits: [100, window.innerHeight / 3],
|
||||
value: window.innerHeight / 4
|
||||
},
|
||||
{
|
||||
name: 'Starting Opacity',
|
||||
limits: [0, 0.9],
|
||||
step: 0.1,
|
||||
value: 0
|
||||
}
|
||||
],
|
||||
blur: [
|
||||
{
|
||||
name: 'Distance',
|
||||
limits: [100, window.innerHeight / 3],
|
||||
value: window.innerHeight / 4
|
||||
},
|
||||
{
|
||||
name: 'Strength',
|
||||
limits: [0, 50],
|
||||
value: 20
|
||||
}
|
||||
],
|
||||
spin: [
|
||||
{
|
||||
name: 'Distance',
|
||||
limits: [10, 3000],
|
||||
value: window.innerHeight
|
||||
},
|
||||
{
|
||||
name: 'Stength',
|
||||
limits: [1, 360],
|
||||
value: 360
|
||||
}
|
||||
],
|
||||
scale: [
|
||||
{
|
||||
name: 'Distance',
|
||||
limits: [50, window.innerHeight / 3],
|
||||
value: window.innerHeight / 4
|
||||
},
|
||||
{
|
||||
name: 'Starting Scale',
|
||||
limits: [0, 1],
|
||||
step: 0.1,
|
||||
value: 0.6
|
||||
}
|
||||
],
|
||||
slide: [
|
||||
{
|
||||
name: 'Distance',
|
||||
limits: [50, window.innerHeight],
|
||||
value: window.innerHeight / 4
|
||||
},
|
||||
{
|
||||
name: 'Amount',
|
||||
limits: [-1000, 1000],
|
||||
value: 500
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const enabledPresets = ["fadeInOut", "seesaw"]
|
||||
|
||||
const controlData = {
|
||||
fadeIn: [...baseSettings.fade],
|
||||
fadeOut: [...baseSettings.fade],
|
||||
fadeInOut: [...baseSettings.fade],
|
||||
scaleIn: [...baseSettings.scale],
|
||||
scaleOut: [...baseSettings.scale],
|
||||
scaleInOut: [...baseSettings.scale],
|
||||
slideX: [...baseSettings.slide],
|
||||
slideY: [...baseSettings.slide],
|
||||
jiggle: [
|
||||
{
|
||||
name: 'Distance',
|
||||
limits: [40, 200],
|
||||
value: 50
|
||||
},
|
||||
{
|
||||
name: 'Stength',
|
||||
limits: [1, 100],
|
||||
value: 20
|
||||
}
|
||||
],
|
||||
seesaw: [
|
||||
{
|
||||
name: 'Distance',
|
||||
limits: [40, 200],
|
||||
value: 140
|
||||
},
|
||||
{
|
||||
name: 'Stength',
|
||||
limits: [1, 40],
|
||||
value: 20
|
||||
}
|
||||
],
|
||||
zigzag: [
|
||||
{
|
||||
name: 'Distance',
|
||||
limits: [40, 200],
|
||||
value: 170
|
||||
},
|
||||
{
|
||||
name: 'Stength',
|
||||
limits: [1, 500],
|
||||
value: 200
|
||||
}
|
||||
],
|
||||
hueRotate: [...baseSettings.spin],
|
||||
spin: [...baseSettings.spin],
|
||||
flipX: [...baseSettings.spin],
|
||||
flipY: [...baseSettings.spin],
|
||||
blurIn: [...baseSettings.blur],
|
||||
blurOur: [...baseSettings.blur],
|
||||
blurInOut: [...baseSettings.blur],
|
||||
}
|
||||
|
||||
/*
|
||||
UI Update methods
|
||||
*/
|
||||
|
||||
function updateLaxSettings() {
|
||||
lax.removeElements("#laxLogo")
|
||||
|
||||
const presets = []
|
||||
|
||||
enabledPresets.forEach((presetName) => {
|
||||
const control = controlData[presetName]
|
||||
const controlStr = control ? ":" + control.map(c => c.value).join(":") : ''
|
||||
const settingString = `${presetName}${controlStr}`
|
||||
presets.push(settingString)
|
||||
})
|
||||
|
||||
lax.addElements("#laxLogo", {
|
||||
scrollY: {
|
||||
presets
|
||||
}
|
||||
})
|
||||
|
||||
presetString.value = presets.map((p) => {
|
||||
return `lax_preset_${p}`
|
||||
}).join(" ")
|
||||
}
|
||||
|
||||
function onSliderUpdate(name, value) {
|
||||
const valueEl = document.getElementById(name + '-value')
|
||||
if (valueEl) valueEl.innerHTML = value
|
||||
updateLaxSettings()
|
||||
}
|
||||
|
||||
function onCheckBoxUpdate(controlName) {
|
||||
const index = enabledPresets.indexOf(controlName)
|
||||
const enabled = index >= 0
|
||||
const sliders = document.getElementById(controlName + "-controls")
|
||||
|
||||
if (enabled) {
|
||||
enabledPresets.splice(index, 1)
|
||||
if (sliders) sliders.classList.remove('visible')
|
||||
} else {
|
||||
enabledPresets.push(controlName)
|
||||
if (sliders) sliders.classList.add('visible')
|
||||
}
|
||||
|
||||
const checkbox = document.getElementById(controlName + "-checkbox")
|
||||
if (checkbox) checkbox.checked = !enabled
|
||||
}
|
||||
|
||||
/*
|
||||
Build UI
|
||||
*/
|
||||
|
||||
const controlsContainer = document.getElementById('controlsContainer')
|
||||
const presetString = document.getElementById('presetString')
|
||||
|
||||
function renderSlider(control, controlName) {
|
||||
const container = document.createElement('div')
|
||||
container.className = "slider"
|
||||
|
||||
const slider = document.createElement('input')
|
||||
const id = [controlName.toLowerCase(), control.name.toLowerCase()].join("-")
|
||||
|
||||
slider.type = 'range'
|
||||
slider.min = control.limits[0]
|
||||
slider.max = control.limits[1]
|
||||
slider.step = control.step || 1
|
||||
slider.value = control.value
|
||||
slider.oninput = function (el) {
|
||||
control.value = el.target.value
|
||||
onSliderUpdate(id, el.target.value)
|
||||
}
|
||||
|
||||
const labelContainer = document.createElement('div')
|
||||
labelContainer.className = "labelContainer"
|
||||
|
||||
const label = document.createElement('label')
|
||||
label.innerHTML = control.name
|
||||
|
||||
const value = document.createElement('label')
|
||||
value.innerHTML = control.value
|
||||
value.id = id + "-value"
|
||||
|
||||
labelContainer.appendChild(label)
|
||||
labelContainer.appendChild(value)
|
||||
container.appendChild(labelContainer)
|
||||
container.appendChild(slider)
|
||||
|
||||
return container
|
||||
}
|
||||
|
||||
function renderCheckbox(controlName) {
|
||||
const container = document.createElement('div')
|
||||
container.className = "checkBox"
|
||||
|
||||
const checkbox = document.createElement('input')
|
||||
checkbox.id = controlName + "-checkbox"
|
||||
checkbox.type = "checkbox"
|
||||
|
||||
const enabled = enabledPresets.indexOf(controlName) > -1
|
||||
checkbox.checked = enabled
|
||||
|
||||
const id = [controlName.toLowerCase(), name.toLowerCase()].join()
|
||||
|
||||
checkbox.oninput = function (el) {
|
||||
["blur", "scale", "fade"].forEach((type) => {
|
||||
if (controlName.includes(type)) {
|
||||
enabledPresets.forEach((presetName) => {
|
||||
if (presetName !== controlName && presetName.includes(type)) {
|
||||
onCheckBoxUpdate(presetName, true)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
onCheckBoxUpdate(controlName, enabled)
|
||||
updateLaxSettings()
|
||||
}
|
||||
|
||||
const label = document.createElement('label')
|
||||
label.innerHTML = controlName
|
||||
|
||||
container.appendChild(label)
|
||||
container.appendChild(checkbox)
|
||||
|
||||
return container
|
||||
}
|
||||
|
||||
function renderControl(controlName) {
|
||||
const controls = controlData[controlName]
|
||||
const container = document.createElement('div')
|
||||
container.className = 'control'
|
||||
|
||||
const checkbox = renderCheckbox(controlName)
|
||||
container.appendChild(checkbox)
|
||||
|
||||
const sliderControls = document.createElement('div')
|
||||
sliderControls.className = 'sliderControls'
|
||||
sliderControls.id = controlName + "-controls"
|
||||
|
||||
controls.forEach((control, i) => {
|
||||
const slider = renderSlider(control, controlName)
|
||||
sliderControls.appendChild(slider)
|
||||
})
|
||||
|
||||
const enabled = enabledPresets.indexOf(controlName) > -1
|
||||
if (enabled) sliderControls.classList.add("visible")
|
||||
|
||||
container.appendChild(sliderControls)
|
||||
controlsContainer.appendChild(container)
|
||||
}
|
||||
|
||||
Object.keys(controlData).forEach((controlName) => renderControl(controlName))
|
||||
|
||||
/*
|
||||
Initialise lax
|
||||
*/
|
||||
|
||||
lax.init()
|
||||
|
||||
lax.addDriver('scrollY', function () {
|
||||
return window.scrollY
|
||||
})
|
||||
|
||||
updateLaxSettings()
|
||||
}
|
||||
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<style>
|
||||
body {
|
||||
padding: 0;
|
||||
background-color: #dedbde;
|
||||
background-image: linear-gradient(rgba(255, 255, 255, .2) 50%, transparent 50%, transparent);
|
||||
margin: 0;
|
||||
background-size: 700px 700px;
|
||||
height: 220vh;
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
#laxLogo {
|
||||
margin-top: 100vh;
|
||||
left: 0;
|
||||
z-index: 1000;
|
||||
font-size: 100px;
|
||||
text-align: center;
|
||||
transform-origin: 50% 50%;
|
||||
color: #a94fe4;
|
||||
width: 240px;
|
||||
margin-left: calc(50vw - 150px - 120px);
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.hint {
|
||||
left: 0;
|
||||
width: 100%;
|
||||
opacity: 0.2;
|
||||
z-index: 1000;
|
||||
font-size: 24px;
|
||||
text-align: center;
|
||||
position: absolute;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#controlsContainer {
|
||||
position: fixed;
|
||||
width: 300px;
|
||||
background: white;
|
||||
height: 100vh;
|
||||
box-shadow: 0px 0px 15px 3px rgba(0, 0, 0, 0.2);
|
||||
padding: 20;
|
||||
box-sizing: border-box;
|
||||
overflow-y: auto;
|
||||
padding-bottom: 100px;
|
||||
}
|
||||
|
||||
#controlsContainer h1 {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
#canvas {
|
||||
width: calc(100vw - 300px);
|
||||
margin-left: 300px;
|
||||
position: relative;
|
||||
height: 220vh;
|
||||
}
|
||||
|
||||
#presetString {
|
||||
background: #4a525a;
|
||||
color: white;
|
||||
font-family: monospace;
|
||||
padding: 10px;
|
||||
border: 0;
|
||||
border-radius: 10px;
|
||||
width: 195px;
|
||||
margin-right: 5px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#presetStringContainer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
background: #e1e8e8;
|
||||
left: 0;
|
||||
width: 260px;
|
||||
padding: 20px 20px;
|
||||
}
|
||||
|
||||
.control {
|
||||
border-top: 1px solid #d8d7d7;
|
||||
}
|
||||
|
||||
.checkBox,
|
||||
.labelContainer {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
;
|
||||
}
|
||||
|
||||
.sliderControls {
|
||||
height: 0px;
|
||||
overflow-y: hidden;
|
||||
overflow-x: visible;
|
||||
transition: height 200ms ease-out;
|
||||
}
|
||||
|
||||
.sliderControls.visible {
|
||||
height: 105px;
|
||||
}
|
||||
|
||||
.slider input {
|
||||
width: 100%;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
label {
|
||||
font-weight: lighter;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.checkBox label {
|
||||
font-weight: 800;
|
||||
margin-bottom: 15px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
#copyButton {
|
||||
border: 0;
|
||||
width: 55px;
|
||||
height: 36px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
background: #a26ddc;
|
||||
border-radius: 10px;
|
||||
font-family: inherit;
|
||||
font-weight: bold;
|
||||
color: white;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#copyButton:hover {
|
||||
opacity: 0.8;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
#presetTextHelper {
|
||||
margin-bottom: 10px;
|
||||
font-size: 12px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body>
|
||||
<div id="controlsContainer">
|
||||
<h1>Lax preset explorer</h1>
|
||||
|
||||
<div id="presetStringContainer">
|
||||
<div id="presetTextHelper">
|
||||
<span>Preset code</span>
|
||||
<a href="https://github.com/alexfoxy/lax.js#using-presets">How to use</a>
|
||||
</div>
|
||||
|
||||
<input id="presetString" />
|
||||
<button onclick="copyPreset()" id="copyButton">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="canvas">
|
||||
<h4 class='hint' style="top: 120px;">Scroll Down..</h4>
|
||||
<h4 class='hint' style="bottom: 140px;">Scroll Up..</h4>
|
||||
|
||||
<img src="assets/lax.png" id="laxLogo" />
|
||||
</div>
|
||||
|
||||
</body>
|
||||
5
jsconfig.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"checkJs": true
|
||||
},
|
||||
}
|
||||
974
lib/lax.js
2
lib/lax.min.js
vendored
63
package.json
@@ -1,29 +1,36 @@
|
||||
{
|
||||
"name": "lax.js",
|
||||
"version": "1.0.4",
|
||||
"scripts": {
|
||||
"build": "babel src -d lib; uglifyjs lib/lax.js -o lib/lax.min.js -c -m; gzip < lib/lax.min.js > lib/lax.min.js.gz"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.2.3",
|
||||
"@babel/core": "^7.3.4",
|
||||
"@babel/preset-env": "^7.3.4"
|
||||
},
|
||||
"description": "Simple & light weight (2kb minified & zipped) vanilla javascript plugin to create smooth & beautiful animations when you scrolllll! Harness the power of the most intuitive interaction and make your websites come alive!",
|
||||
"license": "MIT",
|
||||
"author": "alexfoxy@gmail.com",
|
||||
"repository": {
|
||||
"type" : "git",
|
||||
"url" : "https://github.com/alexfoxy/laxxx"
|
||||
},
|
||||
"main": "lib/lax.min.js",
|
||||
"keywords": [
|
||||
"javascript",
|
||||
"parallax",
|
||||
"scroll",
|
||||
"animation",
|
||||
"effects",
|
||||
"css",
|
||||
"html"
|
||||
]
|
||||
}
|
||||
"name": "lax.js",
|
||||
"version": "2.0.3",
|
||||
"scripts": {
|
||||
"build": "babel src -d lib && uglifyjs lib/lax.js -o lib/lax.min.js -c -m && gzip -c lib/lax.min.js > lib/lax.min.js.gz && cp lib/lax.min.js docs/lib/lax.min.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.12.1",
|
||||
"@babel/core": "^7.12.3",
|
||||
"@babel/plugin-proposal-class-properties": "^7.12.1",
|
||||
"@babel/preset-env": "^7.12.1",
|
||||
"uglify-js": "^3.9.4"
|
||||
},
|
||||
"description": "Simple & lightweight (<4kb gzipped) vanilla JavaScript library to create smooth & beautiful animations when you scroll.",
|
||||
"license": "MIT",
|
||||
"author": "alexfoxy@gmail.com",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/alexfoxy/lax.js"
|
||||
},
|
||||
"main": "lib/lax.min.js",
|
||||
"keywords": [
|
||||
"javascript",
|
||||
"lax",
|
||||
"laxxx",
|
||||
"lax.js",
|
||||
"laxjs",
|
||||
"parallax",
|
||||
"scroll",
|
||||
"animation",
|
||||
"effects",
|
||||
"css",
|
||||
"html"
|
||||
],
|
||||
"dependencies": {}
|
||||
}
|
||||
813
src/lax.js
@@ -1,261 +1,616 @@
|
||||
//
|
||||
// lax v0.0.1 (Alex Fox)
|
||||
// Simple & light weight vanilla javascript plugin to create beautiful animations things when you scrolllll!!
|
||||
//
|
||||
// Licensed under the terms of the MIT license.
|
||||
//
|
||||
// You may use it in your theme if you credit me.
|
||||
// It is also free to use on any individual website.
|
||||
//
|
||||
// Exception:
|
||||
// The only restriction is to not publish any
|
||||
// extension for browsers or native application
|
||||
// without getting a written permission first.
|
||||
//
|
||||
(() => {
|
||||
const inOutMap = (y = 30) => {
|
||||
return ["elInY+elHeight", `elCenterY-${y}`, "elCenterY", `elCenterY+${y}`, "elOutY-elHeight"]
|
||||
}
|
||||
|
||||
(function() {
|
||||
var lax = (function() {
|
||||
var lax = {
|
||||
elements: []
|
||||
}
|
||||
const laxPresets = {
|
||||
fadeInOut: (y = 30, str = 0) => ({
|
||||
"opacity": [
|
||||
inOutMap(y),
|
||||
[str, 1, 1, 1, str]
|
||||
],
|
||||
}),
|
||||
fadeIn: (y = 'elCenterY', str = 0) => ({
|
||||
"opacity": [
|
||||
["elInY+elHeight", y],
|
||||
[str, 1],
|
||||
],
|
||||
}),
|
||||
fadeOut: (y = 'elCenterY', str = 0) => ({
|
||||
"opacity": [
|
||||
[y, "elOutY-elHeight"],
|
||||
[1, str],
|
||||
],
|
||||
}),
|
||||
blurInOut: (y = 100, str = 20) => ({
|
||||
"blur": [
|
||||
inOutMap(y),
|
||||
[str, 0, 0, 0, str],
|
||||
],
|
||||
}),
|
||||
blurIn: (y = 'elCenterY', str = 20) => ({
|
||||
"blur": [
|
||||
["elInY+elHeight", y],
|
||||
[str, 0],
|
||||
],
|
||||
}),
|
||||
blurOut: (y = 'elCenterY', str = 20) => ({
|
||||
"opacity": [
|
||||
[y, "elOutY-elHeight"],
|
||||
[0, str],
|
||||
],
|
||||
}),
|
||||
scaleInOut: (y = 100, str = 0.6) => ({
|
||||
"scale": [
|
||||
inOutMap(y),
|
||||
[str, 1, 1, 1, str],
|
||||
],
|
||||
}),
|
||||
scaleIn: (y = 'elCenterY', str = 0.6) => ({
|
||||
"scale": [
|
||||
["elInY+elHeight", y],
|
||||
[str, 1],
|
||||
],
|
||||
}),
|
||||
scaleOut: (y = 'elCenterY', str = 0.6) => ({
|
||||
"scale": [
|
||||
[y, "elOutY-elHeight"],
|
||||
[1, str],
|
||||
],
|
||||
}),
|
||||
slideX: (y = 0, str = 500) => ({
|
||||
"translateX": [
|
||||
['elInY', y],
|
||||
[0, str],
|
||||
],
|
||||
}),
|
||||
slideY: (y = 0, str = 500) => ({
|
||||
"translateY": [
|
||||
['elInY', y],
|
||||
[0, str],
|
||||
],
|
||||
}),
|
||||
spin: (y = 1000, str = 360) => ({
|
||||
"rotate": [
|
||||
[0, y],
|
||||
[0, str],
|
||||
{
|
||||
modValue: y,
|
||||
}
|
||||
],
|
||||
}),
|
||||
flipX: (y = 1000, str = 360) => ({
|
||||
"rotateX": [
|
||||
[0, y],
|
||||
[0, str],
|
||||
{
|
||||
modValue: y
|
||||
}
|
||||
],
|
||||
}),
|
||||
flipY: (y = 1000, str = 360) => ({
|
||||
"rotateY": [
|
||||
[0, y],
|
||||
[0, str],
|
||||
{
|
||||
modValue: y
|
||||
}
|
||||
],
|
||||
}),
|
||||
jiggle: (y = 50, str = 40) => ({
|
||||
"skewX": [
|
||||
[0, y * 1, y * 2, y * 3, y * 4],
|
||||
[0, str, 0, -str, 0],
|
||||
{
|
||||
modValue: y * 4,
|
||||
}
|
||||
],
|
||||
}),
|
||||
seesaw: (y = 50, str = 40) => ({
|
||||
"skewY": [
|
||||
[0, y * 1, y * 2, y * 3, y * 4],
|
||||
[0, str, 0, -str, 0],
|
||||
{
|
||||
modValue: y * 4,
|
||||
}
|
||||
],
|
||||
}),
|
||||
zigzag: (y = 100, str = 100) => ({
|
||||
"translateX": [
|
||||
[0, y * 1, y * 2, y * 3, y * 4],
|
||||
[0, str, 0, -str, 0],
|
||||
{
|
||||
modValue: y * 4,
|
||||
}
|
||||
],
|
||||
}),
|
||||
hueRotate: (y = 600, str = 360) => ({
|
||||
"hue-rotate": [
|
||||
[0, y],
|
||||
[0, str],
|
||||
{
|
||||
modValue: y,
|
||||
}
|
||||
],
|
||||
}),
|
||||
}
|
||||
|
||||
const transforms = {
|
||||
"data-lax-opacity": function(style, v) { style.opacity = v },
|
||||
"data-lax-translate": function(style, v) { style.transform += ` translate(${v}px, ${v}px)` },
|
||||
"data-lax-translate-x": function(style, v) { style.transform += ` translateX(${v}px)` },
|
||||
"data-lax-translate-y": function(style, v) { style.transform += ` translateY(${v}px)` },
|
||||
"data-lax-scale": function(style, v) { style.transform += ` scale(${v})` },
|
||||
"data-lax-scale-x": function(style, v) { style.transform += ` scaleX(${v})` },
|
||||
"data-lax-scale-y": function(style, v) { style.transform += ` scaleY(${v})` },
|
||||
"data-lax-skew": function(style, v) { style.transform += ` skew(${v}deg, ${v}deg)` },
|
||||
"data-lax-skew-x": function(style, v) { style.transform += ` skewX(${v}deg)` },
|
||||
"data-lax-skew-y": function(style, v) { style.transform += ` skewY(${v}deg)` },
|
||||
"data-lax-rotate": function(style, v) { style.transform += ` rotate(${v}deg)` },
|
||||
const laxInstance = (() => {
|
||||
const transformKeys = ["perspective", "scaleX", "scaleY", "scale", "skewX", "skewY", "skew", "rotateX", "rotateY", "rotate"]
|
||||
const filterKeys = ["blur", "hue-rotate", "brightness"]
|
||||
const translate3dKeys = ["translateX", "translateY", "translateZ"]
|
||||
|
||||
"data-lax-brightness": function(style, v) { style.filter += ` brightness(${v}%)` },
|
||||
"data-lax-contrast": function(style, v) { style.filter += ` contrast(${v}%)` },
|
||||
"data-lax-hue-rotate": function(style, v) { style.filter += ` hue-rotate(${v}deg)` },
|
||||
"data-lax-blur": function(style, v) { style.filter += ` blur(${v}px)` },
|
||||
"data-lax-invert": function(style, v) { style.filter += ` invert(${v}%)` },
|
||||
"data-lax-saturate": function(style, v) { style.filter += ` saturate(${v}%)` },
|
||||
"data-lax-grayscale": function(style, v) { style.filter += ` grayscale(${v}%)` },
|
||||
}
|
||||
const pxUnits = ["perspective", "border-radius", "blur", "translateX", "translateY", "translateZ"]
|
||||
const degUnits = ["hue-rotate", "rotate", "rotateX", "rotateY", "skew", "skewX", "skewY"]
|
||||
|
||||
let crazy = ""
|
||||
function getArrayValues(arr, windowWidth) {
|
||||
if (Array.isArray(arr)) return arr
|
||||
|
||||
for(var i=0;i<100;i++) {
|
||||
crazy += " " + (window.innerHeight*(i/100)) + " " + (Math.random() * 360) + ", "
|
||||
}
|
||||
const keys = Object.keys(arr).map(x => parseInt(x)).sort((a, b) => a > b ? 1 : -1)
|
||||
|
||||
lax.presets = {
|
||||
linger: (v) => {
|
||||
return { "data-lax-translate-y": `(vh*0.7) 0, 0 200, -500 0` }
|
||||
},
|
||||
lazy: (v=100) => {
|
||||
return { "data-lax-translate-y": `(vh) 0, (-elh) ${v}` }
|
||||
},
|
||||
eager: (v=100) => {
|
||||
return { "data-lax-translate-y": `(vh) 0, (-elh) -${v}` }
|
||||
},
|
||||
slalom: (v=50) => {
|
||||
return { "data-lax-translate-x": `vh ${v}, (vh*0.8) ${-v}, (vh*0.6) ${v}, (vh*0.4) ${-v}, (vh*0.2) ${v}, (vh*0) ${-v}, (-elh) ${v}` }
|
||||
},
|
||||
crazy: (v) => {
|
||||
return { "data-lax-hue-rotate": crazy }
|
||||
},
|
||||
spin: (v=360) => {
|
||||
return { "data-lax-rotate": `(vh) 0, (-elh) ${v}` }
|
||||
},
|
||||
spinRev: (v=360) => {
|
||||
return { "data-lax-rotate": `(vh) 0, (-elh) ${-v}` }
|
||||
},
|
||||
spinIn: (v=360) => {
|
||||
return { "data-lax-rotate": `vh ${v}, (vh*0.5) 0` }
|
||||
},
|
||||
spinOut: (v=360) => {
|
||||
return { "data-lax-rotate": `(vh*0.5) 0, -elh ${v}` }
|
||||
},
|
||||
blurInOut: (v=40) => {
|
||||
return { "data-lax-blur": `(vh) ${v}, (vh*0.8) 0, (vh*0.2) 0, 0 ${v}` }
|
||||
},
|
||||
blurIn: (v=40) => {
|
||||
return { "data-lax-blur": `(vh) ${v}, (vh*0.7) 0` }
|
||||
},
|
||||
blurOut: (v=40) => {
|
||||
return { "data-lax-blur": `(vh*0.3) 0, 0 ${v}` }
|
||||
},
|
||||
fadeInOut: () => {
|
||||
return { "data-lax-opacity": "(vh) 0, (vh*0.8) 1, (vh*0.2) 1, 0 0" }
|
||||
},
|
||||
fadeIn: () => {
|
||||
return { "data-lax-opacity": "(vh) 0, (vh*0.7) 1" }
|
||||
},
|
||||
fadeOut: () => {
|
||||
return { "data-lax-opacity": "(vh*0.3) 1, 0 0" }
|
||||
},
|
||||
driftLeft: (v=100) => {
|
||||
return { "data-lax-translate-x": `vh ${v}, -elh ${-v}` }
|
||||
},
|
||||
driftRight: (v=100) => {
|
||||
return { "data-lax-translate-x": `vh ${-v}, -elh ${v}` }
|
||||
},
|
||||
leftToRight: (v=1) => {
|
||||
return { "data-lax-translate-x": `vh 0, -elh (vw*${v})` }
|
||||
},
|
||||
rightToLeft: (v=1) => {
|
||||
return { "data-lax-translate-x": `vh 0, -elh (vw*${-v})` }
|
||||
},
|
||||
zoomInOut: (v=0.2) => {
|
||||
return { "data-lax-scale": `(vh) ${v}, (vh*0.8) 1, (vh*0.2) 1, -elh ${v}` }
|
||||
},
|
||||
zoomIn: (v=0.2) => {
|
||||
return { "data-lax-scale": `(vh) ${v}, (vh*0.7) 1` }
|
||||
},
|
||||
zoomOut: (v=0.2) => {
|
||||
return { "data-lax-scale": `(vh*0.3) 1, -elh ${v}` }
|
||||
},
|
||||
}
|
||||
|
||||
lax.addPreset = (name, o) => {
|
||||
lax.presets[name] = o
|
||||
}
|
||||
|
||||
function intrp(table, v) {
|
||||
var i = 0
|
||||
|
||||
while(table[i][0] <= v && table[i+1] !== undefined) {
|
||||
i+=1
|
||||
let retKey = keys[keys.length - 1]
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i]
|
||||
if (windowWidth < key) {
|
||||
retKey = key
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var x = table[i][0]
|
||||
var prevX = table[i-1] === undefined ? x : table[i-1][0]
|
||||
|
||||
var y = table[i][1]
|
||||
var prevY = table[i-1] === undefined ? y : table[i-1][1]
|
||||
|
||||
var xPoint = Math.min(Math.max((v-prevX)/(x-prevX),0),1)
|
||||
var yPoint = (xPoint*(y-prevY)) + prevY
|
||||
|
||||
return yPoint
|
||||
return arr[retKey]
|
||||
}
|
||||
|
||||
lax.setup = function(o) {
|
||||
lax.populateElements()
|
||||
function lerp(start, end, t) {
|
||||
return start * (1 - t) + end * t
|
||||
}
|
||||
|
||||
lax.populateElements = function() {
|
||||
lax.elements = []
|
||||
function invlerp(a, b, v) {
|
||||
return (v - a) / (b - a)
|
||||
}
|
||||
|
||||
function interpolate(arrA, arrB, v, easingFn) {
|
||||
let k = 0
|
||||
|
||||
arrA.forEach((a) => {
|
||||
if (a < v) k++
|
||||
})
|
||||
|
||||
if (k <= 0) {
|
||||
return arrB[0]
|
||||
}
|
||||
|
||||
if (k >= arrA.length) {
|
||||
return arrB[arrA.length - 1]
|
||||
}
|
||||
|
||||
const j = k - 1
|
||||
|
||||
let vector = invlerp(arrA[j], arrA[k], v)
|
||||
if (easingFn) vector = easingFn(vector)
|
||||
const lerpVal = lerp(arrB[j], arrB[k], vector)
|
||||
return lerpVal
|
||||
}
|
||||
|
||||
const easings = {
|
||||
easeInQuad: t => t * t,
|
||||
easeOutQuad: t => t * (2 - t),
|
||||
easeInOutQuad: t => t < .5 ? 2 * t * t : -1 + (4 - 2 * t) * t,
|
||||
easeInCubic: t => t * t * t,
|
||||
easeOutCubic: t => (--t) * t * t + 1,
|
||||
easeInOutCubic: t => t < .5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1,
|
||||
easeInQuart: t => t * t * t * t,
|
||||
easeOutQuart: t => 1 - (--t) * t * t * t,
|
||||
easeInOutQuart: t => t < .5 ? 8 * t * t * t * t : 1 - 8 * (--t) * t * t * t,
|
||||
easeInQuint: t => t * t * t * t * t,
|
||||
easeOutQuint: t => 1 + (--t) * t * t * t * t,
|
||||
easeInOutQuint: t => t < .5 ? 16 * t * t * t * t * t : 1 + 16 * (--t) * t * t * t * t,
|
||||
easeOutBounce: t => {
|
||||
const n1 = 7.5625
|
||||
const d1 = 2.75
|
||||
|
||||
if (t < 1 / d1) {
|
||||
return n1 * t * t
|
||||
} else if (t < 2 / d1) {
|
||||
return n1 * (t -= 1.5 / d1) * t + 0.75
|
||||
} else if (t < 2.5 / d1) {
|
||||
return n1 * (t -= 2.25 / d1) * t + 0.9375
|
||||
} else {
|
||||
return n1 * (t -= 2.625 / d1) * t + 0.984375
|
||||
}
|
||||
},
|
||||
easeInBounce: t => {
|
||||
return 1 - easings.easeOutBounce(1 - t)
|
||||
},
|
||||
easeOutBack: t => {
|
||||
const c1 = 1.70158
|
||||
const c3 = c1 + 1
|
||||
|
||||
return 1 + c3 * Math.pow(t - 1, 3) + c1 * Math.pow(t - 1, 2)
|
||||
},
|
||||
easeInBack: t => {
|
||||
const c1 = 1.70158
|
||||
const c3 = c1 + 1
|
||||
|
||||
return c3 * t * t * t - c1 * t * t
|
||||
},
|
||||
}
|
||||
|
||||
function flattenStyles(styles) {
|
||||
const flattenedStyles = {
|
||||
transform: '',
|
||||
filter: ''
|
||||
}
|
||||
|
||||
const translate3dValues = {
|
||||
translateX: 0.00001,
|
||||
translateY: 0.00001,
|
||||
translateZ: 0.00001
|
||||
}
|
||||
|
||||
Object.keys(styles).forEach((key) => {
|
||||
const val = styles[key]
|
||||
const unit = pxUnits.includes(key) ? 'px' : (degUnits.includes(key) ? 'deg' : '')
|
||||
|
||||
if (translate3dKeys.includes(key)) {
|
||||
translate3dValues[key] = val
|
||||
} else if (transformKeys.includes(key)) {
|
||||
flattenedStyles.transform += `${key}(${val}${unit}) `
|
||||
} else if (filterKeys.includes(key)) {
|
||||
flattenedStyles.filter += `${key}(${val}${unit}) `
|
||||
} else {
|
||||
flattenedStyles[key] = `${val}${unit} `
|
||||
}
|
||||
})
|
||||
|
||||
flattenedStyles.transform = `translate3d(${translate3dValues.translateX}px, ${translate3dValues.translateY}px, ${translate3dValues.translateZ}px) ${flattenedStyles.transform}`
|
||||
|
||||
return flattenedStyles
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Window/scrollY#Notes
|
||||
function getScrollPosition() {
|
||||
const supportPageOffset = window.pageXOffset !== undefined
|
||||
const isCSS1Compat = ((document.compatMode || '') === 'CSS1Compat')
|
||||
|
||||
const x = supportPageOffset ? window.pageXOffset : isCSS1Compat ? document.documentElement.scrollLeft : document.body.scrollLeft
|
||||
const y = supportPageOffset ? window.pageYOffset : isCSS1Compat ? document.documentElement.scrollTop : document.body.scrollTop
|
||||
|
||||
return [y, x]
|
||||
}
|
||||
|
||||
function parseValue(val, { width, height, x, y }, index) {
|
||||
if (typeof val === 'number') {
|
||||
return val
|
||||
}
|
||||
|
||||
const pageHeight = document.body.scrollHeight
|
||||
const pageWidth = document.body.scrollWidth
|
||||
const screenWidth = window.innerWidth
|
||||
const screenHeight = window.innerHeight
|
||||
const [scrollTop, scrollLeft] = getScrollPosition()
|
||||
|
||||
const left = x + scrollLeft
|
||||
const right = left + width
|
||||
const top = y + scrollTop
|
||||
const bottom = top + height
|
||||
|
||||
return Function(`return ${val
|
||||
.replace(/screenWidth/g, screenWidth)
|
||||
.replace(/screenHeight/g, screenHeight)
|
||||
.replace(/pageHeight/g, pageHeight)
|
||||
.replace(/pageWidth/g, pageWidth)
|
||||
.replace(/elWidth/g, width)
|
||||
.replace(/elHeight/g, height)
|
||||
.replace(/elInY/g, top - screenHeight)
|
||||
.replace(/elOutY/g, bottom)
|
||||
.replace(/elCenterY/g, top + (height / 2) - (screenHeight / 2))
|
||||
.replace(/elInX/g, left - screenWidth)
|
||||
.replace(/elOutX/g, right)
|
||||
.replace(/elCenterX/g, left + (width / 2) - (screenWidth / 2))
|
||||
.replace(/index/g, index)
|
||||
}`)()
|
||||
}
|
||||
|
||||
class LaxDriver {
|
||||
getValueFn
|
||||
name = ''
|
||||
lastValue = 0
|
||||
frameStep = 1
|
||||
m1 = 0
|
||||
|
||||
m2 = 0
|
||||
inertia = 0
|
||||
inertiaEnabled = false
|
||||
|
||||
|
||||
var selector = Object.keys(transforms).map(t => `[${t}]`).join(",")
|
||||
selector += ",[data-lax-preset]"
|
||||
constructor(name, getValueFn, options = {}) {
|
||||
this.name = name
|
||||
this.getValueFn = getValueFn
|
||||
|
||||
document.querySelectorAll(selector).forEach(function(el) {
|
||||
var o = {
|
||||
el: el,
|
||||
transforms: []
|
||||
Object.keys(options).forEach((key) => {
|
||||
this[key] = options[key]
|
||||
})
|
||||
|
||||
this.lastValue = this.getValueFn(0)
|
||||
}
|
||||
|
||||
getValue = (frame) => {
|
||||
let value = this.lastValue
|
||||
|
||||
if (frame % this.frameStep === 0) {
|
||||
value = this.getValueFn(frame)
|
||||
}
|
||||
|
||||
var presetNames = el.attributes["data-lax-preset"] && el.attributes["data-lax-preset"].value
|
||||
if(presetNames) {
|
||||
presetNames.split(" ").forEach((p) => {
|
||||
const bits = p.split("-")
|
||||
const fn = lax.presets[bits[0]]
|
||||
if(!fn) {
|
||||
console.error(`preset #{bits[0]} is not defined`)
|
||||
} else {
|
||||
const d = fn(bits[1])
|
||||
for(var k in d) {
|
||||
el.setAttribute(k, d[k])
|
||||
if (this.inertiaEnabled) {
|
||||
const delta = value - this.lastValue
|
||||
const damping = 0.8
|
||||
|
||||
this.m1 = this.m1 * damping + delta * (1 - damping)
|
||||
this.m2 = this.m2 * damping + this.m1 * (1 - damping)
|
||||
this.inertia = Math.round(this.m2 * 5000) / 15000
|
||||
}
|
||||
|
||||
this.lastValue = value
|
||||
return [this.lastValue, this.inertia]
|
||||
}
|
||||
}
|
||||
|
||||
class LaxElement {
|
||||
domElement
|
||||
transformsData
|
||||
styles = {}
|
||||
selector = ''
|
||||
|
||||
groupIndex = 0
|
||||
laxInstance
|
||||
|
||||
onUpdate
|
||||
|
||||
constructor(selector, laxInstance, domElement, transformsData, groupIndex = 0, options = {}) {
|
||||
this.selector = selector
|
||||
this.laxInstance = laxInstance
|
||||
this.domElement = domElement
|
||||
this.transformsData = transformsData
|
||||
this.groupIndex = groupIndex
|
||||
|
||||
const { style = {}, onUpdate } = options
|
||||
|
||||
Object.keys(style).forEach(key => {
|
||||
domElement.style.setProperty(key, style[key])
|
||||
})
|
||||
|
||||
if (onUpdate) this.onUpdate = onUpdate
|
||||
|
||||
this.calculateTransforms()
|
||||
}
|
||||
|
||||
update = (driverValues, frame) => {
|
||||
const { transforms } = this
|
||||
|
||||
const styles = {}
|
||||
|
||||
for (let driverName in transforms) {
|
||||
const styleBindings = transforms[driverName]
|
||||
|
||||
if (!driverValues[driverName]) {
|
||||
console.error("No lax driver with name: ", driverName)
|
||||
}
|
||||
|
||||
const [value, inertiaValue] = driverValues[driverName]
|
||||
|
||||
for (let key in styleBindings) {
|
||||
const [arr1, arr2, options = {}] = styleBindings[key]
|
||||
const { modValue, frameStep = 1, easing, inertia, inertiaMode, cssFn, cssUnit = '' } = options
|
||||
|
||||
const easingFn = easings[easing]
|
||||
|
||||
if (frame % frameStep === 0) {
|
||||
const v = modValue ? value % modValue : value
|
||||
|
||||
let interpolatedValue = interpolate(arr1, arr2, v, easingFn)
|
||||
|
||||
if (inertia) {
|
||||
let inertiaExtra = inertiaValue * inertia
|
||||
if (inertiaMode === 'absolute') inertiaExtra = Math.abs((inertiaExtra))
|
||||
interpolatedValue += inertiaExtra
|
||||
}
|
||||
|
||||
const unit = cssUnit || pxUnits.includes(key) ? 'px' : (degUnits.includes(key) ? 'deg' : '')
|
||||
const dp = unit === 'px' ? 0 : 3
|
||||
const val = interpolatedValue.toFixed(dp)
|
||||
styles[key] = cssFn ? cssFn(val, this.domElement) : val + cssUnit
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.applyStyles(styles)
|
||||
if (this.onUpdate) this.onUpdate(driverValues, this.domElement)
|
||||
}
|
||||
|
||||
calculateTransforms = () => {
|
||||
this.transforms = {}
|
||||
const windowWidth = this.laxInstance.windowWidth
|
||||
|
||||
for (let driverName in this.transformsData) {
|
||||
let styleBindings = this.transformsData[driverName]
|
||||
|
||||
const parsedStyleBindings = {}
|
||||
|
||||
const { presets = [] } = styleBindings
|
||||
|
||||
presets.forEach((presetString) => {
|
||||
|
||||
const [presetName, y, str] = presetString.split(":")
|
||||
|
||||
const presetFn = window.lax.presets[presetName]
|
||||
|
||||
if (!presetFn) {
|
||||
console.error("Lax preset cannot be found with name: ", presetName)
|
||||
} else {
|
||||
const preset = presetFn(y, str)
|
||||
|
||||
Object.keys(preset).forEach((key) => {
|
||||
styleBindings[key] = preset[key]
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
el.setAttribute("data-lax-anchor", "self")
|
||||
el.attributes.removeNamedItem("data-lax-preset")
|
||||
delete styleBindings.presets
|
||||
|
||||
for (let key in styleBindings) {
|
||||
let [arr1 = [-1e9, 1e9], arr2 = [-1e9, 1e9], options = {}] = styleBindings[key]
|
||||
|
||||
const saveTransform = this.domElement.style.transform
|
||||
this.domElement.style.removeProperty("transform")
|
||||
const bounds = this.domElement.getBoundingClientRect()
|
||||
this.domElement.style.transform = saveTransform
|
||||
|
||||
const parsedArr1 = getArrayValues(arr1, windowWidth).map(i => parseValue(i, bounds, this.groupIndex))
|
||||
const parsedArr2 = getArrayValues(arr2, windowWidth).map(i => parseValue(i, bounds, this.groupIndex))
|
||||
|
||||
parsedStyleBindings[key] = [parsedArr1, parsedArr2, options]
|
||||
}
|
||||
|
||||
this.transforms[driverName] = parsedStyleBindings
|
||||
}
|
||||
}
|
||||
|
||||
applyStyles = (styles) => {
|
||||
const mergedStyles = flattenStyles(styles)
|
||||
|
||||
Object.keys(mergedStyles).forEach((key) => {
|
||||
this.domElement.style.setProperty(key, mergedStyles[key])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
class Lax {
|
||||
drivers = []
|
||||
elements = []
|
||||
frame = 0
|
||||
|
||||
debug = false
|
||||
|
||||
windowWidth = 0
|
||||
windowHeight = 0
|
||||
presets = laxPresets
|
||||
|
||||
debugData = {
|
||||
frameLengths: []
|
||||
}
|
||||
|
||||
init = () => {
|
||||
this.findAndAddElements()
|
||||
|
||||
window.requestAnimationFrame(this.onAnimationFrame)
|
||||
this.windowWidth = document.body.clientWidth
|
||||
this.windowHeight = document.body.clientHeight
|
||||
|
||||
window.onresize = this.onWindowResize
|
||||
}
|
||||
|
||||
onWindowResize = () => {
|
||||
const changed = document.body.clientWidth !== this.windowWidth ||
|
||||
document.body.clientHeight !== this.windowHeight
|
||||
|
||||
if (changed) {
|
||||
this.windowWidth = document.body.clientWidth
|
||||
this.windowHeight = document.body.clientHeight
|
||||
this.elements.forEach(el => el.calculateTransforms())
|
||||
}
|
||||
}
|
||||
|
||||
onAnimationFrame = (e) => {
|
||||
if (this.debug) {
|
||||
this.debugData.frameStart = Date.now()
|
||||
}
|
||||
|
||||
const optimise = !(el.attributes["data-lax-optimize"] && el.attributes["data-lax-optimize"].value == 'false')
|
||||
if(optimise) el.style["-webkit-backface-visibility"] = "hidden"
|
||||
if(el.attributes["data-lax-optimize"]) el.attributes.removeNamedItem("data-lax-optimize")
|
||||
const driverValues = {}
|
||||
|
||||
for(var i=0; i<el.attributes.length; i++) {
|
||||
var a = el.attributes[i]
|
||||
var bits = a.name.split("-")
|
||||
if(bits[1] === "lax") {
|
||||
if(a.name === "data-lax-anchor") {
|
||||
o["data-lax-anchor"] = a.value === "self" ? el : document.querySelector(a.value)
|
||||
const rect = o["data-lax-anchor"].getBoundingClientRect()
|
||||
o["data-lax-anchor-top"] = Math.floor(rect.top) + window.scrollY
|
||||
} else {
|
||||
o.transforms[a.name] = a.value
|
||||
.replace(new RegExp('vw', 'g'), window.innerWidth)
|
||||
.replace(new RegExp('vh', 'g'), window.innerHeight)
|
||||
.replace(new RegExp('elh', 'g'), el.clientHeight)
|
||||
.replace(new RegExp('elw', 'g'), el.clientWidth)
|
||||
.replace(new RegExp('-vw', 'g'), -window.innerWidth)
|
||||
.replace(new RegExp('-vh', 'g'), -window.innerHeight)
|
||||
.replace(new RegExp('-elh', 'g'), -el.clientHeight)
|
||||
.replace(new RegExp('-elw', 'g'), -el.clientWidth).replace(/\s+/g," ")
|
||||
.split(",").map((x) => {
|
||||
return x.trim().split(" ").map(y => {
|
||||
if(y[0] === "(") return eval(y)
|
||||
else return parseFloat(y)
|
||||
})
|
||||
}).sort((a,b) => {
|
||||
return a[0] - b[0]
|
||||
})
|
||||
this.drivers.forEach((driver) => {
|
||||
driverValues[driver.name] = driver.getValue(this.frame)
|
||||
})
|
||||
|
||||
this.elements.forEach((element) => {
|
||||
element.update(driverValues, this.frame)
|
||||
})
|
||||
|
||||
if (this.debug) {
|
||||
this.debugData.frameLengths.push(Date.now() - this.debugData.frameStart)
|
||||
}
|
||||
|
||||
if (this.frame % 60 === 0 && this.debug) {
|
||||
const averageFrameTime = Math.ceil((this.debugData.frameLengths.reduce((a, b) => a + b, 0) / 60))
|
||||
console.log(`Average frame calculation time: ${averageFrameTime}ms`)
|
||||
this.debugData.frameLengths = []
|
||||
}
|
||||
|
||||
this.frame++
|
||||
|
||||
window.requestAnimationFrame(this.onAnimationFrame)
|
||||
}
|
||||
|
||||
addDriver = (name, getValueFn, options = {}) => {
|
||||
this.drivers.push(
|
||||
new LaxDriver(name, getValueFn, options)
|
||||
)
|
||||
}
|
||||
|
||||
removeDriver = (name) => {
|
||||
this.drivers = this.drivers.filter(driver => driver.name !== name)
|
||||
}
|
||||
|
||||
findAndAddElements = () => {
|
||||
this.elements = []
|
||||
const elements = document.querySelectorAll(".lax")
|
||||
|
||||
elements.forEach((domElement) => {
|
||||
const driverName = "scrollY"
|
||||
const presets = []
|
||||
|
||||
domElement.classList.forEach((className) => {
|
||||
if (className.includes("lax_preset")) {
|
||||
const preset = className.replace("lax_preset_", "")
|
||||
presets.push(preset)
|
||||
}
|
||||
})
|
||||
|
||||
const transforms = {
|
||||
[driverName]: {
|
||||
presets
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lax.elements.push(o)
|
||||
})
|
||||
this.elements.push(new LaxElement('.lax', this, domElement, transforms, 0, {}))
|
||||
})
|
||||
}
|
||||
|
||||
addElements = (selector, transforms, options = {}) => {
|
||||
const domElements = options.domElements || document.querySelectorAll(selector)
|
||||
|
||||
domElements.forEach((domElement, i) => {
|
||||
this.elements.push(new LaxElement(selector, this, domElement, transforms, i, options))
|
||||
})
|
||||
}
|
||||
|
||||
removeElements = (selector) => {
|
||||
this.elements = this.elements.filter(element => element.selector !== selector)
|
||||
}
|
||||
|
||||
addElement = (domElement, transforms, options) => {
|
||||
this.elements.push(new LaxElement('', this, domElement, transforms, 0, options))
|
||||
}
|
||||
|
||||
removeElement = (domElement) => {
|
||||
this.elements = this.elements.filter(element => element.domElement !== domElement)
|
||||
}
|
||||
}
|
||||
|
||||
var lastScroll = 0
|
||||
|
||||
lax.update = function(y) {
|
||||
var momentum = lastScroll-y
|
||||
lastScroll = y
|
||||
|
||||
lax.elements.forEach(function(o) {
|
||||
var transformString = ""
|
||||
var r = o["data-lax-anchor-top"] ? o["data-lax-anchor-top"]-y : y
|
||||
|
||||
var style = {
|
||||
transform: "",
|
||||
filter: ""
|
||||
}
|
||||
|
||||
for(var i in o.transforms) {
|
||||
var arr = o.transforms[i]
|
||||
var t = transforms[i]
|
||||
var v = intrp(arr, r)
|
||||
|
||||
if(!t) {
|
||||
console.error("lax: " + i + " is not supported")
|
||||
return
|
||||
}
|
||||
|
||||
t(style, v)
|
||||
}
|
||||
|
||||
for(let k in style) {
|
||||
if(style.opacity === 0) { // if opacity 0 don't update
|
||||
o.el.style.opacity = 0
|
||||
} else {
|
||||
o.el.style[k] = style[k]
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
return lax;
|
||||
})();
|
||||
return new Lax()
|
||||
})()
|
||||
|
||||
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined')
|
||||
module.exports = lax;
|
||||
module.exports = laxInstance
|
||||
else
|
||||
window.lax = lax;
|
||||
})();
|
||||
window.lax = laxInstance
|
||||
})()
|
||||
|
||||