Skip to content

Commit

Permalink
feat: enable v3 (replace v1 syntax) (#36)
Browse files Browse the repository at this point in the history
* feat: migrate v1 syntax => v3

BREAKING CHANGE: not work with v1 anymore

* chore: disable eslint rule "react/react-in-jsx-scope"

* chore: add `declarationMap` for dev experience

This enables jump to src code

* doc: add example

* chore(ci): remove yarn.lock for monorepo dev

* chore: rm eslint cache
  • Loading branch information
ken0x0a committed Jun 23, 2023
1 parent c0aa74c commit c3c20d7
Show file tree
Hide file tree
Showing 13 changed files with 243 additions and 9,826 deletions.
1 change: 0 additions & 1 deletion .eslintcache

This file was deleted.

1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module.exports = {
rules: {
camelcase: 0,
"react/require-default-props": 0,
"react/react-in-jsx-scope": 0,
"@typescript-eslint/naming-convention": 0,
},
};
8 changes: 4 additions & 4 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ jobs:
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/package.json') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install dependencies
run: yarn install --frozen-lockfile
run: yarn install

- name: Type Check
run: yarn run type-check
Expand All @@ -48,12 +48,12 @@ jobs:
id: yarn-cache # use this to check for `cache-hit` (`steps.yarn-cache.outputs.cache-hit != 'true'`)
with:
path: ${{ steps.yarn-cache-dir-path.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/package.json') }}
restore-keys: |
${{ runner.os }}-yarn-
- name: Install dependencies
run: yarn install --frozen-lockfile
run: yarn install

- name: Lint
run: yarn run lint
70 changes: 62 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,23 @@

Non JS thread blocking indicator components for React Native.

There is [the awesome indicators library (react-native-indicators)][react-native-indicators] from long time ago.
But, as `Animated` from `react-native` contact with JS thread when animation is finished, it makes `InteractionManager.runAfterInteraction` never run...
<p align="center">
<img src="doc/example.gif" width="300" />
</p>

<sup align="center">

The code for this example is [here](#example).

</sup>

---

- [Usage](#usage)
- [Components](#components)
- [1. `<BallIndicator />`](#1-ballindicator)
- [2. `<DotIndicator />`](#2-dotindicator)
- [1. `<BallIndicator />`](#1-ballindicator-)
- [2. `<DotIndicator />`](#2-dotindicator-)
- [Example](#example)
- [Status](#status)
- [Why I created this library](#why-i-created-this-library)

Expand All @@ -28,7 +36,7 @@ yarn add react-native-reanimated-indicators

### 1. `<BallIndicator />`

Looks almost same with original.
Looks almost the same as the original.

```tsx
<BallIndicator color="orange" />
Expand All @@ -42,19 +50,65 @@ Looks like the indicator at "Messages" at "mac os" or "iOS", but I couldn't crea
<DotIndicator color="tomato" scaleEnabled={true} />
```

## Example

```tsx
import React from "react";
import { StyleSheet, Text, View } from "react-native";
import { BallIndicator, DotIndicator } from "react-native-reanimated-indicators";

export const IndicatorScreen: React.FC = () => {
return (
<View style={styles.container}>
<Text>IndicatorScreen</Text>
<View style={styles.ball}>
<DotIndicator color="pink" interval={1400} />
<DotIndicator color="pink" />
<DotIndicator color="pink" scaleEnabled={true} />
</View>
<View style={styles.ball}>
<BallIndicator color="pink" dotSize={4} size={30} />
<BallIndicator color="pink" />
<BallIndicator color="pink" dotSize={14} />
</View>
</View>
);
};

const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "black",
},
rect: {
flex: 1,
alignItems: "center",
justifyContent: "center",
flexDirection: "row",
},
ball: {
flex: 1,
flexDirection: "row",
},
});
```

## Status

If anyone interested in adding new indicators, I appreciate the PR 🙌
If anyone is interested in adding new indicators, I appreciate the PR 🙌

## Why I created this library

There is [an awesome indicators library (react-native-indicators)][react-native-indicators] from a long time ago.
But, as `Animated` from `react-native` contact with JS thread when animation is finished, it makes `InteractionManager.runAfterInteraction` never run...

I was using [the awesome library (react-native-indicators)][react-native-indicators], until use it with `InteractionManager.runAfterInteraction()`.

As `Animated` from `react-native` contact with JS thread when animation is finished, `InteractionManager.runAfterInteraction` never run...
As `Animated` from `react-native` contact with JS thread when animation is finished, `InteractionManager.runAfterInteraction` never runs...
I didn't understand for a while, and I switch to `ActivityIndicator` at that moment.

After that,
I got to know [Can it be done in React Native?](https://www.youtube.com/user/wcandill/videos) and I was start thinking "by using the library `react-native-reanimated`, CAN IT BE DONE?"
I got to know [Can it be done in React Native?](https://www.youtube.com/user/wcandill/videos) and I started to think "CAN IT BE DONE? by using `react-native-reanimated`"



Expand Down
Binary file added doc/example.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
18 changes: 9 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,21 +33,21 @@
"react-native-reanimated": ">2.0.0"
},
"dependencies": {
"react-native-reanimated-hooks": "^3.0.0"
"react-native-reanimated-hooks": "^4.0.0"
},
"devDependencies": {
"@ken0x0a/configs": "^2.8.3",
"@ken0x0a/eslint-config-react-deps": "^6.3.1",
"@ken0x0a/configs": "^2.8.5",
"@ken0x0a/eslint-config-react-deps": "^6.3.2",
"@react-native-community/bob": "0.17.1",
"@semantic-release/changelog": "^6.0.1",
"@semantic-release/changelog": "^6.0.3",
"@semantic-release/git": "^10.0.1",
"@types/react": "^18.0.25",
"@types/react": "^18.2.13",
"@types/react-native": "0.70",
"react": "18.1.0",
"react-native": "0.70.5",
"react-native-reanimated": "^2.12.0",
"react": "18.2.0",
"react-native": "0.72.0",
"react-native-reanimated": "^3.3.0",
"semantic-release": "^19.0.5",
"typescript": "4.8.4"
"typescript": "5.1.3"
},
"@react-native-community/bob": {
"source": "src",
Expand Down
76 changes: 43 additions & 33 deletions src/BallIndicator.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import type { ReactNode } from "react";
import React, { useMemo } from "react";
import { useMemo } from "react";
import type { StyleProp, ViewProps, ViewStyle } from "react-native";
import { StyleSheet, View } from "react-native";
import Animated, { EasingNode as Easing, interpolateNode as interpolate } from "react-native-reanimated";
import type { ReanimatedLoopState as LoopState, UseLoopOptions } from "react-native-reanimated-hooks";
import Animated, { Easing, interpolate, useAnimatedStyle } from "react-native-reanimated";
import { useLoop } from "react-native-reanimated-hooks";
import { range } from "./utils/array";
import { getLoopInterpolateRanges } from "./utils/get-loop-interpolate-range";

interface BallIndicatorProps extends UseLoopOptions, ViewProps {
animating?: Animated.Value<LoopState>;
type BallIndicatorProps = ViewProps & {
animating?: boolean;
/**
* @default 'black'
*/
Expand All @@ -26,7 +25,7 @@ interface BallIndicatorProps extends UseLoopOptions, ViewProps {
/**
* @default Easing.linear
*/
easing?: Animated.EasingNodeFunction;
easing?: Animated.EasingFunction;
/**
* @default 1000
*/
Expand All @@ -36,10 +35,9 @@ interface BallIndicatorProps extends UseLoopOptions, ViewProps {
* @default 52
*/
size?: number;
}
};

export function BallIndicator({
// animating
animating,
interval = 1000,
easing = Easing.linear,
Expand All @@ -56,7 +54,7 @@ export function BallIndicator({
}: BallIndicatorProps) {
const animation = useLoop({ animating, interval, easing });

const dots = useMemo<ReactNode>(() => {
const balls = useMemo<ReactNode>(() => {
const ballStyle = {
backgroundColor,
borderRadius: dotSize / 2,
Expand All @@ -65,40 +63,52 @@ export function BallIndicator({
};

return range(count).map((_, index) => {
const angle = (index * 360) / count;

const rotate = {
transform: [{ rotateZ: `${angle}deg` }],
alignItems: "center" as const,
};

const count_m1 = count - 1;
const interpolationRanges = getLoopInterpolateRanges({
count,
calcOutputRange: (idx) => 1.0 - (0.46 / count_m1) * ((idx + count_m1 - index) % count),
});

const ballAnimStyle = {
transform: [{ scale: interpolate(animation.position, interpolationRanges) }],
};

return (
<Animated.View style={[StyleSheet.absoluteFill, rotate]} key={index}>
<Animated.View style={[ballStyle, ballAnimStyle]} />
</Animated.View>
);
return <Ball key={index} style={ballStyle} value={animation.value} {...{ index, count }} />;
});
}, [backgroundColor, dotSize, count, animation.position]);
}, [backgroundColor, dotSize, count, animation.value]);

return (
<View style={[styles.container, containerStyle]}>
<Animated.View style={[{ width: size, height: size }, style]} {...viewProps}>
{dots}
{balls}
</Animated.View>
</View>
);
}

type BallProps = {
style: StyleProp<Animated.AnimateStyle<StyleProp<ViewStyle>>>;
index: number;
count: number;
value: Animated.SharedValue<number>;
};
function Ball({ style, index, count, value }: BallProps) {
const angle = (index * 360) / count;

const rotate = {
transform: [{ rotateZ: `${angle}deg` }],
alignItems: "center" as const,
};

const ballAnimStyle = useAnimatedStyle(() => {
const count_m1 = count - 1;
const interpolationRanges = getLoopInterpolateRanges({
count,
calcOutputRange: (idx) => 1.0 - (0.46 / count_m1) * ((idx + count_m1 - index) % count),
});
return {
transform: [
{ scale: interpolate(value.value, interpolationRanges.inputRange, interpolationRanges.outputRange) },
],
};
}, [value]);
return (
<Animated.View style={[StyleSheet.absoluteFill, rotate]}>
<Animated.View style={[style, ballAnimStyle]} />
</Animated.View>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
Expand Down
Loading

0 comments on commit c3c20d7

Please sign in to comment.