Skip to content

Commit

Permalink
feat(docs): better docs
Browse files Browse the repository at this point in the history
  • Loading branch information
cxspxr committed Oct 25, 2022
1 parent f57013f commit 789c688
Show file tree
Hide file tree
Showing 11 changed files with 1,018 additions and 756 deletions.
816 changes: 60 additions & 756 deletions README.md

Large diffs are not rendered by default.

Empty file added docs/.nojekyll
Empty file.
111 changes: 111 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
<h1>Navigation</h1>

- [Quick start](/)
- [Usage](usage.md)
- [Why this package](comparison.md)
- [API](api.md)
- [Advanced usage](advanced-usage.md)
- [Q&A](qa.md)

## Quick start

### Installation

```bash
$ npm i use-listen-on-animation-frame
```

### Use

Library provides 2 hooks which are `useAnimationFrame`, `useListenOnAnimationFrame`. See [Usage](usage.md), [API](api.md) and [Advanced usage](advanced-usage.md) for more.

#### Basic

Run function on every animation frame

```tsx
import React, { useCallback, useState } from "react";
import { useAnimationFrame } from "use-listen-on-animation-frame";

const getDate = () => new Date();

const Component = () => {
const [date, setDate] = useState(getDate());

const setDateOnAnimationFrame = useCallback(() => {
setDate(getDate());
}, []);

useAnimationFrame(setDateOnAnimationFrame);

return <div>{date}</div>;
};
```

#### Multiple side effects

Run function once on every animation frame, and apply multiple listeners to it. Stop and start function & listeners when you want.

```tsx
import React, { useState } from "react";
import { useListenOnAnimationFrame } from "use-listen-on-animation-frame";

const getCostlyRandomNumber = () => {
const random = Math.random() * 300;

let counter = 0;

for (; counter < random; counter++) {
counter++;
}

return counter;
};

const Component = () => {
const [number, setNumber] = useState(0);

const [addListener, _removeListener, stop, start] = useListenOnAnimationFrame(
getCostlyRandomNumber,
{ autoStart: false } // optional
);

useEffect(() => {
addListener((thisFrameRandom, _previousFrameRandom) => {
setNumber(thisFrameRandom);
});

addListener((thisFrameRandom) => {
// do something;
});

addListener((thisFrameRandom) => {
for (let i = 0; i < thisFrameRandom; i++) {
// do something
}
});
}, [addListener]);

useEffect(() => {
if (somethingBadHappened) {
stop();
}
}, [stop, somethingBadHappened]);

useEffect(() => {
if (somethingGoodHappened) {
start();
}
}, [start, somethingGoodHappened]);

return (
<div>
<span>{number}</span>
<AnotherComponent
addFrameListener={addListener}
stopFrameListening={stop}
/>
</div>
);
};
```
29 changes: 29 additions & 0 deletions docs/_coverpage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<!-- _coverpage.md -->

# use-listen-on-animation-frame

> Invoke & track your functions on every animation frame
<p align="center">
<em>
ESM
· CommonJS
· 1 dependency
</em>
</p>

<p align="center">
<a href="https://github.com/artelydev/use-listen-on-animation-frame/actions?query=workflow%3AMain+branch%3Amain">
<img alt="Github Actions Build Status" src="https://img.shields.io/github/workflow/status/artelydev/use-listen-on-animation-frame/Main?label=Build&style=flat-square"></img></a>
<a href="https://www.npmjs.com/package/use-listen-on-animation-frame">
<img alt="npm version" src="https://img.shields.io/npm/v/use-listen-on-animation-frame.svg?style=flat-square"></img></a>
<a href="https://github.com/artelydev/use-listen-on-animation-frame/blob/main/LICENSE">
<img alt="license: MIT" src="https://img.shields.io/github/license/artelydev/use-listen-on-animation-frame">
</img>
</a>
<img alt="npm bundle size" src="https://img.shields.io/bundlephobia/minzip/use-listen-on-animation-frame"></img>
<img alt="typed" src="https://shields.io/badge/TypeScript-3178C6?logo=TypeScript&logoColor=FFF&style=flat-square"> </img>
</p>

