├── src
├── types
│ └── png.d.ts
└── screens
│ └── Main
│ ├── components
│ ├── PoofCard
│ │ ├── assets
│ │ │ ├── poof1.png
│ │ │ ├── poof2.png
│ │ │ ├── poof3.png
│ │ │ ├── poof4.png
│ │ │ └── poof5.png
│ │ ├── styles.ts
│ │ └── index.tsx
│ ├── ShredCard
│ │ ├── assets
│ │ │ └── shredder.png
│ │ ├── components
│ │ │ └── Captured
│ │ │ │ ├── styles.tsx
│ │ │ │ └── index.tsx
│ │ ├── styles.ts
│ │ └── index.tsx
│ ├── VanishCard
│ │ ├── index.tsx
│ │ └── styles.ts
│ └── HoleCard
│ │ ├── styles.ts
│ │ └── index.tsx
│ ├── styles.ts
│ └── index.tsx
├── tsconfig.json
├── App.tsx
├── babel.config.js
├── README.md
├── package.json
└── .gitignore
/src/types/png.d.ts:
--------------------------------------------------------------------------------
1 | declare module "*.png" {}
2 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {},
3 | "extends": "expo/tsconfig.base"
4 | }
5 |
--------------------------------------------------------------------------------
/App.tsx:
--------------------------------------------------------------------------------
1 | import Main from "./src/screens/Main";
2 |
3 | export default function App() {
4 | return ;
5 | }
6 |
--------------------------------------------------------------------------------
/src/screens/Main/components/PoofCard/assets/poof1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lklima/rn-transitions/HEAD/src/screens/Main/components/PoofCard/assets/poof1.png
--------------------------------------------------------------------------------
/src/screens/Main/components/PoofCard/assets/poof2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lklima/rn-transitions/HEAD/src/screens/Main/components/PoofCard/assets/poof2.png
--------------------------------------------------------------------------------
/src/screens/Main/components/PoofCard/assets/poof3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lklima/rn-transitions/HEAD/src/screens/Main/components/PoofCard/assets/poof3.png
--------------------------------------------------------------------------------
/src/screens/Main/components/PoofCard/assets/poof4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lklima/rn-transitions/HEAD/src/screens/Main/components/PoofCard/assets/poof4.png
--------------------------------------------------------------------------------
/src/screens/Main/components/PoofCard/assets/poof5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lklima/rn-transitions/HEAD/src/screens/Main/components/PoofCard/assets/poof5.png
--------------------------------------------------------------------------------
/src/screens/Main/components/ShredCard/assets/shredder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lklima/rn-transitions/HEAD/src/screens/Main/components/ShredCard/assets/shredder.png
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = function (api) {
2 | api.cache(true);
3 | return {
4 | presets: ["babel-preset-expo"],
5 | plugins: ["react-native-reanimated/plugin"],
6 | };
7 | };
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # RN Transitions
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | React Native animated app with reanimated + expo.
11 |
12 | Give me a ⭐️ if liked it and follow me.
13 |
14 | https://user-images.githubusercontent.com/44346970/175845652-96978d45-07cf-4412-b6ac-42bad317702c.mp4
15 |
16 |
17 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "dependencies": {
3 | "expo": "^45.0.0",
4 | "react": "17.0.2",
5 | "react-dom": "17.0.2",
6 | "react-native": "0.68.2",
7 | "react-native-reanimated": "~2.8.0",
8 | "react-native-view-shot": "3.1.2",
9 | "react-native-web": "0.17.7",
10 | "styled-components": "^5.3.5"
11 | },
12 | "devDependencies": {
13 | "@babel/core": "^7.12.9",
14 | "@types/react": "~17.0.21",
15 | "@types/react-native": "~0.67.6",
16 | "@types/styled-components-react-native": "^5.1.3",
17 | "typescript": "~4.3.5"
18 | },
19 | "scripts": {
20 | "start": "expo start",
21 | "android": "expo start --android",
22 | "ios": "expo start --ios",
23 | "web": "expo start --web"
24 | },
25 | "version": "1.0.0",
26 | "private": true,
27 | "resolutions": {
28 | "@types/react": "~17.0.21"
29 | },
30 | "name": "rn-transitions"
31 | }
32 |
--------------------------------------------------------------------------------
/src/screens/Main/components/PoofCard/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from "styled-components/native";
2 | import { Dimensions } from "react-native";
3 | import Animated from "react-native-reanimated";
4 |
5 | const { width } = Dimensions.get("window");
6 |
7 | interface ContainerProps {
8 | index: number;
9 | }
10 |
11 | export const Container = styled.View`
12 | width: ${width / 2}px;
13 | align-items: center;
14 | justify-content: center;
15 | overflow: hidden;
16 | margin-bottom: 10px;
17 | padding-left: ${({ index }) => (index % 2 === 0 ? 10 : 0)}px;
18 | padding-right: ${({ index }) => (index % 2 === 0 ? 0 : 10)}px;
19 | `;
20 |
21 | export const CardView = styled(Animated.View)`
22 | height: ${width / 2 - 20}px;
23 | width: ${width / 2 - 20}px;
24 | background: #007aff;
25 | border-radius: 20px;
26 | `;
27 |
28 | export const Image = styled(Animated.Image)`
29 | height: 250px;
30 | width: 250px;
31 | position: absolute;
32 | opacity: 0;
33 | `;
34 |
--------------------------------------------------------------------------------
/src/screens/Main/components/PoofCard/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { FadeIn } from "react-native-reanimated";
3 |
4 | import * as S from "./styles";
5 |
6 | import Poof1 from "./assets/poof1.png";
7 | import Poof2 from "./assets/poof2.png";
8 | import Poof3 from "./assets/poof3.png";
9 | import Poof4 from "./assets/poof4.png";
10 | import Poof5 from "./assets/poof5.png";
11 |
12 | interface CardProps {
13 | index: number;
14 | }
15 |
16 | export default function PoofCard({ index }: CardProps) {
17 | return (
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | );
27 | }
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # OSX
2 | #
3 | .DS_Store
4 |
5 | # Xcode
6 | #
7 | build/
8 | *.pbxuser
9 | !default.pbxuser
10 | *.mode1v3
11 | !default.mode1v3
12 | *.mode2v3
13 | !default.mode2v3
14 | *.perspectivev3
15 | !default.perspectivev3
16 | xcuserdata
17 | *.xccheckout
18 | *.moved-aside
19 | DerivedData
20 | *.hmap
21 | *.ipa
22 | *.xcuserstate
23 | project.xcworkspace
24 |
25 | # Android/IntelliJ
26 | #
27 | build/
28 | .idea
29 | .gradle
30 | local.properties
31 | *.iml
32 | *.hprof
33 |
34 | # node.js
35 | #
36 | node_modules/
37 | npm-debug.log
38 | yarn-error.log
39 |
40 | # BUCK
41 | buck-out/
42 | \.buckd/
43 | *.keystore
44 |
45 | # fastlane
46 | #
47 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
48 | # screenshots whenever they are needed.
49 | # For more information about the recommended setup visit:
50 | # https://docs.fastlane.tools/best-practices/source-control/
51 |
52 | */fastlane/report.xml
53 | */fastlane/Preview.html
54 | */fastlane/screenshots
55 |
56 | # Bundle artifacts
57 | *.jsbundle
58 |
59 | # CocoaPods
60 | /ios/Pods/
61 |
62 | # Expo
63 | .expo/*
64 | web-build/
65 |
--------------------------------------------------------------------------------
/src/screens/Main/components/ShredCard/components/Captured/styles.tsx:
--------------------------------------------------------------------------------
1 | import Animated from "react-native-reanimated";
2 | import styled from "styled-components/native";
3 |
4 | interface Props {
5 | width: number;
6 | height?: number;
7 | index?: number;
8 | dir?: number;
9 | shredWidth?: number;
10 | }
11 |
12 | export const Container = styled(Animated.View)`
13 | width: ${(props) => props.width}px;
14 | height: ${(props) => props.height}px;
15 | margin-bottom: 10px;
16 | position: absolute;
17 | opacity: 0;
18 | `;
19 |
20 | export const Slice = styled(Animated.View)`
21 | max-width: ${(props) => props.width}px;
22 | max-width: ${(props) => props.width}px;
23 | position: absolute;
24 | bottom: 0;
25 | top: 0;
26 | left: ${(props) => props.width * props.index}px;
27 | overflow: hidden;
28 | background: white;
29 | `;
30 |
31 | export const SliceImage = styled.Image`
32 | position: absolute;
33 | top: 0;
34 | bottom: 0;
35 | left: ${(props) => props.shredWidth * -props.index}px;
36 | opacity: ${(props) => (props.dir === 1 ? 1 : 1.0 - 0.2 * 1)};
37 | width: ${(props) => props.width}px;
38 | height: ${(props) => props.height}px;
39 | `;
40 |
--------------------------------------------------------------------------------
/src/screens/Main/components/VanishCard/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { FadeIn, FlipInEasyX, ZoomOut } from "react-native-reanimated";
3 | import { useWindowDimensions } from "react-native";
4 |
5 | import * as S from "./styles";
6 |
7 | interface CardProps {
8 | index: number;
9 | }
10 |
11 | export default function PoofCard({ index }: CardProps) {
12 | const { width } = useWindowDimensions();
13 |
14 | const size = width / 2 - 20;
15 | const particlesAmount = 150;
16 | const particles = Array.from({ length: particlesAmount }, (_, index) => index);
17 |
18 | function renderParticles() {
19 | return particles.map((particle) => {
20 | const ramdomTop = Math.random() * size - 10;
21 | const ramdomLeft = Math.random() * size - 10;
22 |
23 | return (
24 |
30 | );
31 | });
32 | }
33 |
34 | return (
35 |
36 |
37 |
38 | {renderParticles()}
39 |
40 |
41 | );
42 | }
43 |
--------------------------------------------------------------------------------
/src/screens/Main/components/VanishCard/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from "styled-components/native";
2 | import { Dimensions } from "react-native";
3 | import Animated from "react-native-reanimated";
4 |
5 | const { width } = Dimensions.get("window");
6 |
7 | interface ContainerProps {
8 | index: number;
9 | }
10 |
11 | interface ParticleProps {
12 | top: number;
13 | left: number;
14 | }
15 |
16 | export const Container = styled.View`
17 | width: ${width / 2}px;
18 | align-items: center;
19 | justify-content: center;
20 | margin-bottom: 10px;
21 | padding-left: ${({ index }) => (index % 2 === 0 ? 10 : 0)}px;
22 | padding-right: ${({ index }) => (index % 2 === 0 ? 0 : 10)}px;
23 | `;
24 |
25 | export const CardView = styled(Animated.View)`
26 | height: ${width / 2 - 20}px;
27 | width: ${width / 2 - 20}px;
28 | background: #007aff;
29 | border-radius: 20px;
30 | z-index: 999;
31 | `;
32 |
33 | export const ParticleContent = styled(Animated.View)`
34 | height: ${width / 2 - 20}px;
35 | width: ${width / 2 - 20}px;
36 | position: absolute;
37 | flex-direction: row;
38 | flex-wrap: wrap;
39 | opacity: 0;
40 | `;
41 |
42 | export const Particle = styled(Animated.View)`
43 | height: 30px;
44 | width: 30px;
45 | background: #007aff;
46 | border-radius: 30px;
47 | opacity: 0.3;
48 | position: absolute;
49 | `;
50 |
--------------------------------------------------------------------------------
/src/screens/Main/components/ShredCard/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from "styled-components/native";
2 | import { Dimensions } from "react-native";
3 | import Animated from "react-native-reanimated";
4 |
5 | const { width } = Dimensions.get("window");
6 |
7 | interface ContainerProps {
8 | index: number;
9 | }
10 |
11 | export const Container = styled.View`
12 | width: ${width / 2}px;
13 | align-items: center;
14 | justify-content: center;
15 | margin-bottom: 10px;
16 | padding-left: ${({ index }) => (index % 2 === 0 ? 10 : 0)}px;
17 | padding-right: ${({ index }) => (index % 2 === 0 ? 0 : 10)}px;
18 | `;
19 |
20 | export const CardView = styled(Animated.View)`
21 | height: ${width / 2 - 20}px;
22 | width: ${width / 2 - 20}px;
23 | background: ${({ index }) => (index % 2 === 0 ? "#ffab00" : "#007aff")};
24 | border-radius: 20px;
25 | align-items: center;
26 | justify-content: center;
27 | overflow: hidden;
28 | `;
29 |
30 | export const CardText = styled.Text`
31 | font-size: 35px;
32 | font-weight: bold;
33 | color: white;
34 | text-align: center;
35 | `;
36 |
37 | export const Shredder = styled(Animated.Image)`
38 | width: ${width / 2 - 2}px;
39 | position: absolute;
40 | z-index: 9999;
41 | align-self: center;
42 | left: ${({ index }) => (index % 2 === 0 ? 5 : -3)}px;
43 | opacity: 0;
44 | `;
45 |
--------------------------------------------------------------------------------
/src/screens/Main/components/HoleCard/styles.ts:
--------------------------------------------------------------------------------
1 | import styled from "styled-components/native";
2 | import { Dimensions } from "react-native";
3 | import Animated from "react-native-reanimated";
4 |
5 | const { width } = Dimensions.get("window");
6 |
7 | interface ContainerProps {
8 | index: number;
9 | }
10 |
11 | export const Container = styled.View`
12 | width: ${width / 2}px;
13 | align-items: center;
14 | overflow: hidden;
15 | margin-top: -20px;
16 | margin-bottom: -40px;
17 | padding-left: ${({ index }) => (index % 2 === 0 ? 10 : 0)}px;
18 | padding-right: ${({ index }) => (index % 2 === 0 ? 0 : 10)}px;
19 | `;
20 |
21 | export const CardHidder = styled.View`
22 | align-items: center;
23 | padding: 0 60px;
24 | padding-bottom: 60px;
25 | padding-top: 20px;
26 | margin-bottom: -32px;
27 | border-bottom-left-radius: 110px;
28 | border-bottom-right-radius: 110px;
29 | overflow: hidden;
30 | z-index: 999;
31 | `;
32 |
33 | export const CardView = styled(Animated.View)`
34 | height: ${width / 2 - 20}px;
35 | width: ${width / 2 - 20}px;
36 | align-items: center;
37 | justify-content: center;
38 | background: #007aff;
39 | border-radius: 20px;
40 | `;
41 |
42 | export const Text = styled.Text`
43 | color: white;
44 | font-size: 30px;
45 | text-align: center;
46 | `;
47 |
48 | export const HoleContainer = styled(Animated.View)`
49 | transform: scale(0);
50 | `;
51 |
52 | export const HoleBorder = styled.View`
53 | width: 33px;
54 | height: 33px;
55 | background: gray;
56 | border-radius: 50px;
57 | transform: scaleX(6);
58 | overflow: hidden;
59 | `;
60 |
61 | export const Hole = styled.View`
62 | width: 33px;
63 | height: 33px;
64 | background: black;
65 | border-radius: 50px;
66 | margin-top: 7px;
67 | `;
68 |
--------------------------------------------------------------------------------
/src/screens/Main/components/HoleCard/index.tsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { withSequence, withTiming, FlipInEasyX } from "react-native-reanimated";
3 |
4 | import * as S from "./styles";
5 |
6 | interface CardProps {
7 | index: number;
8 | }
9 |
10 | export default function Card({ index }: CardProps) {
11 | function cardExiting() {
12 | "worklet";
13 | const animations = {
14 | transform: [
15 | { translateY: withSequence(withTiming(-10), withTiming(250, { duration: 600 })) },
16 | {
17 | rotateZ: withSequence(
18 | withTiming("5deg"),
19 | withTiming("0deg", { duration: 600 })
20 | ),
21 | },
22 | ],
23 | };
24 | const initialValues = {
25 | transform: [{ translateY: 0 }, { rotateZ: "0deg" }],
26 | };
27 | return {
28 | initialValues,
29 | animations,
30 | };
31 | }
32 |
33 | function holeExiting() {
34 | "worklet";
35 | const animations = {
36 | transform: [
37 | {
38 | scale: withSequence(
39 | withTiming(1, { duration: 400 }),
40 | withTiming(1, { duration: 600 }),
41 | withTiming(0, { duration: 400 })
42 | ),
43 | },
44 | ],
45 | };
46 | const initialValues = {
47 | transform: [{ scale: 0 }],
48 | };
49 | return {
50 | initialValues,
51 | animations,
52 | };
53 | }
54 |
55 | return (
56 |
57 |
58 |
59 | Hello{"\n"}World!
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | );
69 | }
70 |
--------------------------------------------------------------------------------
/src/screens/Main/styles.ts:
--------------------------------------------------------------------------------
1 | import { Dimensions } from "react-native";
2 | import Animated from "react-native-reanimated";
3 | import styled from "styled-components/native";
4 |
5 | const { width } = Dimensions.get("window");
6 |
7 | interface TextPops {
8 | selected: boolean;
9 | }
10 |
11 | export const Container = styled.SafeAreaView`
12 | flex: 1;
13 | align-items: center;
14 | `;
15 |
16 | export const OptionsContent = styled.View`
17 | width: ${width - 35}px;
18 | height: 80px;
19 | border-radius: 8px;
20 | background: #eeedee;
21 | overflow: hidden;
22 | flex-direction: row;
23 | margin-top: 20px;
24 | `;
25 |
26 | export const Highlight = styled(Animated.View)`
27 | height: ${(width - 35) / 3}px;
28 | width: ${(width - 35) / 3}px;
29 | background: #007aff;
30 | position: absolute;
31 | `;
32 |
33 | export const Option = styled.TouchableOpacity.attrs({
34 | activeOpacity: 0.8,
35 | })`
36 | flex: 1;
37 | align-items: center;
38 | justify-content: center;
39 | `;
40 |
41 | export const OptionText = styled.Text`
42 | font-size: 20px;
43 | color: ${({ selected }) => (selected ? "white" : "#007aff")};
44 | `;
45 |
46 | export const RowContent = styled.View`
47 | width: 90%;
48 | flex-direction: row;
49 | align-items: center;
50 | justify-content: space-between;
51 | margin-top: 15px;
52 | `;
53 |
54 | export const Text = styled.Text`
55 | font-size: 20px;
56 | `;
57 |
58 | export const Count = styled.Text`
59 | font-size: 20px;
60 | color: gray;
61 | `;
62 |
63 | export const AddButtonsContent = styled.View`
64 | flex-direction: row;
65 | width: 100px;
66 | align-items: center;
67 | justify-content: center;
68 | border-radius: 8px;
69 | background: #eeedee;
70 | height: 40px;
71 | `;
72 |
73 | export const Button = styled.TouchableOpacity`
74 | flex: 1;
75 | align-items: center;
76 | `;
77 |
78 | export const CardContent = styled.View`
79 | width: 100%;
80 | flex-direction: row;
81 | justify-content: space-between;
82 | flex-wrap: wrap;
83 | margin-top: 20px;
84 | `;
85 |
--------------------------------------------------------------------------------
/src/screens/Main/components/ShredCard/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useRef } from "react";
2 | import { FlipInEasyX, withSequence, withTiming } from "react-native-reanimated";
3 | import { useWindowDimensions } from "react-native";
4 |
5 | import * as S from "./styles";
6 |
7 | import Captured from "./components/Captured";
8 |
9 | import shredder from "./assets/shredder.png";
10 |
11 | interface CardProps {
12 | index: number;
13 | }
14 |
15 | export default function ShredCard({ index }: CardProps) {
16 | const { width } = useWindowDimensions();
17 | const capture = useRef(null);
18 |
19 | function cardExiting() {
20 | "worklet";
21 | const animations = {
22 | transform: [
23 | {
24 | translateY: withSequence(
25 | withTiming(2, { duration: 50 }),
26 | withTiming(-2, { duration: 50 }),
27 | withTiming(20, { duration: 200 }),
28 | withTiming(20, { duration: 200 }),
29 | withTiming(140, { duration: 600 }),
30 | withTiming(140, { duration: 100 }),
31 | withTiming(135, { duration: 50 }),
32 | withTiming(200, { duration: 200 })
33 | ),
34 | },
35 | ],
36 | height: withTiming(0, { duration: 1200 }),
37 | };
38 | const initialValues = {
39 | transform: [{ translateY: 0 }],
40 | height: width / 2 - 20,
41 | };
42 | return {
43 | initialValues,
44 | animations,
45 | };
46 | }
47 |
48 | function shredExiting() {
49 | "worklet";
50 | const animations = {
51 | transform: [
52 | {
53 | translateY: withSequence(
54 | withTiming(30, { duration: 1000 }),
55 | withTiming(33, { duration: 100 }),
56 | withTiming(30, { duration: 100 })
57 | ),
58 | },
59 | ],
60 | opacity: withSequence(
61 | withTiming(1, { duration: 200 }),
62 | withTiming(1, { duration: 1300 }),
63 | withTiming(0, { duration: 200 })
64 | ),
65 | };
66 | const initialValues = {
67 | transform: [{ translateY: 70 }],
68 | opacity: 0,
69 | };
70 | return {
71 | initialValues,
72 | animations,
73 | };
74 | }
75 |
76 | return (
77 |
78 |
84 |
85 |
91 | Hello{"\n"}World
92 |
93 |
94 | );
95 | }
96 |
--------------------------------------------------------------------------------
/src/screens/Main/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { AntDesign } from "@expo/vector-icons";
3 | import { useWindowDimensions } from "react-native";
4 | import {
5 | useAnimatedStyle,
6 | useSharedValue,
7 | interpolate,
8 | withSpring,
9 | } from "react-native-reanimated";
10 |
11 | import * as S from "./styles";
12 |
13 | import HoleCard from "./components/HoleCard";
14 | import PoofCard from "./components/PoofCard";
15 | import ShredCard from "./components/ShredCard";
16 |
17 | export default function Main() {
18 | const { width } = useWindowDimensions();
19 |
20 | const highlightTranlateX = useSharedValue(0);
21 |
22 | const [count, setCount] = useState(0);
23 | const [optionSelected, setOptionSelected] = useState(0);
24 |
25 | const cards = Array.from({ length: count }, (_, index) => index);
26 |
27 | function handleCard(type: string) {
28 | if (type === "add") {
29 | setCount(count + 1);
30 | } else {
31 | if (count > 0) {
32 | setCount(count - 1);
33 | }
34 | }
35 | }
36 |
37 | useEffect(() => {
38 | const highlightWidth = (width - 35) / 3;
39 |
40 | highlightTranlateX.value = withSpring(
41 | interpolate(optionSelected, [0, 1, 2], [0, highlightWidth, highlightWidth * 2]),
42 | { damping: 14 }
43 | );
44 | }, [optionSelected]);
45 |
46 | const highlightAnimatedStyle = useAnimatedStyle(() => ({
47 | transform: [{ translateX: highlightTranlateX.value }],
48 | }));
49 |
50 | function renderCards() {
51 | if (optionSelected === 0) {
52 | return cards.map((card) => );
53 | } else if (optionSelected === 1) {
54 | return cards.map((card) => );
55 | }
56 |
57 | return cards.map((card) => );
58 | }
59 |
60 | return (
61 |
62 |
63 |
64 | setOptionSelected(0)}>
65 | Shredded
66 |
67 | setOptionSelected(1)}>
68 | Hole
69 |
70 | setOptionSelected(2)}>
71 | Poof
72 |
73 |
74 |
75 |
76 | View Count ({count})
77 |
78 |
79 | handleCard("remove")}>
80 |
81 |
82 | handleCard("add")}>
83 |
84 |
85 |
86 |
87 | {renderCards()}
88 |
89 | );
90 | }
91 |
--------------------------------------------------------------------------------
/src/screens/Main/components/ShredCard/components/Captured/index.tsx:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { useWindowDimensions } from "react-native";
3 | import { withDelay, withSequence, withTiming } from "react-native-reanimated";
4 | import { captureRef } from "react-native-view-shot";
5 |
6 | import { Container, Slice, SliceImage } from "./styles";
7 |
8 | interface Props {
9 | capture: any;
10 | }
11 |
12 | export default function Captured({ capture }: Props) {
13 | const { width } = useWindowDimensions();
14 |
15 | const [img, setImg] = useState(null);
16 |
17 | const size = width / 2 - 20;
18 | const shredCount = 16;
19 | const shredWidth = size / shredCount;
20 |
21 | useEffect(() => {
22 | if (capture.current && width) {
23 | captureRef(capture, { width: size, height: size }).then(setImg);
24 | }
25 | }, [capture, width]);
26 |
27 | function cardExiting() {
28 | "worklet";
29 | const animations = {
30 | transform: [
31 | {
32 | translateY: withSequence(
33 | withTiming(5, { duration: 100 }),
34 | withTiming(25, { duration: 200 }),
35 | withTiming(25, { duration: 200 }),
36 | withTiming(140, { duration: 600 }),
37 | withTiming(140, { duration: 100 }),
38 | withTiming(135, { duration: 50 }),
39 | withTiming(200, { duration: 200 })
40 | ),
41 | },
42 | ],
43 | opacity: withDelay(100, withTiming(1)),
44 | };
45 | const initialValues = {
46 | transform: [{ translateY: 0 }],
47 | opacity: 0,
48 | };
49 | return {
50 | initialValues,
51 | animations,
52 | };
53 | }
54 |
55 | if (!img) return <>>;
56 |
57 | return (
58 |
59 | {new Array(shredCount).fill(0).map((_, index) => {
60 | // Alternate direction
61 | const fromMiddle = index - shredCount / 2;
62 | const dir = index % 2 === 0 ? 1 : -1;
63 |
64 | return (
65 | {
70 | "worklet";
71 | const animations = {
72 | transform: [
73 | { perspective: withTiming(400, { duration: 800 }) },
74 | {
75 | rotateZ: withTiming(index % 2 === 0 ? "-2deg" : "2deg", {
76 | duration: 1200,
77 | }),
78 | },
79 | {
80 | rotateZ: withSequence(
81 | withTiming(-fromMiddle, {
82 | duration: 1200,
83 | }),
84 | withTiming(index % 2 === 0 ? -fromMiddle * 9 : fromMiddle * 9, {
85 | duration: 400,
86 | })
87 | ),
88 | },
89 | {
90 | rotateX: withTiming(index % 2 === 0 ? "-15deg" : "15deg", {
91 | duration: 1200,
92 | }),
93 | },
94 | ],
95 | opacity: withDelay(1300, withTiming(0)),
96 | };
97 | const initialValues = {
98 | transform: [
99 | { perspective: 0 },
100 | { rotateZ: "0deg" },
101 | { rotateZ: "0deg" },
102 | { rotateX: "0deg" },
103 | ],
104 | opacity: 1,
105 | };
106 | return {
107 | initialValues,
108 | animations,
109 | };
110 | }}
111 | >
112 |
120 |
121 | );
122 | })}
123 |
124 | );
125 | }
126 |
--------------------------------------------------------------------------------