feat(hooks): usePulseAnimation for live indicators
This commit is contained in:
1 parent
ea70ecbaa4
commit
e8f828d90a
2 files changed
+93
No files matched your search
@@ -0,0 +1,49 @@
|
||||
import React from 'react';
|
||||
import TestRenderer from 'react-test-renderer';
|
||||
|
||||
jest.mock('react-native-reanimated', () => {
|
||||
return {
|
||||
Easing: { out: () => (() => 0), inOut: () => (() => 0), quad: () => 0 },
|
||||
useSharedValue: (initial: number) => ({ value: initial }),
|
||||
useAnimatedStyle: (fn: () => unknown) => fn(),
|
||||
withTiming: (to: number) => to,
|
||||
withRepeat: (to: number) => to,
|
||||
cancelAnimation: () => undefined,
|
||||
};
|
||||
});
|
||||
|
||||
import { usePulseAnimation } from '../hooks/usePulseAnimation';
|
||||
|
||||
function Probe() {
|
||||
const style = usePulseAnimation(false) as any;
|
||||
return null;
|
||||
}
|
||||
|
||||
describe('usePulseAnimation', () => {
|
||||
it('returns initial opacity 0.5 and scale 1 when inactive', () => {
|
||||
let captured: any = null;
|
||||
function Capture() {
|
||||
const style = usePulseAnimation(false) as any;
|
||||
captured = style;
|
||||
return null;
|
||||
}
|
||||
TestRenderer.act(() => {
|
||||
TestRenderer.create(React.createElement(Capture));
|
||||
});
|
||||
expect(captured.transform[0].scale).toBe(1);
|
||||
expect(captured.opacity).toBe(0.5);
|
||||
});
|
||||
|
||||
it('returns opacity 1 when active', () => {
|
||||
let captured: any = null;
|
||||
function Capture() {
|
||||
const style = usePulseAnimation(true) as any;
|
||||
captured = style;
|
||||
return null;
|
||||
}
|
||||
TestRenderer.act(() => {
|
||||
TestRenderer.create(React.createElement(Capture));
|
||||
});
|
||||
expect(captured.opacity).toBe(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
import { useEffect } from 'react';
|
||||
import {
|
||||
Easing,
|
||||
cancelAnimation,
|
||||
useAnimatedStyle,
|
||||
useSharedValue,
|
||||
withRepeat,
|
||||
withTiming,
|
||||
} from 'react-native-reanimated';
|
||||
|
||||
type AnimatedStyle = {
|
||||
transform: Array<{ scale: number }>;
|
||||
opacity: number;
|
||||
};
|
||||
|
||||
export function usePulseAnimation(active: boolean, durationMs = 1600): AnimatedStyle {
|
||||
const scale = useSharedValue(1);
|
||||
const opacity = useSharedValue(active ? 1 : 0.5);
|
||||
|
||||
useEffect(() => {
|
||||
if (active) {
|
||||
scale.value = withRepeat(
|
||||
withTiming(1.4, { duration: durationMs / 2, easing: Easing.out(Easing.quad) }),
|
||||
-1,
|
||||
true,
|
||||
);
|
||||
opacity.value = withRepeat(
|
||||
withTiming(0.55, { duration: durationMs / 2, easing: Easing.inOut(Easing.quad) }),
|
||||
-1,
|
||||
true,
|
||||
);
|
||||
} else {
|
||||
cancelAnimation(scale);
|
||||
cancelAnimation(opacity);
|
||||
scale.value = withTiming(1, { duration: 160 });
|
||||
opacity.value = withTiming(0.5, { duration: 160 });
|
||||
}
|
||||
}, [active, durationMs, scale, opacity]);
|
||||
|
||||
return useAnimatedStyle<AnimatedStyle>(() => ({
|
||||
transform: [{ scale: scale.value }],
|
||||
opacity: opacity.value,
|
||||
}));
|
||||
}
|
||||
Reference in new issue
Block a user