-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
489 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
root = true | ||
|
||
[*] | ||
indent_style = space | ||
indent_size = 2 | ||
end_of_line = lf | ||
charset = utf-8 | ||
trim_trailing_whitespace = true | ||
insert_final_newline = true | ||
|
||
[*.md] | ||
trim_trailing_whitespace = false |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
node_modules/ | ||
package-lock.json | ||
.idea/ | ||
coverage/ | ||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
registry="https://registry.npmjs.org/" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
toc: | ||
- Kuliso | ||
- mouseConfig | ||
- MouseScene | ||
- EffectCallback | ||
- RangeOffset | ||
- RangeName | ||
- CSSUnitValue | ||
- AbsoluteOffsetContext | ||
- name: utilities | ||
children: | ||
- lerp | ||
- defaultTo | ||
- frameThrottle |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
{ | ||
"name": "kuliso", | ||
"version": "0.0.1", | ||
"description": "Tiny library for performant pointer-driven or gyroscope-driven effects", | ||
"main": "dist/index.cjs", | ||
"module": "index.js", | ||
"types": "types.d.ts", | ||
"type": "module", | ||
"scripts": { | ||
"build": "rollup -c", | ||
"test": "c8 ava test/*.spec.js -s", | ||
"test:debug": "ava test/*.spec.js -s", | ||
"docs": "documentation build src/Kuliso.js -f html -o docs/reference -c documentation.yml", | ||
"rtfm": "npm run docs && http-server ./docs/reference" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/wix-incubator/kuliso.git" | ||
}, | ||
"keywords": [ | ||
"animation", | ||
"js", | ||
"pointer", | ||
"gyroscope" | ||
], | ||
"author": "wow!Team", | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/wix-incubator/kuliso/issues" | ||
}, | ||
"homepage": "https://github.com/wix-incubator/kuliso#readme", | ||
"devDependencies": { | ||
"ava": "^5.3.1", | ||
"c8": "^8.0.1", | ||
"documentation": "^14.0.2", | ||
"http-server": "^14.1.1", | ||
"rollup": "^4.6.0", | ||
"rollup-plugin-filesize": "^10.0.0", | ||
"rollup-plugin-progress": "^1.1.2" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import progress from 'rollup-plugin-progress'; | ||
import filesize from 'rollup-plugin-filesize'; | ||
|
||
export default { | ||
input: 'index.js', | ||
output: { | ||
file: 'dist/index.cjs', | ||
format: 'cjs' | ||
}, | ||
plugins: [ | ||
progress({ | ||
clearLine: false | ||
}), | ||
filesize() | ||
] | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,241 @@ | ||
import { getController } from './controller.js'; | ||
import { defaultTo, frameThrottle, lerp } from './utilities.js'; | ||
|
||
/** | ||
* @private | ||
*/ | ||
const DEFAULTS = { | ||
transitionActive: false, | ||
transitionFriction: 0.9, | ||
transitionEpsilon: 1, | ||
velocityActive: false, | ||
velocityMax: 1 | ||
}; | ||
|
||
/** | ||
* @class Kuliso | ||
* @param {mouseConfig} config | ||
* | ||
* @example | ||
* import { Kuliso } from 'kuliso'; | ||
* | ||
* const kuliso = new Kuliso({ | ||
* scenes: [...] | ||
* }); | ||
* kuliso.start(); | ||
*/ | ||
export class Kuliso { | ||
constructor (config = {}) { | ||
this.config = defaultTo(config, DEFAULTS); | ||
|
||
this.progress = { | ||
p: 0, | ||
prevP: 0, | ||
vp: 0 | ||
}; | ||
this.currentProgress = { | ||
p: 0, | ||
prevP: 0, | ||
vp: 0 | ||
}; | ||
|
||
this._lerpFrameId = 0; | ||
this.effect = null; | ||
// if no root or root is document.body then use window | ||
this.config.root = (!this.config.root || this.config.root === window.document.body) ? window : this.config.root; | ||
this.config.resetProgress = this.config.resetProgress || this.resetProgress.bind(this); | ||
|
||
this._measure = this.config.measure || (() => { | ||
const root = this.config.root; | ||
// get current scroll position from window or element | ||
this.progress.p = this.config.horizontal | ||
? root.scrollX || root.scrollLeft || 0 | ||
: root.scrollY || root.scrollTop || 0; | ||
}); | ||
|
||
this._trigger = frameThrottle(() => { | ||
this._measure?.(); | ||
this.tick(true); | ||
}); | ||
} | ||
|
||
/** | ||
* Setup event and effect, and reset progress and frame. | ||
*/ | ||
start () { | ||
this.setupEffect(); | ||
this.setupEvent(); | ||
this.resetProgress(); | ||
this.tick(); | ||
} | ||
|
||
/** | ||
* Removes event listener. | ||
*/ | ||
pause () { | ||
this.removeEvent(); | ||
} | ||
|
||
/** | ||
* Reset progress in the DOM and inner state to given x and y. | ||
* | ||
* @param {Object} [pointerPosition] | ||
* @param {number} [pointerPosition.x] | ||
* @param {number} [pointerPosition.y] | ||
*/ | ||
resetProgress (pointerPosition = {}) { | ||
// get current pointer position (support window, element) | ||
const root = this.config.root; | ||
const x = pointerPosition.x || pointerPosition.x === 0 ? pointerPosition.x : root.scrollX || root.scrollLeft || 0; | ||
const y = pointerPosition.y || pointerPosition.y === 0 ? pointerPosition.y : root.scrollY || root.scrollTop || 0; | ||
this.progress.x = x; | ||
this.progress.y = y; | ||
this.progress.prevX = x; | ||
this.progress.prevY = y; | ||
this.progress.vx = 0; | ||
this.progress.vy = 0; | ||
|
||
if (pointerPosition) { | ||
this.config.root.scrollTo(x, y); | ||
} | ||
} | ||
|
||
/** | ||
* Handle animation frame work. | ||
* | ||
* @param {boolean} [clearLerpFrame] whether to cancel an existing lerp frame | ||
*/ | ||
tick (clearLerpFrame) { | ||
const hasLerp = this.config.transitionActive; | ||
|
||
// if transition is active interpolate to next point | ||
if (hasLerp) { | ||
this.lerp(); | ||
} | ||
|
||
// choose the object we iterate on | ||
const progress = hasLerp ? this.currentProgress : this.progress; | ||
|
||
if (this.config.velocityActive) { | ||
const dp = progress.p - progress.prevP; | ||
const factorP = dp < 0 ? -1 : 1; | ||
progress.vp = Math.min(this.config.velocityMax, Math.abs(dp)) / this.config.velocityMax * factorP; | ||
} | ||
|
||
// update effect | ||
this.effect.tick(progress); | ||
|
||
if (hasLerp && (progress.p !== this.progress.p)) { | ||
if (clearLerpFrame && this._lerpFrameId) { | ||
window.cancelAnimationFrame(this._lerpFrameId); | ||
} | ||
|
||
this._lerpFrameId = window.requestAnimationFrame(() => this.tick()); | ||
} | ||
|
||
progress.prevP = progress.p; | ||
} | ||
|
||
/** | ||
* Calculate current progress. | ||
*/ | ||
lerp () { | ||
this.currentProgress.p = lerp(this.currentProgress.p, this.progress.p, +(1 - this.config.transitionFriction).toFixed(3), this.config.transitionEpsilon); | ||
} | ||
|
||
/** | ||
* Stop the event and effect, and remove all DOM side-effects. | ||
*/ | ||
destroy () { | ||
this.pause(); | ||
this.removeEffect(); | ||
} | ||
|
||
/** | ||
* Register to pointermove for triggering update. | ||
*/ | ||
setupEvent () { | ||
this.removeEvent(); | ||
this.config.root.addEventListener('pointermove', this._trigger); | ||
} | ||
|
||
/** | ||
* Remove pointermove handler. | ||
*/ | ||
removeEvent () { | ||
this.config.root.removeEventListener('pointermove', this._trigger); | ||
} | ||
|
||
/** | ||
* Reset registered effect. | ||
*/ | ||
setupEffect () { | ||
this.removeEffect(); | ||
this.effect = getController(this.config); | ||
} | ||
|
||
/** | ||
* Remove registered effect. | ||
*/ | ||
removeEffect () { | ||
this.effect && this.effect.destroy(); | ||
this.effect = null; | ||
} | ||
} | ||
|
||
/** | ||
* @typedef {object} scrollConfig | ||
* @property {ScrollScene[]} scenes list of effect scenes to perform during scroll. | ||
* @property {boolean} [horizontal] whether to use the horizontal axis. Defaults to `false`. | ||
* @property {boolean} [transitionActive] whether to animate effect progress. | ||
* @property {number} [transitionFriction] between 0 to 1, amount of friction effect in the transition. 1 being no movement and 0 as no friction. Defaults to 0.4. | ||
* @property {boolean} [velocityActive] whether to calculate velocity with progress. | ||
* @property {number} [velocityMax] max possible value for velocity. Velocity value will be normalized according to this number, so it is kept between 0 and 1. Defaults to 1. | ||
* @property {boolean} [observeViewportEntry] whether to observe entry/exit of scenes into viewport for disabling/enabling them. Defaults to `true`. | ||
* @property {boolean} [viewportRootMargin] `rootMargin` option to be used for viewport observation. Defaults to `'7% 7%'`. | ||
* @property {boolean} [observeViewportResize] whether to observe resize of the visual viewport. Defaults to `false`. | ||
* @property {boolean} [observeSourcesResize] whether to observe resize of view-timeline source elements. Defaults to `false`. | ||
* @property {Element|Window} [root] the scrollable element, defaults to window. | ||
*/ | ||
|
||
/** | ||
* @typedef {Object} ScrollScene | ||
* @desc A configuration object for a scene. Must be provided an effect function, and either a start and end, a start and duration, or a duration as RangeName. | ||
* @example { effects: (scene, p) => { animation.currentTime = p; }, duration: 'contain' } | ||
* @property {EffectCallback} effect the effect to perform. | ||
* @property {number|RangeOffset} start scroll position in pixels where effect starts. | ||
* @property {number|RangeName} [duration] duration of effect in pixels. Defaults to end - start. | ||
* @property {number|RangeOffset} [end] scroll position in pixels where effect ends. Defaults to start + duration. | ||
* @property {boolean} [disabled] whether to perform updates on the scene. Defaults to false. | ||
* @property {Element} [viewSource] an element to be used for observing intersection with viewport for disabling/enabling the scene, or the source of a ViewTimeline if scene start/end are provided as ranges. | ||
*/ | ||
|
||
/** | ||
* @typedef {function(scene: ScrollScene, progress: number, velocity: number): void} EffectCallback | ||
* @param {ScrollScene} scene | ||
* @param {number} progress | ||
* @param {number} velocity | ||
*/ | ||
|
||
/** | ||
* @typedef {'entry' | 'contain' | 'exit' | 'cover'} RangeName | ||
*/ | ||
|
||
/** | ||
* @typedef {Object} RangeOffset | ||
* @property {RangeName} name | ||
* @property {number} offset | ||
* @property {CSSUnitValue} [add] | ||
*/ | ||
|
||
/** | ||
* @typedef {Object} CSSUnitValue | ||
* @property {number} value | ||
* @property {'px'|'vh'|'vw'} unit | ||
*/ | ||
|
||
/** | ||
* @typedef {Object} AbsoluteOffsetContext | ||
* @property {number} viewportWidth | ||
* @property {number} viewportHeight | ||
*/ |
Oops, something went wrong.