|
1 |
| -import Animated, { |
2 |
| - Easing, |
3 |
| - useAnimatedStyle, |
4 |
| - useReducedMotion, |
5 |
| - useSharedValue, |
6 |
| - withTiming, |
7 |
| -} from 'react-native-reanimated'; |
8 |
| -import { Dimensions, StyleSheet, View } from 'react-native'; |
9 |
| -import React, { useState } from 'react'; |
| 1 | +import App from 'common-app'; |
10 | 2 |
|
11 |
| -const { width, height } = Dimensions.get('window'); |
12 |
| - |
13 |
| -function randBetween(min: number, max: number) { |
14 |
| - return min + Math.random() * (max - min); |
15 |
| -} |
16 |
| - |
17 |
| -function Circle() { |
18 |
| - const shouldReduceMotion = useReducedMotion(); |
19 |
| - |
20 |
| - const [power] = useState(randBetween(0, 1)); |
21 |
| - const [duration] = useState(randBetween(2000, 3000)); |
22 |
| - |
23 |
| - const size = 100 + power * 250; |
24 |
| - const opacity = 0.1 + (1 - power) * 0.1; |
25 |
| - const config = { duration, easing: Easing.linear }; |
26 |
| - |
27 |
| - const left = useSharedValue(randBetween(0, width) - size / 2); |
28 |
| - const top = useSharedValue(randBetween(0, height) - size / 2); |
29 |
| - const hue = useSharedValue(randBetween(100, 200)); |
30 |
| - |
31 |
| - const update = () => { |
32 |
| - left.value = withTiming(left.value + randBetween(-100, 100), config); |
33 |
| - top.value = withTiming(top.value + randBetween(-100, 100), config); |
34 |
| - hue.value = withTiming(hue.value + randBetween(0, 100), config); |
35 |
| - }; |
36 |
| - |
37 |
| - React.useEffect(() => { |
38 |
| - update(); |
39 |
| - if (shouldReduceMotion) { |
40 |
| - return; |
41 |
| - } |
42 |
| - const id = setInterval(update, duration); |
43 |
| - return () => clearInterval(id); |
44 |
| - }); |
45 |
| - |
46 |
| - const animatedStyle = useAnimatedStyle( |
47 |
| - () => ({ |
48 |
| - backgroundColor: `hsl(${hue.value},100%,50%)`, |
49 |
| - width: size, |
50 |
| - height: size, |
51 |
| - left: left.value, |
52 |
| - top: top.value, |
53 |
| - }), |
54 |
| - [] |
55 |
| - ); |
56 |
| - |
57 |
| - return <Animated.View style={[styles.circle, { opacity }, animatedStyle]} />; |
58 |
| -} |
59 |
| - |
60 |
| -interface BokehProps { |
61 |
| - count: number; |
62 |
| -} |
63 |
| - |
64 |
| -function Bokeh({ count }: BokehProps) { |
65 |
| - return ( |
66 |
| - <> |
67 |
| - {[...Array(count)].map((_, i) => ( |
68 |
| - <Circle key={i} /> |
69 |
| - ))} |
70 |
| - </> |
71 |
| - ); |
72 |
| -} |
73 |
| - |
74 |
| -export default function BokehExample() { |
75 |
| - return ( |
76 |
| - <View style={styles.container}> |
77 |
| - <Bokeh count={100} /> |
78 |
| - </View> |
79 |
| - ); |
80 |
| -} |
81 |
| - |
82 |
| -const styles = StyleSheet.create({ |
83 |
| - container: { |
84 |
| - flex: 1, |
85 |
| - alignItems: 'center', |
86 |
| - justifyContent: 'center', |
87 |
| - backgroundColor: 'black', |
88 |
| - overflow: 'hidden', |
89 |
| - }, |
90 |
| - circle: { |
91 |
| - position: 'absolute', |
92 |
| - borderRadius: 999, |
93 |
| - }, |
94 |
| -}); |
| 3 | +export default App; |
0 commit comments