diff --git a/.changeset/empty-steaks-judge.md b/.changeset/empty-steaks-judge.md
new file mode 100644
index 0000000..318c9fc
--- /dev/null
+++ b/.changeset/empty-steaks-judge.md
@@ -0,0 +1,5 @@
+---
+"motion-on-scroll": major
+---
+
+v1.0.0 release - now out of beta! Full documentation on the features and functionality can be found at [motion-on-scroll.pages.dev](https://motion-on-scroll.pages.dev/).
diff --git a/README.md b/README.md
index d0fd261..cbfc5be 100644
--- a/README.md
+++ b/README.md
@@ -4,8 +4,6 @@
Looking for the main package? Go to [motion-on-scroll](/packages/motion-on-scroll/).
-## Note: this is currently in beta. Feel free to try it though :)
-
Effortless, AOS-compatible scroll animations powered by [Motion](https://motion.dev).
Framework-agnostic, MOS lets you add scroll-triggered animations with nothing but `data-mos` attributes. Under the hood it uses Motion’s powerful `animate` API, giving you:
diff --git a/apps/docs/src/content/docs/reference/attributes.mdx b/apps/docs/src/content/docs/reference/attributes.mdx
index 57d42b7..544f462 100644
--- a/apps/docs/src/content/docs/reference/attributes.mdx
+++ b/apps/docs/src/content/docs/reference/attributes.mdx
@@ -11,7 +11,7 @@ import { Aside } from '@astrojs/starlight/components';
*Type*: `PresetName`
*Default*: —
-Preset animation key (e.g. `fade-up`, `zoom-in`).
+Any [preset animation key](/reference/presets/) (e.g. `fade-up`, `zoom-in`).
Need something else? Define custom keyframes with [`MOS.registerKeyframes`](/reference/api/#registerkeyframes), or entire animations with [`MOS.registerAnimation`](/reference/api/#registeranimation) and use it in your presets.
diff --git a/packages/aos-anime/src/aos/anime/animations.ts b/packages/aos-anime/src/aos/anime/animations.ts
deleted file mode 100644
index 07edb79..0000000
--- a/packages/aos-anime/src/aos/anime/animations.ts
+++ /dev/null
@@ -1,438 +0,0 @@
-import anime from "animejs/lib/anime.es.js";
-
-import { type AnimeOptions } from "../helpers/aosTypes";
-
-export const getAnimation = (
- element: HTMLElement,
- animationName: string,
- defaults: AnimeOptions,
-) => {
- const tempDelay = element.dataset.aosDelay;
- const delay = tempDelay ? parseFloat(tempDelay) * 1000 : defaults.delay * 1000;
-
- const tempDistance = element.dataset.aosDistance;
- const distance = tempDistance ? parseFloat(tempDistance) : defaults.distance;
-
- const tempDuration = element.dataset.aosDuration;
- const duration = tempDuration ? parseFloat(tempDuration) * 1000 : defaults.duration * 1000;
-
- const onStartFunction = () => {
- element.classList.add("aos-animate");
- };
-
- let animation;
-
- if (animationName.includes("fade")) {
- // fade animations
- switch (animationName) {
- case "fade-in":
- animation = anime({
- targets: element,
- opacity: [0, 1],
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- case "fade-up":
- animation = anime({
- targets: element,
- opacity: [0, 1],
- translateY: [distance, 0],
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- case "fade-down":
- animation = anime({
- targets: element,
- opacity: [0, 1],
- translateY: [-1 * distance, 0],
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- case "fade-left":
- animation = anime({
- targets: element,
- opacity: [0, 1],
- translateX: [distance, 0],
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- case "fade-right":
- animation = anime({
- targets: element,
- opacity: [0, 1],
- translateX: [-1 * distance, 0],
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- case "fade-up-right":
- animation = anime({
- targets: element,
- opacity: [0, 1],
- translateX: [-1 * distance, 0],
- translateY: [distance, 0],
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- case "fade-up-left":
- animation = anime({
- targets: element,
- opacity: [0, 1],
- translateX: [distance, 0],
- translateY: [distance, 0],
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- case "fade-down-right":
- animation = anime({
- targets: element,
- opacity: [0, 1],
- translateX: [-1 * distance, 0],
- translateY: [-1 * distance, 0],
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- case "fade-down-left":
- animation = anime({
- targets: element,
- opacity: [0, 1],
- translateX: [distance, 0],
- translateY: [-1 * distance, 0],
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- default:
- // default is fade-in
- animation = anime({
- targets: element,
- opacity: [0, 1],
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- }
- } else if (animationName.includes("zoom")) {
- // zoom animations
- switch (animationName) {
- case "zoom-in":
- animation = anime({
- targets: element,
- opacity: [0, 1],
- scale: [0.6, 1],
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- case "zoom-in-up":
- animation = anime({
- targets: element,
- opacity: [0, 1],
- scale: [0.6, 1],
- translateY: [distance, 0],
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- case "zoom-in-down":
- animation = anime({
- targets: element,
- opacity: [0, 1],
- scale: [0.6, 1],
- translateY: [-1 * distance, 0],
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- case "zoom-in-left":
- animation = anime({
- targets: element,
- opacity: [0, 1],
- scale: [0.6, 1],
- translateX: [distance, 0],
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- case "zoom-in-right":
- animation = anime({
- targets: element,
- opacity: [0, 1],
- scale: [0.6, 1],
- translateX: [-1 * distance, 0],
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- case "zoom-out":
- animation = anime({
- targets: element,
- opacity: [0, 1],
- scale: [1.2, 1],
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- case "zoom-out-up":
- animation = anime({
- targets: element,
- opacity: [0, 1],
- scale: [1.2, 1],
- translateY: [distance, 0],
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- case "zoom-out-down":
- animation = anime({
- targets: element,
- opacity: [0, 1],
- scale: [1.2, 1],
- translateY: [-1 * distance, 0],
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- case "zoom-out-left":
- animation = anime({
- targets: element,
- opacity: [0, 1],
- scale: [1.2, 1],
- translateX: [distance, 0],
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- case "zoom-out-right":
- animation = anime({
- targets: element,
- opacity: [0, 1],
- scale: [1.2, 1],
- translateX: [-1 * distance, 0],
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- default:
- // default is zoom-in
- animation = anime({
- targets: element,
- opacity: [0, 1],
- scale: [0.6, 1],
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- }
- } else if (animationName.includes("slide")) {
- // slide animations
- switch (animationName) {
- case "slide-up":
- animation = anime({
- targets: element,
- translateY: ["100%", 0],
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- case "slide-down":
- animation = anime({
- targets: element,
- translateY: ["-100%", 0],
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- case "slide-left":
- animation = anime({
- targets: element,
- translateX: ["100%", 0],
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- case "slide-right":
- animation = anime({
- targets: element,
- translateX: ["-100%", 0],
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- default:
- // default is slide-up
- animation = anime({
- targets: element,
- translateY: ["100%", 0],
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- }
- } else if (animationName.includes("flip")) {
- // flip animations
- switch (animationName) {
- case "flip-up":
- animation = anime({
- targets: element,
- rotateX: ["100deg", 0],
- perspective: "2500px",
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- case "flip-down":
- animation = anime({
- targets: element,
- rotateX: ["-100deg", 0],
- perspective: "2500px",
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- case "flip-left":
- animation = anime({
- targets: element,
- rotateY: ["-100deg", 0],
- perspective: "2500px",
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- case "flip-right":
- animation = anime({
- targets: element,
- rotateY: ["100deg", 0],
- perspective: "2500px",
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- default:
- // default is flip-up
- animation = anime({
- targets: element,
- translateY: ["100%", 0],
- perspective: "2500px",
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- break;
- }
- } else {
- // overall default is fade-in
- animation = anime({
- targets: element,
- opacity: [0, 1],
- duration: duration,
- delay: delay,
- easing: defaults.easing,
- begin: onStartFunction,
- autoplay: false,
- });
- }
-
- return animation;
-};
-
-export default getAnimation;
diff --git a/packages/aos-anime/src/aos/aos.ts b/packages/aos-anime/src/aos/aos.ts
deleted file mode 100644
index 17ddca8..0000000
--- a/packages/aos-anime/src/aos/aos.ts
+++ /dev/null
@@ -1,239 +0,0 @@
-/**
- * *******************************************************
- * * AOS (Animate on scroll) - but using AnimeJS for animations
- * * made to animate elements on scroll in both directions
- * *******************************************************
- *
- * It is largely based off of AOS code, with modifications to use AnimeJS for the animations instead of pure CSS.
- * Why? Because AOS had issues with view transitions, and AnimeJS does not. Also, AOS hasn't been updated in 6 years.
- *
- * It works off of data attributes on elements:
- * - data-aos = animation name. Currently supports
- * Fades: fade-up, fade-down, fade-left, fade-right, fade-in, fade-up-right, fade-up-left, fade-down-right, fade-down-left
- * Flips: flip-up, flip-down, flip-left, flip-right
- * Slides: slide-up, slide-down, slide-left, slide-right
- * Zooms: zoom-in, zoom-in-up, zoom-in-down, zoom-in-left, zoom-in-right, zoom-out, zoom-out-up, zoom-out-down, zoom-out-left, zoom-out-right
- * - data-aos-delay = delay to wait to start animation after triggered (seconds)
- * - data-aos-duration = duration of animation (seconds)
- * - data-aos-distance = distance for animation travel (pixels)
- * - data-aos-trigger = selector of element to trigger animation on, if not the element itself (usually an id like "#hero1")
- * - data-aos-once = if you have options.once set to false, then this will make an individual element animation only happen once ("true" or "false")
- * - data-aos-mirror = this will make an individual element animate when scrolling up as well as down ("true" or "false"). Requires options.once set to false
- */
-
-// Modules & helpers
-import debounce from "lodash.debounce";
-import throttle from "lodash.throttle";
-
-import { type AOSDefaultOptions, type AOSElement } from "./helpers/aosTypes.js";
-import detect from "./helpers/detector.js";
-import elements from "./helpers/elements.js";
-import handleScroll from "./helpers/handleScroll.js";
-import { getPositionIn, getPositionOut } from "./helpers/offsetCalculator.js";
-import prepare from "./helpers/prepare.js";
-import observer from "./libs/observer.js";
-
-/**
- * Private variables
- */
-let aosElements = [] as AOSElement[];
-let initialized = false;
-
-/**
- * Default options, can be overwritten in the init method
- */
-let options: AOSDefaultOptions = {
- offset: 100, // pixels of offet for triggering (from bottom of viewport)
- delay: 0, // delay before animation executes once triggered
- duration: 0.8, // seconds the animation lasts
- distance: 20, // distance the animation should travel (pixels)
- once: false, // whether animation should happen only once - while scrolling down
- mirror: false, // whether elements should animate when scrolling up as well as down
- easing: "easeOutCubic", // easing function, see https://animejs.com/documentation/#linearEasing for other easing options
- disable: false, // if animations should be disabled
- anchorPlacement: "top-bottom",
- startEvent: "DOMContentLoaded", // if you need a different start event from DOMContentLoaded. Recommend not changing this
- animatedClassName: "aos-animate", // always at least include "aos-animate" for CSS purposes
- initClassName: "aos-init", // uneccesary but leaving it for now
- disableMutationObserver: true,
- throttleDelay: 99,
- debounceDelay: 50,
-};
-
-const initializeScroll = function initializeScroll() {
- // Extend elements objects in aosElements with their positions and animation
- aosElements = prepare(aosElements, options);
- // Perform scroll event, to refresh view and show/hide elements
- handleScroll(aosElements);
-
- /**
- * Handle scroll event to animate elements on scroll
- */
- window.addEventListener(
- "scroll",
- throttle(() => {
- // handleScroll(aosElements, options.once);
- handleScroll(aosElements);
- }, options.throttleDelay),
- );
-
- return aosElements;
-};
-
-/**
- * Refresh AOS
- */
-const refresh = function refresh(initialize = false) {
- // Allow refresh only when it was first initialized on startEvent
- // console.log("refresh");
- if (initialize) initialized = true;
- if (initialized) initializeScroll();
-};
-
-/**
- * Recalculate element positions
- */
-const recalculatePositions = function recalculate() {
- if (initialized) {
- aosElements.forEach((el) => {
- el.position = {
- in: getPositionIn(el.node, options.offset, options.anchorPlacement),
- out: options.mirror && getPositionOut(el.node, options.offset),
- };
- });
- // Perform scroll event, to refresh view and show/hide elements
- handleScroll(aosElements);
- }
-};
-
-/**
- * Hard refresh
- * create array with new elements and trigger refresh
- */
-const refreshHard = function refreshHard() {
- // console.log("refresh hard");
- aosElements = elements();
-
- if (isDisabled(options.disable)) {
- return disable();
- }
-
- refresh();
-};
-
-/**
- * Disable AOS
- * Remove all attributes to reset applied styles
- */
-const disable = function () {
- aosElements.forEach((el) => {
- el.node.removeAttribute("data-aos");
- el.node.removeAttribute("data-aos-delay");
- el.node.removeAttribute("data-aos-distance");
- el.node.removeAttribute("data-aos-duration");
-
- if (options.initClassName) {
- el.node.classList.remove(options.initClassName);
- }
-
- if (options.animatedClassName) {
- el.node.classList.remove(options.animatedClassName);
- }
- });
-};
-
-/**
- * Check if AOS should be disabled based on provided setting
- */
-const isDisabled = function (optionDisable) {
- return (
- optionDisable === true ||
- (optionDisable === "mobile" && detect.mobile()) ||
- (optionDisable === "phone" && detect.phone()) ||
- (optionDisable === "tablet" && detect.tablet()) ||
- (typeof optionDisable === "function" && optionDisable() === true)
- );
-};
-
-/**
- * Initializing AOS
- * - Create options merging defaults with user defined options
- * - Set attributes on
as global setting - css relies on it
- * - Attach preparing elements to options.startEvent,
- * window resize and orientation change
- * - Attach function that handle scroll and everything connected to it
- * to window scroll event and fire once document is ready to set initial state
- */
-const init = function init(settings?: Partial) {
- options = Object.assign(options, settings);
-
- // Create initial array with elements -> to be fullfilled later with prepare()
- aosElements = elements();
-
- /**
- * Disable mutation observing if not supported
- */
- if (!options.disableMutationObserver && !observer.isSupported()) {
- console.info(`
- aos: MutationObserver is not supported on this browser,
- code mutations observing has been disabled.
- You may have to call "refreshHard()" by yourself.
- `);
- options.disableMutationObserver = true;
- }
-
- /**
- * Observe [aos] elements
- * If something is loaded by AJAX
- * it'll refresh plugin automatically
- */
- if (!options.disableMutationObserver) {
- observer.ready("[data-aos]", refreshHard);
- }
-
- /**
- * Don't init plugin if option `disable` is set
- */
- if (isDisabled(options.disable)) {
- return disable();
- }
-
- /**
- * Handle initializing
- */
- if (["DOMContentLoaded", "load"].indexOf(options.startEvent) === -1) {
- // Listen to options.startEvent and initialize AOS
- document.addEventListener(options.startEvent, function () {
- refresh(true);
- });
- } else {
- // window.addEventListener("load", function () {
- // refresh(true);
- // });
- window.addEventListener("DOMContentLoaded", function () {
- refresh(true);
- });
- }
-
- /**
- * Recalculate positions of elements on window resize or orientation change
- */
- window.addEventListener("resize", debounce(recalculatePositions, options.debounceDelay, true));
-
- window.addEventListener(
- "orientationchange",
- debounce(recalculatePositions, options.debounceDelay, true),
- );
-
- return aosElements;
-};
-
-/**
- * Export Public API
- */
-
-export default {
- init,
- refresh,
- refreshHard,
-};
diff --git a/packages/aos-anime/src/aos/helpers/aosTypes.ts b/packages/aos-anime/src/aos/helpers/aosTypes.ts
deleted file mode 100644
index bcf55c1..0000000
--- a/packages/aos-anime/src/aos/helpers/aosTypes.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-export interface AnimeOptions {
- delay: number;
- duration: number;
- distance: number;
- easing: string; // one of the supported animeJS easings: https://animejs.com/documentation/#pennerFunctions
-}
-
-export type TriggerPlacement =
- | "top-bottom"
- | "center-bottom"
- | "bottom-bottom"
- | "top-center"
- | "center-center"
- | "bottom-center"
- | "top-top"
- | "bottom-top"
- | "center-top";
-
-export interface AOSDefaultOptions extends AnimeOptions {
- offset: number;
- once: boolean;
- mirror: boolean;
- disable: boolean;
- anchorPlacement: TriggerPlacement;
- startEvent: string;
- animatedClassName: string;
- initClassName: string;
- disableMutationObserver: boolean;
- throttleDelay: number;
- debounceDelay: number;
-}
-
-// Define a type for anime.js animation instance
-export interface AnimeInstance {
- play: () => void;
- pause: () => void;
- restart: () => void;
- reverse: () => void;
- seek: (time: number) => void;
- reversed: boolean;
- completed: boolean;
-}
-
-export interface AOSElement {
- node: HTMLElement;
- animation?: AnimeInstance; // Replace 'any' with a more specific type
- animated?: boolean;
- position?: {
- in?: number;
- out?: number | false;
- };
- options?: {
- once?: boolean;
- mirror?: boolean;
- animatedClassNames?: string[];
- trigger?: string;
- };
-}
diff --git a/packages/aos-anime/src/aos/helpers/detector.ts b/packages/aos-anime/src/aos/helpers/detector.ts
deleted file mode 100644
index 8e77519..0000000
--- a/packages/aos-anime/src/aos/helpers/detector.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-/**
- * Device detector
- */
-
-const fullNameRe =
- /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i;
-const prefixRe =
- /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i;
-const fullNameMobileRe =
- /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino|android|ipad|playbook|silk/i;
-const prefixMobileRe =
- /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw-(n|u)|c55\/|capi|ccwa|cdm-|cell|chtm|cldc|cmd-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc-s|devi|dica|dmob|do(c|p)o|ds(12|-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(-|_)|g1 u|g560|gene|gf-5|g-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd-(m|p|t)|hei-|hi(pt|ta)|hp( i|ip)|hs-c|ht(c(-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i-(20|go|ma)|i230|iac( |-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|-[a-w])|libw|lynx|m1-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|-([1-8]|c))|phil|pire|pl(ay|uc)|pn-2|po(ck|rt|se)|prox|psio|pt-g|qa-a|qc(07|12|21|32|60|-[2-7]|i-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h-|oo|p-)|sdk\/|se(c(-|0|1)|47|mc|nd|ri)|sgh-|shar|sie(-|m)|sk-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h-|v-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl-|tdg-|tel(i|m)|tim-|t-mo|to(pl|sh)|ts(70|m-|m3|m5)|tx-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas-|your|zeto|zte-/i;
-
-function ua() {
- return navigator.userAgent || "";
-}
-
-class Detector {
- phone() {
- const a = ua();
- return !!(fullNameRe.test(a) || prefixRe.test(a.substr(0, 4)));
- }
-
- mobile() {
- const a = ua();
- return !!(fullNameMobileRe.test(a) || prefixMobileRe.test(a.substr(0, 4)));
- }
-
- tablet() {
- return this.mobile() && !this.phone();
- }
-
- // http://browserhacks.com/#hack-acea075d0ac6954f275a70023906050c
- ie11() {
- return (
- "-ms-scroll-limit" in document.documentElement.style &&
- "-ms-ime-align" in document.documentElement.style
- );
- }
-}
-
-export default new Detector();
diff --git a/packages/aos-anime/src/aos/helpers/elements.ts b/packages/aos-anime/src/aos/helpers/elements.ts
deleted file mode 100644
index 3d306c6..0000000
--- a/packages/aos-anime/src/aos/helpers/elements.ts
+++ /dev/null
@@ -1,11 +0,0 @@
-/**
- * Generate initial array with elements as objects
- * This array will be extended later with elements attributes values
- * like 'position', 'animation', 'options' etc.
- */
-import { type AOSElement } from "./aosTypes.js";
-
-export default (): AOSElement[] => {
- const elements = document.querySelectorAll("[data-aos]");
- return Array.prototype.map.call(elements, (node) => ({ node }));
-};
diff --git a/packages/aos-anime/src/aos/helpers/getInlineOption.ts b/packages/aos-anime/src/aos/helpers/getInlineOption.ts
deleted file mode 100644
index 94215bf..0000000
--- a/packages/aos-anime/src/aos/helpers/getInlineOption.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-/**
- * Get inline option with a fallback.
- *
- * @param {Node} el [Dom element]
- * @param {String} key [Option key]
- * @param {String} fallback [Default (fallback) value]
- * @return {Mixed} [Option set with inline attribute or fallback value if not set]
- */
-
-export default (
- el: Element,
- key: string,
- fallback?: string | number | boolean,
-): string | number | boolean => {
- const attr = el.getAttribute("data-aos-" + key);
-
- fallback = fallback || "";
-
- if (typeof attr !== "undefined") {
- if (attr === "true") {
- return true;
- } else if (attr === "false") {
- return false;
- }
- }
-
- return attr || fallback;
-};
diff --git a/packages/aos-anime/src/aos/helpers/handleScroll.ts b/packages/aos-anime/src/aos/helpers/handleScroll.ts
deleted file mode 100644
index 5124b9c..0000000
--- a/packages/aos-anime/src/aos/helpers/handleScroll.ts
+++ /dev/null
@@ -1,100 +0,0 @@
-import { type AOSElement } from "./aosTypes.js";
-
-/**
- * Adds multiple classes on node
- * @param {DOMNode} node
- * @param {array} classes
- */
-const addClasses = (node, classes) =>
- classes && classes.forEach((className) => node.classList.add(className));
-
-/**
- * Removes multiple classes from node
- * @param {DOMNode} node
- * @param {array} classes
- */
-const removeClasses = (node, classes) =>
- classes && classes.forEach((className) => node.classList.remove(className));
-
-const fireEvent = (eventName, data) => {
- const customEvent = new CustomEvent(eventName, {
- detail: data,
- });
-
- return document.dispatchEvent(customEvent);
-};
-
-/**
- * Set or remove aos-animate class
- * @param {node} el element
- * @param {int} top scrolled distance
- */
-const applyClasses = (el: AOSElement, top: number) => {
- const { options, position, node } = el;
-
- const hide = () => {
- if (!el.animated || !el.animation) return;
-
- removeClasses(node, options?.animatedClassNames);
-
- // reverse animation for hiding
- el.animation.reverse();
-
- // if animation is not already playing, play it
- if (!el.animation.completed) {
- el.animation.play();
- }
-
- fireEvent("aos:out", node);
-
- if (el.options?.trigger) {
- fireEvent(`aos:in:${el.options.trigger}`, node);
- }
-
- el.animated = false;
- };
-
- const show = () => {
- if (el.animated || !el.animation) return;
-
- addClasses(node, options?.animatedClassNames);
-
- // if animation is reversed (from hiding), reverse it back
- if (el.animation.reversed) {
- el.animation.reverse();
- }
-
- // play animation
- el.animation.play();
-
- fireEvent("aos:in", node);
-
- if (el.options?.trigger) {
- fireEvent(`aos:in:${el.options.trigger}`, node);
- }
-
- el.animated = true;
- };
-
- if (
- options?.mirror &&
- position?.out !== undefined &&
- position.out !== false &&
- top >= position.out &&
- !options?.once
- ) {
- hide();
- } else if (position?.in !== undefined && top >= position.in) {
- show();
- } else if (el.animated && !options?.once) {
- hide();
- }
-};
-
-/**
- * Scroll logic - add or remove 'aos-animate' class on scroll
- */
-const handleScroll = (elements: AOSElement[]) =>
- elements.forEach((el) => applyClasses(el, window.scrollY));
-
-export default handleScroll;
diff --git a/packages/aos-anime/src/aos/helpers/offsetCalculator.ts b/packages/aos-anime/src/aos/helpers/offsetCalculator.ts
deleted file mode 100644
index 668e7ba..0000000
--- a/packages/aos-anime/src/aos/helpers/offsetCalculator.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-/**
- * Calculate offset
- * based on element's settings like:
- * - trigger
- * - offset
- */
-
-import getOffset from "../libs/offset.js";
-import { type TriggerPlacement } from "./aosTypes.js";
-import getInlineOption from "./getInlineOption.js";
-
-// returns the final offset that will be used to trigger animation in good position
-export const getPositionIn = (
- el: HTMLElement,
- defaultOffset: number,
- defaultTriggerPlacement: TriggerPlacement,
-): number => {
- const windowHeight = window.innerHeight;
- const trigger = getInlineOption(el, "trigger") as string | null;
- const inlinetriggerPlacement = getInlineOption(el, "trigger-placement");
- const additionalOffset = Number(
- getInlineOption(el, "offset", inlinetriggerPlacement ? 0 : defaultOffset),
- );
- const triggerPlacement = inlinetriggerPlacement || defaultTriggerPlacement;
- let finalEl = el;
-
- if (trigger && document.querySelectorAll(trigger)) {
- finalEl = document.querySelectorAll(trigger)[0] as HTMLElement;
- }
-
- let triggerPoint = getOffset(finalEl).top - windowHeight;
-
- switch (triggerPlacement) {
- case "top-bottom":
- // Default offset
- break;
- case "center-bottom":
- triggerPoint += finalEl.offsetHeight / 2;
- break;
- case "bottom-bottom":
- triggerPoint += finalEl.offsetHeight;
- break;
- case "top-center":
- triggerPoint += windowHeight / 2;
- break;
- case "center-center":
- triggerPoint += windowHeight / 2 + finalEl.offsetHeight / 2;
- break;
- case "bottom-center":
- triggerPoint += windowHeight / 2 + finalEl.offsetHeight;
- break;
- case "top-top":
- triggerPoint += windowHeight;
- break;
- case "bottom-top":
- triggerPoint += windowHeight + finalEl.offsetHeight;
- break;
- case "center-top":
- triggerPoint += windowHeight + finalEl.offsetHeight / 2;
- break;
- }
-
- return triggerPoint + additionalOffset;
-};
-
-export const getPositionOut = (el: HTMLElement, defaultOffset: number): number => {
- const trigger = getInlineOption(el, "trigger") as string;
- const additionalOffset = getInlineOption(el, "offset", defaultOffset) as number;
- let finalEl = el;
-
- if (trigger && document.querySelectorAll(trigger)) {
- finalEl = document.querySelectorAll(trigger)[0] as HTMLElement;
- }
-
- const elementOffsetTop = getOffset(finalEl).top;
-
- return elementOffsetTop + finalEl.offsetHeight - additionalOffset;
-};
diff --git a/packages/aos-anime/src/aos/helpers/prepare.ts b/packages/aos-anime/src/aos/helpers/prepare.ts
deleted file mode 100644
index 051f559..0000000
--- a/packages/aos-anime/src/aos/helpers/prepare.ts
+++ /dev/null
@@ -1,42 +0,0 @@
-/* Clearing variables */
-
-// Scroll animations
-import { getAnimation } from "../anime/animations.js";
-import { type AOSDefaultOptions, type AOSElement } from "./aosTypes.js";
-import getInlineOption from "./getInlineOption.js";
-import { getPositionIn, getPositionOut } from "./offsetCalculator.js";
-
-const prepare = function (aosElements: AOSElement[], options: AOSDefaultOptions): AOSElement[] {
- aosElements.forEach((el) => {
- const animationName = el.node.getAttribute("data-aos") as string;
- const mirror = getInlineOption(el.node, "mirror", options.mirror) as boolean;
- const once = getInlineOption(el.node, "once", options.once) as boolean;
- const trigger = getInlineOption(el.node, "trigger") as string | undefined;
-
- const animatedClassNames = [options.animatedClassName].filter(
- (className) => typeof className === "string",
- );
-
- if (options.initClassName) {
- el.node.classList.add(options.initClassName);
- }
-
- el.position = {
- in: getPositionIn(el.node, options.offset, options.anchorPlacement),
- out: mirror && getPositionOut(el.node, options.offset),
- };
-
- el.animation = getAnimation(el.node, animationName, options);
-
- el.options = {
- once,
- mirror,
- animatedClassNames,
- trigger,
- };
- });
-
- return aosElements;
-};
-
-export default prepare;
diff --git a/packages/aos-anime/src/aos/libs/observer.ts b/packages/aos-anime/src/aos/libs/observer.ts
deleted file mode 100644
index 561c1f5..0000000
--- a/packages/aos-anime/src/aos/libs/observer.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-let callback = () => {};
-
-function containsAOSNode(nodes) {
- let i, currentNode, result;
-
- for (i = 0; i < nodes.length; i += 1) {
- currentNode = nodes[i];
-
- if (currentNode.dataset && currentNode.dataset.aos) {
- return true;
- }
-
- result = currentNode.children && containsAOSNode(currentNode.children);
-
- if (result) {
- return true;
- }
- }
-
- return false;
-}
-
-function check(mutations) {
- if (!mutations) return;
-
- mutations.forEach((mutation) => {
- const addedNodes = Array.prototype.slice.call(mutation.addedNodes);
- const removedNodes = Array.prototype.slice.call(mutation.removedNodes);
- const allNodes = addedNodes.concat(removedNodes);
-
- if (containsAOSNode(allNodes)) {
- return callback();
- }
- });
-}
-
-function getMutationObserver() {
- return window.MutationObserver;
-}
-
-function isSupported() {
- return !!getMutationObserver();
-}
-
-function ready(selector, fn) {
- const doc = window.document;
- const MutationObserver = getMutationObserver();
-
- const observer = new MutationObserver(check);
- callback = fn;
-
- observer.observe(doc.documentElement, {
- childList: true,
- subtree: true,
- });
-}
-
-export default { isSupported, ready };
diff --git a/packages/aos-anime/src/aos/libs/offset.ts b/packages/aos-anime/src/aos/libs/offset.ts
deleted file mode 100644
index 436bfb2..0000000
--- a/packages/aos-anime/src/aos/libs/offset.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * Get offset of DOM element
- * like there were no transforms applied on it
- *
- * @param {Node} el [DOM element]
- * @return {Object} [top and left offset]
- */
-const offset = function (el: HTMLElement) {
- let _x = 0;
- let _y = 0;
-
- while (el && !isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) {
- _x += el.offsetLeft - (el.tagName != "BODY" ? el.scrollLeft : 0);
- _y += el.offsetTop - (el.tagName != "BODY" ? el.scrollTop : 0);
- el = el.offsetParent as HTMLElement;
- }
-
- return {
- top: _y,
- left: _x,
- };
-};
-
-export default offset;
diff --git a/packages/motion-on-scroll/README.md b/packages/motion-on-scroll/README.md
index 57d5aa7..e481d26 100644
--- a/packages/motion-on-scroll/README.md
+++ b/packages/motion-on-scroll/README.md
@@ -2,8 +2,6 @@
[](https://npmjs.org/package/motion-on-scroll) [](https://npmjs.org/package/motion-on-scroll) [](LICENSE) [](https://twitter.com/webreaper)
-## Note: this is currently in beta. Feel free to try it though :)
-
Effortless, AOS-compatible scroll animations powered by [Motion](https://motion.dev).
Framework-agnostic, MOS lets you add scroll-triggered animations with nothing but `data-mos` attributes. Under the hood it uses Motion’s powerful `animate` API, giving you:
diff --git a/vitest.config.ts b/vitest.config.ts
deleted file mode 100644
index f6ae203..0000000
--- a/vitest.config.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { defineConfig } from "vitest/config";
-
-export default defineConfig({
- test: {
- environment: "jsdom",
- globals: true,
- setupFiles: ["./vitest.setup.ts"],
- include: ["packages/**/src/__tests__/**/*.spec.ts"],
- },
-});