Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

added a straight rain option #9

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 14 additions & 9 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
import React from 'react';
import { View, Text } from 'react-native';
import MakeItRain from './makeItRain';
import Confetti from './confetti';
import React from "react";
import { View, Text } from "react-native";
import MakeItRain from "./makeItRain";
import StraightRain from "./straightRain";
import Confetti from "./confetti";

function Root(props) {
const itemComponent = props.itemComponent || <View style={props.itemDimensions}/>;
const itemComponent = props.itemComponent || (
<View style={props.itemDimensions} />
);

switch (props.flavor) {
case "arrive":
return ( <Confetti {...props} itemComponent={itemComponent}/> );
return <Confetti {...props} itemComponent={itemComponent} />;
case "rain":
return ( <MakeItRain {...props} itemComponent={itemComponent}/> );
return <MakeItRain {...props} itemComponent={itemComponent} />;
case "straightrain":
return <StraightRain {...props} itemComponent={itemComponent} />;
default:
return ( <Text>Unsupported flavor</Text> );
return <Text>Unsupported flavor</Text>;
}
}

Root.defaultProps = {
numItems: 100,
itemDimensions: { width: 20, height: 10 },
itemColors: ['#00e4b2', '#09aec5', '#107ed5'],
itemColors: ["#00e4b2", "#09aec5", "#107ed5"],
itemTintStrength: 1.0,
flavor: "arrive",
fallSpeed: 50,
Expand Down
192 changes: 192 additions & 0 deletions src/straightRain.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
// Spring physics from https://blog.swmansion.com/simple-physics-with-reanimated-part-3-a168d69faa51

import React, { useEffect, useMemo } from "react";
import Animated from "react-native-reanimated";
import { View, Dimensions, StyleSheet } from "react-native";

const {
View: ReanimatedView,
Clock,
Value,
useCode,
block,
startClock,
stopClock,
set,
abs,
add,
sub,
divide,
diff,
multiply,
cond,
clockRunning,
greaterThan,
lessThan,
lessOrEq,
eq,
} = Animated;

const randomize = (max, base = 0) => Math.random() * max + base;

const StraightRain = (props) => {
const [containerDims, setContainerDims] = React.useState(
Dimensions.get("screen")
);
const [items, setItems] = React.useState(createItems(containerDims));
const clock = new Clock();

useEffect(() => {
return () => {
// func indicates unmount
stopClock(clock);
setItems([]);
};
}, []);

// Update item positioning if screen changes (e.g. rotation)
const onLayout = (event) => {
setContainerDims({
width: event.nativeEvent.layout.width,
height: event.nativeEvent.layout.height,
});
};

function createItems(dimensions) {
return useMemo(() => {
const { width, height } = dimensions;
// Adapt velocity props
const xVelMax = props.horizSpeed;
const yVelMax = props.fallSpeed * 3;
const angleVelMax = props.flipSpeed;

return [...new Array(props.numItems)].map((_, index) => {
let x = randomize(width - props.itemDimensions.width);
let y = -props.itemDimensions.height * 4;
let anchor = randomize(width / 3, width / 12);
let xArc = Math.abs(x - anchor);

return {
index,
x: new Value(x),
y: new Value(y),
xArc: new Value(xArc),
yBase: new Value(y),
angle: new Value(0),
anchor: new Value(anchor),
tension: new Value((4 * Math.min(xArc, width / 2)) / width),
xVel: new Value(randomize(xVelMax / 2, xVelMax / 2)),
yVel: new Value(randomize(yVelMax, yVelMax)),
angleVel: new Value(randomize(angleVelMax / 2, angleVelMax / 2)),
delay: new Value(((index / props.numItems) * height) / yVelMax),
color: props.itemColors[index % props.itemColors.length],
};
});
}, [dimensions]);
}

const useDraw = (_items) => {
const nativeCode = useMemo(() => {
const timeDiff = diff(clock);
const nativeCode = _items.map(
({ x, yBase, angle, xVel, yVel, angleVel, delay }) => {
const dt = divide(timeDiff, 1000);
const dy = multiply(dt, yVel);
const dAngle = multiply(dt, angleVel);

return [
cond(
lessOrEq(
yBase,
containerDims.height + props.itemDimensions.height
),
cond(
greaterThan(delay, 0),
[set(delay, sub(delay, dt))],
[
set(yBase, add(yBase, dy)),
set(x, add(x, multiply(xVel, dt))),
set(angle, add(angle, dAngle)),
]
)
),
cond(
eq(props.continuous, true),
cond(
greaterThan(
yBase,
containerDims.height + props.itemDimensions.height
),
set(yBase, -props.itemDimensions.height * 4)
)
),
];
}
);

nativeCode.push(cond(clockRunning(clock), 0, startClock(clock)), clock);
return block(nativeCode);
}, [_items]);

useCode(() => nativeCode, [nativeCode]);
};

useDraw(items);

return (
<View
pointerEvents='none'
style={StyleSheet.absoluteFill}
onLayout={this.onLayout}
>
{items.map(({ index, x, y, angle, color: backgroundColor }) => {
return (
<ReanimatedView
key={index}
style={[
styles.animContainer,
props.itemDimensions,
{
transform: [
{ translateX: x },
{ translateY: y },
{ rotate: angle },
{ rotateX: angle },
{ rotateY: angle },
],
},
]}
>
{props.itemComponent}
<View
style={[
{ backgroundColor },
props.itemDimensions,
styles.itemContainer,
]}
opacity={props.itemTintStrength}
/>
</ReanimatedView>
);
})}
</View>
);
};

export default StraightRain;

const styles = StyleSheet.create({
animContainer: {
position: "absolute",
top: 0,
left: 0,
alignItems: "center",
justifyContent: "center",
overflow: "hidden",
},
itemContainer: {
position: "absolute",
top: 0,
left: 0,
},
});