[GitHub](https://github.com/artelydev/use-listen-on-animation-frame/)
[Get Started](#quick-start)
15 changes: 15 additions & 0 deletions docs/_sidebar.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
- [Quick start](/)
- [Usage](usage.md)
- [Do something on animation frame](usage.md#do-something-on-animation-frames)
- [Animation frame counter](usage.md#animation-frame-counter)
- [Video current time](usage.md#video-amp-current-timer)
- [Side effects](usage.md#track-your-function-return-on-every-animation-frame)
- [Why this package](comparison.md)
- [API](api.md)
- [`useAnimationFrame`](api.md#useanimationframe)
- [`useListenOnAnimationFrame`](api.md#uselistenonanimationframe)
- [When to use `useListenOnAnimationFrame` over `useAnimationFrame`](api.md#when-to-use-uselistenonanimationframe-over-useanimationframe)
- [Advanced usage](advanced-usage.md)
- [Previous animation frame return](advanced-usage.md#access-previous-animation-frame-function-return)
- [Start & stop](advanced-usage.md#start-and-stop-tracking-your-function)
- [Q&A](qa.md)
217 changes: 217 additions & 0 deletions docs/advanced-usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
# Advanced usage

## Access previous animation frame function return

If you for some reason need previous animation frame return of your function - it is easily possible.

[Try it on codesandbox](https://codesandbox.io/s/ms-elapsed-from-1970-pwgpft)

```tsx
import React, { useEffect, useState } from "react";
import { useListenOnAnimationFrame } from "use-listen-on-animation-frame";

const getMsElapsedFrom1970 = (previousFrameTimeElapsed) => {
if (previousFrameTimeElapsed) {
console.log(
"you wouldn't want to do it here but",
previousFrameTimeElapsed
);
}

return new Date().getTime();
};

const MilisecondsElapsedFrom1970: React.FC = () => {
const [ms, setMs] = useState<number>(new Date().getTime());

const [addListener] = useListenOnAnimationFrame(getMsElapsedFrom1970);

useEffect(() => {
addListener((_, previousFrameTimeElapsed) => {
/**
* Can be undefined, on the first call,
* because there was no previous one
*/
if (previousFrameTimeElapsed) {
setMs(previousFrameTimeElapsed);
}
});
}, [addListener]);

return (
<>
<p>ms elapsed from 1970 on previous browser frame: {ms}</p>
</>
);
};
```

---

## Start and stop tracking your function

You can stop and start tracking again whenever you want.

[Try it on codesandbox](https://codesandbox.io/s/controllable-timer-292wmz)

<em>[Btw, compare the above performance with `setInterval`. You couldn't achieve same smoothness when event loop is busy](https://codesandbox.io/s/interval-vs-animation-frame-065es8)</em>

```tsx
import React, { useEffect, useState } from "react";
import { useListenOnAnimationFrame } from "use-listen-on-animation-frame";

const formatDate = (date: Date) => {
return `${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}.${date.getMilliseconds()}`;
};

const getDate = () => {
return new Date();
};

export const ExtremelySmoothTimer: React.FC = () => {
const [currentTime, setCurrentTime] = useState<string>(
formatDate(new Date())
);
const [isTicking, setIsTicking] = useState(true);

const [addListener, , stop, start] = useListenOnAnimationFrame(getDate, {
/**
* optionally indicate that the getDate function and
* listeners should not be invoked until you `start()`
*/
autoStart: false,
});

useEffect(() => {
addListener((date) => {
setCurrentTime(formatDate(date));
});
}, [addListener]);

useEffect(() => {
if (isTicking) {
/* start tracking getDate & listeners */
start();
} else {
/* stop tracking getDate & listeners */
stop();
}
}, [isTicking, start, stop]);

return (
<>
<div>Smooth timer: {currentTime}</div>
<div>
{!isTicking && (
<button
onClick={() => {
setIsTicking(true);
}}
>
Start timer
</button>
)}
{isTicking && (
<button
onClick={() => {
setIsTicking(false);
}}
>
Stop timer
</button>
)}
</div>
</>
);
};
```

---

## Optimize/Unoptimize your listeners

By default, if you don't provide [`shouldInvokeListeners` option](#optionsshouldinvokelisteners) - listeners will be invoked only if tracked function return changes. It means that a supplied function will still be invoked on every animation frame, but listeners will not.

[Try it on codesandbox](https://codesandbox.io/s/player-timer-heavy-load-yqz79q)

```tsx
import React, { useCallback, useEffect, useState, useRef } from "react";
import { useListenOnAnimationFrame } from "use-listen-on-animation-frame";

const conditionallyInvokeListeners = (
nextValue: number,
_previousValue: number /* previous animation frame value */
) => {
/* defaults to return nextValue !== previousValue */

/**
* invoke only if current animation
* frame current time is bigger than 2
* second AND lesser than 3 seconds
*/
return nextValue > 2 && nextValue < 3;
};

const alwaysInvokeListeners = () => {
/**
* usually you shouldn't do this, as we try to cut
* performance costs, we don't want to invoke a bunch
* of functions even if tracked function return hasn't changed
*
* Listeners will be invoked even if player current time hasn't changed
*/
return true;
};

const VideoWithCurrentTime: React.FC = () => {
const [videoCurrentTime, setVideoCurrentTime] = useState<number>(0);
const videoRef = useRef<HTMLVideoElement>(null);

/* better memoized */
const getVideoTime = useCallback(() => {
if (videoRef.current) {
return videoRef.current.currentTime;
}
}, []);

const [addOptimizedListener] = useListenOnAnimationFrame(getVideoTime, {
shouldInvokeListeners: conditionallyInvokeListeners,
});

const [addNotOptimizedListener] = useListenOnAnimationFrame(getVideoTime, {
shouldInvokeListeners: alwaysInvokeListeners,
});

useEffect(() => {
addNotOptimizedListener((currentTime) => {
setVideoCurrentTime(currentTime);
});
}, [addNotOptimizedListener]);

useEffect(() => {
addOptimizedListener(() => {
/**
* does something heavy only when video current time
* is between 1 and 2 seconds
*/
for (let i = 0; i < 1000; i++) {
console.log(":)");
}

console.clear();
});
}, [addOptimizedListener]);

return (
<>
<h1>Player with timer & heavy load between 2 and 3s</h1>
<video
controls
src="https://www.w3schools.com/html/mov_bbb.mp4"
ref={videoRef}
/>
<p>Video time is: {videoCurrentTime}</p>
</>
);
};
```
Loading

0 comments on commit 789c688

Please sign in to comment.