From bd2809e91f88fe252b713aa99d3c11589830f5e0 Mon Sep 17 00:00:00 2001 From: Mickael Chanrion Date: Fri, 22 Dec 2023 10:20:25 -0300 Subject: [PATCH] docs: create documentation --- README.md | 241 +++++++++++++++++++++++++++++++++++++++++++++++++-- src/index.ts | 20 +++-- 2 files changed, 245 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 9432743..8aa972c 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,19 @@ -# data-scroll +# DataScroll [![npm version][npm-version-src]][npm-version-href] [![npm downloads][npm-downloads-src]][npm-downloads-href] [![bundle][bundle-src]][bundle-href] -[![Codecov][codecov-src]][codecov-href] + -Scroll parallax, animation from/to using data attributes +Easily create responsive parallax effect and scroll animations on any element using data attributes. + +- 👀 The element only animates while inside the viewport. +- 🎯 The element will have its normal position when in the middle of the viewport. +- 🎨 Animate any property from/to any value. +- 🌈 Apply breakpoint-specific speeds. +- ⚙️ Customize the data-attribute names (hell, even use classes if you want!). +- 🩻 Add helpful markers during development/troubleshooting +- 🚀 Leverages [GSAP](https://gsap.com/docs/v3/GSAP/) and [ScrollTrigger](https://gsap.com/docs/v3/Plugins/ScrollTrigger/) under the hood. ## Usage @@ -25,16 +33,231 @@ pnpm install data-scroll bun install data-scroll ``` -Import: +Import and instantiate: + +```ts +import { useDataScroll } from "data-scroll"; + +useDataScroll(); +``` + +Play around! +```html +
Oowee!
+``` + +## Data attributes + +By default, the following data attributes are used: + +### `data-scroll-speed` + +The speed factor at which the element will move. + +> - 1 is the default +> - 0 is ignored +> - 0.5 will make that element go at half-speed +> - 2 will make it go twice as fast. + +```html +
Oowee!
+``` + +#### Responsive speed + +You can specify the speed depending on a breakpoint. + +```html +
Oowee!
+``` +> In this example, the speed will be 0.5 starting at medium screens and 0.2 starting at large screens. + +#### Clamp + +Your element is above the fold and you want it to start from its normal position? Use `clamp`! + +```html +
Oowee!
+``` + +> If the element is below the fold, clamp will be ignored. + +### `data-scroll-from` + +The style from which the element will animate from. See it as a `gsap.from()` + +> Beware that the value has to be valid JSON. For functions, use the `getFrom` option. + +```html +
Oowee!
+``` + +> In this example, the element will animate from a black background color to its original background color. + +### `data-scroll-to` + +The style to which the element will animate to. See it as a `gsap.to()` + +> Beware that the value has to be valid JSON. For functions, use the `getTo` option. + +```html +
Oowee!
+``` + +> In this example, the element will rotate to 360 degrees. + +### `data-scroll-markers` + +Add helpful markers for development/troubleshooting. It's ScrollTrigger's markers option. + +```html +
Oowee!
+``` + +## Options + +### autoStart + +If `true`, will automatically find and instantiate elements. +If `false`, you will have to instantiate manually. + +- type: `boolean` +- default: `true` + +```ts +const dataScroll = useDataScroll({ + autoStart: false, +}); + +const targets = document.querySelectorAll('[data-scroll-speed]') +for (const target of targets) { + dataScroll.apply(target) +} +``` + +### screens + +The breakpoints used for the `data-scroll-speed` attribute. The defaults are the same as [TailwindCSS](https://tailwindcss.com/docs/breakpoints) but you can customize them. + +> Only min-width breakpoints are supported. + +- type: `Record` +- default: +```ts +{ + sm: '640px', + md: '768px', + lg: '1024px', + xl: '1280px', + "2xl": '1536px' +} +``` + +Feel free to have as few or as many screens as you want, naming them in whatever way you’d prefer for your project. + +```ts +useDataScroll({ + screens: { + 'tablet': '640px', + 'laptop': '1024px', + 'desktop': '1280px', + }, +}); +``` + +```html +
Oowee!
+``` + +### selector + +The selector used to find elements to instantiate. +- type: `string` +- default: `[data-scroll-speed],[data-scroll-from],[data-scroll-to]` + +```ts +useDataScroll({ + selector: '.foo', +}); +``` + +### getSpeed + +A function that returns the speed of an element. +- type: `(target: HTMLElement) => string | void` +- default: `(target) => target.dataset.scrollSpeed` + +> If you don't want or just can't use the default data-attributes, you can plug your custom logic. -```js -// ESM -import {} from "data-scroll"; +Let's say you want to use classes instead of data-attributes: -// CommonJS -const {} = require("data-scroll"); +```ts +// Extract from a string the contents inside brackets +function extractValue(value: string) { + const matches = value.match(/\[(.*?)\]/); + if (matches) { + return matches[1].replaceAll("_", " "); + } + return ""; +} + +useDataScroll({ + selector: '[class^="scroll-speed"]', + getSpeed: (target) => { + // Find the class that starts with "scroll-speed" + const value = Array.from(target.classList).find((className) => { + return className.startsWith("scroll-speed"), + }); + // Extract the value inside the brackets + if (value) return extractValue(value); + }, +}) +``` + +```html +
Oowee!
+
Oooooowwweeeee!
``` +### getFrom + +A function that returns the style to animate the element from. +- type: `(target: HTMLElement) => gsap.TweenVars | void` +- default: +```ts +(target: HTMLElement) => { + const data = target.dataset.scrollFrom; + if (data) return JSON.parse(data); +} +``` + +> You could use predefined animations +```ts +useDataScroll({ + getFrom: (target) => { + if (target.classList.contains("rotate-360")) { + return { rotate: 360 }; + } + } +}) +``` + +```html +
+``` + +### getTo + +A function that returns the style to animate the element to. See [getFrom](#getfrom). + +## Roadmap + +- [x] documentation +- [ ] tests +- [ ] playground +- [ ] Add data-scroll-progress +- [ ] Add hooks for ScrollTrigger like toggleClass, onEnter, onLeave, etc. + ## Development - Clone this repository diff --git a/src/index.ts b/src/index.ts index b14b8e8..2d5b646 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,14 +1,16 @@ import { gsap } from 'gsap' import { ScrollTrigger } from 'gsap/ScrollTrigger' +gsap.registerPlugin(ScrollTrigger) + type Screens = Record export interface UseDataScrollOptions { autoStart?: boolean screens?: Screens selector?: string - getSpeed?: (target: HTMLElement) => string | undefined - getFrom?: (target: HTMLElement) => gsap.TweenVars | undefined - getTo?: (target: HTMLElement) => gsap.TweenVars | undefined + getSpeed?: (target: HTMLElement) => string | void + getFrom?: (target: HTMLElement) => gsap.TweenVars | void + getTo?: (target: HTMLElement) => gsap.TweenVars | void getUseMarkers?: (target: HTMLElement) => boolean } @@ -31,8 +33,7 @@ let matchMedia: gsap.MatchMedia function getVariables(element: HTMLElement, dataKey: string) { const data = element.dataset[dataKey] - if (!data) return - return JSON.parse(data) as gsap.TweenVars + if (data) return JSON.parse(data) as gsap.TweenVars } function getMediaQuery( @@ -43,7 +44,8 @@ function getMediaQuery( if (!screens) screens = defaultScreen if (!screen) { if (type === 'min') return '(min-width: 0px)' - if (type === 'max') throw new Error('Only min is supported without screen.') + if (type === 'max') + throw new Error('Only the type "min" is allowed if screen is undefined.') return undefined as never } @@ -205,7 +207,11 @@ function apply(target: HTMLElement, options?: ApplyOptions) { ) // Prevent the element from being visible before the animation starts - if (speed > 1 && startOffset !== 0) { + if ( + speed > 1 && + startOffset !== 0 && + ScrollTrigger.positionInViewport(target, 'bottom') > 1 + ) { gsap.set(target, { visibility: 'hidden', })