Compare commits
88 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 |
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
|
||||
|
||||
553
README.md
@@ -1,292 +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 (<3kb gzipped) 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/)
|
||||
|
||||
[>>> MARIO DEMO <<<](https://alexfox.dev/laxxx/sprite.html)
|
||||
---
|
||||
## 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..
|
||||
|
||||

|
||||
## 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)
|
||||
|
||||
## Getting Started
|
||||
|
||||
# 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 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() // init
|
||||
lax.addDriver(
|
||||
'scrollY', // Driver name
|
||||
function(laxFrame) {
|
||||
return window.scrollY // Value method
|
||||
},
|
||||
{ } // Options
|
||||
)
|
||||
```
|
||||
|
||||
const updateLax = () => {
|
||||
lax.update(window.scrollY)
|
||||
window.requestAnimationFrame(updateLax)
|
||||
}
|
||||
### Driver options
|
||||
|
||||
window.requestAnimationFrame(updateLax)
|
||||
#### `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 class and attributes to the HTML tags you want to animate e.g.
|
||||
```html
|
||||
<p class="lax" 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).
|
||||
|
||||
### Usage With React, Vue.js, EmberJS & DOM Changes
|
||||
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.js or EmberJS, it is likely that you are adding elements after the initial `window.onload`. Because of this you will need to call `lax.addElement(domElement)` when you add components to the DOM that you want to animate.
|
||||
|
||||
See below for working examples:
|
||||
* [react](https://codepen.io/alexfoxy/pen/PLaKaE)
|
||||
* [react (using hooks)](https://github.com/arthurdenner/use-lax)
|
||||
* [vue](https://codepen.io/alexfoxy/pen/ZPRZBq)
|
||||
* [ember](https://github.com/redpencilio/ember-lax)
|
||||
|
||||
You can also call `lax.removeElement(domElement)` when the component unmounts.
|
||||
|
||||
|
||||
## Presets
|
||||
The easiest way to get started is to use the presets via the `data-lax-preset` attribute. You can chain multiple presets 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, ... | option=val` e.g:
|
||||
```html
|
||||
<p class="lax" data-lax-opacity="0 1, 100 1, 200 0 | loop=200">
|
||||
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 class="lax" 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
|
||||
|
||||
## Custom animations
|
||||
Custom animations are defined using an object.
|
||||
|
||||
```javascript
|
||||
// Animation data
|
||||
{
|
||||
scrollY: { // Driver name
|
||||
translateX: [ // CSS property
|
||||
['elInY', 'elOutY'], // Driver value map
|
||||
[0, 'screenWidth'], // Animation value map
|
||||
{
|
||||
inertia: 10 // Options
|
||||
}
|
||||
],
|
||||
opacity: [
|
||||
// etc
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Special Values
|
||||
### 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).
|
||||
|
||||
| Key | Value |
|
||||
| ------------- | ------------- |
|
||||
| vw | window.innerWidth |
|
||||
| vh | window.innerHeight |
|
||||
| elw | targetElement.clientWidth |
|
||||
| elh | targetElement.clientHeight |
|
||||
### 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).
|
||||
|
||||
You can use these instead of integer values for the scrollPos e.g.
|
||||
```html
|
||||
<p class="lax" 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>
|
||||
> 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 |
|
||||
```
|
||||
|
||||
### Calculated Values
|
||||
Within the maps you can use strings for simple formulas as well as use special values. e.g:
|
||||
|
||||
You can also use vanilla JS within `( )` for calculations and access to more variables e.g.
|
||||
```html
|
||||
<p class="lax" 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>
|
||||
```javascript
|
||||
['elInY', 'elCenterY-200', 'elCenterY',
|
||||
```
|
||||
|
||||
See a list of available values [here](#special-values).
|
||||
|
||||
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
|
||||
|
||||
You can pass options into your custom animations for more control e.g.
|
||||
#### `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.
|
||||
|
||||
```html
|
||||
<p class="lax" data-lax-opacity="0 1, 100 0, 200 100 | loop=200 offset=100 speed=2">
|
||||
I start at 0 opacity and
|
||||
fade in and out every 50px
|
||||
</p>
|
||||
```
|
||||
#### `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.
|
||||
|
||||
| Option | Effect |
|
||||
| ------------- | ------------- |
|
||||
| loop | modulus the input scrollY position so the animation will loop every `loop` pixels |
|
||||
| offset | add `offset` to scrollY position so the animation will begin at this point |
|
||||
| speed | multiplies the input scrollY position by `speed` to change the speed of the animation |
|
||||
#### `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).
|
||||
|
||||
|
||||
## Responsive Design
|
||||
You can set multiple presets and animations for different screen widths. When setting up lax you need to pass in your screen width breakpoints e.g.
|
||||
#### `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
|
||||
lax.setup({
|
||||
breakpoints: { small: 0, large: 992 }
|
||||
})
|
||||
```
|
||||
Then you can define presets or transforms per breakpoint.
|
||||
```html
|
||||
<p class="lax" data-lax-preset_small="spin">
|
||||
I only spin when the screen is smaller than 992px.
|
||||
</p>
|
||||
|
||||
<p class="lax" data-lax-scale_small="0 1, 500 0" data-lax-scale_large="0 1, 500 2">
|
||||
I shrink when the screen is smaller than 992px but grow when the screen is larger 992px.
|
||||
</p>
|
||||
// Box-shadow example
|
||||
(val) => {
|
||||
return `${val}px ${val}px ${val}px rgba(0,0,0,0.5)`;
|
||||
};
|
||||
```
|
||||
|
||||
## Supported Presets
|
||||
#### `easing: string`
|
||||
See a list of available values [here](#supported-easings).
|
||||
|
||||
| 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 |
|
||||
| swing | 30 |
|
||||
| speedy | 30 |
|
||||
## 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.
|
||||
|
||||
## Supported Attribute Keys
|
||||
# Glossary
|
||||
|
||||
Transforms
|
||||
## CSS properties
|
||||
|
||||
| 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 |
|
||||
| rotateX | data-lax-rotate-x |
|
||||
| rotateY | data-lax-rotate-y |
|
||||
| name |
|
||||
| ---------- |
|
||||
| opacity |
|
||||
| scaleX |
|
||||
| scaleY |
|
||||
| scale |
|
||||
| skewX |
|
||||
| skewY |
|
||||
| skew |
|
||||
| rotateX |
|
||||
| rotateY |
|
||||
| rotate |
|
||||
| translateX |
|
||||
| translateY |
|
||||
| translateZ |
|
||||
| blur |
|
||||
| hue-rotate |
|
||||
| brightness |
|
||||
|
||||
Filters (note - these may be unperformant on low powered machines)
|
||||
## Special values
|
||||
|
||||
| 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 |
|
||||
| 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` |
|
||||
|
||||
Other
|
||||
|
||||
| Filter | Key |
|
||||
| ------------- | ------------- |
|
||||
| background position | data-lax-bg-pos |
|
||||
| background position-x | data-lax-bg-pos-x |
|
||||
| background position-y | data-lax-bg-pos-y |
|
||||
|
||||
|
||||
|
||||
## Sprite Sheet Animations
|
||||
You can create animations using sprite sheets. See a demo [here](https://alexfox.dev/laxxx/sprite.html).
|
||||
|
||||
The `data-lax-sprite-data` is required and formated like so `[frameWidth, frameHeight, frameCount, columnCount, scrollStep]`. You can either set the image using CSS or the `data-lax-sprite-image` attribute. e.g.
|
||||
|
||||
```html
|
||||
<div
|
||||
class="lax"
|
||||
data-lax-sprite-data="500,500,36,36,10"
|
||||
data-lax-sprite-image="./spritesheet.png"
|
||||
/>
|
||||
```
|
||||
|
||||
You can turn a gif or a video into a sprite sheet with this tool: https://ezgif.com/gif-to-sprite
|
||||
|
||||
Note: current implimentation requires the element to be the same size as the frame width & height.
|
||||
|
||||
## 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 class="lax" data-lax-preset="myCoolPreset">
|
||||
I'm the coolest preset in the world 😎
|
||||
</p>
|
||||
```
|
||||
|
||||
## Performance Tips
|
||||
|
||||
* Avoid nesting lax enabled elements within each other, you'll get better performance using lax with smaller elements in the dom tree.
|
||||
* Avoid transforms on large elements, e.g. full screen backgrounds.
|
||||
* By default elements that have opacity 0 aren't updated. You can either manually set up a `data-lax-opacity` to control this yourself or use `data-lax-optimize` which will set the elements opacity to 0 when it goes off -screen.
|
||||
* 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 `data-lax-use-gpu="false"` to your element.
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
### 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.
|
||||
```
|
||||
window.addEventListener("resize", function() {
|
||||
lax.updateElements()
|
||||
});
|
||||
```
|
||||
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 increment the scroll position in steps which can cause the animations to look janky.
|
||||
|
||||
### Merging Existing Styles
|
||||
Only inline styles for transforms and filters will be merged in to the animation. Transforms and filters derived from CSS will be overwritten.
|
||||
|
||||
## To Do / Ideas
|
||||
* Implement a tween for scroll wheels to remove reliance on smoothscroll
|
||||
* A way to add weight/momentum to moving objecs
|
||||
* More cool demos
|
||||
* More concise read me for react / vue.js
|
||||
* ~~Support for sprite sheet animations~~
|
||||
## Supported easings
|
||||
|
||||
| name |
|
||||
| -------------- |
|
||||
| easeInQuad |
|
||||
| easeOutQuad |
|
||||
| easeInOutQuad |
|
||||
| easeInCubic |
|
||||
| easeOutCubic |
|
||||
| easeInOutCubic |
|
||||
| easeInQuart |
|
||||
| easeOutQuart |
|
||||
| easeInOutQuart |
|
||||
| easeInQuint |
|
||||
| easeOutQuint |
|
||||
| easeInOutQuint |
|
||||
| easeOutBounce |
|
||||
| easeInBounce |
|
||||
| easeOutBack |
|
||||
| easeInBack |
|
||||
|
||||
@@ -1,69 +0,0 @@
|
||||
<!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">
|
||||
|
||||
<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"></script>
|
||||
<!-- <script src="../src/lax.js"></script> -->
|
||||
|
||||
<script type="text/javascript">
|
||||
window.onload = function() {
|
||||
document.getElementById("main").classList.add("loaded")
|
||||
|
||||
lax.setup()
|
||||
|
||||
let toAdd = 0
|
||||
let lastY = 0
|
||||
const scrolls = [0,0,0,0,0]
|
||||
const update = () => {
|
||||
scrolls.unshift()
|
||||
if(lastY !== window.scrollY) scrolls.push(window.scrollY)
|
||||
lastY = window.scrollY
|
||||
const sum = scrolls.reduce(function(a, b) { return a + b; });
|
||||
const y = sum / scrolls.length
|
||||
|
||||
lax.update(y)
|
||||
window.requestAnimationFrame(update)
|
||||
}
|
||||
|
||||
window.requestAnimationFrame(update)
|
||||
|
||||
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
body,html {
|
||||
margin:0;
|
||||
padding:0;
|
||||
color: #F3F4F5;
|
||||
background: #191818;
|
||||
overflow-x: hidden;
|
||||
height: 10000vh;
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
}
|
||||
|
||||
#letter {
|
||||
width: 10vw;
|
||||
margin-left: 10vw;
|
||||
position: fixed;
|
||||
top: 20vh;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
|
||||
<body>
|
||||
<div id="main">
|
||||
<div id="header" class="section">
|
||||
<img src="./img/a.png" id="letter" data-lax-translate-x="0 0, 1000 1000" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</body>
|
||||
@@ -1,5 +0,0 @@
|
||||
// stats.js - http://github.com/mrdoob/stats.js
|
||||
(function(f,e){"object"===typeof exports&&"undefined"!==typeof module?module.exports=e():"function"===typeof define&&define.amd?define(e):f.Stats=e()})(this,function(){var f=function(){function e(a){c.appendChild(a.dom);return a}function u(a){for(var d=0;d<c.children.length;d++)c.children[d].style.display=d===a?"block":"none";l=a}var l=0,c=document.createElement("div");c.style.cssText="position:fixed;top:0;left:0;cursor:pointer;opacity:0.9;z-index:10000";c.addEventListener("click",function(a){a.preventDefault();
|
||||
u(++l%c.children.length)},!1);var k=(performance||Date).now(),g=k,a=0,r=e(new f.Panel("FPS","#0ff","#002")),h=e(new f.Panel("MS","#0f0","#020"));if(self.performance&&self.performance.memory)var t=e(new f.Panel("MB","#f08","#201"));u(0);return{REVISION:16,dom:c,addPanel:e,showPanel:u,begin:function(){k=(performance||Date).now()},end:function(){a++;var c=(performance||Date).now();h.update(c-k,200);if(c>g+1E3&&(r.update(1E3*a/(c-g),100),g=c,a=0,t)){var d=performance.memory;t.update(d.usedJSHeapSize/
|
||||
1048576,d.jsHeapSizeLimit/1048576)}return c},update:function(){k=this.end()},domElement:c,setMode:u}};f.Panel=function(e,f,l){var c=Infinity,k=0,g=Math.round,a=g(window.devicePixelRatio||1),r=80*a,h=48*a,t=3*a,v=2*a,d=3*a,m=15*a,n=74*a,p=30*a,q=document.createElement("canvas");q.width=r;q.height=h;q.style.cssText="width:80px;height:48px";var b=q.getContext("2d");b.font="bold "+9*a+"px Helvetica,Arial,sans-serif";b.textBaseline="top";b.fillStyle=l;b.fillRect(0,0,r,h);b.fillStyle=f;b.fillText(e,t,v);
|
||||
b.fillRect(d,m,n,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d,m,n,p);return{dom:q,update:function(h,w){c=Math.min(c,h);k=Math.max(k,h);b.fillStyle=l;b.globalAlpha=1;b.fillRect(0,0,r,m);b.fillStyle=f;b.fillText(g(h)+" "+e+" ("+g(c)+"-"+g(k)+")",t,v);b.drawImage(q,d+a,m,n-a,p,d,m,n-a,p);b.fillRect(d+n-a,m,a,p);b.fillStyle=l;b.globalAlpha=.9;b.fillRect(d+n-a,m,a,g((1-h/w)*p))}}};return f});
|
||||
@@ -1,66 +0,0 @@
|
||||
<!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">
|
||||
<script src="../src/lax.js"></script>
|
||||
<script src="./js/stats.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
|
||||
window.onload = function() {
|
||||
var stats = new Stats();
|
||||
stats.showPanel( 0 ); // 0: fps, 1: ms, 2: mb, 3+: custom
|
||||
document.body.appendChild( stats.dom );
|
||||
|
||||
for(let i=0; i<1000; i++) {
|
||||
const el = document.createElement("img")
|
||||
el.src="../docs/img/a.png"
|
||||
el.className="lax"
|
||||
el.style.width = 50 + 'px'
|
||||
el.style.height = 50 + 'px'
|
||||
el.style.left = 50*(i%25) + 'px'
|
||||
el.style.top = 50*Math.floor(i/25) + 'px'
|
||||
el.style.position = "absolute"
|
||||
el.setAttribute("data-lax-rotate","0 0, 500 1000")
|
||||
el.setAttribute("data-lax-opacity","0 1, 500 0")
|
||||
el.setAttribute("data-lax-scale","0 1, 500 0.5")
|
||||
document.body.appendChild(el)
|
||||
}
|
||||
|
||||
lax.setup()
|
||||
|
||||
let i = 0
|
||||
const update = () => {
|
||||
stats.begin();
|
||||
lax.update(i%500)
|
||||
i++
|
||||
stats.end();
|
||||
window.requestAnimationFrame(update)
|
||||
}
|
||||
|
||||
window.requestAnimationFrame(update)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
body,html {
|
||||
margin:0;
|
||||
padding:0;
|
||||
color: #F3F4F5;
|
||||
background: #191818;
|
||||
overflow-x: hidden;
|
||||
height: 500vh;
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
}
|
||||
|
||||
img {
|
||||
position: fixed;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
</body>
|
||||
@@ -1,90 +0,0 @@
|
||||
<!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">
|
||||
|
||||
<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"></script> -->
|
||||
<script src="../src/lax.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.onload = function() {
|
||||
document.getElementById("main").classList.add("loaded")
|
||||
|
||||
lax.setup({
|
||||
breakpoints: { xs: 0, s: 576, m: 768, l: 992 }
|
||||
})
|
||||
|
||||
const update = () => {
|
||||
lax.update(window.scrollY)
|
||||
window.requestAnimationFrame(update)
|
||||
}
|
||||
|
||||
window.requestAnimationFrame(update)
|
||||
|
||||
let w = window.innerWidth
|
||||
window.addEventListener("resize", function() {
|
||||
if(w !== window.innerWidth) {
|
||||
lax.populateElements()
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
body,html {
|
||||
margin:0;
|
||||
padding:0;
|
||||
color: #F3F4F5;
|
||||
background: #191818;
|
||||
overflow-x: hidden;
|
||||
height: 500vh;
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
}
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
#header img {
|
||||
width: 78pt;
|
||||
margin-top: 12pt;
|
||||
position: fixed;
|
||||
transform: scale(0.5)
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="main">
|
||||
<div id="header" class="section">
|
||||
<img src="./img/a.png"
|
||||
class="lax"
|
||||
data-lax-scale="0 1, vh 5"
|
||||
data-lax-scale_s="0 1, vh 1"
|
||||
data-lax-preset_s="fadeInOut"
|
||||
data-lax-scale_xs="0 1, vh 0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
@@ -1,64 +0,0 @@
|
||||
<!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">
|
||||
|
||||
|
||||
<!-- <script src="./lib/lax.min.js"></script> -->
|
||||
<script src="../src/lax.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
window.onload = function() {
|
||||
document.getElementById("main").classList.add("loaded")
|
||||
|
||||
lax.setup()
|
||||
|
||||
let scrollPosTarget = 0
|
||||
let currentVal = 0
|
||||
const maxStep = 10
|
||||
|
||||
const update = () => {
|
||||
if(scrollPosTarget != window.scrollY) {
|
||||
scrollPosTarget = window.scrollY
|
||||
}
|
||||
|
||||
const d = scrollPosTarget - currentVal
|
||||
|
||||
currentVal += Math.abs(d) > maxStep ? (d > 0 ? maxStep : -maxStep) : d
|
||||
|
||||
lax.update(currentVal)
|
||||
window.requestAnimationFrame(update)
|
||||
}
|
||||
|
||||
window.requestAnimationFrame(update)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
body,html {
|
||||
margin:0;
|
||||
padding:0;
|
||||
color: #F3F4F5;
|
||||
background: #191818;
|
||||
overflow-x: hidden;
|
||||
height: 10000vh;
|
||||
}
|
||||
|
||||
#letter {
|
||||
width: 10vw;
|
||||
margin-left: 10vw;
|
||||
position: fixed;
|
||||
top: 20vh;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
|
||||
|
||||
<body>
|
||||
<div id="main">
|
||||
<div id="header" class="section">
|
||||
<img src="../docs/img/a.png" class="lax" id="letter" data-lax-translate-x="0 0, 1000 1000" />
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
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 |
|
Before Width: | Height: | Size: 122 KiB 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 |
158
docs/demo.html
@@ -1,158 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<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">
|
||||
|
||||
|
||||
<style>
|
||||
body,html {
|
||||
margin:0;
|
||||
padding:0;
|
||||
color: rgb(10,10,10);
|
||||
background: white;
|
||||
overflow-x: hidden;
|
||||
height: 10000vh;
|
||||
font-family: "Raleway", "HelveticaNeue", sans-serif;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size: 2rem;
|
||||
line-height: 1.35;
|
||||
letter-spacing: -.08rem;
|
||||
}
|
||||
|
||||
#main {
|
||||
opacity: 0;
|
||||
transition: opacity 200ms;
|
||||
}
|
||||
|
||||
#main.loaded {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#background {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.leaf1 {
|
||||
width: 200pt;
|
||||
height: 80pt;
|
||||
position: absolute;
|
||||
background: url('img/leaf1.svg') no-repeat;
|
||||
mix-blend-mode: multiply;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.leaf {
|
||||
width: 200pt;
|
||||
height: 100pt;
|
||||
position: absolute;
|
||||
mix-blend-mode: multiply;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
#center {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
width: 100vw;
|
||||
}
|
||||
|
||||
#avatar {
|
||||
width: 150pt;
|
||||
height: 150pt;
|
||||
background: gray;
|
||||
border-radius: 75pt;
|
||||
margin-top: 50pt
|
||||
}
|
||||
|
||||
#bio {
|
||||
width: 400pt;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
#bio p {
|
||||
font-size: 14pt;
|
||||
line-height: 26pt;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
.github-corner {
|
||||
z-index: 1000;
|
||||
display: block;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<!-- <script src="./lib/lax.min.js"></script> -->
|
||||
<script src="../src/lax.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
window.onload = function() {
|
||||
document.getElementById("main").classList.add("loaded")
|
||||
|
||||
lax.setup()
|
||||
|
||||
const update = () => {
|
||||
lax.update(window.scrollY)
|
||||
window.requestAnimationFrame(update)
|
||||
}
|
||||
|
||||
window.requestAnimationFrame(update)
|
||||
}
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
<body>
|
||||
<div id="main">
|
||||
<div id="background">
|
||||
<div
|
||||
class="lax leaf"
|
||||
style="transform: scale(1.5); background: url('img/leaf1.svg') no-repeat;"
|
||||
data-lax-rotate= "0 -20, 900 20, 2000 -20 | loop=2000 offset=200"
|
||||
data-lax-translate-x= "0 110, 800 -100, 900 -110, 1900 100, 2000 110 | loop=2000 offset=200"
|
||||
data-lax-translate-y= "0 -elh, (vh*10) (vh+elh) | loop=(vh*10) offset=1000 speed=1"
|
||||
></div>
|
||||
|
||||
<div
|
||||
class="lax leaf"
|
||||
style="transform: scale(2); background: url('img/leaf2.svg') no-repeat;"
|
||||
data-lax-rotate= "0 -20, 900 20, 2000 -20 | loop=2000 offset=500 speed=1.5"
|
||||
data-lax-translate-x= "0 110, 800 -100, 900 -110, 1900 100, 2000 110 | loop=2000 offset=500 speed=1.5"
|
||||
data-lax-translate-y= "0 -elh, (vh*10) (vh+elh) | loop=(vh*10) offset=1000 speed=1"
|
||||
></div>
|
||||
|
||||
<div
|
||||
class="lax leaf"
|
||||
style="transform: scale(1.2); background: url('img/leaf3.svg') no-repeat;"
|
||||
data-lax-rotate= "0 -20, 900 20, 2000 -20 | loop=2000 offset=1000"
|
||||
data-lax-translate-x= "0 110, 800 -100, 900 -110, 1900 100, 2000 110 | loop=2000 offset=1000"
|
||||
data-lax-translate-y= "0 -elh, (vh*10) (vh+elh) | loop=(vh*10) offset=400 speed=1"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div id="center">
|
||||
<div
|
||||
id="avatar"
|
||||
class="lax"
|
||||
/></div>
|
||||
|
||||
<div id="bio">
|
||||
<h4>Scroll Dowwwnn...</h4>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<a href="https://github.com/alexfoxy/laxxx" class="github-corner" aria-label="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:#000000; color:#ffffff; 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>
|
||||
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>
|
||||
BIN
docs/img/a.png
|
Before Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 137 KiB |
|
Before Width: | Height: | Size: 41 KiB |
|
Before Width: | Height: | Size: 27 KiB |
BIN
docs/img/l.png
|
Before Width: | Height: | Size: 237 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 155.56 54.07"><defs><style>.cls-1{fill:url(#linear-gradient);}</style><linearGradient id="linear-gradient" x1="17.69" y1="36.08" x2="165.43" y2="35.97" gradientTransform="translate(-20.45 21.43) rotate(-18.21)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#c135d4"/><stop offset="1" stop-color="#6c56e3"/></linearGradient></defs><title>leaf1</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M155.56,27c-17.91,16.44-46.08,27-77.77,27S17.91,43.47,0,27C17.91,10.59,46.09,0,77.79,0S137.65,10.59,155.56,27Z"/></g></g></svg>
|
||||
|
Before Width: | Height: | Size: 684 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 155.56 54.07"><defs><style>.cls-1{fill:url(#linear-gradient);}</style><linearGradient id="linear-gradient" x1="-18.84" y1="30.22" x2="128.9" y2="30.11" gradientTransform="translate(16.08 15.58) rotate(-18.21)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#d4b43d"/><stop offset="1" stop-color="#ff309e"/></linearGradient></defs><title>leaf2</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M155.56,27c-17.91,16.44-46.08,27-77.77,27S17.91,43.47,0,27C17.91,10.59,46.09,0,77.79,0S137.65,10.59,155.56,27Z"/></g></g></svg>
|
||||
|
Before Width: | Height: | Size: 683 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 155.56 54.07"><defs><style>.cls-1{fill:url(#linear-gradient);}</style><linearGradient id="linear-gradient" x1="-0.57" y1="33.15" x2="147.16" y2="33.04" gradientTransform="translate(-2.19 18.5) rotate(-18.21)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#008ad4"/><stop offset="1" stop-color="#57ffb6"/></linearGradient></defs><title>leaf3</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M155.56,27c-17.91,16.44-46.08,27-77.77,27S17.91,43.47,0,27C17.91,10.59,46.09,0,77.79,0S137.65,10.59,155.56,27Z"/></g></g></svg>
|
||||
|
Before Width: | Height: | Size: 682 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 155.56 54.07"><defs><style>.cls-1{fill:url(#linear-gradient);}</style><linearGradient id="linear-gradient" x1="-17.27" y1="74.53" x2="130.46" y2="74.42" gradientTransform="translate(0.75 -26.02) rotate(-18.21)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#393fe3"/><stop offset="1" stop-color="#16d7de"/></linearGradient></defs><title>leaf4</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M155.56,27c-17.91,16.44-46.08,27-77.77,27S17.91,43.47,0,27C17.91,10.59,46.09,0,77.79,0S137.65,10.59,155.56,27Z"/></g></g></svg>
|
||||
|
Before Width: | Height: | Size: 684 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 155.56 54.07"><defs><style>.cls-1{fill:url(#linear-gradient);}</style><linearGradient id="linear-gradient" x1="0.99" y1="77.45" x2="148.73" y2="77.34" gradientTransform="translate(-17.52 -23.09) rotate(-18.21)" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#cce371"/><stop offset="1" stop-color="#37e310"/></linearGradient></defs><title>leaf5</title><g id="Layer_2" data-name="Layer 2"><g id="Layer_1-2" data-name="Layer 1"><path class="cls-1" d="M155.56,27c-17.91,16.44-46.08,27-77.77,27S17.91,43.47,0,27C17.91,10.59,46.09,0,77.79,0S137.65,10.59,155.56,27Z"/></g></g></svg>
|
||||
|
Before Width: | Height: | Size: 684 B |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 27 KiB |
BIN
docs/img/x.png
|
Before Width: | Height: | Size: 2.3 KiB |
770
docs/index.html
@@ -1,378 +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"></script>
|
||||
<!-- <script src="../src/lax.js"></script> -->
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
window.onload = function() {
|
||||
document.getElementById("main").classList.add("loaded")
|
||||
window.scrollTo(0, 0)
|
||||
|
||||
lax.setup()
|
||||
window.onload = function () {
|
||||
|
||||
const update = () => {
|
||||
lax.update(window.scrollY)
|
||||
window.requestAnimationFrame(update)
|
||||
}
|
||||
lax.init()
|
||||
|
||||
window.requestAnimationFrame(update)
|
||||
lax.addDriver(
|
||||
"scrollY",
|
||||
function () {
|
||||
return document.documentElement.scrollTop;
|
||||
},
|
||||
{ frameStep: 1 }
|
||||
);
|
||||
|
||||
let w = window.innerWidth
|
||||
window.addEventListener("resize", function() {
|
||||
if(w !== window.innerWidth) {
|
||||
lax.updateElements()
|
||||
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.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 50pt;
|
||||
background: url(img/button-bg.jpg);
|
||||
background-size: 100pt;
|
||||
border-radius: 10pt;
|
||||
font-weight: 600;
|
||||
color: black !important;
|
||||
}
|
||||
|
||||
a {
|
||||
line-height: 70px;
|
||||
font-size: 30px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: black !important;
|
||||
}
|
||||
|
||||
.button:hover {
|
||||
background: white;
|
||||
color: black !important;
|
||||
position: absolute;
|
||||
top: 425vh;
|
||||
border-radius: 20px;
|
||||
left: 50vw;
|
||||
margin-left: -125px;
|
||||
z-index: 100;
|
||||
pointer-events: all;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.github-corner {
|
||||
z-index: 1000;
|
||||
display: block;
|
||||
.bottombg {
|
||||
background-color: #8d77ed;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
top: 380vh;
|
||||
z-index: 50;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
.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">
|
||||
<img src="./img/l.png" class="lax" style="width: 103pt; margin-left: 26pt; margin-bottom: -4pt;" data-lax-translate-x="0 0, vh 200" data-lax-optimize=true />
|
||||
<img src="./img/a.png" class="lax" data-lax-translate-x="0 0, vh -200" data-lax-optimize=true />
|
||||
<img src="./img/x.png" class="lax" data-lax-opacity="0 1, (0.8*vh) 0" />
|
||||
<img src="./img/x.png" class="lax" style="margin-top: -79pt" data-lax-translate-y="0 0, vh 200" data-lax-opacity="0 1, (0.8*vh) 0" />
|
||||
<img class="lax" src="./img/x.png" style="margin-top: -79pt" data-lax-translate-y="0 0, vh 400" data-lax-opacity="0 1, (0.8*vh) 0" />
|
||||
<img class="lax" src="./img/x.png" style="margin-top: -79pt" data-lax-translate-y="0 0, vh 600" data-lax-opacity="0 1, (0.8*vh) 0" />
|
||||
<img class="lax" src="./img/x.png" style="margin-top: -79pt" data-lax-translate-y="0 0, vh 800" data-lax-opacity="0 1, (0.8*vh) 0" />
|
||||
<img class="lax" src="./img/x.png" style="margin-top: -79pt" data-lax-translate-y="0 0, vh 1000" data-lax-opacity="0 1, (0.8*vh) 0" />
|
||||
<h2 class="lax" 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 class="lax" data-lax-opacity="0 1, (vh*0.05) 0">scroll down</h4>
|
||||
<i class="lax 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="lax bubble a"
|
||||
style="background: #EDD943"
|
||||
data-lax-preset="lazy-250"
|
||||
></div>
|
||||
|
||||
<div class="lax bubble c"
|
||||
style="background: #ED2471; margin-left: 80pt"
|
||||
data-lax-preset="lazy-100"
|
||||
></div>
|
||||
|
||||
<div class="lax bubble b"
|
||||
style="background: #35D5E5; margin-left: 160pt"
|
||||
data-lax-preset="lazy-300"
|
||||
></div>
|
||||
|
||||
<h3 data-lax-preset="driftRight" data-lax-optimize=true class="lax chunkyText" style="color: #35D5E5;">oooh!</h3>
|
||||
</div>
|
||||
|
||||
<div class="right">
|
||||
<div class="lax bubble a"
|
||||
style="background: #35D5E5; margin-left: 120pt"
|
||||
data-lax-preset="lazy-200"
|
||||
></div>
|
||||
|
||||
<div class="lax bubble c"
|
||||
style="background: #EDD943; margin-left: -20pt"
|
||||
data-lax-preset="lazy-150"
|
||||
></div>
|
||||
|
||||
<div class="lax bubble b"
|
||||
style="background: #ED2471; margin-left: 20pt; margin-top: 200pt"
|
||||
data-lax-preset="lazy-350"
|
||||
></div>
|
||||
<h3 data-lax-optimize=true data-lax-preset="driftLeft" class="lax chunkyText" style="color: #ED2471; margin-top: 200pt;">aaah!</h3>
|
||||
</div>
|
||||
|
||||
<h3 data-lax-preset="crazy zoomInOut" class="lax crazyText" data-lax-optimize=true>sooo crazy</h3>
|
||||
</div>
|
||||
|
||||
<div id="section2" class="section">
|
||||
<div class="lax blockContainer" data-lax-preset="leftToRight-1.1 fadeInOut">
|
||||
<div class="lax block"
|
||||
style="background: #35D5E5;"
|
||||
data-lax-preset="spin"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div class="lax blockContainer" data-lax-preset="leftToRight-1.2 fadeInOut">
|
||||
<div class="lax block"
|
||||
style="background: #EDD943; margin-top: -50pt; margin-left: -50pt; width: 40pt; height: 40pt;"
|
||||
data-lax-preset="spinRev-500"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div class="lax blockContainer" data-lax-preset="leftToRight-1.4 fadeInOut">
|
||||
<div class="lax block"
|
||||
style="background: #ED2471; margin-top: -90pt; margin-left: -0pt;"
|
||||
data-lax-preset="spin-500"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div class="lax blockContainer" data-lax-preset="leftToRight-1.5 fadeInOut">
|
||||
<div class="lax block"
|
||||
style="background: #EDD943; margin-top: 70pt; margin-left: -150pt; width: 40pt; height: 40pt;"
|
||||
data-lax-preset="spinRev-500"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div class="lax blockContainer" data-lax-preset="leftToRight-1.3 fadeInOut">
|
||||
<div class="lax block"
|
||||
style="background: #EDD943; margin-top: 100pt; margin-left: -60pt; width: 25pt; height: 25pt;"
|
||||
data-lax-preset="spin-500"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div class="lax blockContainer" data-lax-preset="leftToRight-1.05 fadeInOut">
|
||||
<div class="lax block"
|
||||
style="background: #ED2471; margin-top: -30pt; margin-left: -70pt;"
|
||||
data-lax-preset="spin"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<h3 data-lax-preset="leftToRight-0.8 speedy" data-lax-optimize=true class="lax chunkyText" style="
|
||||
color: #white; position: absolute; margin-top: -20pt; margin-left: -100pt">
|
||||
wheee!
|
||||
</h3>
|
||||
|
||||
<div class="lax blockContainer" data-lax-preset="leftToRight-1.15 fadeInOut">
|
||||
<div class="lax block"
|
||||
style="background: #35D5E5; margin-top: -70pt; margin-left: -20pt; width: 40pt; height: 40pt;"
|
||||
data-lax-preset="spinRev-500"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div class="lax blockContainer" data-lax-preset="leftToRight-1.45 fadeInOut">
|
||||
<div class="lax block"
|
||||
style="background: #ED2471; margin-top: -50pt; margin-left: -50pt; width: 25pt; height: 25pt;"
|
||||
data-lax-preset="spin-500"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div class="lax blockContainer" data-lax-preset="leftToRight-1.5 fadeInOut">
|
||||
<div class="lax block"
|
||||
style="background: #35D5E5; margin-top: 30pt; margin-left: -20pt;"
|
||||
data-lax-preset="spinRev-500"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div class="lax blockContainer" data-lax-preset="leftToRight-1.25 fadeInOut">
|
||||
<div class="lax block"
|
||||
style="background: #ED2471; margin-top: 80pt; margin-left: -10pt;"
|
||||
data-lax-preset="spin-500"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="section3" class="lax section">
|
||||
<p class="lax" data-lax-preset="linger" data-lax-optimize=true>
|
||||
Harness the power of scrolling and make your websites come alive!
|
||||
</p>
|
||||
<a class="lax button" data-lax-preset="linger" data-lax-optimize=true data-lax-bg-pos-x="0 0, 3000 1000" 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>
|
||||
|
||||
<a href="https://github.com/alexfoxy/laxxx" 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>
|
||||
<h2 class="scrolldown">scroll down</h2>
|
||||
|
||||
</body>
|
||||
<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>
|
||||
206
docs/sprite.html
@@ -1,206 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5">
|
||||
|
||||
|
||||
<style>
|
||||
body,html {
|
||||
margin:0;
|
||||
padding:0;
|
||||
color: #F3F4F5;
|
||||
background: #3c2d7f;
|
||||
overflow-x: hidden;
|
||||
height: 10000vh;
|
||||
font-family: 'Montserrat', sans-serif;
|
||||
}
|
||||
|
||||
#main {
|
||||
opacity: 0;
|
||||
transition: opacity 200ms;
|
||||
}
|
||||
|
||||
#main.loaded {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#mario {
|
||||
width: 256px;
|
||||
height: 256px;
|
||||
/*left: 0;*/
|
||||
position: fixed;
|
||||
top: calc(70vh - 256px)
|
||||
}
|
||||
|
||||
#bowser {
|
||||
width: 524px;
|
||||
height: 350px;
|
||||
/*left: 100vw;*/
|
||||
position: fixed;
|
||||
top: calc(70vh - 330px)
|
||||
}
|
||||
|
||||
#stars {
|
||||
width: 100vw;
|
||||
height: 70vh;
|
||||
background: url("img/mario-stars.gif");
|
||||
background-size: 800px;
|
||||
position: fixed;
|
||||
opacity: 0.7;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
#foreground {
|
||||
width: 100vw;
|
||||
height: 30vh;
|
||||
background: url("img/mario-foreground.png");
|
||||
background-size: 15vh;
|
||||
position: fixed;
|
||||
top: 70vh;
|
||||
}
|
||||
|
||||
#nyan {
|
||||
width: 300px;
|
||||
height: 119px;
|
||||
position: fixed;
|
||||
top: 20vh;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
#logo {
|
||||
width: 300pt;
|
||||
height: 100pt;
|
||||
background: url(img/mario-logo.png) no-repeat center top;
|
||||
background-size: contain;
|
||||
position: fixed;
|
||||
top: 10vh;
|
||||
left: calc(50vw - 150pt);
|
||||
z-index: 200;
|
||||
}
|
||||
|
||||
#scrolltext {
|
||||
width: 300pt;
|
||||
height: 50pt;
|
||||
background: url(img/mario-scrolldown.png) no-repeat center top;
|
||||
background-size: contain;
|
||||
position: fixed;
|
||||
top: 83vh;
|
||||
left: calc(50vw - 150pt);
|
||||
}
|
||||
|
||||
#planet1 {
|
||||
width: 30vw;
|
||||
height: 30vw;
|
||||
position: fixed;
|
||||
left: 100vw;
|
||||
bottom: 29vh;
|
||||
}
|
||||
|
||||
#planet2 {
|
||||
width: 20vw;
|
||||
height: 20vw;
|
||||
top: 10vh;
|
||||
left: 30pt;
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
.github-corner {
|
||||
z-index: 1000;
|
||||
display: block;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<script src="./lib/lax.min.js"></script>
|
||||
<!-- <script src="../src/lax.js"></script> -->
|
||||
|
||||
<script type="text/javascript">
|
||||
window.onload = function() {
|
||||
document.getElementById("main").classList.add("loaded")
|
||||
|
||||
lax.setup()
|
||||
|
||||
const update = () => {
|
||||
lax.update(window.scrollY)
|
||||
window.requestAnimationFrame(update)
|
||||
}
|
||||
|
||||
window.requestAnimationFrame(update)
|
||||
}
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
|
||||
<body>
|
||||
<div id="main">
|
||||
<div id="header" class="section">
|
||||
|
||||
<div
|
||||
id="stars"
|
||||
class="lax"
|
||||
data-lax-bg-pos-x="0 0, 10000 -800 | loop=10000"
|
||||
></div>
|
||||
|
||||
<div
|
||||
id="planet1"
|
||||
class="lax"
|
||||
data-lax-translate-x="0 0, 5000 (-vw-elw)"
|
||||
data-lax-sprite-data="382,382,15,5,10"
|
||||
data-lax-sprite-image="./img/bart.png"
|
||||
></div>
|
||||
|
||||
<div
|
||||
id="foreground"
|
||||
class="lax"
|
||||
data-lax-bg-pos-x="0 0, 7000 (-15*vh) | loop=7000"
|
||||
></div>
|
||||
|
||||
<div
|
||||
id="logo"
|
||||
></div>
|
||||
|
||||
<div
|
||||
id="scrolltext"
|
||||
data-lax-translate-y="0 0, 360 350"
|
||||
class="lax"
|
||||
></div>
|
||||
|
||||
<div
|
||||
id="nyan"
|
||||
data-lax-sprite-data="300,119,12,4,10"
|
||||
data-lax-translate-x="0 vw, 600 -elw | loop=600"
|
||||
data-lax-sprite-image="./img/nyan.png"
|
||||
class="lax"
|
||||
></div>
|
||||
|
||||
<div
|
||||
id="mario"
|
||||
class="lax"
|
||||
data-lax-translate-x="0 -elw, 720 (vw+elw) | loop=720"
|
||||
data-lax-translate-y="0 0, 240 0, 300 -500, 420 0, 720 0 | loop=720"
|
||||
data-lax-sprite-data="256,256,12,4,5"
|
||||
data-lax-sprite-image="./img/mario-sprite.png"
|
||||
></div>
|
||||
|
||||
<div
|
||||
id="bowser"
|
||||
class="lax"
|
||||
data-lax-translate-x="0 vw, 720 -elw | loop=720"
|
||||
data-lax-sprite-data="524,350,8,4,10"
|
||||
data-lax-sprite-image="./img/bowser-sprite.png"
|
||||
></div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<a href="https://github.com/alexfoxy/laxxx" 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>
|
||||
5
jsconfig.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"checkJs": true
|
||||
},
|
||||
}
|
||||
1171
lib/lax.js
2
lib/lax.min.js
vendored
61
package.json
@@ -1,29 +1,36 @@
|
||||
{
|
||||
"name": "lax.js",
|
||||
"version": "1.2.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.5.0",
|
||||
"@babel/core": "^7.5.0",
|
||||
"@babel/preset-env": "^7.5.0"
|
||||
},
|
||||
"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": {}
|
||||
}
|
||||
958
src/lax.js
@@ -1,406 +1,616 @@
|
||||
//
|
||||
// lax v1.2.3 (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 lax = (() => {
|
||||
const lax = {
|
||||
elements: []
|
||||
}
|
||||
const inOutMap = (y = 30) => {
|
||||
return ["elInY+elHeight", `elCenterY-${y}`, "elCenterY", `elCenterY+${y}`, "elOutY-elHeight"]
|
||||
}
|
||||
|
||||
let lastY = 0;
|
||||
let currentBreakpoint = 'default'
|
||||
const breakpointsSeparator = "_"
|
||||
|
||||
const transformFns = {
|
||||
"data-lax-opacity": (style, v) => { style.opacity = v },
|
||||
"data-lax-translate": (style, v) => { style.transform += ` translate(${v}px, ${v}px)` },
|
||||
"data-lax-translate-x": (style, v) => { style.transform += ` translateX(${v}px)` },
|
||||
"data-lax-translate-y": (style, v) => { style.transform += ` translateY(${v}px)` },
|
||||
"data-lax-scale": (style, v) => { style.transform += ` scale(${v})` },
|
||||
"data-lax-scale-x": (style, v) => { style.transform += ` scaleX(${v})` },
|
||||
"data-lax-scale-y": (style, v) => { style.transform += ` scaleY(${v})` },
|
||||
"data-lax-skew": (style, v) => { style.transform += ` skew(${v}deg, ${v}deg)` },
|
||||
"data-lax-skew-x": (style, v) => { style.transform += ` skewX(${v}deg)` },
|
||||
"data-lax-skew-y": (style, v) => { style.transform += ` skewY(${v}deg)` },
|
||||
"data-lax-rotate": (style, v) => { style.transform += ` rotate(${v}deg)` },
|
||||
"data-lax-rotate-x": (style, v) => { style.transform += ` rotateX(${v}deg)` },
|
||||
"data-lax-rotate-y": (style, v) => { style.transform += ` rotateY(${v}deg)` },
|
||||
"data-lax-brightness": (style, v) => { style.filter += ` brightness(${v}%)` },
|
||||
"data-lax-contrast": (style, v) => { style.filter += ` contrast(${v}%)` },
|
||||
"data-lax-hue-rotate": (style, v) => { style.filter += ` hue-rotate(${v}deg)` },
|
||||
"data-lax-blur": (style, v) => { style.filter += ` blur(${v}px)` },
|
||||
"data-lax-invert": (style, v) => { style.filter += ` invert(${v}%)` },
|
||||
"data-lax-saturate": (style, v) => { style.filter += ` saturate(${v}%)` },
|
||||
"data-lax-grayscale": (style, v) => { style.filter += ` grayscale(${v}%)` },
|
||||
"data-lax-bg-pos": (style, v) => { style.backgroundPosition = `${v}px ${v}px` },
|
||||
"data-lax-bg-pos-x": (style, v) => { style.backgroundPositionX = `${v}px` },
|
||||
"data-lax-bg-pos-y": (style, v) => { style.backgroundPositionY = `${v}px` }
|
||||
}
|
||||
|
||||
let crazy = ""
|
||||
|
||||
for(let i=0;i<20;i++) {
|
||||
crazy += " " + i*5 + " " + (i*47)%360 + ", "
|
||||
}
|
||||
|
||||
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} | loop=20` }
|
||||
},
|
||||
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}` }
|
||||
},
|
||||
speedy: (v=30) => {
|
||||
return { "data-lax-skew-x": `(vh) ${v}, -elh ${-v}` }
|
||||
},
|
||||
swing: (v=30) => {
|
||||
return { "data-lax-skew-y": `(vh) ${v}, -elh ${-v}` }
|
||||
}
|
||||
}
|
||||
|
||||
lax.addPreset = (p, o) => {
|
||||
lax.presets[p] = o
|
||||
}
|
||||
|
||||
function intrp(t, v) {
|
||||
let i = 0
|
||||
|
||||
while(t[i][0] <= v && t[i+1] !== undefined) {
|
||||
i+=1
|
||||
}
|
||||
|
||||
const x = t[i][0]
|
||||
const prevX = t[i-1] === undefined ? x : t[i-1][0]
|
||||
|
||||
const y = t[i][1]
|
||||
const prevY = t[i-1] === undefined ? y : t[i-1][1]
|
||||
|
||||
const xPoint = Math.min(Math.max((v-prevX)/(x-prevX),0),1)
|
||||
const yPoint = (xPoint*(y-prevY)) + prevY
|
||||
|
||||
return yPoint
|
||||
}
|
||||
|
||||
function fnOrVal(s) {
|
||||
if(s[0] === "(") return eval(s)
|
||||
else return parseFloat(s)
|
||||
}
|
||||
|
||||
lax.setup = (o={}) => {
|
||||
lax.breakpoints = o.breakpoints || {}
|
||||
|
||||
lax.selector = o.selector || '.lax'
|
||||
lax.populateElements()
|
||||
}
|
||||
|
||||
lax.removeElement = (el) => {
|
||||
const i = lax.elements.findIndex(o => o.el = el)
|
||||
if(i > -1) {
|
||||
lax.elements.splice(i, 1)
|
||||
}
|
||||
}
|
||||
|
||||
lax.createLaxObject = (el) => {
|
||||
const o = {
|
||||
el: el,
|
||||
originalStyle: {
|
||||
transform: el.style.transform,
|
||||
filter: el.style.filter
|
||||
},
|
||||
transforms: {}
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
|
||||
lax.calcTransforms = (o) => {
|
||||
const { el } = o
|
||||
|
||||
//find presets in data attributes
|
||||
const presets = []
|
||||
for(let i=0; i<el.attributes.length; i++) {
|
||||
const a = el.attributes[i]
|
||||
if(a.name.indexOf("data-lax-preset") > -1) {
|
||||
presets.push(a);
|
||||
}
|
||||
}
|
||||
|
||||
//unwrap presets into transformations
|
||||
for(let i=0; i<presets.length; i++) {
|
||||
const a = presets[i]
|
||||
const b = a.name.split(breakpointsSeparator)
|
||||
const breakpoint = b[1] ? `${breakpointsSeparator}${b[1]}` : ''
|
||||
a.value.split(" ").forEach((p) => {
|
||||
const bits = p.split("-")
|
||||
const fn = lax.presets[bits[0]]
|
||||
if(!fn) {
|
||||
console.log(`lax error: preset ${bits[0]} is not defined`)
|
||||
} else {
|
||||
const d = fn(bits[1])
|
||||
for(let k in d) {
|
||||
el.setAttribute(`${k}${breakpoint}`, d[k])
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const currentAnchor = el.getAttribute("data-lax-anchor")
|
||||
if(!currentAnchor || currentAnchor === "") el.setAttribute("data-lax-anchor", "self")
|
||||
el.attributes.removeNamedItem(a.name)
|
||||
|
||||
}
|
||||
|
||||
// use gpu
|
||||
const useGpu = !(el.attributes["data-lax-use-gpu"] && el.attributes["data-lax-use-gpu"].value === 'false')
|
||||
if(useGpu) {
|
||||
el.style["backface-visibility"] = "hidden"
|
||||
el.style["-webkit-backface-visibility"] = "hidden"
|
||||
}
|
||||
if(el.attributes["data-lax-use-gpu"]) el.attributes.removeNamedItem("data-lax-use-gpu")
|
||||
|
||||
// optmise off screen
|
||||
o.optimise = false
|
||||
if(el.attributes["data-lax-optimize"] && el.attributes["data-lax-optimize"].value === 'true') {
|
||||
o.optimise = true
|
||||
const bounds = el.getBoundingClientRect()
|
||||
el.setAttribute("data-lax-opacity", `${-bounds.height-1} 0, ${-bounds.height} 1, ${window.innerHeight} 1, ${window.innerHeight+1} 0`)
|
||||
el.attributes.removeNamedItem("data-lax-optimize")
|
||||
}
|
||||
|
||||
// build transform list
|
||||
for(let i=0; i<el.attributes.length; i++) {
|
||||
const a = el.attributes[i]
|
||||
if(a.name.indexOf("data-lax") < 0) continue
|
||||
|
||||
const b = a.name.split(breakpointsSeparator)
|
||||
const bits = b[0].split("-")
|
||||
const breakpoint = b[1] || "default"
|
||||
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.anchorTop = Math.floor(rect.top) + window.scrollY
|
||||
} else {
|
||||
const tString = a.value
|
||||
.replace(/vw/g, window.innerWidth)
|
||||
.replace(/vh/g, window.innerHeight)
|
||||
.replace(/elh/g, el.clientHeight)
|
||||
.replace(/elw/g, el.clientWidth)
|
||||
.replace(/\s+/g," ")
|
||||
|
||||
const [arrString, optionString] = tString.split("|")
|
||||
const options = {}
|
||||
|
||||
if(optionString) {
|
||||
optionString.split(" ").forEach((o) => {
|
||||
const [key, val] = o.split("=")
|
||||
options[key] = key && fnOrVal(val)
|
||||
})
|
||||
}
|
||||
|
||||
const name = b[0]
|
||||
const valueMap = arrString.split(",").map((x) => {
|
||||
return x.trim().split(" ").map(fnOrVal)
|
||||
}).sort((a,b) => {
|
||||
return a[0] - b[0]
|
||||
})
|
||||
|
||||
if(!o.transforms[name]) {
|
||||
o.transforms[name] = {}
|
||||
}
|
||||
|
||||
o.transforms[name][breakpoint] = { valueMap, options }
|
||||
}
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
// sprite sheet animation
|
||||
const spriteData = el.attributes["data-lax-sprite-data"] && el.attributes["data-lax-sprite-data"].value
|
||||
if(spriteData) {
|
||||
o.spriteData = spriteData.split(",").map(x => parseInt(x))
|
||||
el.style.height = o.spriteData[1] + "px"
|
||||
el.style.width = o.spriteData[0] + "px"
|
||||
|
||||
const spriteImage = el.attributes["data-lax-sprite-image"] && el.attributes["data-lax-sprite-image"].value
|
||||
if(spriteImage) {
|
||||
el.style.backgroundImage = `url(${spriteImage})`
|
||||
],
|
||||
}),
|
||||
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,
|
||||
}
|
||||
],
|
||||
}),
|
||||
}
|
||||
|
||||
return o
|
||||
}
|
||||
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"]
|
||||
|
||||
lax.addElement = (el) => {
|
||||
const o = lax.calcTransforms(lax.createLaxObject(el))
|
||||
lax.elements.push(o)
|
||||
lax.updateElement(o)
|
||||
}
|
||||
const pxUnits = ["perspective", "border-radius", "blur", "translateX", "translateY", "translateZ"]
|
||||
const degUnits = ["hue-rotate", "rotate", "rotateX", "rotateY", "skew", "skewX", "skewY"]
|
||||
|
||||
lax.populateElements = () => {
|
||||
lax.elements = []
|
||||
document.querySelectorAll(lax.selector).forEach(lax.addElement)
|
||||
currentBreakpoint = lax.getCurrentBreakPoint()
|
||||
}
|
||||
function getArrayValues(arr, windowWidth) {
|
||||
if (Array.isArray(arr)) return arr
|
||||
|
||||
lax.updateElements = () => {
|
||||
lax.elements.forEach((o) => {
|
||||
lax.calcTransforms(o)
|
||||
lax.updateElement(o)
|
||||
})
|
||||
const keys = Object.keys(arr).map(x => parseInt(x)).sort((a, b) => a > b ? 1 : -1)
|
||||
|
||||
currentBreakpoint = lax.getCurrentBreakPoint()
|
||||
}
|
||||
|
||||
|
||||
lax.getCurrentBreakPoint = () => {
|
||||
let b = 'default'
|
||||
const w = window.innerWidth
|
||||
|
||||
for(let i in lax.breakpoints) {
|
||||
const px = parseFloat(lax.breakpoints[i])
|
||||
if(px <= w) {
|
||||
b = i
|
||||
} else {
|
||||
let retKey = keys[keys.length - 1]
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const key = keys[i]
|
||||
if (windowWidth < key) {
|
||||
retKey = key
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return b
|
||||
return arr[retKey]
|
||||
}
|
||||
|
||||
lax.updateElement = (o) => {
|
||||
const { originalStyle, anchorTop, transforms, spriteData, el } = o
|
||||
function lerp(start, end, t) {
|
||||
return start * (1 - t) + end * t
|
||||
}
|
||||
|
||||
let r = anchorTop ? anchorTop-lastY : lastY
|
||||
function invlerp(a, b, v) {
|
||||
return (v - a) / (b - a)
|
||||
}
|
||||
|
||||
const style = {
|
||||
transform: originalStyle.transform,
|
||||
filter: originalStyle.filter
|
||||
function interpolate(arrA, arrB, v, easingFn) {
|
||||
let k = 0
|
||||
|
||||
arrA.forEach((a) => {
|
||||
if (a < v) k++
|
||||
})
|
||||
|
||||
if (k <= 0) {
|
||||
return arrB[0]
|
||||
}
|
||||
|
||||
for(let i in transforms) {
|
||||
const transformData = transforms[i][currentBreakpoint] || transforms[i]["default"]
|
||||
|
||||
if(!transformData) {
|
||||
// console.log(`lax error: there is no setting for key ${i} and screen size ${currentBreakpoint}. Try adding a default value!`)
|
||||
continue
|
||||
}
|
||||
|
||||
let _r = r
|
||||
if(transformData.options.offset) _r = _r+transformData.options.offset
|
||||
if(transformData.options.speed) _r = _r*transformData.options.speed
|
||||
if(transformData.options.loop) _r = _r%transformData.options.loop
|
||||
|
||||
const t = transformFns[i]
|
||||
const v = intrp(transformData.valueMap, _r)
|
||||
|
||||
if(!t) {
|
||||
// console.info(`lax: error ${i} is not supported`)
|
||||
continue
|
||||
}
|
||||
|
||||
t(style, v)
|
||||
if (k >= arrA.length) {
|
||||
return arrB[arrA.length - 1]
|
||||
}
|
||||
|
||||
if(spriteData) {
|
||||
const [frameW,frameH,numFrames,cols,scrollStep] = spriteData
|
||||
const frameNo = Math.floor(lastY/scrollStep) % numFrames
|
||||
const framePosX = frameNo%cols
|
||||
const framePosY = Math.floor(frameNo/cols)
|
||||
style.backgroundPosition = `-${framePosX*frameW}px -${framePosY*frameH}px`
|
||||
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: ''
|
||||
}
|
||||
|
||||
if(style.opacity === 0) { // if opacity 0 don't update
|
||||
el.style.opacity = 0
|
||||
} else {
|
||||
for(let k in style) {
|
||||
el.style[k] = style[k]
|
||||
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
|
||||
|
||||
|
||||
constructor(name, getValueFn, options = {}) {
|
||||
this.name = name
|
||||
this.getValueFn = getValueFn
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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]
|
||||
}
|
||||
}
|
||||
|
||||
lax.update = (y) => {
|
||||
if(lastY === y) return
|
||||
lastY = y
|
||||
lax.elements.forEach(lax.updateElement)
|
||||
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]
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
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])
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return lax;
|
||||
})();
|
||||
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 driverValues = {}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
return new Lax()
|
||||
})()
|
||||
|
||||
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined')
|
||||
module.exports = lax;
|
||||
module.exports = laxInstance
|
||||
else
|
||||
window.lax = lax;
|
||||
})();
|
||||
window.lax = laxInstance
|
||||
})()
|
||||
|
||||