Skip to content

Commit

Permalink
feat(popper): allowing custom position and fading spring configs
Browse files Browse the repository at this point in the history
  • Loading branch information
mateoguzmana committed Jun 9, 2024
1 parent 068040a commit ccb15a7
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 15 deletions.
44 changes: 43 additions & 1 deletion example/src/components/Examples.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
View,
} from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
import { Canvas, useFont } from '@shopify/react-native-skia';
import { Canvas, Circle, useFont } from '@shopify/react-native-skia';
import {
Fireworks,
FiestaThemes,
Expand All @@ -23,6 +23,7 @@ import {
PopperDirection,
Confettis,
Confetti,
Popper,
} from 'react-native-fiesta';
import { useActionSheet } from '@expo/react-native-action-sheet';
import Header from './Header';
Expand Down Expand Up @@ -218,6 +219,47 @@ export function Examples() {
Confettis
</Text>
</TouchableOpacity>

<TouchableOpacity
onPress={() =>
onChangeComponent(
<Popper
direction={PopperDirection.Ascending}
theme={selectedTheme}
key={dynamicKey}
spacing={10}
renderItem={({ x, y, colors }, index) => (
<Circle
key={`circle-${index}`}
cx={x}
cy={y}
r={10}
color={colors[index]}
/>
)}
positionSpringConfig={{
stiffness: 100,
damping: 100,
mass: 11000,
}}
fadeSpringConfig={{
stiffness: 1,
damping: 1,
mass: 1000,
}}
/>
)
}
style={styles.pressable}
>
<Canvas style={styles.canvas}>
<Circle cx={10} cy={10} r={20} color="rgba(255, 0, 255, 0.4)" />
</Canvas>

<Text style={[styles.pressableText, styles.textColor]}>
Custom elements using Popper
</Text>
</TouchableOpacity>
</ScrollView>

{componentToRender}
Expand Down
7 changes: 5 additions & 2 deletions src/components/Confetti.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ import {
withTiming,
} from 'react-native-reanimated';
import { screenHeight } from '../constants/dimensions';
import { degreesToRadians, randomIntFromInterval } from '../utils/confettis';
import { DEFAULT_ANIMATION_DURATION } from './Confettis';
import {
degreesToRadians,
randomIntFromInterval,
DEFAULT_ANIMATION_DURATION,
} from '../utils/confettis';

export interface ConfettiProps {
initialPosition?: { x: number; y: number };
Expand Down
2 changes: 1 addition & 1 deletion src/components/Confettis.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@ import { colorsFromTheme } from '../utils/colors';
import { generateRandomCoordinates } from '../utils/fireworks';
import { screenHeight } from '../constants/dimensions';
import { Confetti, type ConfettiProps } from './Confetti';
import { DEFAULT_ANIMATION_DURATION } from '../utils/confettis';

const optimalNumberOfConfettis = 30;
export const DEFAULT_ANIMATION_DURATION = 6000;

export interface ConfettisProps
extends Pick<ConfettiProps, 'size' | 'duration'> {
Expand Down
48 changes: 42 additions & 6 deletions src/components/Popper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
useSharedValue,
withSpring,
} from 'react-native-reanimated';
import type { SpringConfig } from 'react-native-reanimated/lib/typescript/reanimated2/animation/springUtils';
import { screenHeight } from '../constants/dimensions';
import { screenWidth } from '../constants/dimensions';
import { shuffleArray } from '../utils/array';
Expand Down Expand Up @@ -47,10 +48,25 @@ export interface PopperProps {
* Direction in which the elements will be popped
*/
direction?: PopperDirection;
/**
* Controls the speed of the elements moving across the screen
* @default FiestaSpeed.Normal
*/
positionSpringConfig?: SpringConfig;
/**
* Controls the speed of the elements fading out
* @default singleItemFadeSpeed – mass is set to screenHeight, which calculates the fade speed based on the screen height
*/
fadeSpringConfig?: SpringConfig;
}

export type StartPopperParams = Pick<
PopperProps,
'theme' | 'direction' | 'positionSpringConfig' | 'fadeSpringConfig'
>;

export interface PopperHandler {
start(params?: Pick<PopperProps, 'theme' | 'direction'>): void;
start(params?: StartPopperParams): void;
}

export type PopperRef = ForwardedRef<PopperHandler>;
Expand All @@ -64,13 +80,21 @@ export const Popper = memo(
renderItem,
autoPlay = true,
direction = PopperDirection.Descending,
positionSpringConfig = FiestaSpeed.Normal,
fadeSpringConfig = singleItemFadeSpeed,
}: PopperProps,
ref: PopperRef
) => {
// properties that might be controlled are also defined in the state
const [controlledTheme, setControlledTheme] = useState<string[]>(theme);
const [controlledDirection, setControlledDirection] =
useState<PopperDirection>(direction);
const [
controlledPositionSpringConfig,
setControlledPositionSpringConfig,
] = useState<SpringConfig>(positionSpringConfig);
const [controlledFadeSpringConfig, setControlledFadeSpringConfig] =
useState<SpringConfig>(fadeSpringConfig);

const [displayCanvas, setDisplayCanvas] = useState<boolean>(autoPlay);
const initialPosition = useMemo(
Expand Down Expand Up @@ -113,7 +137,7 @@ export const Popper = memo(
const changeItemPosition = useCallback(() => {
containerYPosition.value = withSpring(
finalPosition,
FiestaSpeed.Normal,
controlledPositionSpringConfig,
(finished) => {
if (finished) {
containerYPosition.value = initialPosition;
Expand All @@ -123,8 +147,15 @@ export const Popper = memo(
}
);

opacity.value = withSpring(0, singleItemFadeSpeed);
}, [containerYPosition, finalPosition, initialPosition, opacity]);
opacity.value = withSpring(0, controlledFadeSpringConfig);
}, [
containerYPosition,
controlledFadeSpringConfig,
controlledPositionSpringConfig,
finalPosition,
initialPosition,
opacity,
]);

const transform = useDerivedValue(() => [
{ translateY: containerYPosition.value },
Expand All @@ -140,14 +171,19 @@ export const Popper = memo(
containerYPosition.value = initialPosition;
opacity.value = 1;

// @TODO: to avoid re-renders probably some of this values could use a ref
// @TODO: to avoid unnecessary re-renders probably some of this values could use a ref
if (params?.theme) {
setControlledTheme(params.theme);
}

if (params?.direction) {
setControlledDirection(params.direction);
}
if (params?.positionSpringConfig) {
setControlledPositionSpringConfig(params.positionSpringConfig);
}
if (params?.fadeSpringConfig) {
setControlledFadeSpringConfig(params.fadeSpringConfig);
}

// plays the animation again
setDisplayCanvas(true);
Expand Down
5 changes: 2 additions & 3 deletions src/contexts/FiestaContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
Stars as _Stars,
EmojiPopper as _EmojiPopper,
type PopperHandler,
type PopperProps,
type StartPopperParams,
} from '../components';

export enum FiestaAnimations {
Expand All @@ -22,8 +22,7 @@ export enum FiestaAnimations {
Fireworks = 'Fireworks',
}

interface RunFiestaAnimationParams
extends Pick<PopperProps, 'theme' | 'direction'> {
interface RunFiestaAnimationParams extends StartPopperParams {
animation: FiestaAnimations;
}

Expand Down
2 changes: 2 additions & 0 deletions src/utils/confettis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,5 @@ export function randomIntFromInterval(min: number, max: number): number {
// min and max included
return Math.floor(Math.random() * (max - min + 1) + min);
}

export const DEFAULT_ANIMATION_DURATION = 6000;
9 changes: 7 additions & 2 deletions src/utils/fireworks.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { screenWidth, screenHeight } from '../constants/dimensions';

interface Coordinates {
x: number;
y: number;
}

export function fromRadians(angle: number) {
return angle * (180.0 / Math.PI);
}
Expand All @@ -8,9 +13,9 @@ export function generateRandomCoordinates(
numElements: number,
specificHeight?: number,
specificWidth?: number
): Array<{ x: number; y: number }> {
): Array<Coordinates> {
// Create an array to store the generated coordinates
const coordinates: Array<{ x: number; y: number }> = [];
const coordinates: Array<Coordinates> = [];

// Generate random x,y coordinates until we have the desired number of elements
while (coordinates.length < numElements) {
Expand Down

0 comments on commit ccb15a7

Please sign in to comment.