1- import React , { useState } from 'react' ;
1+ import { useEffect , useMemo } from 'react' ;
22import { Dimensions , StyleSheet , View } from 'react-native' ;
33import Animated , {
44 Easing ,
55 useAnimatedStyle ,
6- useReducedMotion ,
76 useSharedValue ,
7+ withRepeat ,
88 withTiming ,
99} from 'react-native-reanimated' ;
1010
1111const { width, height } = Dimensions . get ( 'window' ) ;
1212
13- function randBetween ( min : number , max : number ) {
14- return min + Math . random ( ) * ( max - min ) ;
13+ interface CircleProps {
14+ size : number ;
15+ opacity : number ;
16+ duration : number ;
17+ startX : number ;
18+ startY : number ;
19+ endX : number ;
20+ endY : number ;
21+ startHue : number ;
22+ endHue : number ;
1523}
1624
17- function Circle ( ) {
18- const shouldReduceMotion = useReducedMotion ( ) ;
25+ function Circle ( {
26+ size,
27+ opacity,
28+ duration,
29+ startX,
30+ startY,
31+ endX,
32+ endY,
33+ startHue,
34+ endHue,
35+ } : CircleProps ) {
36+ const left = useSharedValue ( startX ) ;
37+ const top = useSharedValue ( startY ) ;
38+ const hue = useSharedValue ( startHue ) ;
1939
20- const [ power ] = useState ( randBetween ( 0 , 1 ) ) ;
21- const [ duration ] = useState ( randBetween ( 2000 , 3000 ) ) ;
40+ useEffect ( ( ) => {
41+ left . value = startX ;
42+ top . value = startY ;
43+ hue . value = startHue ;
2244
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- } ;
45+ const config = { duration, easing : Easing . linear } ;
3646
37- React . useEffect ( ( ) => {
38- update ( ) ;
39- if ( shouldReduceMotion ) {
40- return ;
41- }
42- const id = setInterval ( update , duration ) ;
43- return ( ) => clearInterval ( id ) ;
44- } ) ;
47+ left . value = withRepeat ( withTiming ( endX , config ) , - 1 , true ) ;
48+ top . value = withRepeat ( withTiming ( endY , config ) , - 1 , true ) ;
49+ hue . value = withRepeat ( withTiming ( endHue , config ) , - 1 , true ) ;
50+ } , [ duration , startX , startY , startHue , endX , endY , endHue , hue , left , top ] ) ;
4551
4652 const animatedStyle = useAnimatedStyle (
4753 ( ) => ( {
4854 backgroundColor : `hsl(${ hue . value } ,100%,50%)` ,
49- width : size ,
50- height : size ,
51- left : left . value ,
52- top : top . value ,
55+ transform : [ { translateX : left . value } , { translateY : top . value } ] ,
5356 } ) ,
5457 [ ]
5558 ) ;
5659
57- return < Animated . View style = { [ styles . circle , { opacity } , animatedStyle ] } /> ;
60+ return (
61+ < Animated . View
62+ style = { [
63+ styles . circle ,
64+ { opacity, width : size , height : size } ,
65+ animatedStyle ,
66+ ] }
67+ />
68+ ) ;
5869}
5970
6071interface BokehProps {
6172 count : number ;
6273}
6374
6475function Bokeh ( { count } : BokehProps ) {
65- return (
66- < >
67- { [ ...Array ( count ) ] . map ( ( _ , i ) => (
68- < Circle key = { i } />
69- ) ) }
70- </ >
76+ const circles = useMemo (
77+ ( ) => Array . from ( { length : count } , makeBokehCircleParams ) ,
78+ [ count ]
7179 ) ;
80+
81+ return circles . map ( ( circleProps , i ) => < Circle { ...circleProps } key = { i } /> ) ;
7282}
7383
7484export default function BokehExample ( ) {
@@ -79,11 +89,42 @@ export default function BokehExample() {
7989 ) ;
8090}
8191
92+ function randBetween ( min : number , max : number ) {
93+ return min + Math . random ( ) * ( max - min ) ;
94+ }
95+
96+ function makeBokehCircleParams ( ) : CircleProps {
97+ const power = randBetween ( 0 , 1 ) ;
98+ const size = 100 + power * 250 ;
99+ const opacity = 0.1 + ( 1 - power ) * 0.1 ;
100+
101+ const duration = randBetween ( 2000 , 3000 ) ;
102+
103+ const startX = randBetween ( 0 , width ) - size / 2 ;
104+ const startY = randBetween ( 0 , height ) - size / 2 ;
105+
106+ const endX = startX + randBetween ( - 100 , 100 ) ;
107+ const endY = startY + randBetween ( - 100 , 100 ) ;
108+
109+ const startHue = randBetween ( 0 , 360 ) ;
110+ const endHue = randBetween ( 0 , 360 ) ;
111+
112+ return {
113+ size,
114+ opacity,
115+ duration,
116+ startX,
117+ startY,
118+ endX,
119+ endY,
120+ startHue,
121+ endHue,
122+ } ;
123+ }
124+
82125const styles = StyleSheet . create ( {
83126 container : {
84127 flex : 1 ,
85- alignItems : 'center' ,
86- justifyContent : 'center' ,
87128 backgroundColor : 'black' ,
88129 overflow : 'hidden' ,
89130 } ,
0 commit comments