├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ ├── feature_request.md
│ └── new-animation-resquest.md
└── workflows
│ └── .github
│ └── workflows
│ └── contributors.yml
├── .gitignore
├── .idea
├── .gitignore
├── .name
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── compiler.xml
├── deploymentTargetSelector.xml
├── gradle.xml
├── inspectionProfiles
│ └── Project_Default.xml
├── kotlinc.xml
├── migrations.xml
├── misc.xml
├── runConfigurations.xml
├── studiobot.xml
└── vcs.xml
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── SECURITY.md
├── app
├── .gitignore
├── build.gradle.kts
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── com
│ │ └── sp45
│ │ └── android_animations
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── sp45
│ │ │ └── android_animations
│ │ │ ├── AppNavGraph.kt
│ │ │ ├── MainActivity.kt
│ │ │ ├── MainScreen.kt
│ │ │ ├── animations
│ │ │ ├── AnimatedPlacement.kt
│ │ │ ├── AutoImageTransition.kt
│ │ │ ├── BouncingBallAnimation.kt
│ │ │ ├── ButtonToImage.kt
│ │ │ ├── CardFlipAnimation.kt
│ │ │ ├── CardFlippingAnimation.kt
│ │ │ ├── CarouselSlider.kt
│ │ │ ├── ConfettiAnimation.kt
│ │ │ ├── ContentAnimation.kt
│ │ │ ├── EmojiProgressBar.kt
│ │ │ ├── ExpandableCardsAnimation.kt
│ │ │ ├── ExpandingRingsAnimation.kt
│ │ │ ├── FloatingElements.kt
│ │ │ ├── OrbitingObjects.kt
│ │ │ ├── SlidingDoor.kt
│ │ │ ├── SwipeToDeleteAnimation.kt
│ │ │ ├── TextCollapsingAnimation.kt
│ │ │ ├── TypeWriterAnimation.kt
│ │ │ ├── ValueSpringAnimation.kt
│ │ │ └── WaveLoadingBar.kt
│ │ │ ├── ui
│ │ │ └── theme
│ │ │ │ ├── Color.kt
│ │ │ │ ├── Theme.kt
│ │ │ │ └── Type.kt
│ │ │ └── util
│ │ │ ├── AnimationCard.kt
│ │ │ ├── FadingTextAnimation.kt
│ │ │ ├── ThemeSwitch.kt
│ │ │ ├── TopAppBar.kt
│ │ │ └── WebViewComponent.kt
│ └── res
│ │ ├── drawable
│ │ ├── avd_crakcode.xml
│ │ ├── cat1.jpg
│ │ ├── cat2.jpg
│ │ ├── cat3.png
│ │ ├── cat4.png
│ │ ├── cat5.png
│ │ ├── crakcode.png
│ │ ├── ic_launcher_background.xml
│ │ ├── ic_launcher_foreground.xml
│ │ ├── img.png
│ │ └── img_2.png
│ │ ├── mipmap-anydpi
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── values
│ │ ├── arrays.xml
│ │ ├── colors.xml
│ │ ├── splash.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ │ └── xml
│ │ ├── backup_rules.xml
│ │ └── data_extraction_rules.xml
│ └── test
│ └── java
│ └── com
│ └── sp45
│ └── android_animations
│ └── ExampleUnitTest.kt
├── build.gradle.kts
├── gradle.properties
├── gradle
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle.kts
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/new-animation-resquest.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: New Animation Resquest
3 | about: Request a new animation to be added to the project.
4 | title: ''
5 | labels: ''
6 | assignees: shubhampandey45
7 |
8 | ---
9 |
10 | ## Describe the Animation
11 | Provide a clear and concise description of the animation you want to be added.
12 | - What does the animation do?
13 | - Where could it be used?
14 |
15 | ---
16 |
17 | ## Use Case
18 | Explain how this animation could be helpful for the project.
19 | - Is it relevant to a particular feature or screen?
20 | - How does it improve the user experience?
21 |
22 | ---
23 |
24 | ## Expected Behavior
25 | Describe what the animation should look like and how it should behave.
26 | - Include details like duration, effects, transitions, etc.
27 | - Mention specific attributes such as easing, timing, or interaction.
28 |
29 | ---
30 |
31 | ## References/Examples
32 | If you have any references, screenshots, videos, or links to examples of similar animations, include them here.
33 |
34 | ---
35 |
36 | ## Additional Context
37 | Add any other details or context about the animation.
38 |
39 | ---
40 |
41 | ## Optional Additional Items
42 | - **Issue default title**: `New Animation: [Short Description]`
43 | - **Assignees**:
44 | - **Labels**: `animation-request`
45 |
--------------------------------------------------------------------------------
/.github/workflows/.github/workflows/contributors.yml:
--------------------------------------------------------------------------------
1 | name: Update Contributors
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | schedule:
8 | - cron: '0 0 * * *' # Runs daily at midnight (optional)
9 |
10 | jobs:
11 | update-contributors:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - name: Check out the repository
15 | uses: actions/checkout@v2
16 |
17 | - name: Generate contributors list
18 | run: |
19 | echo "" > CONTRIBUTORS_LIST.md
20 | contributors=$(git log --format='%aN' | sort -u)
21 | for contributor in $contributors; do
22 | # Fetch GitHub username using GitHub API (requires API token if private)
23 | username=$(curl -s "https://api.github.com/search/users?q=${contributor}" | jq -r '.items[0].login')
24 | if [ "$username" != "null" ]; then
25 | echo " " >> CONTRIBUTORS_LIST.md
26 | fi
27 | done
28 |
29 | - name: Update README with Contributors
30 | run: |
31 | sed -i '//,//{//!d}' README.md
32 | sed -i '//r CONTRIBUTORS_LIST.md' README.md
33 |
34 | - name: Commit and push changes
35 | run: |
36 | git config --local user.name "github-actions[bot]"
37 | git config --local user.email "github-actions[bot]@users.noreply.github.com"
38 | git add README.md CONTRIBUTORS_LIST.md
39 | git commit -m "Update contributors section"
40 | git push
41 | env:
42 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
43 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | android-animations
--------------------------------------------------------------------------------
/.idea/codeStyles/Project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | xmlns:android
18 |
19 | ^$
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | xmlns:.*
29 |
30 | ^$
31 |
32 |
33 | BY_NAME
34 |
35 |
36 |
37 |
38 |
39 |
40 | .*:id
41 |
42 | http://schemas.android.com/apk/res/android
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 | .*:name
52 |
53 | http://schemas.android.com/apk/res/android
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | name
63 |
64 | ^$
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 | style
74 |
75 | ^$
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | .*
85 |
86 | ^$
87 |
88 |
89 | BY_NAME
90 |
91 |
92 |
93 |
94 |
95 |
96 | .*
97 |
98 | http://schemas.android.com/apk/res/android
99 |
100 |
101 | ANDROID_ATTRIBUTE_ORDER
102 |
103 |
104 |
105 |
106 |
107 |
108 | .*
109 |
110 | .*
111 |
112 |
113 | BY_NAME
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/deploymentTargetSelector.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/.idea/kotlinc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/migrations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/.idea/studiobot.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | We as members, contributors, and leaders pledge to make participation in our
6 | community a harassment-free experience for everyone, regardless of age, body
7 | size, visible or invisible disability, ethnicity, sex characteristics, gender
8 | identity and expression, level of experience, education, socio-economic status,
9 | nationality, personal appearance, race, religion, or sexual identity
10 | and orientation.
11 |
12 | We pledge to act and interact in ways that contribute to an open, welcoming,
13 | diverse, inclusive, and healthy community.
14 |
15 | ## Our Standards
16 |
17 | Examples of behavior that contributes to a positive environment for our
18 | community include:
19 |
20 | * Demonstrating empathy and kindness toward other people
21 | * Being respectful of differing opinions, viewpoints, and experiences
22 | * Giving and gracefully accepting constructive feedback
23 | * Accepting responsibility and apologizing to those affected by our mistakes,
24 | and learning from the experience
25 | * Focusing on what is best not just for us as individuals, but for the
26 | overall community
27 |
28 | Examples of unacceptable behavior include:
29 |
30 | * The use of sexualized language or imagery, and sexual attention or
31 | advances of any kind
32 | * Trolling, insulting or derogatory comments, and personal or political attacks
33 | * Public or private harassment
34 | * Publishing others' private information, such as a physical or email
35 | address, without their explicit permission
36 | * Other conduct which could reasonably be considered inappropriate in a
37 | professional setting
38 |
39 | ## Enforcement Responsibilities
40 |
41 | Community leaders are responsible for clarifying and enforcing our standards of
42 | acceptable behavior and will take appropriate and fair corrective action in
43 | response to any behavior that they deem inappropriate, threatening, offensive,
44 | or harmful.
45 |
46 | Community leaders have the right and responsibility to remove, edit, or reject
47 | comments, commits, code, wiki edits, issues, and other contributions that are
48 | not aligned to this Code of Conduct, and will communicate reasons for moderation
49 | decisions when appropriate.
50 |
51 | ## Scope
52 |
53 | This Code of Conduct applies within all community spaces, and also applies when
54 | an individual is officially representing the community in public spaces.
55 | Examples of representing our community include using an official e-mail address,
56 | posting via an official social media account, or acting as an appointed
57 | representative at an online or offline event.
58 |
59 | ## Enforcement
60 |
61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
62 | reported to the community leaders responsible for enforcement at
63 | .
64 | All complaints will be reviewed and investigated promptly and fairly.
65 |
66 | All community leaders are obligated to respect the privacy and security of the
67 | reporter of any incident.
68 |
69 | ## Enforcement Guidelines
70 |
71 | Community leaders will follow these Community Impact Guidelines in determining
72 | the consequences for any action they deem in violation of this Code of Conduct:
73 |
74 | ### 1. Correction
75 |
76 | **Community Impact**: Use of inappropriate language or other behavior deemed
77 | unprofessional or unwelcome in the community.
78 |
79 | **Consequence**: A private, written warning from community leaders, providing
80 | clarity around the nature of the violation and an explanation of why the
81 | behavior was inappropriate. A public apology may be requested.
82 |
83 | ### 2. Warning
84 |
85 | **Community Impact**: A violation through a single incident or series
86 | of actions.
87 |
88 | **Consequence**: A warning with consequences for continued behavior. No
89 | interaction with the people involved, including unsolicited interaction with
90 | those enforcing the Code of Conduct, for a specified period of time. This
91 | includes avoiding interactions in community spaces as well as external channels
92 | like social media. Violating these terms may lead to a temporary or
93 | permanent ban.
94 |
95 | ### 3. Temporary Ban
96 |
97 | **Community Impact**: A serious violation of community standards, including
98 | sustained inappropriate behavior.
99 |
100 | **Consequence**: A temporary ban from any sort of interaction or public
101 | communication with the community for a specified period of time. No public or
102 | private interaction with the people involved, including unsolicited interaction
103 | with those enforcing the Code of Conduct, is allowed during this period.
104 | Violating these terms may lead to a permanent ban.
105 |
106 | ### 4. Permanent Ban
107 |
108 | **Community Impact**: Demonstrating a pattern of violation of community
109 | standards, including sustained inappropriate behavior, harassment of an
110 | individual, or aggression toward or disparagement of classes of individuals.
111 |
112 | **Consequence**: A permanent ban from any sort of public interaction within
113 | the community.
114 |
115 | ## Attribution
116 |
117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage],
118 | version 2.0, available at
119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
120 |
121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct
122 | enforcement ladder](https://github.com/mozilla/diversity).
123 |
124 | [homepage]: https://www.contributor-covenant.org
125 |
126 | For answers to common questions about this code of conduct, see the FAQ at
127 | https://www.contributor-covenant.org/faq. Translations are available at
128 | https://www.contributor-covenant.org/translations.
129 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 |
Contributing to Android Animations with Jetpack Compose 🎨
2 |
3 | Thank you for your interest in contributing to the
4 | Android Animations with Jetpack Compose project! Contributions are what make the open-source community an incredible place to learn, inspire, and create. Your help in improving this project, fixing bugs, and adding new animations is highly appreciated.
5 |
6 |
7 |
8 |
9 | 🛠 How to Contribute
10 | 1. Fork the Repository
11 |
12 | Click the Fork button at the top-right corner of this repository to create your copy.
13 |
14 |
15 | 2. Clone Your Fork
16 |
17 | git clone https://github.com/<your-username>/android-animations.git
18 |
19 |
20 | 3. Create a New Branch
21 | Create a new branch for your contribution. Use a descriptive branch name, such as:
22 |
23 | git checkout -b feature/<your-feature-name>
24 |
25 |
26 | 4. Make Changes
27 |
28 | Add your animation in an independent file for better modularity.
29 | Follow Kotlin Coding Conventions .
30 | Test your animation locally using the provided app structure.
31 |
32 |
33 | 5. Commit Your Changes
34 | Write a concise and descriptive commit message:
35 |
36 | git commit -m "Add: Bouncing Ball Animation"
37 |
38 |
39 | 6. Push to Your Fork
40 | Push your branch to the forked repository:
41 |
42 | git push origin feature/<your-feature-name>
43 |
44 |
45 | 7. Open a Pull Request
46 |
47 | Go to the original repository and click New Pull Request . Fill out the pull request template with details about your changes.
48 |
49 |
50 |
51 |
52 | ✨ Contribution Guidelines
53 |
54 | Animation Structure : Add each animation as an independent Kotlin file in the project. The animations should be reusable and well-documented.
55 | Naming Conventions : Use descriptive names for files and components (e.g., BouncingBallAnimation.kt
).
56 | UI Design : Ensure your animations align with the project’s UI theme and look good on different devices.
57 | Testing : Test your animation on multiple devices/emulators with API level 26 or higher.
58 | Documentation : Add comments to your code and update the README.md
if needed.
59 | Commit Messages : Use clear and concise commit messages that describe the changes.
60 | No Breaking Changes : Ensure your changes do not break existing animations or the app structure.
61 |
62 |
63 |
64 |
65 | 🔍 Code of Conduct
66 |
67 | This project follows the Contributor Covenant Code of Conduct. By participating, you agree to uphold a welcoming and inclusive environment.
68 |
69 |
70 |
71 |
72 | 🌟 Get Started Now!
73 |
74 |
75 | Have an idea for a new animation? Let’s bring it to life!
76 | Need help? Check the discussions or reach out via issues.
77 |
78 |
79 |
80 |
81 |
82 | Thank you for contributing! Together, let’s make this a great resource for Android developers. 🚀
83 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 crakcode-hub
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Android Animations with Jetpack Compose
6 |
7 |
8 | A repository showcasing various animations using Jetpack Compose. Perfect for developers looking to learn and practice animation techniques on Android.
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | ## Table of Contents
24 | - About the Project
25 | - Showcase
26 | - Quick Start
27 | - Requirements
28 | - Reporting Issues
29 | - Contributing
30 | - Contributors
31 | - License
32 |
33 | About the Project📃
34 |
35 | This repository demonstrates various animation techniques available in Jetpack Compose, helping developers learn how to create smooth, interactive UI animations. Includes various animations, making it easy to explore and implement these animations.
36 |
37 | Showcase⭐
38 |
39 |
40 |
41 |
42 |
43 |
Swipe to delete
44 |
45 |
46 |
47 |
48 |
49 |
Confetti Animation
50 |
51 |
52 |
53 |
54 |
55 |
Bouncing ball
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
Wave Loading Bar
64 |
65 |
66 |
67 |
68 |
69 |
Carousel Slider
70 |
71 |
72 |
73 |
74 |
75 |
Card Flip
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
Emoji Progress Bar
84 |
85 |
86 |
87 |
88 |
89 |
Type Writer Animation
90 |
91 |
92 |
93 |
94 |
95 |
Floating Elements
96 |
97 |
98 |
99 |
100 |
101 |
102 | Quick Start 🚀
103 |
104 | Clone the repository :
105 | git clone https://github.com/crakcode-hub/android-animations.git
106 |
107 | Open the project :
108 |
109 | Use Android Studio Bumblebee (2021.1.1) or newer.
110 | Navigate to the cloned folder and open the project in Android Studio.
111 |
112 |
113 | Run the app :
114 |
115 | Build and run the app on a physical device or an emulator.
116 | For optimal performance, test on devices/emulators with Android 8.0 (API level 26) or above.
117 |
118 |
119 |
120 |
121 | Requirements 🛠️
122 |
123 | Android Studio Bumblebee (2021.1.1) or newer
124 | Android Gradle Plugin version 7.0.3
or higher
125 | JDK 11
126 | An emulator or a physical Android device with Android 8.0 (API level 26) or higher
127 |
128 |
129 | Reporting Issues 🐛
130 | If you encounter any bugs or have suggestions for improvement, we’d love to hear from you! Here’s how you can help:
131 |
132 | Check if the issue already exists in the GitHub Issues section.
133 | If it doesn’t, open a new issue :
134 |
135 | Describe the problem in detail.
136 | Include steps to reproduce the issue.
137 | Attach screenshots or logs if applicable.
138 | Mention the device, emulator, and Android version used.
139 |
140 |
141 |
142 | Thank you for helping us improve this project!
143 |
144 | Contributing 🧑💻
145 | We welcome contributions to make this project better! Whether you want to fix a bug, add a new feature, or improve documentation, your help is appreciated.
146 |
147 | How to Contribute
148 |
149 | Fork the Repository : Click the Fork button on the top-right corner of the repository page.
150 | Clone Your Fork : Clone the forked repository to your local machine:
151 | git clone https://github.com/your-username/android-animations.git
152 |
153 | Create a New Branch : Use a descriptive name for your branch:
154 | git checkout -b feature/your-feature-name
155 |
156 | Make Your Changes : Edit the code, fix bugs, or add features. Make sure to follow the coding guidelines and test your changes locally.
157 | Commit Your Changes : Write clear and concise commit messages:
158 | git commit -m "Add: Feature description"
159 |
160 | Push Your Changes : Push the changes to your forked repository:
161 | git push origin feature/your-feature-name
162 |
163 | Open a Pull Request : Navigate to the original repository and click the New Pull Request button. Provide a detailed description of the changes you’ve made.
164 |
165 |
166 | Contribution Guidelines
167 |
168 | Follow the Kotlin Coding Conventions .
169 | Write descriptive commit messages for better traceability.
170 | Test your changes on multiple Android devices and emulator configurations.
171 | Ensure the app runs without crashes or errors before submitting your pull request.
172 |
173 |
174 | Learn More✅
175 |
176 | If you're interested in diving deeper into Android development, including advanced topics and best practices, visit our website for more resources and mentorship.
177 |
178 |
179 |
180 | Explore More on CrakCode Website
181 |
182 |
183 |
184 | ## Contributors
185 |
186 | Join us to make this project even better. Happy Animating! 🎉
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 | License📄
198 | This project is licensed under the MIT License.
199 |
200 | Built with 💚 by CrakCode
201 |
202 | Back to Top 🔝
203 |
204 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Reporting a Vulnerability
4 |
5 | We take the security of this project seriously. If you discover any vulnerabilities, please follow the steps below to report them responsibly.
6 |
7 | ### Steps to Report
8 | 1. **Email**: Send details of the vulnerability to [tarun.crakcode@gmail.com](mailto:tarun.crakcode@gmail.com).
9 | 2. **Include the following in your report**:
10 | - Description of the vulnerability.
11 | - Steps to reproduce the issue.
12 | - Potential impact of the vulnerability.
13 | - If possible, suggest a fix or mitigation strategy.
14 | 3. **Timeline**:
15 | - We will acknowledge your report within 2 business days.
16 | - Updates will be provided every 5 business days on the status of your report.
17 | - If accepted, a fix will be implemented and deployed within 30 days of the initial report (barring unforeseen circumstances).
18 |
19 | ### What to Expect
20 | - **Acknowledgment**: A confirmation that we have received your report.
21 | - **Assessment**: We will evaluate the validity and impact of the vulnerability.
22 | - **Action**: If confirmed, we will prioritize a fix and may request further information or collaboration from you.
23 | - **Credit**: With your consent, you will be acknowledged in our release notes or public announcements.
24 |
25 | ### Important Notes
26 | - Please do not share the vulnerability publicly or with others until it has been resolved.
27 | - Avoid testing vulnerabilities on live systems or end users, as this can disrupt services or compromise data.
28 |
29 | Thank you for helping to keep this project secure and safe for everyone! 🚀
30 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle.kts:
--------------------------------------------------------------------------------
1 | plugins {
2 | alias(libs.plugins.android.application)
3 | alias(libs.plugins.kotlin.android)
4 | alias(libs.plugins.kotlin.compose)
5 | }
6 |
7 | android {
8 | namespace = "com.sp45.android_animations"
9 | compileSdk = 34
10 |
11 | defaultConfig {
12 | applicationId = "com.sp45.android_animations"
13 | minSdk = 26
14 | targetSdk = 34
15 | versionCode = 1
16 | versionName = "1.0"
17 |
18 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
19 | }
20 |
21 | buildTypes {
22 | release {
23 | isMinifyEnabled = false
24 | proguardFiles(
25 | getDefaultProguardFile("proguard-android-optimize.txt"),
26 | "proguard-rules.pro"
27 | )
28 | }
29 | }
30 | compileOptions {
31 | sourceCompatibility = JavaVersion.VERSION_1_8
32 | targetCompatibility = JavaVersion.VERSION_1_8
33 | }
34 | kotlinOptions {
35 | jvmTarget = "1.8"
36 | }
37 | buildFeatures {
38 | compose = true
39 | }
40 | }
41 |
42 | dependencies {
43 |
44 | implementation(libs.androidx.core.ktx)
45 | implementation(libs.androidx.lifecycle.runtime.ktx)
46 | implementation(libs.androidx.activity.compose)
47 | implementation(platform(libs.androidx.compose.bom))
48 | implementation(libs.androidx.ui)
49 | implementation(libs.androidx.ui.graphics)
50 | implementation(libs.androidx.ui.tooling.preview)
51 | implementation(libs.androidx.material3)
52 | implementation(libs.androidx.ui.text.google.fonts)
53 | implementation(libs.androidx.animation.android)
54 | implementation(libs.androidx.foundation.layout.android)
55 | testImplementation(libs.junit)
56 | androidTestImplementation(libs.androidx.junit)
57 | androidTestImplementation(libs.androidx.espresso.core)
58 | androidTestImplementation(platform(libs.androidx.compose.bom))
59 | androidTestImplementation(libs.androidx.ui.test.junit4)
60 | debugImplementation(libs.androidx.ui.tooling)
61 | debugImplementation(libs.androidx.ui.test.manifest)
62 |
63 | implementation(libs.androidx.navigation.compose)
64 | implementation(libs.androidx.core.splashscreen)
65 | implementation(libs.androidx.lifecycle.viewmodel.compose)
66 |
67 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/sp45/android_animations/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.sp45.android_animations", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
16 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/AppNavGraph.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations
2 |
3 | import androidx.compose.runtime.Composable
4 | import androidx.navigation.NavHostController
5 | import androidx.navigation.NavType
6 | import androidx.navigation.compose.NavHost
7 | import androidx.navigation.compose.composable
8 | import androidx.navigation.compose.rememberNavController
9 | import androidx.navigation.navArgument
10 | import com.sp45.android_animations.animations.AutoImageTransition
11 | import com.sp45.android_animations.animations.BirthdayPopperAnimation
12 | import com.sp45.android_animations.animations.BouncingBallAnimation
13 | import com.sp45.android_animations.animations.ButtonToImage
14 | import com.sp45.android_animations.animations.CardFlipping
15 | import com.sp45.android_animations.animations.CarouselSlider
16 | import com.sp45.android_animations.animations.ContentAnimation
17 | import com.sp45.android_animations.animations.EmojiProgressBar
18 | import com.sp45.android_animations.animations.ExpandableCardAnimation
19 | import com.sp45.android_animations.animations.ExpandingRings
20 | import com.sp45.android_animations.animations.FlipCard
21 | import com.sp45.android_animations.animations.FloatingElements
22 | import com.sp45.android_animations.animations.OrbitingObjects
23 | import com.sp45.android_animations.animations.SlidingDoorAnimation
24 | import com.sp45.android_animations.animations.SwipeToDeleteAnimation
25 | import com.sp45.android_animations.animations.TextExplosion
26 | import com.sp45.android_animations.animations.TypeWriterAnimation
27 | import com.sp45.android_animations.animations.ValueSpringAnimation
28 | import com.sp45.android_animations.animations.WaveLoadingBar
29 | import com.sp45.android_animations.util.WebViewScreen
30 | import java.net.URLDecoder
31 | import java.net.URLEncoder
32 | import java.nio.charset.StandardCharsets
33 |
34 | @Composable
35 | fun AppNavGraph(navController: NavHostController = rememberNavController()) {
36 | NavHost(
37 | navController = navController,
38 | startDestination = NavigationDestinations.MAIN
39 | ) {
40 | composable(NavigationDestinations.MAIN) {
41 | MainScreen(navController)
42 | }
43 |
44 | composable(NavigationDestinations.WEB_VIEW) {
45 | WebViewScreen()
46 | }
47 |
48 | composable(
49 | route = NavigationDestinations.ANIMATION,
50 | arguments = listOf(
51 | navArgument("animationName") {
52 | type = NavType.StringType
53 | }
54 | )
55 | ) { backStackEntry ->
56 | val encodedAnimationName = backStackEntry.arguments?.getString("animationName") ?: ""
57 | val animationName =
58 | URLDecoder.decode(encodedAnimationName, StandardCharsets.UTF_8.toString())
59 |
60 | when (animationName) {
61 | "Swipe To Delete Animation" -> SwipeToDeleteAnimation()
62 | "Bouncing Ball Animation" -> BouncingBallAnimation()
63 | "Value Spring Animation" -> ValueSpringAnimation()
64 | "Content Animation" -> ContentAnimation()
65 | "Expandable Card Animation" -> ExpandableCardAnimation()
66 | "Wave Loading Bar" -> WaveLoadingBar()
67 | "Confetti Animation" -> BirthdayPopperAnimation()
68 | "Carousel Slider" -> CarouselSlider()
69 | "Flip Card" -> FlipCard()
70 | "Floating Elements" -> FloatingElements()
71 | "Type Writer Animation" -> TypeWriterAnimation()
72 | "Emoji Progress Bar" -> EmojiProgressBar()
73 | "Card Flipping" -> CardFlipping()
74 | "Button to Image" -> ButtonToImage()
75 | "Image Transition" -> AutoImageTransition()
76 | "Sliding Door" -> SlidingDoorAnimation()
77 | "Expanding Rings" -> ExpandingRings()
78 | "Orbiting Objects" -> OrbitingObjects()
79 | "Text Explosion" -> TextExplosion()
80 | else -> MainScreen(navController)
81 | }
82 | }
83 | }
84 | }
85 |
86 | object NavigationDestinations {
87 | const val MAIN = "main"
88 | const val ANIMATION = "animation/{animationName}"
89 | const val WEB_VIEW = "webView"
90 |
91 | fun createAnimationRoute(animationName: String): String {
92 | val encodedName = URLEncoder.encode(animationName, StandardCharsets.UTF_8.toString())
93 | return "animation/$encodedName"
94 | }
95 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations
2 |
3 | import android.os.Bundle
4 | import androidx.activity.ComponentActivity
5 | import androidx.activity.compose.setContent
6 | import androidx.compose.foundation.isSystemInDarkTheme
7 | import androidx.compose.runtime.Composable
8 | import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
9 | import com.sp45.android_animations.ui.theme.AndroidAnimationsTheme
10 | import com.sp45.android_animations.util.ThemeManager
11 |
12 | class MainActivity : ComponentActivity() {
13 | override fun onCreate(savedInstanceState: Bundle?) {
14 | super.onCreate(savedInstanceState)
15 | installSplashScreen().setKeepOnScreenCondition {
16 | false
17 | }
18 | setContent {
19 | AndroidAnimationsApp()
20 | }
21 | }
22 | }
23 |
24 | @Composable
25 | fun AndroidAnimationsApp() {
26 | val systemDarkTheme = isSystemInDarkTheme()
27 | ThemeManager.initializeTheme(systemDarkTheme)
28 | val darkTheme = ThemeManager.observeTheme()
29 |
30 | AndroidAnimationsTheme(darkTheme = darkTheme) {
31 | AppNavGraph()
32 | }
33 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/MainScreen.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations
2 |
3 | import androidx.compose.foundation.background
4 | import androidx.compose.foundation.layout.Arrangement
5 | import androidx.compose.foundation.layout.Column
6 | import androidx.compose.foundation.layout.PaddingValues
7 | import androidx.compose.foundation.layout.Row
8 | import androidx.compose.foundation.layout.Spacer
9 | import androidx.compose.foundation.layout.fillMaxSize
10 | import androidx.compose.foundation.layout.fillMaxWidth
11 | import androidx.compose.foundation.layout.padding
12 | import androidx.compose.foundation.layout.size
13 | import androidx.compose.foundation.layout.width
14 | import androidx.compose.foundation.lazy.LazyColumn
15 | import androidx.compose.material.icons.Icons
16 | import androidx.compose.material.icons.filled.Info
17 | import androidx.compose.material3.Icon
18 | import androidx.compose.material3.MaterialTheme
19 | import androidx.compose.material3.Scaffold
20 | import androidx.compose.runtime.Composable
21 | import androidx.compose.ui.Alignment
22 | import androidx.compose.ui.Modifier
23 | import androidx.compose.ui.graphics.Color
24 | import androidx.compose.ui.res.stringResource
25 | import androidx.compose.ui.unit.dp
26 | import androidx.navigation.NavController
27 | import com.sp45.android_animations.util.AnimationCard
28 | import com.sp45.android_animations.util.AnimationItem
29 | import com.sp45.android_animations.util.CustomTopAppBar
30 | import com.sp45.android_animations.util.FadingTextAnimation
31 | import com.sp45.android_animations.util.WebAppText
32 |
33 | val animations = listOf(
34 | AnimationItem("Swipe To Delete Animation", Color(0xFF6200EE)),
35 | AnimationItem("Bouncing Ball Animation", Color(0xFF03DAC5)),
36 | AnimationItem("Value Spring Animation", Color(0xFF00BCD4)),
37 | AnimationItem("Content Animation", Color(0xFFFFEB3B)),
38 | AnimationItem("Carousel Slider", Color(0xFF3F51B5)),
39 | AnimationItem("Expandable Card Animation", Color(0xFF4CAF50)),
40 | AnimationItem("Wave Loading Bar", Color(0xFFFF9800)),
41 | AnimationItem("Confetti Animation", Color(0xFF9C27B0)),
42 | AnimationItem("Flip Card", Color(0xFF2196F3)),
43 | AnimationItem("Floating Elements", Color(0xFFE91E63)),
44 | AnimationItem("Type Writer Animation", Color(0xFF4CAF50)),
45 | AnimationItem("Emoji Progress Bar", Color(0xFF3F51B5)),
46 | AnimationItem("Expanding Rings", Color(0xFF4CAF50)),
47 | AnimationItem("Orbiting Objects", Color(0xFF3F51B5)),
48 | AnimationItem("Card Flipping", Color(0xFFE91E63)),
49 | AnimationItem("Button to Image", Color(0xFFFF9800)),
50 | AnimationItem("Image Transition", Color(0xFF6200EE)),
51 | AnimationItem("Sliding Door", Color(0xFF00BCD4)),
52 | AnimationItem("Text Explosion", Color(0xFFE91E63))
53 | )
54 |
55 | @Composable
56 | fun MainScreen(
57 | navController: NavController
58 | ) {
59 | Scaffold(
60 | topBar = {
61 | CustomTopAppBar(
62 | title = "Android Animations"
63 | )
64 | },
65 | content = { paddingValues ->
66 | Column(
67 | modifier = Modifier
68 | .background(MaterialTheme.colorScheme.background)
69 | .padding(paddingValues)
70 | ) {
71 | Row(
72 | modifier = Modifier
73 | .fillMaxWidth()
74 | .padding(start = 10.dp, top = 7.dp, end = 10.dp, bottom = 7.dp),
75 | horizontalArrangement = Arrangement.Start,
76 | verticalAlignment = Alignment.CenterVertically,
77 | ) {
78 | Icon(
79 | Icons.Default.Info,
80 | contentDescription = stringResource(R.string.open_web_app),
81 | tint = MaterialTheme.colorScheme.primary,
82 | modifier = Modifier.size(22.dp)
83 | )
84 | Spacer(Modifier.width(5.dp))
85 | WebAppText(
86 | onClick = {
87 | navController.navigate(NavigationDestinations.WEB_VIEW)
88 | }
89 | )
90 | Spacer(Modifier.width(5.dp))
91 | FadingTextAnimation()
92 | }
93 | LazyColumn(
94 | contentPadding = PaddingValues(16.dp),
95 | verticalArrangement = Arrangement.spacedBy(16.dp),
96 | modifier = Modifier
97 | .fillMaxSize()
98 | .background(MaterialTheme.colorScheme.background)
99 | ) {
100 | items(
101 | animations.size,
102 | ) { index ->
103 | AnimationCard(
104 | animationItem = animations[index],
105 | onClick = {
106 | val route =
107 | NavigationDestinations.createAnimationRoute(animations[index].name)
108 | navController.navigate(route)
109 | }
110 | )
111 | }
112 | }
113 | }
114 | }
115 | )
116 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/animations/AnimatedPlacement.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations.animations
2 |
3 | import androidx.compose.animation.core.AnimationVector2D
4 | import androidx.compose.animation.core.DeferredTargetAnimation
5 | import androidx.compose.animation.core.ExperimentalAnimatableApi
6 | import androidx.compose.animation.core.VectorConverter
7 | import androidx.compose.animation.core.tween
8 | import androidx.compose.foundation.background
9 | import androidx.compose.foundation.clickable
10 | import androidx.compose.foundation.layout.Box
11 | import androidx.compose.foundation.layout.Column
12 | import androidx.compose.foundation.layout.Row
13 | import androidx.compose.foundation.layout.fillMaxSize
14 | import androidx.compose.foundation.layout.padding
15 | import androidx.compose.foundation.layout.size
16 | import androidx.compose.foundation.shape.RoundedCornerShape
17 | import androidx.compose.foundation.text.BasicText
18 | import androidx.compose.runtime.Composable
19 | import androidx.compose.runtime.getValue
20 | import androidx.compose.runtime.movableContentOf
21 | import androidx.compose.runtime.mutableStateOf
22 | import androidx.compose.runtime.remember
23 | import androidx.compose.runtime.setValue
24 | import androidx.compose.ui.Alignment
25 | import androidx.compose.ui.Modifier
26 | import androidx.compose.ui.geometry.Offset
27 | import androidx.compose.ui.graphics.Color
28 | import androidx.compose.ui.layout.ApproachLayoutModifierNode
29 | import androidx.compose.ui.layout.ApproachMeasureScope
30 | import androidx.compose.ui.layout.LayoutCoordinates
31 | import androidx.compose.ui.layout.LookaheadScope
32 | import androidx.compose.ui.layout.Measurable
33 | import androidx.compose.ui.layout.MeasureResult
34 | import androidx.compose.ui.layout.Placeable
35 | import androidx.compose.ui.node.ModifierNodeElement
36 | import androidx.compose.ui.text.TextStyle
37 | import androidx.compose.ui.text.font.FontWeight
38 | import androidx.compose.ui.unit.Constraints
39 | import androidx.compose.ui.unit.IntOffset
40 | import androidx.compose.ui.unit.IntSize
41 | import androidx.compose.ui.unit.dp
42 | import androidx.compose.ui.unit.round
43 | import androidx.compose.ui.unit.sp
44 |
45 | @Composable
46 | fun AnimatedPlacement() {
47 | var isInColumn by remember { mutableStateOf(true) }
48 | Column {
49 | BasicText(
50 | "With LookaheadScope",
51 | style = TextStyle.Default.copy(
52 | fontWeight = FontWeight.W900,
53 | fontSize = 30.sp
54 | ),
55 | modifier = Modifier
56 | .padding(30.dp)
57 | .align(Alignment.CenterHorizontally)
58 | )
59 | LookaheadScope {
60 | val items = remember {
61 | movableContentOf {
62 | colors.forEach { color ->
63 | Box(
64 | Modifier
65 | .padding(15.dp)
66 | .size(100.dp, 80.dp)
67 | .then(AnimatePlacementNodeElement(this))
68 | .background(color, RoundedCornerShape(20))
69 | )
70 | }
71 | }
72 | }
73 |
74 | Box(
75 | modifier = Modifier
76 | .fillMaxSize()
77 | .clickable { isInColumn = !isInColumn }) {
78 | if (isInColumn) {
79 | Column(Modifier.fillMaxSize()) { items() }
80 | } else {
81 | Row { items() }
82 | }
83 | }
84 | }
85 | }
86 | }
87 |
88 | class AnimatedPlacementModifierNode(var lookaheadScope: LookaheadScope) :
89 | ApproachLayoutModifierNode, Modifier.Node() {
90 | @OptIn(ExperimentalAnimatableApi::class)
91 | val offsetAnimation: DeferredTargetAnimation =
92 | DeferredTargetAnimation(IntOffset.VectorConverter)
93 |
94 | override fun isMeasurementApproachInProgress(lookaheadSize: IntSize): Boolean {
95 | return false
96 | }
97 |
98 | @OptIn(ExperimentalAnimatableApi::class)
99 | override fun Placeable.PlacementScope.isPlacementApproachInProgress(
100 | lookaheadCoordinates: LayoutCoordinates,
101 | ): Boolean {
102 | val target =
103 | with(lookaheadScope) {
104 | lookaheadScopeCoordinates.localLookaheadPositionOf(lookaheadCoordinates).round()
105 | }
106 | offsetAnimation.updateTarget(target, coroutineScope, animationSpec = tween(300))
107 | return !offsetAnimation.isIdle
108 | }
109 |
110 | @OptIn(ExperimentalAnimatableApi::class)
111 | override fun ApproachMeasureScope.approachMeasure(
112 | measurable: Measurable,
113 | constraints: Constraints,
114 | ): MeasureResult {
115 | val placeable = measurable.measure(constraints)
116 | return layout(placeable.width, placeable.height) {
117 | val coordinates = coordinates
118 | if (coordinates != null) {
119 | val target =
120 | with(lookaheadScope) {
121 | lookaheadScopeCoordinates.localLookaheadPositionOf(coordinates).round()
122 | }
123 | val animatedOffset = offsetAnimation.updateTarget(target, coroutineScope)
124 | val placementOffset =
125 | with(lookaheadScope) {
126 | lookaheadScopeCoordinates
127 | .localPositionOf(coordinates, Offset.Zero)
128 | .round()
129 | }
130 | // Calculates the delta between animated position in scope and current
131 | // position in scope, and places the child at the delta offset. This puts
132 | // the child layout at the animated position.
133 | val (x, y) = animatedOffset - placementOffset
134 | placeable.place(x, y)
135 | } else {
136 | placeable.place(0, 0)
137 | }
138 | }
139 | }
140 | }
141 |
142 | data class AnimatePlacementNodeElement(val lookaheadScope: LookaheadScope) :
143 | ModifierNodeElement() {
144 |
145 | override fun update(node: AnimatedPlacementModifierNode) {
146 | node.lookaheadScope = lookaheadScope
147 | }
148 |
149 | override fun create(): AnimatedPlacementModifierNode {
150 | return AnimatedPlacementModifierNode(lookaheadScope)
151 | }
152 | }
153 |
154 | val colors = listOf(Color(0xffff6f69), Color(0xffffcc5c), Color(0xff264653), Color(0xff2a9d84))
155 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/animations/AutoImageTransition.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations.animations
2 |
3 | import androidx.compose.animation.core.Animatable
4 | import androidx.compose.animation.core.tween
5 | import androidx.compose.foundation.Image
6 | import androidx.compose.foundation.background
7 | import androidx.compose.foundation.layout.Box
8 | import androidx.compose.foundation.layout.fillMaxSize
9 | import androidx.compose.foundation.layout.size
10 | import androidx.compose.material3.MaterialTheme
11 | import androidx.compose.runtime.Composable
12 | import androidx.compose.runtime.LaunchedEffect
13 | import androidx.compose.runtime.getValue
14 | import androidx.compose.runtime.mutableIntStateOf
15 | import androidx.compose.runtime.remember
16 | import androidx.compose.runtime.setValue
17 | import androidx.compose.ui.Alignment
18 | import androidx.compose.ui.Modifier
19 | import androidx.compose.ui.graphics.graphicsLayer
20 | import androidx.compose.ui.layout.ContentScale
21 | import androidx.compose.ui.res.painterResource
22 | import androidx.compose.ui.unit.dp
23 | import com.sp45.android_animations.R
24 | import kotlinx.coroutines.delay
25 |
26 | @Composable
27 | fun AutoImageTransition(
28 | displayTimeMillis: Long = 1000L,
29 | fadeDurationMillis: Int = 500
30 | ) {
31 | val images = listOf(
32 | R.drawable.img_2,
33 | R.drawable.img,
34 | R.drawable.ic_launcher_background
35 | )
36 |
37 | var currentIndex by remember { mutableIntStateOf(0) }
38 | val fadeAnim = remember { Animatable(0f) }
39 |
40 | LaunchedEffect(currentIndex) {
41 | fadeAnim.animateTo(1f, animationSpec = tween(fadeDurationMillis))
42 | delay(displayTimeMillis)
43 | fadeAnim.animateTo(0f, animationSpec = tween(fadeDurationMillis))
44 |
45 | currentIndex = (currentIndex + 1) % images.size
46 | }
47 |
48 | Box(
49 | modifier = Modifier
50 | .fillMaxSize()
51 | .background(MaterialTheme.colorScheme.background),
52 | contentAlignment = Alignment.Center
53 | ) {
54 | Image(
55 | painter = painterResource(images[currentIndex]),
56 | contentDescription = "Carousel Image $currentIndex",
57 | modifier = Modifier
58 | .size(200.dp)
59 | .graphicsLayer(alpha = fadeAnim.value),
60 | contentScale = ContentScale.Crop
61 | )
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/animations/BouncingBallAnimation.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations.animations
2 |
3 | import androidx.compose.animation.core.LinearEasing
4 | import androidx.compose.animation.core.RepeatMode
5 | import androidx.compose.animation.core.infiniteRepeatable
6 | import androidx.compose.animation.core.tween
7 | import androidx.compose.foundation.Canvas
8 | import androidx.compose.foundation.background
9 | import androidx.compose.foundation.layout.Box
10 | import androidx.compose.foundation.layout.fillMaxSize
11 | import androidx.compose.foundation.layout.padding
12 | import androidx.compose.material3.MaterialTheme
13 | import androidx.compose.runtime.Composable
14 | import androidx.compose.runtime.LaunchedEffect
15 | import androidx.compose.runtime.remember
16 | import androidx.compose.ui.Alignment
17 | import androidx.compose.ui.Modifier
18 | import androidx.compose.ui.geometry.Offset
19 | import androidx.compose.ui.graphics.Color
20 | import androidx.compose.ui.unit.dp
21 |
22 | @Composable
23 | fun BouncingBallAnimation() {
24 | val ballPosition = remember { androidx.compose.animation.core.Animatable(0f) }
25 | val ballSize = 50.dp
26 | val ballColor = MaterialTheme.colorScheme.primary
27 | MaterialTheme.colorScheme.primary.copy(alpha = 0.3f)
28 | val ballScale = if (ballPosition.value > 490f) 1.2f else 1f
29 |
30 | LaunchedEffect(Unit) {
31 | ballPosition.animateTo(
32 | targetValue = 500f,
33 | animationSpec = infiniteRepeatable(
34 | animation = tween(1000, easing = LinearEasing),
35 | repeatMode = RepeatMode.Reverse
36 | )
37 | )
38 | }
39 |
40 | Box(
41 | modifier = Modifier
42 | .fillMaxSize()
43 | .background(MaterialTheme.colorScheme.background)
44 | .padding(100.dp,200.dp),
45 | contentAlignment = Alignment.Center,
46 | ) {
47 | Canvas(modifier = Modifier.fillMaxSize()) {
48 | drawCircle(
49 | color = Color.Black.copy(alpha = 0.3f),
50 | radius = (ballSize.toPx() / 2) * ballScale,
51 | center = Offset(size.width / 2, ballPosition.value + 8f)
52 | )
53 | drawCircle(
54 | color = ballColor,
55 | radius = (ballSize.toPx() / 2) * ballScale,
56 | center = Offset(size.width / 2, ballPosition.value)
57 | )
58 | }
59 | }
60 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/animations/ButtonToImage.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations.animations
2 |
3 | import androidx.compose.animation.AnimatedVisibility
4 | import androidx.compose.animation.core.animateFloatAsState
5 | import androidx.compose.animation.core.tween
6 | import androidx.compose.animation.fadeIn
7 | import androidx.compose.animation.fadeOut
8 | import androidx.compose.animation.scaleIn
9 | import androidx.compose.animation.scaleOut
10 | import androidx.compose.foundation.Image
11 | import androidx.compose.foundation.background
12 | import androidx.compose.foundation.layout.Box
13 | import androidx.compose.foundation.layout.fillMaxSize
14 | import androidx.compose.foundation.layout.padding
15 | import androidx.compose.foundation.layout.size
16 | import androidx.compose.material3.Button
17 | import androidx.compose.material3.ButtonDefaults
18 | import androidx.compose.material3.MaterialTheme
19 | import androidx.compose.material3.Text
20 | import androidx.compose.runtime.Composable
21 | import androidx.compose.runtime.getValue
22 | import androidx.compose.runtime.mutableStateOf
23 | import androidx.compose.runtime.remember
24 | import androidx.compose.runtime.setValue
25 | import androidx.compose.ui.Alignment
26 | import androidx.compose.ui.Modifier
27 | import androidx.compose.ui.graphics.graphicsLayer
28 | import androidx.compose.ui.res.painterResource
29 | import androidx.compose.ui.res.stringResource
30 | import androidx.compose.ui.unit.dp
31 | import com.sp45.android_animations.R
32 |
33 | @Composable
34 | fun ButtonToImage() {
35 | var isVisible by remember { mutableStateOf(false) }
36 |
37 | val rotation by animateFloatAsState(
38 | targetValue = if (isVisible) 720f else 0f,
39 | animationSpec = tween(durationMillis = 1200),
40 | label = ""
41 | )
42 |
43 | Box(
44 | modifier = Modifier
45 | .fillMaxSize()
46 | .background(MaterialTheme.colorScheme.background),
47 | contentAlignment = Alignment.Center
48 | ) {
49 | AnimatedVisibility(
50 | visible = !isVisible,
51 | enter = fadeIn(animationSpec = tween(durationMillis = 1000)),
52 | exit = fadeOut(animationSpec = tween(durationMillis = 1000))
53 | ) {
54 | Button(
55 | onClick = { isVisible = true },
56 | colors = ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.primary),
57 | modifier = Modifier
58 | .align(Alignment.BottomStart)
59 | .padding(16.dp)
60 | .size(width = 120.dp, height = 50.dp)
61 | ) {
62 | Text(
63 | text = stringResource(R.string.click_me),
64 | color = MaterialTheme.colorScheme.onPrimary
65 | )
66 | }
67 | }
68 |
69 | AnimatedVisibility(
70 | visible = isVisible,
71 | enter = fadeIn(animationSpec = tween(durationMillis = 1200)) +
72 | scaleIn(initialScale = 0.5f, animationSpec = tween(durationMillis = 1200)),
73 | exit = fadeOut(animationSpec = tween(durationMillis = 1200)) +
74 | scaleOut(targetScale = 0.5f, animationSpec = tween(durationMillis = 1200))
75 | ) {
76 | Image(
77 | painter = painterResource(id = R.drawable.img_2),
78 | contentDescription = stringResource(R.string.crakcode_image),
79 | modifier = Modifier
80 | .fillMaxSize()
81 | .graphicsLayer {
82 | rotationZ = rotation
83 | }
84 | )
85 | }
86 | }
87 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/animations/CardFlipAnimation.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations.animations
2 |
3 | import androidx.compose.animation.core.animateFloatAsState
4 | import androidx.compose.animation.core.tween
5 | import androidx.compose.foundation.Image
6 | import androidx.compose.foundation.background
7 | import androidx.compose.foundation.clickable
8 | import androidx.compose.foundation.layout.Arrangement
9 | import androidx.compose.foundation.layout.Box
10 | import androidx.compose.foundation.layout.Column
11 | import androidx.compose.foundation.layout.Spacer
12 | import androidx.compose.foundation.layout.fillMaxSize
13 | import androidx.compose.foundation.layout.height
14 | import androidx.compose.foundation.layout.size
15 | import androidx.compose.foundation.shape.RoundedCornerShape
16 | import androidx.compose.material3.MaterialTheme
17 | import androidx.compose.material3.Text
18 | import androidx.compose.runtime.Composable
19 | import androidx.compose.runtime.LaunchedEffect
20 | import androidx.compose.runtime.getValue
21 | import androidx.compose.runtime.mutableStateOf
22 | import androidx.compose.runtime.remember
23 | import androidx.compose.runtime.setValue
24 | import androidx.compose.ui.Alignment
25 | import androidx.compose.ui.Modifier
26 | import androidx.compose.ui.draw.clip
27 | import androidx.compose.ui.graphics.Brush
28 | import androidx.compose.ui.graphics.graphicsLayer
29 | import androidx.compose.ui.res.painterResource
30 | import androidx.compose.ui.res.stringResource
31 | import androidx.compose.ui.tooling.preview.Preview
32 | import androidx.compose.ui.unit.dp
33 | import com.sp45.android_animations.R
34 |
35 | @Composable
36 | fun CardFlipping() {
37 | var isFlipped by remember { mutableStateOf(false) }
38 | var showBackImage by remember { mutableStateOf(false) }
39 |
40 | val rotationY by animateFloatAsState(
41 | targetValue = if (isFlipped) 180f else 0f,
42 | animationSpec = tween(durationMillis = 1000), label = ""
43 | )
44 |
45 | LaunchedEffect(rotationY) {
46 | if (rotationY > 90 && !showBackImage) {
47 | showBackImage = true
48 | } else if (rotationY <= 90 && showBackImage) {
49 | showBackImage = false
50 | }
51 | }
52 |
53 | Column(
54 | modifier = Modifier
55 | .fillMaxSize()
56 | .background(MaterialTheme.colorScheme.background),
57 | verticalArrangement = Arrangement.Center,
58 | horizontalAlignment = Alignment.CenterHorizontally
59 | ) {
60 | Box(
61 | modifier = Modifier
62 | .size(220.dp, 300.dp)
63 | .graphicsLayer {
64 | this.rotationY = rotationY
65 | }
66 | .clip(RoundedCornerShape(16.dp))
67 | .background(
68 | Brush.linearGradient(
69 | colors = listOf(
70 | MaterialTheme.colorScheme.primary,
71 | MaterialTheme.colorScheme.secondary
72 | )
73 | )
74 | )
75 | .clickable { isFlipped = !isFlipped },
76 | contentAlignment = Alignment.Center
77 | ) {
78 | val imageResource = if (showBackImage) R.drawable.img else R.drawable.img_2
79 | Image(
80 | painter = painterResource(id = imageResource),
81 | contentDescription = if (showBackImage) stringResource(R.string.back_of_the_card) else stringResource(
82 | R.string.front_of_the_card
83 | ),
84 | modifier = Modifier
85 | .size(180.dp)
86 | .clip(RoundedCornerShape(12.dp))
87 | )
88 | }
89 |
90 | Spacer(modifier = Modifier.height(20.dp))
91 |
92 | Text(
93 | text = if (isFlipped) stringResource(R.string.see_front) else stringResource(R.string.flip),
94 | color = MaterialTheme.colorScheme.onBackground
95 | )
96 | }
97 | }
98 |
99 | @Preview
100 | @Composable
101 | fun RotatingCardPreview() {
102 | CardFlipping()
103 | }
104 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/animations/CardFlippingAnimation.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations.animations
2 |
3 | import androidx.compose.animation.core.animateFloatAsState
4 | import androidx.compose.animation.core.tween
5 | import androidx.compose.foundation.layout.Box
6 | import androidx.compose.foundation.layout.fillMaxSize
7 | import androidx.compose.foundation.layout.padding
8 | import androidx.compose.foundation.layout.size
9 | import androidx.compose.material3.Card
10 | import androidx.compose.material3.CardDefaults
11 | import androidx.compose.material3.MaterialTheme
12 | import androidx.compose.material3.Text
13 | import androidx.compose.runtime.Composable
14 | import androidx.compose.runtime.LaunchedEffect
15 | import androidx.compose.runtime.getValue
16 | import androidx.compose.runtime.mutableStateOf
17 | import androidx.compose.runtime.remember
18 | import androidx.compose.runtime.setValue
19 | import androidx.compose.ui.Alignment
20 | import androidx.compose.ui.Modifier
21 | import androidx.compose.ui.graphics.graphicsLayer
22 | import androidx.compose.ui.res.stringResource
23 | import androidx.compose.ui.text.font.FontWeight
24 | import androidx.compose.ui.unit.dp
25 | import androidx.compose.ui.unit.sp
26 | import com.sp45.android_animations.R
27 | import kotlinx.coroutines.delay
28 |
29 | @Composable
30 | fun FlipCard() {
31 | var cardFlipped by remember { mutableStateOf(false) }
32 | val rotation by animateFloatAsState(
33 | targetValue = if (cardFlipped) 180f else 0f,
34 | animationSpec = tween(600), label = ""
35 | )
36 |
37 | LaunchedEffect(Unit) {
38 | while (true) {
39 | delay(800)
40 | cardFlipped = !cardFlipped
41 | }
42 | }
43 |
44 | Box(
45 | modifier = Modifier
46 | .fillMaxSize()
47 | .padding(16.dp),
48 | contentAlignment = Alignment.Center
49 | ) {
50 | Box(
51 | modifier = Modifier
52 | .size(200.dp)
53 | .graphicsLayer {
54 | rotationY = rotation
55 | cameraDistance = 12f * density
56 | }
57 | ) {
58 | if (rotation <= 90f) {
59 | Card(
60 | modifier = Modifier.fillMaxSize(),
61 | colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.primary),
62 | elevation = CardDefaults.cardElevation(12.dp)
63 | ) {
64 | Box(
65 | modifier = Modifier
66 | .fillMaxSize()
67 | .padding(16.dp),
68 | contentAlignment = Alignment.Center
69 | ) {
70 | Text(
71 | text = stringResource(R.string.front),
72 | fontSize = 24.sp,
73 | fontWeight = FontWeight.Bold,
74 | color = MaterialTheme.colorScheme.onPrimary
75 | )
76 | }
77 | }
78 | } else {
79 | Card(
80 | modifier = Modifier
81 | .fillMaxSize()
82 | .graphicsLayer { rotationY = 180f },
83 | colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.secondary),
84 | elevation = CardDefaults.cardElevation(12.dp)
85 | ) {
86 | Box(
87 | modifier = Modifier
88 | .fillMaxSize()
89 | .padding(16.dp),
90 | contentAlignment = Alignment.Center
91 | ) {
92 | Text(
93 | text = stringResource(R.string.back),
94 | fontSize = 24.sp,
95 | fontWeight = FontWeight.Bold,
96 | color = MaterialTheme.colorScheme.onSecondary
97 | )
98 | }
99 | }
100 | }
101 | }
102 | }
103 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/animations/CarouselSlider.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations.animations
2 |
3 | import android.annotation.SuppressLint
4 | import androidx.compose.animation.core.animateDpAsState
5 | import androidx.compose.animation.core.animateFloatAsState
6 | import androidx.compose.foundation.Image
7 | import androidx.compose.foundation.background
8 | import androidx.compose.foundation.layout.Box
9 | import androidx.compose.foundation.layout.fillMaxSize
10 | import androidx.compose.foundation.layout.fillMaxWidth
11 | import androidx.compose.foundation.layout.offset
12 | import androidx.compose.foundation.layout.padding
13 | import androidx.compose.foundation.layout.size
14 | import androidx.compose.foundation.shape.RoundedCornerShape
15 | import androidx.compose.material3.Card
16 | import androidx.compose.material3.CardDefaults
17 | import androidx.compose.material3.MaterialTheme
18 | import androidx.compose.runtime.Composable
19 | import androidx.compose.runtime.LaunchedEffect
20 | import androidx.compose.runtime.getValue
21 | import androidx.compose.runtime.mutableIntStateOf
22 | import androidx.compose.runtime.remember
23 | import androidx.compose.runtime.setValue
24 | import androidx.compose.ui.Alignment
25 | import androidx.compose.ui.Modifier
26 | import androidx.compose.ui.draw.scale
27 | import androidx.compose.ui.graphics.graphicsLayer
28 | import androidx.compose.ui.layout.ContentScale
29 | import androidx.compose.ui.res.painterResource
30 | import androidx.compose.ui.unit.dp
31 | import com.sp45.android_animations.R
32 | import kotlinx.coroutines.delay
33 |
34 | @SuppressLint("UseOfNonLambdaOffsetOverload")
35 | @Composable
36 | fun CarouselSlider() {
37 | val images = listOf(
38 | R.drawable.cat1,
39 | R.drawable.cat2,
40 | R.drawable.cat3,
41 | R.drawable.cat4
42 | )
43 | var currentIndex by remember { mutableIntStateOf(0) }
44 | val totalImages = images.size
45 |
46 | LaunchedEffect(Unit) {
47 | while (true) {
48 | delay(2000)
49 | currentIndex = (currentIndex + 1) % images.size
50 | }
51 | }
52 |
53 | Box(
54 | modifier = Modifier
55 | .fillMaxSize()
56 | .background(MaterialTheme.colorScheme.background),
57 | contentAlignment = Alignment.Center,
58 | ) {
59 | for (i in -1..1) {
60 | val index = (currentIndex + i + totalImages) % totalImages
61 | val imageRes = images[index]
62 |
63 | val isCenterImage = (i == 0)
64 | val scale by animateFloatAsState(
65 | targetValue = if (isCenterImage) 1.2f else 0.8f, label = ""
66 | )
67 | val offsetX by animateDpAsState(
68 | targetValue = (i * 100).dp, label = ""
69 | )
70 |
71 | Box(
72 | modifier = Modifier
73 | .offset(x = offsetX)
74 | .scale(scale)
75 | .graphicsLayer {
76 | alpha = if (isCenterImage) 1f else 0.5f
77 | },
78 | contentAlignment = Alignment.Center
79 | ) {
80 | Card(
81 | modifier = Modifier
82 | .size(120.dp)
83 | .padding(8.dp),
84 | shape = RoundedCornerShape(16.dp),
85 | elevation = CardDefaults.cardElevation(8.dp),
86 | colors = CardDefaults.cardColors(
87 | containerColor = if (isCenterImage) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.surface,
88 | contentColor = if (isCenterImage) MaterialTheme.colorScheme.onPrimary else MaterialTheme.colorScheme.onSurface
89 | )
90 | ) {
91 | Box(
92 | modifier = Modifier.fillMaxSize(),
93 | contentAlignment = Alignment.Center
94 | ) {
95 | Image(
96 | painter = painterResource(id = imageRes),
97 | contentDescription = null,
98 | contentScale = ContentScale.Crop,
99 | modifier = Modifier
100 | .fillMaxWidth()
101 | )
102 |
103 | }
104 | }
105 | }
106 | }
107 | }
108 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/animations/ConfettiAnimation.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations.animations
2 |
3 | import androidx.compose.animation.core.Animatable
4 | import androidx.compose.animation.core.LinearEasing
5 | import androidx.compose.animation.core.tween
6 | import androidx.compose.foundation.Canvas
7 | import androidx.compose.foundation.background
8 | import androidx.compose.foundation.layout.Box
9 | import androidx.compose.foundation.layout.fillMaxSize
10 | import androidx.compose.foundation.layout.padding
11 | import androidx.compose.material3.Button
12 | import androidx.compose.material3.MaterialTheme
13 | import androidx.compose.material3.Text
14 | import androidx.compose.runtime.Composable
15 | import androidx.compose.runtime.LaunchedEffect
16 | import androidx.compose.runtime.getValue
17 | import androidx.compose.runtime.mutableStateOf
18 | import androidx.compose.runtime.remember
19 | import androidx.compose.runtime.rememberCoroutineScope
20 | import androidx.compose.runtime.setValue
21 | import androidx.compose.ui.Alignment
22 | import androidx.compose.ui.Modifier
23 | import androidx.compose.ui.geometry.Offset
24 | import androidx.compose.ui.graphics.Color
25 | import androidx.compose.ui.res.stringResource
26 | import androidx.compose.ui.unit.dp
27 | import com.sp45.android_animations.R
28 | import kotlinx.coroutines.launch
29 | import kotlin.math.PI
30 | import kotlin.math.cos
31 | import kotlin.math.sin
32 | import kotlin.random.Random
33 |
34 | private data class Confetti(
35 | var position: Offset,
36 | val color: Color,
37 | val size: Float,
38 | var velocity: Offset,
39 | var lifetime: Float
40 | )
41 |
42 | @Composable
43 | fun BirthdayPopperAnimation() {
44 | var isPlaying by remember { mutableStateOf(false) }
45 | var confetti by remember { mutableStateOf(emptyList()) }
46 | val scope = rememberCoroutineScope()
47 |
48 | Box(
49 | modifier = Modifier
50 | .fillMaxSize()
51 | .background(MaterialTheme.colorScheme.background),
52 | contentAlignment = Alignment.Center
53 | ) {
54 | Button(
55 | onClick = {
56 | if (!isPlaying) {
57 | isPlaying = true
58 | confetti = generateConfetti(Offset(500f, 800f))
59 | scope.launch {
60 | kotlinx.coroutines.delay(3000)
61 | isPlaying = false
62 | }
63 | }
64 | },
65 | modifier = Modifier
66 | .align(Alignment.BottomCenter)
67 | .padding(bottom = 50.dp)
68 | ) {
69 | Text(
70 | text = stringResource(R.string.pop),
71 | color = MaterialTheme.colorScheme.onPrimary
72 | )
73 | }
74 |
75 | if (isPlaying) {
76 | ConfettiAnimation(confetti)
77 | }
78 | }
79 | }
80 |
81 | @Composable
82 | private fun ConfettiAnimation(initialConfetti: List) {
83 | val animate = remember { Animatable(0f) }
84 |
85 | LaunchedEffect(Unit) {
86 | animate.animateTo(
87 | targetValue = 1f,
88 | animationSpec = tween(
89 | durationMillis = 3000,
90 | easing = LinearEasing
91 | )
92 | )
93 | }
94 |
95 | Canvas(modifier = Modifier.fillMaxSize()) {
96 | initialConfetti.forEach { confetti ->
97 |
98 | val gravity = 1000f * animate.value * animate.value
99 |
100 | val newPosition = Offset(
101 | x = confetti.position.x + (confetti.velocity.x * 900f * animate.value),
102 | y = confetti.position.y + (confetti.velocity.y * 900f * animate.value) + gravity
103 | )
104 |
105 | val alpha = 2f - animate.value
106 |
107 | drawCircle(
108 | color = confetti.color.copy(alpha = alpha),
109 | radius = confetti.size,
110 | center = newPosition
111 | )
112 | }
113 | }
114 | }
115 |
116 | private fun generateConfetti(origin: Offset): List {
117 | val colors = listOf(
118 | Color(0xFFFF69B4),
119 | Color(0xFFFFD700),
120 | Color(0xFF00FF00),
121 | Color(0xFF00BFFF),
122 | Color(0xFFFF1493),
123 | Color(0xFFFFA500)
124 | )
125 |
126 | return List(200) {
127 | val angle = Random.nextDouble(PI * 0.8, PI * 2.2)
128 | val speed = Random.nextDouble(0.2, 1.0)
129 |
130 | Confetti(
131 | position = origin,
132 | color = colors.random(),
133 | size = Random.nextFloat() * 6f + 2f,
134 | velocity = Offset(
135 | x = (cos(angle) * speed).toFloat(),
136 | y = (sin(angle) * speed).toFloat()
137 | ),
138 | lifetime = 1f
139 | )
140 | }
141 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/animations/ContentAnimation.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations.animations
2 |
3 | import androidx.compose.animation.AnimatedContent
4 | import androidx.compose.animation.fadeIn
5 | import androidx.compose.animation.fadeOut
6 | import androidx.compose.animation.scaleIn
7 | import androidx.compose.animation.scaleOut
8 | import androidx.compose.animation.togetherWith
9 | import androidx.compose.foundation.Image
10 | import androidx.compose.foundation.background
11 | import androidx.compose.foundation.layout.Arrangement
12 | import androidx.compose.foundation.layout.Column
13 | import androidx.compose.foundation.layout.Spacer
14 | import androidx.compose.foundation.layout.fillMaxSize
15 | import androidx.compose.foundation.layout.height
16 | import androidx.compose.foundation.layout.size
17 | import androidx.compose.foundation.shape.RoundedCornerShape
18 | import androidx.compose.material3.Button
19 | import androidx.compose.material3.MaterialTheme
20 | import androidx.compose.material3.Text
21 | import androidx.compose.runtime.Composable
22 | import androidx.compose.runtime.getValue
23 | import androidx.compose.runtime.mutableStateOf
24 | import androidx.compose.runtime.remember
25 | import androidx.compose.runtime.setValue
26 | import androidx.compose.ui.Alignment
27 | import androidx.compose.ui.Modifier
28 | import androidx.compose.ui.draw.clip
29 | import androidx.compose.ui.layout.ContentScale
30 | import androidx.compose.ui.res.painterResource
31 | import androidx.compose.ui.res.stringResource
32 | import androidx.compose.ui.unit.dp
33 | import com.sp45.android_animations.R
34 |
35 | @Composable
36 | fun ContentAnimation() {
37 | var isPictureOne by remember { mutableStateOf(true) }
38 | val image1 = painterResource(R.drawable.img_2)
39 | val image2 = painterResource(R.drawable.img)
40 |
41 | Column(
42 | modifier = Modifier
43 | .fillMaxSize()
44 | .background(MaterialTheme.colorScheme.background),
45 | horizontalAlignment = Alignment.CenterHorizontally,
46 | verticalArrangement = Arrangement.Top,
47 |
48 | ) {
49 | Button(
50 | onClick = { isPictureOne = !isPictureOne },
51 | colors = androidx.compose.material3.ButtonDefaults.buttonColors(
52 | containerColor = MaterialTheme.colorScheme.primary,
53 | contentColor = MaterialTheme.colorScheme.onPrimary
54 | )
55 | ) {
56 | Text(stringResource(R.string.change_profile_picture))
57 | }
58 | Spacer(modifier = Modifier.height(16.dp))
59 | AnimatedContent(
60 | targetState = isPictureOne,
61 | transitionSpec = {
62 | (fadeIn() + scaleIn(initialScale = 0.8f)).togetherWith(
63 | fadeOut() + scaleOut(
64 | targetScale = 1.2f
65 | )
66 | )
67 | }, label = ""
68 | ) { targetState ->
69 | Image(
70 | painter = if (targetState) image1 else image2,
71 | contentDescription = null,
72 | contentScale = ContentScale.FillWidth,
73 | modifier = Modifier
74 | .size(150.dp)
75 | .clip(RoundedCornerShape(16.dp))
76 | .background(MaterialTheme.colorScheme.surface)
77 | )
78 | }
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/animations/EmojiProgressBar.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations.animations
2 |
3 | import androidx.compose.animation.core.LinearEasing
4 | import androidx.compose.animation.core.RepeatMode
5 | import androidx.compose.animation.core.animateFloat
6 | import androidx.compose.animation.core.animateFloatAsState
7 | import androidx.compose.animation.core.infiniteRepeatable
8 | import androidx.compose.animation.core.rememberInfiniteTransition
9 | import androidx.compose.animation.core.tween
10 | import androidx.compose.foundation.background
11 | import androidx.compose.foundation.layout.Arrangement
12 | import androidx.compose.foundation.layout.Box
13 | import androidx.compose.foundation.layout.Column
14 | import androidx.compose.foundation.layout.Row
15 | import androidx.compose.foundation.layout.fillMaxSize
16 | import androidx.compose.foundation.layout.fillMaxWidth
17 | import androidx.compose.foundation.layout.height
18 | import androidx.compose.foundation.layout.padding
19 | import androidx.compose.material3.MaterialTheme
20 | import androidx.compose.material3.Text
21 | import androidx.compose.runtime.Composable
22 | import androidx.compose.runtime.getValue
23 | import androidx.compose.ui.Alignment
24 | import androidx.compose.ui.Modifier
25 | import androidx.compose.ui.graphics.Color
26 | import androidx.compose.ui.graphics.graphicsLayer
27 | import androidx.compose.ui.unit.dp
28 | import androidx.compose.ui.unit.sp
29 |
30 | @Composable
31 | fun EmojiProgressBar() {
32 | val totalCheckpoints = 5
33 | val durationMillis = 3000
34 | val infiniteTransition = rememberInfiniteTransition(label = "")
35 | val progressAnim by infiniteTransition.animateFloat(
36 | initialValue = 0f,
37 | targetValue = 1f,
38 | animationSpec = infiniteRepeatable(
39 | animation = tween(durationMillis, easing = LinearEasing),
40 | repeatMode = RepeatMode.Restart
41 | ), label = ""
42 | )
43 |
44 | Column(
45 | modifier = Modifier
46 | .fillMaxWidth()
47 | .fillMaxSize()
48 | .background(MaterialTheme.colorScheme.background),
49 | horizontalAlignment = Alignment.CenterHorizontally,
50 | verticalArrangement = Arrangement.Center
51 | ) {
52 | ProgressBarEmojiScaling(
53 | progress = progressAnim,
54 | totalCheckpoints = totalCheckpoints
55 | )
56 | }
57 | }
58 |
59 | @Composable
60 | fun ProgressBarEmojiScaling(progress: Float, totalCheckpoints: Int) {
61 | val emojis = listOf("🌟", "🔥", "💪", "🎯", "✅")
62 | val checkpointFraction = 1f / (totalCheckpoints - 1)
63 |
64 | Box(
65 | modifier = Modifier
66 | .fillMaxWidth()
67 | .padding(horizontal = 16.dp)
68 | .height(50.dp),
69 | contentAlignment = Alignment.Center
70 | ) {
71 | Box(
72 | modifier = Modifier
73 | .fillMaxWidth()
74 | .height(4.dp)
75 | .background(MaterialTheme.colorScheme.onBackground.copy(alpha = 0.2f))
76 | ) {
77 | Box(
78 | modifier = Modifier
79 | .fillMaxWidth(fraction = progress)
80 | .height(4.dp)
81 | .background(MaterialTheme.colorScheme.primary)
82 | )
83 | }
84 |
85 | Row(
86 | modifier = Modifier
87 | .fillMaxWidth()
88 | .padding(horizontal = 16.dp),
89 | horizontalArrangement = Arrangement.SpaceBetween,
90 | verticalAlignment = Alignment.CenterVertically
91 | ) {
92 | emojis.forEachIndexed { index, emoji ->
93 | val isReached = progress >= (index * checkpointFraction)
94 | val scale by animateFloatAsState(
95 | targetValue = if (isReached && progress <= ((index + 1) * checkpointFraction)) 1.5f else 1f,
96 | animationSpec = tween(durationMillis = 300), label = ""
97 | )
98 |
99 | Text(
100 | text = emoji,
101 | fontSize = 24.sp,
102 | color = if (isReached) Color(0xFF4CAF50) else Color.LightGray,
103 | modifier = Modifier.graphicsLayer(scaleX = scale, scaleY = scale)
104 | )
105 | }
106 | }
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/animations/ExpandableCardsAnimation.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations.animations
2 |
3 | import androidx.compose.animation.core.EaseInOut
4 | import androidx.compose.animation.core.animateDpAsState
5 | import androidx.compose.animation.core.tween
6 | import androidx.compose.foundation.background
7 | import androidx.compose.foundation.clickable
8 | import androidx.compose.foundation.layout.Arrangement
9 | import androidx.compose.foundation.layout.Box
10 | import androidx.compose.foundation.layout.Column
11 | import androidx.compose.foundation.layout.fillMaxHeight
12 | import androidx.compose.foundation.layout.fillMaxSize
13 | import androidx.compose.foundation.layout.height
14 | import androidx.compose.foundation.layout.padding
15 | import androidx.compose.foundation.layout.width
16 | import androidx.compose.foundation.shape.RoundedCornerShape
17 | import androidx.compose.material3.Card
18 | import androidx.compose.material3.CardDefaults
19 | import androidx.compose.material3.MaterialTheme
20 | import androidx.compose.material3.Text
21 | import androidx.compose.runtime.Composable
22 | import androidx.compose.runtime.getValue
23 | import androidx.compose.runtime.mutableStateOf
24 | import androidx.compose.runtime.remember
25 | import androidx.compose.runtime.setValue
26 | import androidx.compose.ui.Alignment
27 | import androidx.compose.ui.Modifier
28 | import androidx.compose.ui.res.stringResource
29 | import androidx.compose.ui.text.font.FontWeight
30 | import androidx.compose.ui.text.style.TextAlign
31 | import androidx.compose.ui.unit.dp
32 | import androidx.compose.ui.unit.sp
33 | import com.sp45.android_animations.R
34 |
35 | @Composable
36 | fun ExpandableCardAnimation() {
37 | var expanded by remember { mutableStateOf(false) }
38 | val cardHeight by animateDpAsState(
39 | targetValue = if (expanded) 250.dp else 120.dp,
40 | animationSpec = tween(durationMillis = 500, easing = EaseInOut), label = ""
41 | )
42 | val cardWidth by animateDpAsState(
43 | targetValue = if (expanded) 250.dp else 120.dp,
44 | animationSpec = tween(durationMillis = 500, easing = EaseInOut), label = ""
45 | )
46 |
47 | Column(
48 | modifier = Modifier
49 | .fillMaxSize()
50 | .background(MaterialTheme.colorScheme.background),
51 | verticalArrangement = Arrangement.Center,
52 | horizontalAlignment = Alignment.CenterHorizontally
53 | ) {
54 | Box(
55 | modifier = Modifier,
56 | contentAlignment = Alignment.Center,
57 | ) {
58 | Card(
59 | modifier = Modifier
60 | .height(cardHeight)
61 | .width(cardWidth)
62 | .fillMaxHeight()
63 | .clickable { expanded = !expanded },
64 | shape = RoundedCornerShape(24.dp),
65 | elevation = CardDefaults.cardElevation(8.dp),
66 | colors = CardDefaults.cardColors(
67 | containerColor = MaterialTheme.colorScheme.primary,
68 | contentColor = MaterialTheme.colorScheme.onPrimary
69 | ),
70 | ) {
71 | Box(
72 | modifier = Modifier
73 | .fillMaxSize()
74 | .padding(16.dp),
75 | contentAlignment = Alignment.Center
76 | ) {
77 | Text(
78 | text =
79 | if (expanded)
80 | stringResource(R.string.tap_to_collapse)
81 | else
82 | stringResource(R.string.tap_to_expand),
83 | fontSize = if (expanded) 20.sp else 18.sp,
84 | fontWeight = FontWeight.Bold,
85 | style = MaterialTheme.typography.bodyLarge,
86 | color = MaterialTheme.colorScheme.onPrimary,
87 | textAlign = TextAlign.Center
88 | )
89 | }
90 | }
91 | }
92 | }
93 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/animations/ExpandingRingsAnimation.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations.animations
2 |
3 | import androidx.compose.animation.core.LinearEasing
4 | import androidx.compose.animation.core.RepeatMode
5 | import androidx.compose.animation.core.animateFloat
6 | import androidx.compose.animation.core.infiniteRepeatable
7 | import androidx.compose.animation.core.rememberInfiniteTransition
8 | import androidx.compose.animation.core.tween
9 | import androidx.compose.foundation.Canvas
10 | import androidx.compose.foundation.background
11 | import androidx.compose.foundation.layout.Arrangement
12 | import androidx.compose.foundation.layout.Box
13 | import androidx.compose.foundation.layout.Column
14 | import androidx.compose.foundation.layout.fillMaxSize
15 | import androidx.compose.foundation.layout.size
16 | import androidx.compose.material3.MaterialTheme
17 | import androidx.compose.material3.Text
18 | import androidx.compose.runtime.Composable
19 | import androidx.compose.runtime.getValue
20 | import androidx.compose.ui.Alignment
21 | import androidx.compose.ui.Modifier
22 | import androidx.compose.ui.graphics.Color
23 | import androidx.compose.ui.res.stringResource
24 | import androidx.compose.ui.text.style.TextAlign
25 | import androidx.compose.ui.unit.dp
26 | import androidx.compose.ui.unit.sp
27 | import com.sp45.android_animations.R
28 |
29 | @Composable
30 | fun ExpandingRings() {
31 | val infiniteTransition = rememberInfiniteTransition(label = "")
32 |
33 | @Composable
34 | fun CreateRingAnimation(delayMillis: Int): Pair {
35 | val radius by infiniteTransition.animateFloat(
36 | initialValue = 0f,
37 | targetValue = 300f,
38 | animationSpec = infiniteRepeatable(
39 | animation = tween(1200, easing = LinearEasing, delayMillis = delayMillis),
40 | repeatMode = RepeatMode.Restart
41 | ), label = ""
42 | )
43 | val alpha by infiniteTransition.animateFloat(
44 | initialValue = 1f,
45 | targetValue = 0f,
46 | animationSpec = infiniteRepeatable(
47 | animation = tween(1200, easing = LinearEasing, delayMillis = delayMillis),
48 | repeatMode = RepeatMode.Restart
49 | ), label = ""
50 | )
51 | return radius to alpha
52 | }
53 |
54 | val (radius1, alpha1) = CreateRingAnimation(0)
55 | val (radius2, alpha2) = CreateRingAnimation(400)
56 | val (radius3, alpha3) = CreateRingAnimation(800)
57 |
58 | Column(
59 | modifier = Modifier
60 | .fillMaxSize()
61 | .background(MaterialTheme.colorScheme.background),
62 | verticalArrangement = Arrangement.Center,
63 | horizontalAlignment = Alignment.CenterHorizontally
64 | ) {
65 | Box(
66 | modifier = Modifier.fillMaxSize(0.3f),
67 | contentAlignment = Alignment.Center
68 | ) {
69 | Canvas(modifier = Modifier.size(600.dp)) {
70 | drawCircle(color = Color.Cyan.copy(alpha = alpha1), radius = radius1)
71 | drawCircle(color = Color.Magenta.copy(alpha = alpha2), radius = radius2)
72 | drawCircle(color = Color.Yellow.copy(alpha = alpha3), radius = radius3)
73 | }
74 | }
75 | Text(
76 | text = stringResource(R.string.loading),
77 | fontSize = 20.sp,
78 | color = MaterialTheme.colorScheme.onBackground,
79 | textAlign = TextAlign.Center
80 | )
81 | }
82 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/animations/FloatingElements.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations.animations
2 |
3 | import androidx.compose.animation.core.FastOutSlowInEasing
4 | import androidx.compose.animation.core.InfiniteTransition
5 | import androidx.compose.animation.core.LinearEasing
6 | import androidx.compose.animation.core.RepeatMode
7 | import androidx.compose.animation.core.animateFloat
8 | import androidx.compose.animation.core.infiniteRepeatable
9 | import androidx.compose.animation.core.rememberInfiniteTransition
10 | import androidx.compose.animation.core.tween
11 | import androidx.compose.foundation.background
12 | import androidx.compose.foundation.layout.Box
13 | import androidx.compose.foundation.layout.fillMaxSize
14 | import androidx.compose.foundation.layout.offset
15 | import androidx.compose.foundation.layout.size
16 | import androidx.compose.foundation.shape.CircleShape
17 | import androidx.compose.foundation.shape.RoundedCornerShape
18 | import androidx.compose.material3.MaterialTheme
19 | import androidx.compose.runtime.Composable
20 | import androidx.compose.runtime.getValue
21 | import androidx.compose.ui.Alignment
22 | import androidx.compose.ui.Modifier
23 | import androidx.compose.ui.draw.alpha
24 | import androidx.compose.ui.draw.rotate
25 | import androidx.compose.ui.draw.scale
26 | import androidx.compose.ui.unit.dp
27 |
28 | @Composable
29 | fun FloatingElements() {
30 | val infiniteTransition = rememberInfiniteTransition(label = "")
31 |
32 | Box(
33 | modifier = Modifier
34 | .fillMaxSize()
35 | .background(MaterialTheme.colorScheme.background)
36 | ) {
37 | repeat(6) { index ->
38 | AnimatedElement(
39 | infiniteTransition = infiniteTransition,
40 | index = index
41 | )
42 | }
43 | }
44 | }
45 |
46 | @Composable
47 | private fun AnimatedElement(
48 | infiniteTransition: InfiniteTransition,
49 | index: Int
50 | ) {
51 | val baseDelay = 500 * index
52 |
53 | val scale by infiniteTransition.animateFloat(
54 | initialValue = 0.8f,
55 | targetValue = 1.2f,
56 | animationSpec = infiniteRepeatable(
57 | animation = tween(
58 | durationMillis = 2000 + (index * 100),
59 | easing = FastOutSlowInEasing,
60 | delayMillis = baseDelay
61 | ),
62 | repeatMode = RepeatMode.Reverse
63 | ), label = ""
64 | )
65 |
66 | val rotation by infiniteTransition.animateFloat(
67 | initialValue = 0f,
68 | targetValue = 360f,
69 | animationSpec = infiniteRepeatable(
70 | animation = tween(
71 | durationMillis = 3000 + (index * 200),
72 | easing = LinearEasing,
73 | delayMillis = baseDelay
74 | )
75 | ), label = ""
76 | )
77 |
78 | val xOffset by infiniteTransition.animateFloat(
79 | initialValue = -100f,
80 | targetValue = 100f,
81 | animationSpec = infiniteRepeatable(
82 | animation = tween(
83 | durationMillis = 4000 + (index * 300),
84 | easing = FastOutSlowInEasing,
85 | delayMillis = baseDelay
86 | ),
87 | repeatMode = RepeatMode.Reverse
88 | ), label = ""
89 | )
90 |
91 | val yOffset by infiniteTransition.animateFloat(
92 | initialValue = -50f,
93 | targetValue = 50f,
94 | animationSpec = infiniteRepeatable(
95 | animation = tween(
96 | durationMillis = 3000 + (index * 300),
97 | easing = FastOutSlowInEasing,
98 | delayMillis = baseDelay
99 | ),
100 | repeatMode = RepeatMode.Reverse
101 | ), label = ""
102 | )
103 |
104 | val alpha by infiniteTransition.animateFloat(
105 | initialValue = 0.4f,
106 | targetValue = 0.8f,
107 | animationSpec = infiniteRepeatable(
108 | animation = tween(
109 | durationMillis = 2000,
110 | easing = FastOutSlowInEasing,
111 | delayMillis = baseDelay
112 | ),
113 | repeatMode = RepeatMode.Reverse
114 | ), label = ""
115 | )
116 |
117 | val elementColors = listOf(
118 | MaterialTheme.colorScheme.primary,
119 | MaterialTheme.colorScheme.secondary,
120 | MaterialTheme.colorScheme.tertiary,
121 | MaterialTheme.colorScheme.error,
122 | MaterialTheme.colorScheme.inverseOnSurface,
123 | MaterialTheme.colorScheme.inversePrimary
124 | )
125 |
126 | val isSquare = index % 2 == 0
127 | val size = (40 + (index * 5)).dp
128 |
129 | Box(
130 | modifier = Modifier
131 | .fillMaxSize(),
132 | contentAlignment = Alignment.Center
133 | ) {
134 | Box(
135 | modifier = Modifier
136 | .offset(x = xOffset.dp, y = yOffset.dp)
137 | .scale(scale)
138 | .rotate(rotation)
139 | .alpha(alpha)
140 | .size(size)
141 | .background(
142 | elementColors[index % elementColors.size],
143 | if (isSquare) RoundedCornerShape(8.dp) else CircleShape
144 | )
145 | )
146 | }
147 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/animations/OrbitingObjects.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations.animations
2 |
3 | import androidx.compose.animation.core.InfiniteTransition
4 | import androidx.compose.animation.core.LinearEasing
5 | import androidx.compose.animation.core.RepeatMode
6 | import androidx.compose.animation.core.animateFloat
7 | import androidx.compose.animation.core.infiniteRepeatable
8 | import androidx.compose.animation.core.rememberInfiniteTransition
9 | import androidx.compose.animation.core.tween
10 | import androidx.compose.foundation.Canvas
11 | import androidx.compose.foundation.background
12 | import androidx.compose.foundation.layout.Box
13 | import androidx.compose.foundation.layout.fillMaxSize
14 | import androidx.compose.material3.MaterialTheme
15 | import androidx.compose.runtime.Composable
16 | import androidx.compose.ui.Alignment
17 | import androidx.compose.ui.Modifier
18 | import androidx.compose.ui.graphics.Color
19 | import androidx.compose.ui.unit.dp
20 | import kotlin.math.cos
21 | import kotlin.math.sin
22 |
23 | @Composable
24 | fun OrbitingObjects() {
25 | val infiniteTransition = rememberInfiniteTransition(label = "")
26 |
27 | val orbits = listOf(
28 | Triple(100f, 4000, Color.Red),
29 | Triple(150f, 6000, Color.Blue),
30 | Triple(200f, 8000, Color.Green),
31 | Triple(250f, 10000, Color.Yellow)
32 | )
33 |
34 | val animatedAngles = orbits.map { (_, speed, _) ->
35 | createOrbitAnimation(speed, infiniteTransition)
36 | }
37 |
38 | Box(
39 | modifier = Modifier
40 | .fillMaxSize()
41 | .background(MaterialTheme.colorScheme.background),
42 | contentAlignment = Alignment.Center
43 | ) {
44 | Canvas(modifier = Modifier.fillMaxSize()) {
45 | val centerX = size.width / 2
46 | val centerY = size.height / 2
47 |
48 | orbits.forEachIndexed { index, (radius, _, color) ->
49 | val angle = animatedAngles[index]
50 |
51 | val radians = Math.toRadians(angle.toDouble())
52 |
53 | val x = centerX + radius * cos(radians).toFloat()
54 | val y = centerY + radius * sin(radians).toFloat()
55 |
56 | drawCircle(color = color, radius = 10.dp.toPx(), center = androidx.compose.ui.geometry.Offset(x, y))
57 | }
58 |
59 | drawCircle(color = Color.Gray, radius = 20.dp.toPx(), center = androidx.compose.ui.geometry.Offset(centerX, centerY))
60 | }
61 | }
62 | }
63 |
64 | @Composable
65 | fun createOrbitAnimation(speedMillis: Int, infiniteTransition: InfiniteTransition): Float {
66 | return infiniteTransition.animateFloat(
67 | initialValue = 0f,
68 | targetValue = 360f,
69 | animationSpec = infiniteRepeatable(
70 | animation = tween(speedMillis, easing = LinearEasing),
71 | repeatMode = RepeatMode.Restart
72 | ), label = ""
73 | ).value
74 | }
75 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/animations/SlidingDoor.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations.animations
2 |
3 | import android.annotation.SuppressLint
4 | import androidx.compose.animation.core.FastOutSlowInEasing
5 | import androidx.compose.animation.core.animateDpAsState
6 | import androidx.compose.animation.core.tween
7 | import androidx.compose.foundation.Image
8 | import androidx.compose.foundation.background
9 | import androidx.compose.foundation.gestures.detectHorizontalDragGestures
10 | import androidx.compose.foundation.layout.Arrangement
11 | import androidx.compose.foundation.layout.Box
12 | import androidx.compose.foundation.layout.Column
13 | import androidx.compose.foundation.layout.Spacer
14 | import androidx.compose.foundation.layout.fillMaxSize
15 | import androidx.compose.foundation.layout.height
16 | import androidx.compose.foundation.layout.padding
17 | import androidx.compose.foundation.layout.width
18 | import androidx.compose.foundation.shape.RoundedCornerShape
19 | import androidx.compose.material3.MaterialTheme
20 | import androidx.compose.material3.Text
21 | import androidx.compose.runtime.Composable
22 | import androidx.compose.runtime.getValue
23 | import androidx.compose.runtime.mutableFloatStateOf
24 | import androidx.compose.runtime.remember
25 | import androidx.compose.runtime.setValue
26 | import androidx.compose.ui.Alignment
27 | import androidx.compose.ui.Modifier
28 | import androidx.compose.ui.draw.clip
29 | import androidx.compose.ui.input.pointer.pointerInput
30 | import androidx.compose.ui.layout.ContentScale
31 | import androidx.compose.ui.res.painterResource
32 | import androidx.compose.ui.res.stringResource
33 | import androidx.compose.ui.tooling.preview.Preview
34 | import androidx.compose.ui.unit.dp
35 | import com.sp45.android_animations.R
36 |
37 | @SuppressLint("AutoboxingStateCreation")
38 | @Composable
39 | fun SlidingDoorAnimation() {
40 | var widthState by remember { mutableFloatStateOf(0f) }
41 | val width by animateDpAsState(
42 | targetValue = widthState.dp,
43 | animationSpec = tween(durationMillis = 300, easing = FastOutSlowInEasing), label = ""
44 | )
45 |
46 | Box(
47 | modifier = Modifier
48 | .fillMaxSize()
49 | .background(MaterialTheme.colorScheme.background)
50 | .pointerInput(Unit) {
51 | detectHorizontalDragGestures { _, dragAmount ->
52 | val dragChange = dragAmount * 5f
53 | widthState = (widthState + dragChange).coerceIn(0f, 500f)
54 | }
55 | }
56 | ) {
57 | Column(
58 | modifier = Modifier
59 | .fillMaxSize()
60 | .padding(16.dp),
61 | verticalArrangement = Arrangement.Center,
62 | horizontalAlignment = Alignment.CenterHorizontally
63 | ) {
64 | Box(
65 | modifier = Modifier
66 | .width(width)
67 | .height(300.dp)
68 | .clip(RoundedCornerShape(16.dp))
69 | ) {
70 |
71 | Image(
72 | painter = painterResource(id = R.drawable.img_2),
73 | contentDescription = stringResource(R.string.hidden_content),
74 | modifier = Modifier.fillMaxSize(),
75 | contentScale = ContentScale.Crop
76 | )
77 | }
78 |
79 | Spacer(modifier = Modifier.height(20.dp))
80 |
81 | Text(
82 | text = stringResource(R.string.Swipe_right_or_left_to_open_or_close_the_door),
83 | modifier = Modifier.align(Alignment.CenterHorizontally),
84 | color = MaterialTheme.colorScheme.onBackground
85 | )
86 | }
87 | }
88 | }
89 |
90 | @Preview
91 | @Composable
92 | fun PreviewSlidingDoorAnimation() {
93 | SlidingDoorAnimation()
94 | }
95 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/animations/SwipeToDeleteAnimation.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations.animations
2 |
3 | import androidx.compose.animation.AnimatedVisibility
4 | import androidx.compose.animation.core.Animatable
5 | import androidx.compose.animation.core.FastOutSlowInEasing
6 | import androidx.compose.animation.core.Spring
7 | import androidx.compose.animation.core.spring
8 | import androidx.compose.animation.core.tween
9 | import androidx.compose.animation.fadeIn
10 | import androidx.compose.animation.fadeOut
11 | import androidx.compose.animation.slideInVertically
12 | import androidx.compose.animation.slideOutVertically
13 | import androidx.compose.foundation.background
14 | import androidx.compose.foundation.gestures.detectHorizontalDragGestures
15 | import androidx.compose.foundation.layout.Arrangement
16 | import androidx.compose.foundation.layout.Box
17 | import androidx.compose.foundation.layout.Column
18 | import androidx.compose.foundation.layout.Row
19 | import androidx.compose.foundation.layout.Spacer
20 | import androidx.compose.foundation.layout.fillMaxSize
21 | import androidx.compose.foundation.layout.fillMaxWidth
22 | import androidx.compose.foundation.layout.offset
23 | import androidx.compose.foundation.layout.padding
24 | import androidx.compose.foundation.layout.size
25 | import androidx.compose.foundation.layout.width
26 | import androidx.compose.foundation.shape.RoundedCornerShape
27 | import androidx.compose.material.icons.Icons
28 | import androidx.compose.material.icons.automirrored.rounded.KeyboardArrowLeft
29 | import androidx.compose.material.icons.rounded.Delete
30 | import androidx.compose.material.icons.rounded.Refresh
31 | import androidx.compose.material3.ButtonDefaults
32 | import androidx.compose.material3.Card
33 | import androidx.compose.material3.CardDefaults
34 | import androidx.compose.material3.ElevatedButton
35 | import androidx.compose.material3.Icon
36 | import androidx.compose.material3.MaterialTheme
37 | import androidx.compose.material3.Text
38 | import androidx.compose.runtime.Composable
39 | import androidx.compose.runtime.getValue
40 | import androidx.compose.runtime.mutableFloatStateOf
41 | import androidx.compose.runtime.mutableStateOf
42 | import androidx.compose.runtime.remember
43 | import androidx.compose.runtime.rememberCoroutineScope
44 | import androidx.compose.runtime.setValue
45 | import androidx.compose.ui.Alignment
46 | import androidx.compose.ui.Modifier
47 | import androidx.compose.ui.draw.alpha
48 | import androidx.compose.ui.draw.clip
49 | import androidx.compose.ui.input.pointer.pointerInput
50 | import androidx.compose.ui.res.stringResource
51 | import androidx.compose.ui.unit.IntOffset
52 | import androidx.compose.ui.unit.dp
53 | import com.sp45.android_animations.R
54 | import kotlinx.coroutines.launch
55 | import kotlin.math.roundToInt
56 |
57 | @Composable
58 | fun SwipeToDeleteAnimation() {
59 | var cardVisible by remember { mutableStateOf(true) }
60 | var showUndo by remember { mutableStateOf(false) }
61 |
62 | Box(
63 | modifier = Modifier.fillMaxSize(),
64 | contentAlignment = Alignment.Center
65 | ) {
66 | if (cardVisible) {
67 | SwipeAbleCard(
68 | modifier = Modifier.fillMaxWidth(),
69 | onDismiss = {
70 | cardVisible = false
71 | showUndo = true
72 | }
73 | )
74 | }
75 |
76 | AnimatedVisibility(
77 | visible = showUndo,
78 | enter = fadeIn() + slideInVertically(),
79 | exit = fadeOut() + slideOutVertically()
80 | ) {
81 | ElevatedButton(
82 | onClick = {
83 | cardVisible = true
84 | showUndo = false
85 | },
86 | modifier = Modifier.padding(top = 8.dp),
87 | colors = ButtonDefaults.elevatedButtonColors(
88 | containerColor = MaterialTheme.colorScheme.secondaryContainer,
89 | contentColor = MaterialTheme.colorScheme.onSecondaryContainer
90 | ),
91 | elevation = ButtonDefaults.elevatedButtonElevation(
92 | defaultElevation = 6.dp,
93 | pressedElevation = 8.dp
94 | )
95 | ) {
96 | Icon(
97 | imageVector = Icons.Rounded.Refresh,
98 | contentDescription = stringResource(R.string.undo),
99 | modifier = Modifier.size(16.dp)
100 | )
101 | Spacer(modifier = Modifier.width(8.dp))
102 | Text(stringResource(R.string.undo))
103 | }
104 | }
105 | }
106 | }
107 |
108 | @Composable
109 | fun SwipeAbleCard(
110 | modifier: Modifier = Modifier,
111 | onDismiss: () -> Unit
112 | ) {
113 | val coroutineScope = rememberCoroutineScope()
114 | val offsetX = remember { Animatable(0f) }
115 | val dismissThreshold = 300f
116 |
117 | val swipeBackgroundColor = MaterialTheme.colorScheme.errorContainer
118 | val swipeIconColor = MaterialTheme.colorScheme.onErrorContainer
119 | val cardContainerColor = MaterialTheme.colorScheme.surface
120 | val cardTextColor = MaterialTheme.colorScheme.onSurface
121 |
122 | val swipeProgress by remember(offsetX.value) {
123 | mutableFloatStateOf((-offsetX.value / dismissThreshold).coerceIn(0f, 1f))
124 | }
125 |
126 | Box(
127 | modifier = modifier
128 | .padding(16.dp)
129 | .clip(RoundedCornerShape(16.dp))
130 | ) {
131 | Box(
132 | modifier = Modifier
133 | .matchParentSize()
134 | .background(swipeBackgroundColor.copy(alpha = 0.9f * swipeProgress)),
135 | contentAlignment = Alignment.CenterEnd
136 | ) {
137 | Row(
138 | modifier = Modifier.padding(end = 24.dp),
139 | verticalAlignment = Alignment.CenterVertically
140 | ) {
141 | Icon(
142 | imageVector = Icons.Rounded.Delete,
143 | contentDescription = stringResource(R.string.delete),
144 | tint = swipeIconColor,
145 | modifier = Modifier
146 | .size(24.dp)
147 | .alpha(swipeProgress)
148 | )
149 | Spacer(modifier = Modifier.width(8.dp))
150 | Text(
151 | text = stringResource(R.string.delete),
152 | color = swipeIconColor,
153 | style = MaterialTheme.typography.bodyLarge,
154 | modifier = Modifier.alpha(swipeProgress)
155 | )
156 | }
157 | }
158 |
159 | Card(
160 | modifier = Modifier
161 | .fillMaxWidth()
162 | .offset { IntOffset(offsetX.value.roundToInt(), 0) }
163 | .pointerInput(Unit) {
164 | detectHorizontalDragGestures(
165 | onDragEnd = {
166 | coroutineScope.launch {
167 | if (offsetX.value < -dismissThreshold) {
168 | offsetX.animateTo(
169 | targetValue = -size.width.toFloat(),
170 | animationSpec = tween(
171 | durationMillis = 300,
172 | easing = FastOutSlowInEasing
173 | )
174 | )
175 | onDismiss()
176 | } else {
177 | offsetX.animateTo(
178 | targetValue = 0f,
179 | animationSpec = spring(
180 | dampingRatio = Spring.DampingRatioMediumBouncy,
181 | stiffness = Spring.StiffnessLow
182 | )
183 | )
184 | }
185 | }
186 | },
187 | onDragCancel = {
188 | coroutineScope.launch {
189 | offsetX.animateTo(0f)
190 | }
191 | },
192 | onHorizontalDrag = { _, dragAmount ->
193 | coroutineScope.launch {
194 | val newOffset = offsetX.value + dragAmount
195 | offsetX.snapTo(newOffset.coerceAtMost(0f))
196 | }
197 | }
198 | )
199 | },
200 | colors = CardDefaults.cardColors(
201 | containerColor = cardContainerColor
202 | ),
203 | elevation = CardDefaults.cardElevation(
204 | defaultElevation = 4.dp,
205 | pressedElevation = 8.dp
206 | )
207 | ) {
208 | Row(
209 | modifier = Modifier
210 | .fillMaxWidth()
211 | .padding(16.dp),
212 | horizontalArrangement = Arrangement.SpaceBetween,
213 | verticalAlignment = Alignment.CenterVertically
214 | ) {
215 | Column {
216 | Text(
217 | text = stringResource(R.string.swipe_to_delete),
218 | style = MaterialTheme.typography.titleMedium,
219 | color = cardTextColor
220 | )
221 | Text(
222 | text = stringResource(R.string.slide_left_to_remove_this_item),
223 | style = MaterialTheme.typography.bodyMedium,
224 | color = cardTextColor.copy(alpha = 0.7f),
225 | modifier = Modifier.padding(top = 4.dp)
226 | )
227 | }
228 | Icon(
229 | imageVector = Icons.AutoMirrored.Rounded.KeyboardArrowLeft,
230 | contentDescription = stringResource(R.string.swipe_left),
231 | tint = cardTextColor.copy(alpha = 0.7f),
232 | modifier = Modifier.size(24.dp)
233 | )
234 | }
235 | }
236 | }
237 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/animations/TextCollapsingAnimation.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations.animations
2 |
3 | import android.annotation.SuppressLint
4 | import androidx.compose.animation.core.LinearEasing
5 | import androidx.compose.animation.core.RepeatMode
6 | import androidx.compose.animation.core.animateFloat
7 | import androidx.compose.animation.core.infiniteRepeatable
8 | import androidx.compose.animation.core.rememberInfiniteTransition
9 | import androidx.compose.animation.core.tween
10 | import androidx.compose.foundation.background
11 | import androidx.compose.foundation.layout.Arrangement
12 | import androidx.compose.foundation.layout.Box
13 | import androidx.compose.foundation.layout.Row
14 | import androidx.compose.foundation.layout.fillMaxSize
15 | import androidx.compose.foundation.layout.offset
16 | import androidx.compose.material3.MaterialTheme
17 | import androidx.compose.material3.Text
18 | import androidx.compose.runtime.Composable
19 | import androidx.compose.runtime.remember
20 | import androidx.compose.ui.Alignment
21 | import androidx.compose.ui.Modifier
22 | import androidx.compose.ui.graphics.Color
23 | import androidx.compose.ui.res.stringResource
24 | import androidx.compose.ui.text.TextStyle
25 | import androidx.compose.ui.unit.dp
26 | import androidx.compose.ui.unit.sp
27 | import com.sp45.android_animations.R
28 | import kotlin.random.Random
29 |
30 | @SuppressLint("UseOfNonLambdaOffsetOverload")
31 | @Composable
32 | fun TextExplosion() {
33 | val text = stringResource(R.string.crakcode)
34 | val infiniteTransition = rememberInfiniteTransition(label = "")
35 |
36 | val randomOffsets = remember {
37 | text.map {
38 | Pair(
39 | Random.nextFloat() * 500 - 250,
40 | Random.nextFloat() * 500 - 250
41 | )
42 | }
43 | }
44 |
45 | val animations = randomOffsets.mapIndexed { _, (targetX, targetY) ->
46 | val offsetX = infiniteTransition.animateFloat(
47 | initialValue = 0f,
48 | targetValue = targetX,
49 | animationSpec = infiniteRepeatable(
50 | animation = tween(
51 | durationMillis = 3000,
52 | easing = LinearEasing
53 | ),
54 | repeatMode = RepeatMode.Reverse
55 | ), label = ""
56 | )
57 |
58 | val offsetY = infiniteTransition.animateFloat(
59 | initialValue = 0f,
60 | targetValue = targetY,
61 | animationSpec = infiniteRepeatable(
62 | animation = tween(
63 | durationMillis = 3000,
64 | easing = LinearEasing
65 | ),
66 | repeatMode = RepeatMode.Reverse
67 | ), label = ""
68 | )
69 |
70 | Pair(offsetX, offsetY)
71 | }
72 |
73 | Box(
74 | modifier = Modifier
75 | .fillMaxSize()
76 | .background(MaterialTheme.colorScheme.background),
77 | contentAlignment = Alignment.Center
78 | ) {
79 | Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
80 | text.forEachIndexed { index, char ->
81 | val (offsetX, offsetY) = animations[index]
82 |
83 | Box(
84 | modifier = Modifier
85 | .offset(offsetX.value.dp, offsetY.value.dp),
86 | contentAlignment = Alignment.Center
87 | ) {
88 | Text(
89 | text = char.toString(),
90 | style = TextStyle(
91 | color = Color.Green,
92 | fontSize = 32.sp
93 | )
94 | )
95 | }
96 | }
97 | }
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/animations/TypeWriterAnimation.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations.animations
2 |
3 | import androidx.compose.animation.animateContentSize
4 | import androidx.compose.animation.core.tween
5 | import androidx.compose.foundation.layout.Arrangement
6 | import androidx.compose.foundation.layout.Box
7 | import androidx.compose.foundation.layout.Row
8 | import androidx.compose.foundation.layout.fillMaxSize
9 | import androidx.compose.foundation.layout.fillMaxWidth
10 | import androidx.compose.foundation.layout.padding
11 | import androidx.compose.material3.MaterialTheme
12 | import androidx.compose.material3.Text
13 | import androidx.compose.runtime.Composable
14 | import androidx.compose.runtime.LaunchedEffect
15 | import androidx.compose.runtime.mutableIntStateOf
16 | import androidx.compose.runtime.mutableStateOf
17 | import androidx.compose.runtime.remember
18 | import androidx.compose.ui.Alignment
19 | import androidx.compose.ui.Modifier
20 | import androidx.compose.ui.graphics.Color
21 | import androidx.compose.ui.res.stringResource
22 | import androidx.compose.ui.text.TextStyle
23 | import androidx.compose.ui.text.font.FontWeight
24 | import androidx.compose.ui.unit.dp
25 | import androidx.compose.ui.unit.sp
26 | import com.sp45.android_animations.R
27 | import kotlinx.coroutines.delay
28 |
29 | @Composable
30 | fun TypeWriterAnimation() {
31 | Box( modifier = Modifier.fillMaxSize(),
32 | contentAlignment = Alignment.Center){
33 | TypeWriterComponent(
34 | text = stringResource(R.string.type_writer_animation),
35 | textStyle = TextStyle(
36 | fontSize = 24.sp,
37 | fontWeight = FontWeight.Bold,
38 | color = MaterialTheme.colorScheme.onBackground
39 | ),
40 | cursorColor = MaterialTheme.colorScheme.onBackground
41 | )
42 | }
43 | }
44 |
45 |
46 | @Composable
47 | fun TypeWriterComponent(
48 | text: String,
49 | textStyle: TextStyle = MaterialTheme.typography.bodyLarge.copy(
50 | fontSize = 24.sp,
51 | fontWeight = MaterialTheme.typography.bodyLarge.fontWeight,
52 | color = MaterialTheme.colorScheme.onBackground
53 | ),
54 | cursorColor: Color = MaterialTheme.colorScheme.onBackground,
55 | cursorBlinkDuration: Int = 600,
56 | charAppearanceDuration: Int = 350,
57 | charAppearanceDelay: Int = 130
58 | ) {
59 | val currentTextIndex = remember { mutableIntStateOf(0) }
60 | val cursorVisibility = remember { mutableStateOf(true) }
61 |
62 | LaunchedEffect(text) {
63 | while (true) {
64 | while (true) {
65 | cursorVisibility.value = !cursorVisibility.value
66 | delay(cursorBlinkDuration.toLong())
67 | }
68 | }
69 | }
70 |
71 | LaunchedEffect(text) {
72 | for (i in text.indices) {
73 | currentTextIndex.intValue = i
74 | delay(charAppearanceDelay.toLong())
75 | }
76 | }
77 |
78 | Row(
79 | modifier = Modifier
80 | .fillMaxWidth()
81 | .padding(16.dp),
82 | verticalAlignment = Alignment.CenterVertically,
83 | horizontalArrangement = Arrangement.Center
84 | ) {
85 | Text(
86 | text.take(currentTextIndex.intValue),
87 | style = textStyle,
88 | modifier = Modifier.animateContentSize(
89 | animationSpec = tween(charAppearanceDuration)
90 | )
91 | )
92 | if (cursorVisibility.value) {
93 | Text(
94 | "|",
95 | style = textStyle.copy(color = cursorColor),
96 | modifier = Modifier.padding(start = 4.dp)
97 | )
98 | }
99 | }
100 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/animations/ValueSpringAnimation.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations.animations
2 |
3 | import androidx.compose.animation.animateColorAsState
4 | import androidx.compose.animation.core.Spring
5 | import androidx.compose.animation.core.animateDpAsState
6 | import androidx.compose.animation.core.spring
7 | import androidx.compose.foundation.background
8 | import androidx.compose.foundation.layout.Box
9 | import androidx.compose.foundation.layout.Column
10 | import androidx.compose.foundation.layout.fillMaxSize
11 | import androidx.compose.foundation.layout.fillMaxWidth
12 | import androidx.compose.foundation.layout.padding
13 | import androidx.compose.foundation.layout.size
14 | import androidx.compose.foundation.shape.RoundedCornerShape
15 | import androidx.compose.material3.MaterialTheme
16 | import androidx.compose.material3.Slider
17 | import androidx.compose.material3.SliderDefaults
18 | import androidx.compose.material3.Text
19 | import androidx.compose.runtime.Composable
20 | import androidx.compose.runtime.getValue
21 | import androidx.compose.runtime.mutableFloatStateOf
22 | import androidx.compose.runtime.remember
23 | import androidx.compose.runtime.setValue
24 | import androidx.compose.ui.Alignment
25 | import androidx.compose.ui.Modifier
26 | import androidx.compose.ui.draw.clip
27 | import androidx.compose.ui.graphics.Color
28 | import androidx.compose.ui.res.stringResource
29 | import androidx.compose.ui.text.font.FontWeight
30 | import androidx.compose.ui.unit.dp
31 | import com.sp45.android_animations.R
32 |
33 | @Composable
34 | fun ValueSpringAnimation() {
35 | Column(
36 | modifier = Modifier
37 | .fillMaxSize()
38 | .background(MaterialTheme.colorScheme.background),
39 | horizontalAlignment = Alignment.CenterHorizontally
40 | ) {
41 | var sliderPosition by remember { mutableFloatStateOf(0f) }
42 | var roundnessPercent by remember { mutableFloatStateOf(0f) }
43 |
44 | Column(
45 | modifier = Modifier
46 | .fillMaxWidth()
47 | .padding(16.dp),
48 | horizontalAlignment = Alignment.CenterHorizontally
49 | ) {
50 | Slider(
51 | value = sliderPosition,
52 | onValueChange = { sliderPosition = it },
53 | valueRange = 0f..300f,
54 | steps = 5,
55 | colors = SliderDefaults.colors(
56 | thumbColor = MaterialTheme.colorScheme.primary,
57 | activeTrackColor = MaterialTheme.colorScheme.primary,
58 | inactiveTrackColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f)
59 | )
60 | )
61 | Text(
62 | text = stringResource(R.string.dynamic_circle_of_size_dp, sliderPosition.toInt()),
63 | color = MaterialTheme.colorScheme.onBackground,
64 | style = MaterialTheme.typography.bodyMedium,
65 | fontWeight = FontWeight.Bold
66 | )
67 | Slider(
68 | value = roundnessPercent,
69 | onValueChange = { roundnessPercent = it },
70 | valueRange = 0f..100f,
71 | steps = 5,
72 | colors = SliderDefaults.colors(
73 | thumbColor = MaterialTheme.colorScheme.secondary,
74 | activeTrackColor = MaterialTheme.colorScheme.secondary,
75 | inactiveTrackColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.5f)
76 | )
77 | )
78 | Text(
79 | text = stringResource(
80 | R.string.dynamic_circle_of_roundness,
81 | roundnessPercent.toInt()
82 | ),
83 | color = MaterialTheme.colorScheme.onBackground,
84 | style = MaterialTheme.typography.bodyMedium,
85 | fontWeight = FontWeight.Bold
86 | )
87 | }
88 |
89 | val sliderSize by animateDpAsState(
90 | targetValue = sliderPosition.dp,
91 | label = "Size animation",
92 | animationSpec = spring(
93 | dampingRatio = Spring.DampingRatioHighBouncy,
94 | stiffness = Spring.StiffnessVeryLow
95 | )
96 | )
97 |
98 | val color: Color by animateColorAsState(
99 | targetValue = when {
100 | sliderPosition < 100f -> Color.Green
101 | sliderPosition < 200f -> Color.Yellow
102 | else -> Color.Red
103 | },
104 | label = stringResource(R.string.color_animation)
105 | )
106 |
107 | Box(
108 | modifier = Modifier.fillMaxSize(),
109 | contentAlignment = Alignment.Center
110 | ) {
111 | val roundnessDp = (roundnessPercent / 100f) * (sliderSize.value / 2)
112 | Box(
113 | modifier = Modifier
114 | .size(sliderSize)
115 | .clip(RoundedCornerShape(roundnessDp.dp))
116 | .background(color),
117 | contentAlignment = Alignment.Center
118 | ) {
119 |
120 | }
121 | }
122 | }
123 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/animations/WaveLoadingBar.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations.animations
2 |
3 | import androidx.compose.animation.core.LinearEasing
4 | import androidx.compose.animation.core.RepeatMode
5 | import androidx.compose.animation.core.animateFloat
6 | import androidx.compose.animation.core.infiniteRepeatable
7 | import androidx.compose.animation.core.rememberInfiniteTransition
8 | import androidx.compose.animation.core.tween
9 | import androidx.compose.foundation.Canvas
10 | import androidx.compose.foundation.background
11 | import androidx.compose.foundation.layout.Arrangement
12 | import androidx.compose.foundation.layout.Box
13 | import androidx.compose.foundation.layout.Column
14 | import androidx.compose.foundation.layout.fillMaxSize
15 | import androidx.compose.foundation.layout.fillMaxWidth
16 | import androidx.compose.foundation.layout.height
17 | import androidx.compose.foundation.layout.padding
18 | import androidx.compose.material3.MaterialTheme
19 | import androidx.compose.material3.Text
20 | import androidx.compose.runtime.Composable
21 | import androidx.compose.runtime.getValue
22 | import androidx.compose.ui.Alignment
23 | import androidx.compose.ui.Modifier
24 | import androidx.compose.ui.graphics.Color
25 | import androidx.compose.ui.graphics.drawscope.translate
26 | import androidx.compose.ui.res.stringResource
27 | import androidx.compose.ui.unit.dp
28 | import com.sp45.android_animations.R
29 |
30 | @Composable
31 | fun WaveLoadingBar() {
32 | val waveColors = listOf(Color(0xFF4CAF50), Color(0xFF2196F3), Color(0xFFFF5722))
33 | val waveOffsets = listOf(0f, 0.4f, 0.8f)
34 |
35 | Column(
36 | modifier = Modifier
37 | .fillMaxSize()
38 | .background(MaterialTheme.colorScheme.background),
39 | horizontalAlignment = Alignment.CenterHorizontally,
40 | verticalArrangement = Arrangement.Center
41 | ) {
42 | Box(
43 | modifier = Modifier
44 | .fillMaxWidth()
45 | .height(100.dp)
46 | .padding(vertical = 24.dp)
47 | ) {
48 | waveColors.forEachIndexed { index, color ->
49 | Wave(
50 | waveColor = color,
51 | offsetFactor = waveOffsets[index],
52 | modifier = Modifier.fillMaxSize()
53 | )
54 | }
55 | }
56 |
57 | Text(
58 | text = stringResource(R.string.loading),
59 | color = MaterialTheme.colorScheme.onBackground,
60 | style = MaterialTheme.typography.bodyLarge
61 | )
62 | }
63 | }
64 |
65 | @Composable
66 | fun Wave(waveColor: Color, offsetFactor: Float, modifier: Modifier = Modifier) {
67 | val infiniteTransition = rememberInfiniteTransition(label = "")
68 | val waveAnim by infiniteTransition.animateFloat(
69 | initialValue = 0f,
70 | targetValue = 1f,
71 | animationSpec = infiniteRepeatable(
72 | animation = tween(2000, easing = LinearEasing),
73 | repeatMode = RepeatMode.Restart
74 | ), label = ""
75 | )
76 |
77 | Canvas(modifier = modifier) {
78 | val width = size.width
79 | val height = size.height
80 | val waveHeight = 20.dp.toPx()
81 |
82 | val wavePoints = List(10) { i ->
83 | val x = width * i / 9
84 | val y = height / 2 + waveHeight * kotlin.math.sin((i + waveAnim * 10 + offsetFactor * 10) * 0.5f)
85 | x to y
86 | }
87 |
88 | translate(left = -width / 10) {
89 | drawPath(
90 | path = androidx.compose.ui.graphics.Path().apply {
91 | moveTo(0f, height / 2)
92 | wavePoints.forEach { (x, y) ->
93 | lineTo(x, y)
94 | }
95 | lineTo(width, height)
96 | lineTo(0f, height)
97 | close()
98 | },
99 | color = waveColor.copy(alpha = 0.5f)
100 | )
101 | }
102 | }
103 | }
104 |
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations.ui.theme
2 | import androidx.compose.ui.graphics.Color
3 |
4 | val primaryLight = Color(0xFF365E9D)
5 | val onPrimaryLight = Color(0xFFFFFFFF)
6 | val primaryContainerLight = Color(0xFF82A8EC)
7 | val onPrimaryContainerLight = Color(0xFF001C40)
8 | val secondaryLight = Color(0xFF515F79)
9 | val onSecondaryLight = Color(0xFFFFFFFF)
10 | val secondaryContainerLight = Color(0xFFD9E4FF)
11 | val onSecondaryContainerLight = Color(0xFF3C4962)
12 | val tertiaryLight = Color(0xFF814A87)
13 | val onTertiaryLight = Color(0xFFFFFFFF)
14 | val tertiaryContainerLight = Color(0xFFD091D4)
15 | val onTertiaryContainerLight = Color(0xFF36013F)
16 | val errorLight = Color(0xFFBA1A1A)
17 | val onErrorLight = Color(0xFFFFFFFF)
18 | val errorContainerLight = Color(0xFFFFDAD6)
19 | val onErrorContainerLight = Color(0xFF410002)
20 | val backgroundLight = Color(0xFFF9F9FF)
21 | val onBackgroundLight = Color(0xFF1A1C20)
22 | val surfaceLight = Color(0xFFF9F9FF)
23 | val onSurfaceLight = Color(0xFF1A1C20)
24 | val surfaceVariantLight = Color(0xFFDFE2EE)
25 | val onSurfaceVariantLight = Color(0xFF434750)
26 | val outlineLight = Color(0xFF737781)
27 | val outlineVariantLight = Color(0xFFC3C6D2)
28 | val scrimLight = Color(0xFF000000)
29 | val inverseSurfaceLight = Color(0xFF2F3035)
30 | val inverseOnSurfaceLight = Color(0xFFF1F0F6)
31 | val inversePrimaryLight = Color(0xFFAAC7FF)
32 | val surfaceDimLight = Color(0xFFDAD9DF)
33 | val surfaceBrightLight = Color(0xFFF9F9FF)
34 | val surfaceContainerLowestLight = Color(0xFFFFFFFF)
35 | val surfaceContainerLowLight = Color(0xFFF3F3F9)
36 | val surfaceContainerLight = Color(0xFFEEEDF3)
37 | val surfaceContainerHighLight = Color(0xFFE8E7ED)
38 | val surfaceContainerHighestLight = Color(0xFFE2E2E8)
39 |
40 | val primaryDark = Color(0xFFAAC7FF)
41 | val onPrimaryDark = Color(0xFF002F64)
42 | val primaryContainerDark = Color(0xFF6E94D6)
43 | val onPrimaryContainerDark = Color(0xFF000000)
44 | val secondaryDark = Color(0xFFB9C7E5)
45 | val onSecondaryDark = Color(0xFF233148)
46 | val secondaryContainerDark = Color(0xFF323F58)
47 | val onSecondaryContainerDark = Color(0xFFC6D4F3)
48 | val tertiaryDark = Color(0xFFF2B0F5)
49 | val onTertiaryDark = Color(0xFF4D1A55)
50 | val tertiaryContainerDark = Color(0xFFBC7FC0)
51 | val onTertiaryContainerDark = Color(0xFF000000)
52 | val errorDark = Color(0xFFFFB4AB)
53 | val onErrorDark = Color(0xFF690005)
54 | val errorContainerDark = Color(0xFF93000A)
55 | val onErrorContainerDark = Color(0xFFFFDAD6)
56 | val backgroundDark = Color(0xFF121317)
57 | val onBackgroundDark = Color(0xFFE2E2E8)
58 | val surfaceDark = Color(0xFF121317)
59 | val onSurfaceDark = Color(0xFFE2E2E8)
60 | val surfaceVariantDark = Color(0xFF434750)
61 | val onSurfaceVariantDark = Color(0xFFC3C6D2)
62 | val outlineDark = Color(0xFF8D909B)
63 | val outlineVariantDark = Color(0xFF434750)
64 | val scrimDark = Color(0xFF000000)
65 | val inverseSurfaceDark = Color(0xFFE2E2E8)
66 | val inverseOnSurfaceDark = Color(0xFF2F3035)
67 | val inversePrimaryDark = Color(0xFF365E9D)
68 | val surfaceDimDark = Color(0xFF121317)
69 | val surfaceBrightDark = Color(0xFF38393E)
70 | val surfaceContainerLowestDark = Color(0xFF0C0E12)
71 | val surfaceContainerLowDark = Color(0xFF1A1C20)
72 | val surfaceContainerDark = Color(0xFF1E2024)
73 | val surfaceContainerHighDark = Color(0xFF282A2E)
74 | val surfaceContainerHighestDark = Color(0xFF333539)
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/ui/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations.ui.theme
2 |
3 | import androidx.compose.foundation.isSystemInDarkTheme
4 | import androidx.compose.material3.MaterialTheme
5 | import androidx.compose.material3.darkColorScheme
6 | import androidx.compose.material3.lightColorScheme
7 | import androidx.compose.runtime.Composable
8 |
9 | private val lightScheme = lightColorScheme(
10 | primary = primaryLight,
11 | onPrimary = onPrimaryLight,
12 | primaryContainer = primaryContainerLight,
13 | onPrimaryContainer = onPrimaryContainerLight,
14 | secondary = secondaryLight,
15 | onSecondary = onSecondaryLight,
16 | secondaryContainer = secondaryContainerLight,
17 | onSecondaryContainer = onSecondaryContainerLight,
18 | tertiary = tertiaryLight,
19 | onTertiary = onTertiaryLight,
20 | tertiaryContainer = tertiaryContainerLight,
21 | onTertiaryContainer = onTertiaryContainerLight,
22 | error = errorLight,
23 | onError = onErrorLight,
24 | errorContainer = errorContainerLight,
25 | onErrorContainer = onErrorContainerLight,
26 | background = backgroundLight,
27 | onBackground = onBackgroundLight,
28 | surface = surfaceLight,
29 | onSurface = onSurfaceLight,
30 | surfaceVariant = surfaceVariantLight,
31 | onSurfaceVariant = onSurfaceVariantLight,
32 | outline = outlineLight,
33 | outlineVariant = outlineVariantLight,
34 | scrim = scrimLight,
35 | inverseSurface = inverseSurfaceLight,
36 | inverseOnSurface = inverseOnSurfaceLight,
37 | inversePrimary = inversePrimaryLight,
38 | surfaceDim = surfaceDimLight,
39 | surfaceBright = surfaceBrightLight,
40 | surfaceContainerLowest = surfaceContainerLowestLight,
41 | surfaceContainerLow = surfaceContainerLowLight,
42 | surfaceContainer = surfaceContainerLight,
43 | surfaceContainerHigh = surfaceContainerHighLight,
44 | surfaceContainerHighest = surfaceContainerHighestLight,
45 | )
46 |
47 | private val darkScheme = darkColorScheme(
48 | primary = primaryDark,
49 | onPrimary = onPrimaryDark,
50 | primaryContainer = primaryContainerDark,
51 | onPrimaryContainer = onPrimaryContainerDark,
52 | secondary = secondaryDark,
53 | onSecondary = onSecondaryDark,
54 | secondaryContainer = secondaryContainerDark,
55 | onSecondaryContainer = onSecondaryContainerDark,
56 | tertiary = tertiaryDark,
57 | onTertiary = onTertiaryDark,
58 | tertiaryContainer = tertiaryContainerDark,
59 | onTertiaryContainer = onTertiaryContainerDark,
60 | error = errorDark,
61 | onError = onErrorDark,
62 | errorContainer = errorContainerDark,
63 | onErrorContainer = onErrorContainerDark,
64 | background = backgroundDark,
65 | onBackground = onBackgroundDark,
66 | surface = surfaceDark,
67 | onSurface = onSurfaceDark,
68 | surfaceVariant = surfaceVariantDark,
69 | onSurfaceVariant = onSurfaceVariantDark,
70 | outline = outlineDark,
71 | outlineVariant = outlineVariantDark,
72 | scrim = scrimDark,
73 | inverseSurface = inverseSurfaceDark,
74 | inverseOnSurface = inverseOnSurfaceDark,
75 | inversePrimary = inversePrimaryDark,
76 | surfaceDim = surfaceDimDark,
77 | surfaceBright = surfaceBrightDark,
78 | surfaceContainerLowest = surfaceContainerLowestDark,
79 | surfaceContainerLow = surfaceContainerLowDark,
80 | surfaceContainer = surfaceContainerDark,
81 | surfaceContainerHigh = surfaceContainerHighDark,
82 | surfaceContainerHighest = surfaceContainerHighestDark,
83 | )
84 |
85 | @Composable
86 | fun AndroidAnimationsTheme(
87 | darkTheme: Boolean = isSystemInDarkTheme(),
88 | content: @Composable () -> Unit
89 | ) {
90 | val colorScheme = if (darkTheme) darkScheme else lightScheme
91 |
92 | MaterialTheme(
93 | colorScheme = colorScheme,
94 | typography = AppTypography,
95 | content = content
96 | )
97 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/ui/theme/Type.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations.ui.theme
2 |
3 | import androidx.compose.material3.Typography
4 | import androidx.compose.ui.text.font.FontFamily
5 | import androidx.compose.ui.text.googlefonts.Font
6 | import androidx.compose.ui.text.googlefonts.GoogleFont
7 | import com.sp45.android_animations.R
8 |
9 | val provider = GoogleFont.Provider(
10 | providerAuthority = "com.google.android.gms.fonts",
11 | providerPackage = "com.google.android.gms",
12 | certificates = R.array.com_google_android_gms_fonts_certs
13 | )
14 |
15 | val bodyFontFamily = FontFamily(
16 | Font(
17 | googleFont = GoogleFont("Montserrat Alternates"),
18 | fontProvider = provider,
19 | )
20 | )
21 |
22 | val displayFontFamily = FontFamily(
23 | Font(
24 | googleFont = GoogleFont("Abril Fatface"),
25 | fontProvider = provider,
26 | )
27 | )
28 |
29 | val baseline = Typography()
30 |
31 | val AppTypography = Typography(
32 | displayLarge = baseline.displayLarge.copy(fontFamily = displayFontFamily),
33 | displayMedium = baseline.displayMedium.copy(fontFamily = displayFontFamily),
34 | displaySmall = baseline.displaySmall.copy(fontFamily = displayFontFamily),
35 | headlineLarge = baseline.headlineLarge.copy(fontFamily = displayFontFamily),
36 | headlineMedium = baseline.headlineMedium.copy(fontFamily = displayFontFamily),
37 | headlineSmall = baseline.headlineSmall.copy(fontFamily = displayFontFamily),
38 | titleLarge = baseline.titleLarge.copy(fontFamily = displayFontFamily),
39 | titleMedium = baseline.titleMedium.copy(fontFamily = displayFontFamily),
40 | titleSmall = baseline.titleSmall.copy(fontFamily = displayFontFamily),
41 | bodyLarge = baseline.bodyLarge.copy(fontFamily = bodyFontFamily),
42 | bodyMedium = baseline.bodyMedium.copy(fontFamily = bodyFontFamily),
43 | bodySmall = baseline.bodySmall.copy(fontFamily = bodyFontFamily),
44 | labelLarge = baseline.labelLarge.copy(fontFamily = bodyFontFamily),
45 | labelMedium = baseline.labelMedium.copy(fontFamily = bodyFontFamily),
46 | labelSmall = baseline.labelSmall.copy(fontFamily = bodyFontFamily),
47 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/util/AnimationCard.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations.util
2 |
3 | import androidx.compose.animation.AnimatedVisibility
4 | import androidx.compose.animation.animateContentSize
5 | import androidx.compose.animation.core.animateFloatAsState
6 | import androidx.compose.animation.core.tween
7 | import androidx.compose.animation.fadeIn
8 | import androidx.compose.animation.fadeOut
9 | import androidx.compose.animation.scaleIn
10 | import androidx.compose.animation.scaleOut
11 | import androidx.compose.foundation.background
12 | import androidx.compose.foundation.clickable
13 | import androidx.compose.foundation.interaction.MutableInteractionSource
14 | import androidx.compose.foundation.interaction.collectIsHoveredAsState
15 | import androidx.compose.foundation.layout.Arrangement
16 | import androidx.compose.foundation.layout.Box
17 | import androidx.compose.foundation.layout.Column
18 | import androidx.compose.foundation.layout.Row
19 | import androidx.compose.foundation.layout.Spacer
20 | import androidx.compose.foundation.layout.fillMaxWidth
21 | import androidx.compose.foundation.layout.height
22 | import androidx.compose.foundation.layout.padding
23 | import androidx.compose.foundation.shape.RoundedCornerShape
24 | import androidx.compose.material.icons.Icons
25 | import androidx.compose.material.icons.filled.PlayArrow
26 | import androidx.compose.material.ripple.rememberRipple
27 | import androidx.compose.material3.Card
28 | import androidx.compose.material3.CardDefaults
29 | import androidx.compose.material3.Icon
30 | import androidx.compose.material3.IconButton
31 | import androidx.compose.material3.MaterialTheme
32 | import androidx.compose.material3.Text
33 | import androidx.compose.runtime.Composable
34 | import androidx.compose.runtime.LaunchedEffect
35 | import androidx.compose.runtime.getValue
36 | import androidx.compose.runtime.mutableStateOf
37 | import androidx.compose.runtime.remember
38 | import androidx.compose.runtime.setValue
39 | import androidx.compose.ui.Alignment
40 | import androidx.compose.ui.ExperimentalComposeUiApi
41 | import androidx.compose.ui.Modifier
42 | import androidx.compose.ui.draw.clip
43 | import androidx.compose.ui.draw.drawBehind
44 | import androidx.compose.ui.geometry.Offset
45 | import androidx.compose.ui.graphics.Color
46 | import androidx.compose.ui.graphics.drawscope.rotate
47 | import androidx.compose.ui.graphics.graphicsLayer
48 | import androidx.compose.ui.input.pointer.pointerInteropFilter
49 | import androidx.compose.ui.res.stringResource
50 | import androidx.compose.ui.unit.dp
51 | import com.sp45.android_animations.R
52 |
53 | data class AnimationItem(
54 | val name: String,
55 | val color: Color
56 | )
57 |
58 | @OptIn(ExperimentalComposeUiApi::class)
59 | @Composable
60 | fun AnimationCard(
61 | animationItem: AnimationItem,
62 | onClick: () -> Unit
63 | ) {
64 | val interactionSource = remember { MutableInteractionSource() }
65 | val isHovered by interactionSource.collectIsHoveredAsState()
66 |
67 | var isPressed by remember { mutableStateOf(false) }
68 | var isVisible by remember { mutableStateOf(false) }
69 |
70 | val scale by animateFloatAsState(
71 | targetValue = if (isHovered) 1.02f else 1f
72 | )
73 |
74 | val accentColor = animationItem.color
75 |
76 | LaunchedEffect(Unit) {
77 | isVisible = true
78 | }
79 |
80 | AnimatedVisibility(
81 | visible = isVisible,
82 | enter = fadeIn(animationSpec = tween(500)) + scaleIn(animationSpec = tween(500)),
83 | exit = fadeOut(animationSpec = tween(500)) + scaleOut(animationSpec = tween(500))
84 | ) {
85 | Card(
86 | modifier = Modifier
87 | .fillMaxWidth()
88 | .graphicsLayer {
89 | scaleX = scale
90 | scaleY = scale
91 | }
92 | .pointerInteropFilter {
93 | when (it.action) {
94 | android.view.MotionEvent.ACTION_DOWN -> isPressed = true
95 | android.view.MotionEvent.ACTION_UP, android.view.MotionEvent.ACTION_CANCEL -> isPressed =
96 | false
97 | }
98 | false
99 | }
100 | .clickable(
101 | interactionSource = interactionSource,
102 | indication = rememberRipple(bounded = true),
103 | onClick = onClick
104 | )
105 | .animateContentSize(),
106 | shape = RoundedCornerShape(16.dp),
107 | colors = CardDefaults.cardColors(
108 | containerColor = MaterialTheme.colorScheme.secondaryContainer,
109 | contentColor = MaterialTheme.colorScheme.onSecondaryContainer
110 | ),
111 | elevation = CardDefaults.cardElevation(2.dp)
112 | ) {
113 | Box(
114 | modifier = Modifier
115 | .fillMaxWidth()
116 | .drawBehind {
117 | rotate(45f) {
118 | drawCircle(
119 | color = accentColor.copy(alpha = 0.1f),
120 | radius = size.width * 0.2f,
121 | center = Offset(size.width * 0.8f, size.height * 0.2f)
122 | )
123 | }
124 | }
125 | .padding(16.dp),
126 | ) {
127 | Row(
128 | modifier = Modifier.fillMaxWidth(),
129 | horizontalArrangement = Arrangement.SpaceBetween,
130 | verticalAlignment = Alignment.CenterVertically
131 | ) {
132 | Column {
133 |
134 | Text(
135 | text = animationItem.name,
136 | style = MaterialTheme.typography.titleMedium,
137 | color = MaterialTheme.colorScheme.onSurface
138 | )
139 | Spacer(modifier = Modifier.height(4.dp))
140 |
141 | Text(
142 | text = stringResource(R.string.tap_to_preview),
143 | style = MaterialTheme.typography.bodyMedium,
144 | color = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f)
145 | )
146 | }
147 |
148 | IconButton(
149 | onClick = onClick,
150 | modifier = Modifier
151 | .clip(RoundedCornerShape(12.dp))
152 | .background(accentColor.copy(alpha = 0.2f))
153 | ) {
154 | Icon(
155 | imageVector = Icons.Default.PlayArrow,
156 | contentDescription = stringResource(R.string.play_animation),
157 | tint = accentColor
158 | )
159 | }
160 | }
161 | }
162 | }
163 | }
164 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/util/FadingTextAnimation.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations.util
2 |
3 | import androidx.compose.animation.Crossfade
4 | import androidx.compose.animation.core.tween
5 | import androidx.compose.foundation.background
6 | import androidx.compose.foundation.layout.Box
7 | import androidx.compose.material3.MaterialTheme
8 | import androidx.compose.material3.Text
9 | import androidx.compose.runtime.Composable
10 | import androidx.compose.runtime.LaunchedEffect
11 | import androidx.compose.runtime.mutableIntStateOf
12 | import androidx.compose.runtime.remember
13 | import androidx.compose.ui.Alignment
14 | import androidx.compose.ui.Modifier
15 | import androidx.compose.ui.res.stringResource
16 | import androidx.compose.ui.text.TextStyle
17 | import androidx.compose.ui.unit.sp
18 | import com.sp45.android_animations.R
19 | import kotlinx.coroutines.delay
20 |
21 | @Composable
22 | fun FadingTextAnimation() {
23 | val messages: List = listOf(
24 | stringResource(R.string.resources),
25 | stringResource(R.string.mentorship),
26 | stringResource(R.string.interview_prep)
27 | )
28 | val fadeDuration = 1000
29 | val pauseDuration = 1500
30 | val currentMessageIndex = remember { mutableIntStateOf(0) }
31 |
32 | LaunchedEffect(Unit) {
33 | while (true) {
34 | delay((fadeDuration + pauseDuration).toLong())
35 | currentMessageIndex.intValue = (currentMessageIndex.intValue + 1) % messages.size
36 | }
37 | }
38 |
39 | Box(
40 | contentAlignment = Alignment.Center,
41 | modifier = Modifier.background(MaterialTheme.colorScheme.background)
42 | ) {
43 | Crossfade(
44 | targetState = currentMessageIndex.intValue,
45 | animationSpec = tween(durationMillis = fadeDuration)
46 | ) { index ->
47 | Text(
48 | text = messages[index],
49 | style = TextStyle(fontSize = 16.sp),
50 | color = MaterialTheme.colorScheme.onBackground
51 | )
52 | }
53 | }
54 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/util/ThemeSwitch.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations.util
2 |
3 | import androidx.compose.material3.MaterialTheme
4 | import androidx.compose.material3.Switch
5 | import androidx.compose.material3.SwitchDefaults
6 | import androidx.compose.runtime.Composable
7 | import androidx.compose.runtime.mutableStateOf
8 |
9 | @Composable
10 | fun ThemeSwitch(darkTheme: Boolean, onThemeChange: () -> Unit) {
11 | Switch(
12 | checked = darkTheme,
13 | onCheckedChange = { onThemeChange() },
14 | colors = SwitchDefaults.colors(
15 | checkedTrackColor = MaterialTheme.colorScheme.secondaryContainer,
16 | checkedThumbColor = MaterialTheme.colorScheme.primary,
17 | uncheckedTrackColor = MaterialTheme.colorScheme.secondaryContainer,
18 | uncheckedThumbColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f)
19 | ),
20 | enabled = true
21 | )
22 | }
23 |
24 | object ThemeManager {
25 | private var _isDarkTheme = mutableStateOf(false)
26 | private var isInitialized = false
27 |
28 | fun initializeTheme(defaultDarkTheme: Boolean) {
29 | if (!isInitialized) {
30 | _isDarkTheme.value = defaultDarkTheme
31 | isInitialized = true
32 | }
33 | }
34 |
35 | fun toggleTheme() {
36 | _isDarkTheme.value = !_isDarkTheme.value
37 | }
38 |
39 | @Composable
40 | fun observeTheme(): Boolean = _isDarkTheme.value
41 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/util/TopAppBar.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations.util
2 |
3 | import androidx.compose.foundation.layout.Row
4 | import androidx.compose.foundation.layout.fillMaxWidth
5 | import androidx.compose.foundation.layout.padding
6 | import androidx.compose.material3.ExperimentalMaterial3Api
7 | import androidx.compose.material3.MaterialTheme
8 | import androidx.compose.material3.Text
9 | import androidx.compose.material3.TopAppBar
10 | import androidx.compose.material3.TopAppBarDefaults
11 | import androidx.compose.runtime.Composable
12 | import androidx.compose.ui.Modifier
13 | import androidx.compose.ui.text.font.FontWeight
14 | import androidx.compose.ui.unit.dp
15 |
16 | @OptIn(ExperimentalMaterial3Api::class)
17 | @Composable
18 | fun CustomTopAppBar(title: String) {
19 | TopAppBar(
20 | title = {
21 | Text(
22 | text = title,
23 | style = MaterialTheme.typography.titleLarge,
24 | color = MaterialTheme.colorScheme.onPrimary,
25 | fontWeight = FontWeight.Bold
26 | )
27 | },
28 | actions = {
29 | Row(modifier = Modifier.padding(end = 8.dp)) {
30 | ThemeSwitch(
31 | darkTheme = ThemeManager.observeTheme()
32 | ) {
33 | ThemeManager.toggleTheme()
34 | }
35 | }
36 | },
37 | colors = TopAppBarDefaults.topAppBarColors(
38 | containerColor = MaterialTheme.colorScheme.primary,
39 | actionIconContentColor = MaterialTheme.colorScheme.onPrimary,
40 | ),
41 | modifier = Modifier.fillMaxWidth()
42 | )
43 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/sp45/android_animations/util/WebViewComponent.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations.util
2 |
3 | import android.annotation.SuppressLint
4 | import android.webkit.WebView
5 | import android.webkit.WebViewClient
6 | import androidx.compose.foundation.clickable
7 | import androidx.compose.foundation.layout.fillMaxSize
8 | import androidx.compose.material3.MaterialTheme
9 | import androidx.compose.material3.Text
10 | import androidx.compose.runtime.Composable
11 | import androidx.compose.ui.Modifier
12 | import androidx.compose.ui.res.stringResource
13 | import androidx.compose.ui.text.buildAnnotatedString
14 | import androidx.compose.ui.text.style.TextDecoration
15 | import androidx.compose.ui.text.withStyle
16 | import androidx.compose.ui.viewinterop.AndroidView
17 | import com.sp45.android_animations.R
18 |
19 | @SuppressLint("SetJavaScriptEnabled")
20 | @Composable
21 | fun WebViewScreen() {
22 | val url = stringResource(R.string.https_www_crakcode_in)
23 | AndroidView(
24 | modifier = Modifier.fillMaxSize(),
25 | factory = { context ->
26 | WebView(context).apply {
27 | settings.javaScriptEnabled = true
28 | webViewClient = WebViewClient()
29 | loadUrl(url)
30 | }
31 | }
32 | )
33 | }
34 |
35 | @Composable
36 | fun WebAppText(onClick: () -> Unit) {
37 | val annotatedString = buildAnnotatedString {
38 | pushStringAnnotation(tag = "URL", annotation = "https://www.crakcode.in/")
39 | withStyle(
40 | style = MaterialTheme.typography.bodyLarge.copy(
41 | color = MaterialTheme.colorScheme.primary,
42 | textDecoration = TextDecoration.Underline
43 | ).toSpanStyle()
44 | ) {
45 | append(stringResource(R.string.visit_crakcode_website_for))
46 | }
47 | pop()
48 | }
49 |
50 | Text(
51 | text = annotatedString,
52 | style = MaterialTheme.typography.bodyMedium,
53 | modifier = Modifier.clickable { onClick() },
54 | color = MaterialTheme.colorScheme.onBackground
55 | )
56 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/avd_crakcode.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
11 |
17 |
22 |
27 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
46 |
54 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/cat1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crakcode-hub/android-animations/aef97409b7e549057d022e376be676d0d5ff03c6/app/src/main/res/drawable/cat1.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable/cat2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crakcode-hub/android-animations/aef97409b7e549057d022e376be676d0d5ff03c6/app/src/main/res/drawable/cat2.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable/cat3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crakcode-hub/android-animations/aef97409b7e549057d022e376be676d0d5ff03c6/app/src/main/res/drawable/cat3.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/cat4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crakcode-hub/android-animations/aef97409b7e549057d022e376be676d0d5ff03c6/app/src/main/res/drawable/cat4.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/cat5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crakcode-hub/android-animations/aef97409b7e549057d022e376be676d0d5ff03c6/app/src/main/res/drawable/cat5.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/crakcode.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crakcode-hub/android-animations/aef97409b7e549057d022e376be676d0d5ff03c6/app/src/main/res/drawable/crakcode.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crakcode-hub/android-animations/aef97409b7e549057d022e376be676d0d5ff03c6/app/src/main/res/drawable/img.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/img_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crakcode-hub/android-animations/aef97409b7e549057d022e376be676d0d5ff03c6/app/src/main/res/drawable/img_2.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crakcode-hub/android-animations/aef97409b7e549057d022e376be676d0d5ff03c6/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crakcode-hub/android-animations/aef97409b7e549057d022e376be676d0d5ff03c6/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crakcode-hub/android-animations/aef97409b7e549057d022e376be676d0d5ff03c6/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crakcode-hub/android-animations/aef97409b7e549057d022e376be676d0d5ff03c6/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crakcode-hub/android-animations/aef97409b7e549057d022e376be676d0d5ff03c6/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crakcode-hub/android-animations/aef97409b7e549057d022e376be676d0d5ff03c6/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crakcode-hub/android-animations/aef97409b7e549057d022e376be676d0d5ff03c6/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crakcode-hub/android-animations/aef97409b7e549057d022e376be676d0d5ff03c6/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crakcode-hub/android-animations/aef97409b7e549057d022e376be676d0d5ff03c6/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crakcode-hub/android-animations/aef97409b7e549057d022e376be676d0d5ff03c6/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/values/arrays.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | com_google_android_gms_fonts_certs
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/splash.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Android Animations
3 | Front
4 | Back
5 | Pop!
6 | Change Profile Picture
7 | Swipe Up to Collapse
8 | Swipe Down to Expand
9 | Undo
10 | Delete
11 | Swipe to Delete
12 | Slide left to remove this item
13 | Swipe left
14 | Type Writer Animation!
15 | Dynamic Circle of size : %1$d.dp
16 | Dynamic Circle of roundness : %1$d%%
17 | Color Animation
18 | Loading…
19 | Open Web App
20 | Visit CrakCode Website for
21 | https://www.crakcode.in/
22 | Resources
23 | Mentorship
24 | Interview Prep
25 | Play Animation
26 | Tap to preview
27 | Text Explosion
28 | Orbiting Objects
29 | Expanding Rings
30 | Image Transition
31 | Confetti Animation
32 | Rotate the Card
33 | Button to Image
34 | Swipe right or left to open/close the door
35 | Hidden Content
36 | CrakCode
37 | Tap to Flip
38 | Tap to see the front
39 | Tap to Collapse
40 | Tap to Expand
41 | CrakCode Image
42 | Click Me!
43 | Back of the card
44 | Front of the card
45 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
12 |
13 |
19 |
--------------------------------------------------------------------------------
/app/src/test/java/com/sp45/android_animations/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.sp45.android_animations
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | plugins {
3 | alias(libs.plugins.android.application) apply false
4 | alias(libs.plugins.kotlin.android) apply false
5 | alias(libs.plugins.kotlin.compose) apply false
6 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. For more details, visit
12 | # https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
--------------------------------------------------------------------------------
/gradle/libs.versions.toml:
--------------------------------------------------------------------------------
1 | [versions]
2 | agp = "8.7.0"
3 | coreSplashscreen = "1.0.1"
4 | kotlin = "2.0.0"
5 | coreKtx = "1.13.1"
6 | junit = "4.13.2"
7 | junitVersion = "1.2.1"
8 | espressoCore = "3.6.1"
9 | lifecycleRuntimeKtx = "2.8.6"
10 | activityCompose = "1.9.3"
11 | composeBom = "2024.04.01"
12 | lifecycleViewmodelCompose = "2.8.7"
13 | navigationCompose = "2.8.3"
14 | uiTextGoogleFonts = "1.7.4"
15 | animationAndroid = "1.7.2"
16 | foundationLayoutAndroid = "1.7.2"
17 |
18 | [libraries]
19 | androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
20 | androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "coreSplashscreen" }
21 | androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleViewmodelCompose" }
22 | androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
23 | junit = { group = "junit", name = "junit", version.ref = "junit" }
24 | androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
25 | androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
26 | androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
27 | androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
28 | androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
29 | androidx-ui = { group = "androidx.compose.ui", name = "ui" }
30 | androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
31 | androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
32 | androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
33 | androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
34 | androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
35 | androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
36 | androidx-ui-text-google-fonts = { group = "androidx.compose.ui", name = "ui-text-google-fonts", version.ref = "uiTextGoogleFonts" }
37 | androidx-animation-android = { group = "androidx.compose.animation", name = "animation-android", version.ref = "animationAndroid" }
38 | androidx-foundation-layout-android = { group = "androidx.compose.foundation", name = "foundation-layout-android", version.ref = "foundationLayoutAndroid" }
39 |
40 | [plugins]
41 | android-application = { id = "com.android.application", version.ref = "agp" }
42 | kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
43 | kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
44 |
45 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/crakcode-hub/android-animations/aef97409b7e549057d022e376be676d0d5ff03c6/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Oct 25 18:21:14 GMT+05:30 2024
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | #
4 | # Copyright 2015 the original author or authors.
5 | #
6 | # Licensed under the Apache License, Version 2.0 (the "License");
7 | # you may not use this file except in compliance with the License.
8 | # You may obtain a copy of the License at
9 | #
10 | # https://www.apache.org/licenses/LICENSE-2.0
11 | #
12 | # Unless required by applicable law or agreed to in writing, software
13 | # distributed under the License is distributed on an "AS IS" BASIS,
14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 | # See the License for the specific language governing permissions and
16 | # limitations under the License.
17 | #
18 |
19 | ##############################################################################
20 | ##
21 | ## Gradle start up script for UN*X
22 | ##
23 | ##############################################################################
24 |
25 | # Attempt to set APP_HOME
26 | # Resolve links: $0 may be a link
27 | PRG="$0"
28 | # Need this for relative symlinks.
29 | while [ -h "$PRG" ] ; do
30 | ls=`ls -ld "$PRG"`
31 | link=`expr "$ls" : '.*-> \(.*\)$'`
32 | if expr "$link" : '/.*' > /dev/null; then
33 | PRG="$link"
34 | else
35 | PRG=`dirname "$PRG"`"/$link"
36 | fi
37 | done
38 | SAVED="`pwd`"
39 | cd "`dirname \"$PRG\"`/" >/dev/null
40 | APP_HOME="`pwd -P`"
41 | cd "$SAVED" >/dev/null
42 |
43 | APP_NAME="Gradle"
44 | APP_BASE_NAME=`basename "$0"`
45 |
46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48 |
49 | # Use the maximum available, or set MAX_FD != -1 to use that value.
50 | MAX_FD="maximum"
51 |
52 | warn () {
53 | echo "$*"
54 | }
55 |
56 | die () {
57 | echo
58 | echo "$*"
59 | echo
60 | exit 1
61 | }
62 |
63 | # OS specific support (must be 'true' or 'false').
64 | cygwin=false
65 | msys=false
66 | darwin=false
67 | nonstop=false
68 | case "`uname`" in
69 | CYGWIN* )
70 | cygwin=true
71 | ;;
72 | Darwin* )
73 | darwin=true
74 | ;;
75 | MINGW* )
76 | msys=true
77 | ;;
78 | NONSTOP* )
79 | nonstop=true
80 | ;;
81 | esac
82 |
83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84 |
85 |
86 | # Determine the Java command to use to start the JVM.
87 | if [ -n "$JAVA_HOME" ] ; then
88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89 | # IBM's JDK on AIX uses strange locations for the executables
90 | JAVACMD="$JAVA_HOME/jre/sh/java"
91 | else
92 | JAVACMD="$JAVA_HOME/bin/java"
93 | fi
94 | if [ ! -x "$JAVACMD" ] ; then
95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96 |
97 | Please set the JAVA_HOME variable in your environment to match the
98 | location of your Java installation."
99 | fi
100 | else
101 | JAVACMD="java"
102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103 |
104 | Please set the JAVA_HOME variable in your environment to match the
105 | location of your Java installation."
106 | fi
107 |
108 | # Increase the maximum file descriptors if we can.
109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110 | MAX_FD_LIMIT=`ulimit -H -n`
111 | if [ $? -eq 0 ] ; then
112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113 | MAX_FD="$MAX_FD_LIMIT"
114 | fi
115 | ulimit -n $MAX_FD
116 | if [ $? -ne 0 ] ; then
117 | warn "Could not set maximum file descriptor limit: $MAX_FD"
118 | fi
119 | else
120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121 | fi
122 | fi
123 |
124 | # For Darwin, add options to specify how the application appears in the dock
125 | if $darwin; then
126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127 | fi
128 |
129 | # For Cygwin or MSYS, switch paths to Windows format before running java
130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133 |
134 | JAVACMD=`cygpath --unix "$JAVACMD"`
135 |
136 | # We build the pattern for arguments to be converted via cygpath
137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138 | SEP=""
139 | for dir in $ROOTDIRSRAW ; do
140 | ROOTDIRS="$ROOTDIRS$SEP$dir"
141 | SEP="|"
142 | done
143 | OURCYGPATTERN="(^($ROOTDIRS))"
144 | # Add a user-defined pattern to the cygpath arguments
145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147 | fi
148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
149 | i=0
150 | for arg in "$@" ; do
151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
153 |
154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156 | else
157 | eval `echo args$i`="\"$arg\""
158 | fi
159 | i=`expr $i + 1`
160 | done
161 | case $i in
162 | 0) set -- ;;
163 | 1) set -- "$args0" ;;
164 | 2) set -- "$args0" "$args1" ;;
165 | 3) set -- "$args0" "$args1" "$args2" ;;
166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172 | esac
173 | fi
174 |
175 | # Escape application args
176 | save () {
177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178 | echo " "
179 | }
180 | APP_ARGS=`save "$@"`
181 |
182 | # Collect all arguments for the java command, following the shell quoting and substitution rules
183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184 |
185 | exec "$JAVACMD" "$@"
186 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | google {
4 | content {
5 | includeGroupByRegex("com\\.android.*")
6 | includeGroupByRegex("com\\.google.*")
7 | includeGroupByRegex("androidx.*")
8 | }
9 | }
10 | mavenCentral()
11 | gradlePluginPortal()
12 | }
13 | }
14 | dependencyResolutionManagement {
15 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
16 | repositories {
17 | google()
18 | mavenCentral()
19 | }
20 | }
21 |
22 | rootProject.name = "android-animations"
23 | include(":app")
24 |
--------------------------------------------------------------------------------