88 Commits
1.2.4 ... dev

Author SHA1 Message Date
Alex Fox
5d6d4c3772 Update README.md 2025-05-21 07:07:54 +01:00
Alex Fox
3f3284b3a5 Merge pull request #163 from PatrickDesign/patrickW/bugfix--allow-empty-options-within-addElements
[BUGFIX] Continue to allow empty options to be passed to `addElements`
2021-10-25 21:06:02 +01:00
Patrick w
81a2232ba8 Continue to allow empty options to be passed 2021-10-21 12:06:57 -07:00
Alex Fox
4e5185ddde Merge pull request #159 from PatrickDesign/patrickW/feature--allow-domElements-to-be-passed-directly-#158
Allow raw DOM element to be passed to `addElements`
2021-10-16 09:19:04 +01:00
Alex Fox
7a7a1a8260 Update README.md 2021-10-16 09:18:27 +01:00
Patrick w
4468069d8f Update README 2021-10-15 13:44:47 -07:00
Patrick w
dc69c3226f Allow raw DOM element to be passed to addElements
Allows more flexible selection patterns.

See https://github.com/alexfoxy/lax.js/issues/158
2021-10-15 13:43:12 -07:00
Alex Fox
94f9cf896f Merge pull request #143 from alexfoxy/dependabot/npm_and_yarn/lodash-4.17.21
Bump lodash from 4.17.20 to 4.17.21
2021-10-15 21:03:35 +01:00
Alex Fox
c950ec4ce4 Merge pull request #151 from alexfoxy/dependabot/npm_and_yarn/browserslist-4.16.6
Bump browserslist from 4.14.7 to 4.16.6
2021-10-15 21:03:21 +01:00
Alex Fox
72f5c19808 Merge pull request #155 from alexfoxy/dependabot/npm_and_yarn/path-parse-1.0.7
Bump path-parse from 1.0.6 to 1.0.7
2021-10-15 21:03:11 +01:00
Alex Fox
c9e213c766 Merge pull request #160 from alexfoxy/bug/fix-incorrect-element-position-on-resize
Fix resize bug
2021-10-15 21:01:45 +01:00
Alex Fox
a67afd4671 Fix resize bug 2021-10-15 21:00:52 +01:00
Alex Fox
4a484c91d8 Merge pull request #154 from arthurdenner/docs/html-inline-example-link
docs: update link to html-inline example
2021-10-15 20:32:11 +01:00
dependabot[bot]
4d778193d9 Bump path-parse from 1.0.6 to 1.0.7
Bumps [path-parse](https://github.com/jbgutierrez/path-parse) from 1.0.6 to 1.0.7.
- [Release notes](https://github.com/jbgutierrez/path-parse/releases)
- [Commits](https://github.com/jbgutierrez/path-parse/commits/v1.0.7)

---
updated-dependencies:
- dependency-name: path-parse
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-08-10 22:32:51 +00:00
Arthur Denner
75f8c2df7f docs: update link to html-inline example 2021-07-14 08:38:44 +02:00
dependabot[bot]
7aa04ec24c Bump browserslist from 4.14.7 to 4.16.6
Bumps [browserslist](https://github.com/browserslist/browserslist) from 4.14.7 to 4.16.6.
- [Release notes](https://github.com/browserslist/browserslist/releases)
- [Changelog](https://github.com/browserslist/browserslist/blob/main/CHANGELOG.md)
- [Commits](https://github.com/browserslist/browserslist/compare/4.14.7...4.16.6)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-25 00:53:04 +00:00
dependabot[bot]
c3007562a8 Bump lodash from 4.17.20 to 4.17.21
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.20 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.20...4.17.21)

Signed-off-by: dependabot[bot] <support@github.com>
2021-05-08 07:55:31 +00:00
Alex Fox
5abe2aaed5 Update README.md 2021-01-22 22:16:41 +00:00
Alex Fox
1a65068e7e Merge pull request #130 from alexfoxy/master
Master
2020-12-28 19:11:22 +00:00
Alex Fox
607c580c92 Merge pull request #129 from alexfoxy/dev
Merge 2.0.3 release
2020-12-28 19:07:31 +00:00
Alex Fox
9f2a396600 New build 2020-12-28 19:07:16 +00:00
Alex Fox
26496247c2 Updated build 2020-12-28 18:23:47 +00:00
Alex Fox
684a50a795 Merge pull request #128 from alexfoxy/bug/AF01/update-preset-deliminator
Bug: AF01 Updated preset-deliminator to `:` to allow for negative values with presets
2020-12-28 18:20:32 +00:00
Alex Fox
42dd4d8e50 Updated preset-deliminator 2020-12-28 18:19:23 +00:00
Alex Fox
6b5fce714a Merge pull request #119 from heyimbrucelee/master
Use "_" char as delimiter for presets string
2020-12-28 18:12:49 +00:00
Alex Fox
6a6eedf156 Merge pull request #122 from arthurdenner/patch-1
docs: update element and animation options
2020-12-28 18:11:14 +00:00
Alex Fox
c27f2d1208 Merge pull request #127 from alexfoxy/bug/AF01/pass-frame-to-getValueFn
Pass frame to getValueFn
2020-12-28 18:09:40 +00:00
Alex Fox
bdf8c186a0 Merge pull request #126 from alexfoxy/bug/AF01/pass-frame-to-getValueFn
Bug: AF01 - pass frame to get value fn
2020-12-28 18:09:14 +00:00
Alex Fox
c84a79ba67 Pass frame to getValueFn 2020-12-28 18:07:50 +00:00
Alex Fox
47ac9f4531 Merge pull request #124 from susiwen8/fix-scrollY
Fix: scrollY compatibility
2020-12-28 18:04:46 +00:00
Alex Fox
6715763440 Merge branch 'master' into dev 2020-12-28 18:02:52 +00:00
susiwen8
18679a9920 Fix: scrollY compatibility 2020-12-09 10:26:56 +08:00
Arthur Denner
b4c109d20f docs: update element and animation options 2020-11-26 22:40:24 +01:00
me
2d38405398 Use "_" char as delimiter for presets string
--> fix use of negative values
2020-11-23 15:16:55 +01:00
Alex Fox
362595b5db Fix "how to use" link on preset-explorer 2020-11-16 21:03:55 +00:00
Alex Fox
239bc9cb86 Merge branch 'master' of https://github.com/alexfoxy/laxxx into master 2020-11-16 21:02:12 +00:00
Alex Fox
552314fa53 Fix X scale example 2020-11-16 21:02:09 +00:00
Alex Fox
b36213454b Fix readme code 2020-11-16 21:00:34 +00:00
Alex Fox
cf35933a15 Bumped version 2020-11-16 16:49:06 +00:00
Alex Fox
e0d20568d5 Replaced document.body.scrollTop with window.scrollY 2020-11-16 16:48:52 +00:00
Alex Fox
1508393a65 Fix snap scroll example 2020-11-14 16:31:38 +00:00
Alex Fox
9dd8c42a69 Update issue templates 2020-11-14 16:20:06 +00:00
Alex Fox
a54cbc3bfe Update issue templates 2020-11-14 16:10:51 +00:00
Alex Fox
de025733a2 Update readme 2020-11-14 16:03:33 +00:00
Alex Fox
5db870e838 Bump version 2020-11-14 16:02:18 +00:00
Alex Fox
ef9943ca21 Update read-me and package 2020-11-14 15:57:06 +00:00
Alex Fox
6e350824ff Update example lib path 2020-11-14 15:50:21 +00:00
Alex Fox
dac3a9c8d2 Update readme 2020-11-14 15:49:07 +00:00
Alex Fox
bc345a7385 Update README.md 2020-11-14 15:46:16 +00:00
Alex Fox
b6134e4f33 Update README.md 2020-11-14 15:45:19 +00:00
Alex Fox
94c89a54eb Update example lib location 2020-11-14 15:40:33 +00:00
Alex Fox
47463a5220 Update index.html 2020-11-14 15:34:01 +00:00
Alex Fox
537ce7f36b Update index.html 2020-11-14 15:32:39 +00:00
Alex Fox
dd21a86b6c Update readme 2020-11-14 15:26:17 +00:00
Alex Fox
702f26f3d3 Update main lax example 2020-11-14 10:35:35 +00:00
Alex Fox
7ca20b3114 Fix snap-scroll 2020-11-11 19:02:12 +00:00
Alex Fox
2f4e2804d5 Added input example and improved on update 2020-11-11 18:36:09 +00:00
Alex Fox
322ee2726b Fixed issue with chrome scroll bug with position fixed 2020-11-10 20:05:48 +00:00
Alex Fox
5c3ab529bf Update read me 2020-11-08 18:08:47 +00:00
Alex Fox
7790246185 Update readme and small changes 2020-11-08 17:58:33 +00:00
Alex Fox
0beada6c6c Updated examples and preset code 2020-11-07 17:28:21 +00:00
Alex Fox
0edfbdad76 Updated exampels and presets 2020-11-06 18:42:40 +00:00
Alex Fox
e8be532dfb Merge branch 'version-2' of https://github.com/alexfoxy/laxxx into version-2 2020-09-06 10:19:23 +01:00
Alex Fox
6b6cf6aae7 Updated examples 2020-09-06 10:18:29 +01:00
Alex Fox
d7ab09c1da Merge pull request #100 from arthurdenner/fix/links-docs-v2
Update and fix some texts
2020-09-06 10:17:44 +01:00
Alex Fox
c790a0319e Merge pull request #103 from MatixYo/version-2
Enabled setting CSS variables
2020-09-06 10:16:35 +01:00
Mateusz Juszczyk
853e8a1a17 Merge branch 'version-2' of https://github.com/luckypike/lax.js into luckypike-version-2
# Conflicts:
#	lib/lax.min.js
#	lib/lax.min.js.gz
2020-07-19 15:12:26 +02:00
Andy Braga
edb04e2542 Added removeElement for domElement 2020-07-09 13:08:04 +03:00
Andy Braga
ee2df22519 Added function to add DOM element 2020-06-26 14:15:34 +03:00
Mateusz Juszczyk
ee27e5e559 Run cmd in sequence on Windows 2020-06-11 16:02:47 +02:00
Mateusz Juszczyk
d804f6f51e Changed way of setting element styles to allow setting CSS Variables 2020-06-11 15:59:53 +02:00
Mateusz Juszczyk
44ed7346d8 Replaced legacy uglifyjs with uglify-js 2020-06-11 15:58:23 +02:00
Arthur Denner
51dbff484c Update and fix some texts 2020-05-26 21:32:28 +02:00
Alex Fox
4ba449be29 Updated main demo and readme 2020-05-25 16:37:09 +01:00
Alex Fox
0b31b92a3d Updated docs 2020-05-24 10:41:11 +01:00
Alex Fox
66b790b063 Started to add some presets + snap scroll example 2020-05-11 07:38:23 +01:00
Alex Fox
1b545d270e Updated readme 2020-05-07 17:53:33 +01:00
Alex Fox
61544135a8 Updated readme 2020-05-07 17:20:56 +01:00
Alex Fox
99ac0356f7 Added momentum 2020-05-07 17:20:22 +01:00
Alex Fox
dc9df50ffa Version 2.0 initial commit 2020-05-07 07:47:44 +01:00
Alex Fox
38eddc7ab8 Merge pull request #65 from tarunspartan/add-close-script-tag-in-readme
added closing script  tag in readme file
2019-12-20 06:59:44 +00:00
Alex Fox
6d8b4ca9b3 Merge pull request #67 from web-crab/master
Fix lax.removeElement
2019-12-20 06:59:21 +00:00
Andrey Klimash
938b1be1b8 Fix lax.removeElement 2019-11-11 11:32:02 +03:00
Tarun M
69edd64243 added closing script tag in readme file 2019-11-02 20:56:23 +05:30
Alex Fox
1e1067f632 Merge branch 'master' of https://github.com/alexfoxy/laxxx 2019-10-15 15:52:13 +01:00
Alex Fox
19df0b70c1 bumped version 2019-10-15 15:52:01 +01:00
Alex Fox
08bdf99fdd Merge branch 'dev' 2019-10-15 15:51:08 +01:00
Alex Fox
2ab2c5392c Added to .babelrc to .npmignore 2019-10-15 15:51:02 +01:00
65 changed files with 4495 additions and 3421 deletions

View File

@@ -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
View 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.

View 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
View File

@@ -1,2 +1,3 @@
node_modules/
.DS_Store
LaxLogo.sketch

View File

@@ -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
View File

@@ -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/)
![Lax 2.0 Gif](https://i.imgur.com/XNvvAOv.gif)
![](https://i.imgur.com/Jbkna80.gif)
[>> 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..
![](https://i.imgur.com/GGQFucZ.gif)
## 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 |

View File

View File

@@ -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>

View File

@@ -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});

View File

@@ -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>

View File

@@ -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>

View File

@@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
docs/assets/bg1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

BIN
docs/assets/bg2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 KiB

BIN
docs/assets/bg3.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 129 KiB

View File

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 122 KiB

BIN
docs/assets/downarrow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
docs/assets/l.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
docs/assets/lax.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

BIN
docs/assets/pink-zag.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

BIN
docs/assets/purple-zag.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

BIN
docs/assets/shoe1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 KiB

BIN
docs/assets/shoe2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 540 KiB

BIN
docs/assets/shoe3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 KiB

BIN
docs/assets/spritesheet.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 418 KiB

BIN
docs/assets/teal-zag.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

BIN
docs/assets/x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -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
View 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>

View 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>

View 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
View 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>

View 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
View 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>

View 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
View 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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 237 B

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -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

File diff suppressed because one or more lines are too long

488
docs/preset-explorer.html Normal file
View 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>

View File

@@ -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
View File

@@ -0,0 +1,5 @@
{
"compilerOptions": {
"checkJs": true
},
}

1171
lib/lax.js

File diff suppressed because it is too large Load Diff

2
lib/lax.min.js vendored

File diff suppressed because one or more lines are too long

Binary file not shown.

View File

@@ -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": {}
}

View File

@@ -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
})()

2434
yarn.lock

File diff suppressed because it is too large Load Diff