Compare commits
33 Commits
Author | SHA1 | Date |
---|---|---|
![]() |
2a5797c5ad | |
![]() |
2c51255034 | |
![]() |
8d3a15d0aa | |
![]() |
860ef2cb88 | |
![]() |
f062bdef3f | |
![]() |
8451defe73 | |
![]() |
b507d48d68 | |
![]() |
4458eb45da | |
![]() |
7b86d9f838 | |
![]() |
5b6caf2d10 | |
![]() |
0791408495 | |
![]() |
b0ae15a2d9 | |
![]() |
726499f9e5 | |
![]() |
2ea81e9fad | |
![]() |
9a19a70676 | |
![]() |
c0cf2e286b | |
![]() |
316598db8d | |
![]() |
a4a5a539a1 | |
![]() |
9a71edc711 | |
![]() |
712d8bc157 | |
![]() |
97fa443e5c | |
![]() |
c9ecbe03a3 | |
![]() |
d6de3e598d | |
![]() |
6ee6e9b229 | |
![]() |
1fc5659b13 | |
![]() |
d718f4ee18 | |
![]() |
cab01453f5 | |
![]() |
b7e686eca1 | |
![]() |
f6906a7df7 | |
![]() |
c938702786 | |
![]() |
c1df113d53 | |
![]() |
def3737e45 | |
![]() |
7ef64974c5 |
|
@ -0,0 +1,91 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1320"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "ConfettiSwiftUI"
|
||||
BuildableName = "ConfettiSwiftUI"
|
||||
BlueprintName = "ConfettiSwiftUI"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "NO"
|
||||
buildForArchiving = "NO"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "ConfettiSwiftUITests"
|
||||
BuildableName = "ConfettiSwiftUITests"
|
||||
BlueprintName = "ConfettiSwiftUITests"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "ConfettiSwiftUITests"
|
||||
BuildableName = "ConfettiSwiftUITests"
|
||||
BlueprintName = "ConfettiSwiftUITests"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES">
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "ConfettiSwiftUI"
|
||||
BuildableName = "ConfettiSwiftUI"
|
||||
BlueprintName = "ConfettiSwiftUI"
|
||||
ReferencedContainer = "container:">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
Binary file not shown.
After Width: | Height: | Size: 235 KiB |
Binary file not shown.
After Width: | Height: | Size: 316 KiB |
|
@ -26,9 +26,12 @@ let package = Package(
|
|||
// Targets can depend on other targets in this package, and on products in packages this package depends on.
|
||||
.target(
|
||||
name: "ConfettiSwiftUI",
|
||||
dependencies: []),
|
||||
dependencies: [],
|
||||
path: "Sources"),
|
||||
.testTarget(
|
||||
name: "ConfettiSwiftUITests",
|
||||
dependencies: ["ConfettiSwiftUI"]),
|
||||
dependencies: ["ConfettiSwiftUI"],
|
||||
path: "Tests"),
|
||||
|
||||
]
|
||||
)
|
||||
|
|
316
README.md
316
README.md
|
@ -1,154 +1,208 @@
|
|||
# :confetti_ball: ConfettiSwiftUI :confetti_ball:
|
||||
# ConfettiSwiftUI
|
||||
|
||||
Swift package for displaying configurable confetti animation.
|
||||
<img src="https://img.shields.io/badge/PLATFORM-IOS%20|%20MACOS-lightgray?style=for-the-badge" /> <img src="https://img.shields.io/badge/LICENSE-MIT-lightgray?style=for-the-badge" /> <img src="https://img.shields.io/badge/MADE WITH-SWIFTUI-orange?style=for-the-badge" />
|
||||
|
||||
Find the [demo project here](https://github.com/simibac/ConfettiSwiftUIDemo).
|
||||
### Customizable Confetti Animations in SwiftUI
|
||||
|
||||
<p float="center">
|
||||
<img src="./Gifs/default.gif" width="400" />
|
||||
<p align="center">
|
||||
<img src="./Gifs/native_default_iphone.png" width="200" width="480"/>
|
||||
</p>
|
||||
|
||||
### Installation:
|
||||
## 🌄 Example
|
||||
|
||||
It requires iOS 14 and Xcode 12!
|
||||
<p align="center">
|
||||
<img src="./Gifs/default.gif" width="150" />
|
||||
<img src="./Gifs/make-it-rain.gif" width="150"/>
|
||||
<img src="./Gifs/explosion.gif" width="150" />
|
||||
<img src="./Gifs/color.gif" width="150" />
|
||||
</p>
|
||||
|
||||
In Xcode go to `File -> Swift Packages -> Add Package Dependency` and paste in the repo's url: `https://github.com/simibac/ConfettiSwiftUI` and select master branch.
|
||||
## 🔭 Overview
|
||||
|
||||
### Usage
|
||||
This is an open-source library to use with SwiftUI. It allows you to create and customize confetti animations.
|
||||
|
||||
Import the package in the file you would like to use it: `import ConfettiSwiftUI`
|
||||
- Built with pure SwiftUI.
|
||||
- Select from default confetti shapes or inject emojis as text.
|
||||
- Configure the radius and angles of the explosion.
|
||||
- Trigger animation with one state change multiple times.
|
||||
|
||||
## 🔨Support
|
||||
|
||||
If you like the project, don't forget to `put star 🌟`.
|
||||
|
||||
<a href="https://brianmacdonald.github.io/Ethonate/address#0xCBa97323b4cA2fF9330827faF306065da7aA338F">
|
||||
<img src="https://brianmacdonald.github.io/Ethonate/svg/eth-donate-blue.svg"/>
|
||||
</a>
|
||||
|
||||
<!-- <a href="mailto:simibac2@icloud.com"><img src="https://img.shields.io/badge/EMAIL-SIMON-informational?style=for-the-badge&logo=minutemailer&logoColor=white"></a> <a href="https://www.linkedin.com/in/simon-bachmann-73b695151/" target="_blank"><img src="https://img.shields.io/badge/LINKEDIN-informational?style=for-the-badge&logo=linkedin&logoColor=white" ></a> <a href="https://www.paypal.com/donate?business=6H8D2EDR6LBX6&no_recurring=0&item_name=Thanks+for+supporting+open+source+contributions%21¤cy_code=CHF" target="_blank"><img src="https://img.shields.io/badge/Donate-informational?style=for-the-badge&logo=paypal&logoColor=white" ></a> -->
|
||||
|
||||
## 🧭 Navigation
|
||||
|
||||
- [💻 Installation](#-installation)
|
||||
- [Swift Package Manager](#swift-package-manager)
|
||||
- [Manually](#manually)
|
||||
- [🧳 Requirements](#-requirements)
|
||||
- [🛠 Usage](#-usage)
|
||||
- [Parameters](#parameters)
|
||||
- [Configurator Application With Live Preview](#configurator-application-with-live-preview)
|
||||
- [Examples](#examples)
|
||||
- [👨💻 Contributors](#-contributors)
|
||||
- [✍️ Author](#-author)
|
||||
- [📃 License](#-license)
|
||||
- [📦 Projects](#-projects)
|
||||
|
||||
## 💻 Installation
|
||||
|
||||
### Swift Package Manager
|
||||
|
||||
The [Swift Package Manager](https://swift.org/package-manager/) is a tool for managing the distribution of Swift code. It’s integrated with the Swift build system to automate the process of downloading, compiling, and linking dependencies.
|
||||
|
||||
To integrate `ConfettiSwiftUI` into your Xcode project using Xcode 12, specify it in `File > Swift Packages > Add Package Dependency...`:
|
||||
|
||||
```ogdl
|
||||
https://github.com/simibac/ConfettiSwiftUI.git, :branch="master"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Manually
|
||||
|
||||
If you prefer not to use any of dependency managers, you can integrate `ConfettiSwiftUI` into your project manually. Put `Sources/ConfettiSwiftUI` folder in your Xcode project. Make sure to enable `Copy items if needed` and `Create groups`.
|
||||
|
||||
## 🧳 Requirements
|
||||
|
||||
- iOS 14.0+ | macOS 11+
|
||||
- Swift 5+
|
||||
|
||||
## 🛠 Usage
|
||||
|
||||
First, add `import ConfettiSwiftUI` on every `swift` file you would like to use `ConfettiSwiftUI`. Define a integer as a state varable which is responsible for triggering the animation. Any change to that variable will span a new animation (increment and decrement).
|
||||
|
||||
```swift
|
||||
struct ContentView: View {
|
||||
@State var counter:Int = 0
|
||||
import ConfettiSwiftUI
|
||||
import SwiftUI
|
||||
|
||||
struct ContentView: View {
|
||||
|
||||
@State private var counter: Int = 0
|
||||
|
||||
var body: some View {
|
||||
ZStack{
|
||||
Text("🎉").font(.system(size: 50)).onTapGesture(){counter += 1}
|
||||
ConfettiCannon(counter: $counter)
|
||||
Button("🎉") {
|
||||
counter += 1
|
||||
}
|
||||
.confettiCannon(counter: $counter)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Demo
|
||||
|
||||
Added an example project, with **iOS** target: https://github.com/simibac/ConfettiSwiftUIDemo
|
||||
|
||||
## Configurations
|
||||
|
||||
You can use the configurator app in [demo project here](https://github.com/simibac/ConfettiSwiftUIDemo) to make your desired animation or get inspired by one of the many examples.
|
||||
|
||||
<p float="center">
|
||||
<img src="./Gifs/configurator.png" width="300" />
|
||||
<img src="./Gifs/examples.png" width="300" />
|
||||
|
||||
</p>
|
||||
|
||||
#### Default Configuration
|
||||
|
||||
<p float="center">
|
||||
<img src="./Gifs/default.gif" width="300" />
|
||||
</p>
|
||||
|
||||
Code
|
||||
|
||||
```swift
|
||||
ConfettiCannon(counter: $counter1)
|
||||
```
|
||||
|
||||
#### Color and Size Configuration
|
||||
|
||||
<p float="center">
|
||||
<img src="./Gifs/color.gif" width="300" />
|
||||
</p>
|
||||
|
||||
Code:
|
||||
|
||||
```swift
|
||||
ConfettiCannon(counter: $counter2, colors: [.red, .black], confettiSize: 20)
|
||||
```
|
||||
|
||||
#### Repeat Configuration
|
||||
|
||||
<p float="center">
|
||||
<img src="./Gifs/repeat.gif" width="300" />
|
||||
</p>
|
||||
|
||||
Code:
|
||||
|
||||
```swift
|
||||
ConfettiCannon(counter: $counter3, repetitions: 3, repetitionInterval: 0.7)
|
||||
```
|
||||
|
||||
#### Firework Configuration
|
||||
|
||||
<p float="center">
|
||||
<img src="./Gifs/explosion.gif" width="300" />
|
||||
</p>
|
||||
|
||||
Code:
|
||||
|
||||
```swift
|
||||
ConfettiCannon(counter: $counter4, num: 50, openingAngle: Angle(degrees: 0), closingAngle: Angle(degrees: 360), radius: 200)
|
||||
```
|
||||
|
||||
#### Emoji Configuration
|
||||
|
||||
<p float="center">
|
||||
<img src="./Gifs/heart.gif" width="300" />
|
||||
</p>
|
||||
|
||||
Code:
|
||||
|
||||
```swift
|
||||
ConfettiCannon(counter: $counter5, confettis: [.text("❤️"), .text("💙"), .text("💚"), .text("🧡")])
|
||||
```
|
||||
|
||||
#### Endless Configuration
|
||||
|
||||
<p float="center">
|
||||
<img src="./Gifs/constant.gif" width="300" />
|
||||
</p>
|
||||
|
||||
Code:
|
||||
|
||||
```swift
|
||||
ConfettiCannon(counter: $counter6, num:1, confettis: [.text("💩")], confettiSize: 20, repetitions: 100, repetitionInterval: 0.1)
|
||||
```
|
||||
|
||||
#### Make-it-Rain Configuration
|
||||
|
||||
<p float="center">
|
||||
<img src="./Gifs/make-it-rain.gif" width="300" />
|
||||
</p>
|
||||
|
||||
Code:
|
||||
|
||||
```swift
|
||||
ConfettiCannon(counter: $counter7, num:1, confettis: [.text("💵"), .text("💶"), .text("💷"), .text("💴")], confettiSize: 30, repetitions: 50, repetitionInterval: 0.1)
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
| parameter | type | description | default |
|
||||
| -------------------- | ------------ | ----------------------------------------------------- | ------------------------------------------------------- |
|
||||
| counter | Binding<Int> | on any change of this variable triggers the animation | 0 |
|
||||
| num | Int | amount of confettis | 20 |
|
||||
| confettis | [ConfettiType] | list of shapes and text | [.shape(.circle), .shape(.triangle), .shape(.square), .shape(.slimRectangle), .shape(.roundedCross)] |
|
||||
| colors | [Color] | list of colors applied to the default shapes | [.blue, .red, .green, .yellow, .pink, .purple, .orange] |
|
||||
| confettiSize | CGFloat | size that confettis and emojis are scaled to | 10.0 |
|
||||
| rainHeight | CGFloat | vertical distance that confettis pass | 600.0 |
|
||||
| fadesOut | Bool | size that confettis and emojis are scaled to | true |
|
||||
| opacity | Double | maximum opacity during the animation | 1.0 |
|
||||
| openingAngle | Angle | boundary that defines the opening angle in degrees | Angle.degrees(60) |
|
||||
| closingAngle | Angle | boundary that defines the closing angle in degrees | Angle.degrees(120) |
|
||||
| radius | CGFloat | explosion radius | 300.0 |
|
||||
| repetitions | Int | number of repetitions for the explosion | 0 |
|
||||
| repetitionInterval | Double | duration between the repetitions | 1.0 |
|
||||
| parameter | type | description | default |
|
||||
| ------------------ | -------------- | ----------------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
||||
| counter | Binding<Int> | on any change of this variable triggers the animation | 0 |
|
||||
| num | Int | amount of confettis | 20 |
|
||||
| confettis | [ConfettiType] | list of shapes and text | [.shape(.circle), .shape(.triangle), .shape(.square), .shape(.slimRectangle), .shape(.roundedCross)] |
|
||||
| colors | [Color] | list of colors applied to the default shapes | [.blue, .red, .green, .yellow, .pink, .purple, .orange] |
|
||||
| confettiSize | CGFloat | size that confettis and emojis are scaled to | 10.0 |
|
||||
| rainHeight | CGFloat | vertical distance that confettis pass | 600.0 |
|
||||
| fadesOut | Bool | size that confettis and emojis are scaled to | true |
|
||||
| opacity | Double | maximum opacity during the animation | 1.0 |
|
||||
| openingAngle | Angle | boundary that defines the opening angle in degrees | Angle.degrees(60) |
|
||||
| closingAngle | Angle | boundary that defines the closing angle in degrees | Angle.degrees(120) |
|
||||
| radius | CGFloat | explosion radius | 300.0 |
|
||||
| repetitions | Int | number of repetitions for the explosion | 0 |
|
||||
| repetitionInterval | Double | duration between the repetitions | 1.0 |
|
||||
|
||||
### Configurator Application With Live Preview
|
||||
|
||||
You can use the configurator app in [demo project here](https://github.com/simibac/ConfettiSwiftUIDemo) to make your desired animation or get inspired by one of the many examples.
|
||||
|
||||
|
||||
## Projects
|
||||
<p align="center">
|
||||
<img src="./Gifs/configurator.png" width="150" />
|
||||
<img src="./Gifs/examples.png" width="150" />
|
||||
</p>
|
||||
|
||||
### Examples
|
||||
|
||||
#### Color and Size Configuration
|
||||
|
||||
<p align="center">
|
||||
<img src="./Gifs/color.gif" width="150" />
|
||||
</p>
|
||||
|
||||
```swift
|
||||
.confettiCannon(counter: $counter, colors: [.red, .black], confettiSize: 20)
|
||||
```
|
||||
|
||||
#### Repeat Configuration
|
||||
|
||||
<p align="center">
|
||||
<img src="./Gifs/repeat.gif" width="150" />
|
||||
</p>
|
||||
|
||||
```swift
|
||||
.confettiCannon(counter: $counter, repetitions: 3, repetitionInterval: 0.7)
|
||||
```
|
||||
|
||||
#### Firework Configuration
|
||||
|
||||
<p align="center">
|
||||
<img src="./Gifs/explosion.gif" width="150" />
|
||||
</p>
|
||||
|
||||
```swift
|
||||
.confettiCannon(counter: $counter, num: 50, openingAngle: Angle(degrees: 0), closingAngle: Angle(degrees: 360), radius: 200)
|
||||
```
|
||||
|
||||
#### Emoji Configuration
|
||||
|
||||
<p align="center">
|
||||
<img src="./Gifs/heart.gif" width="150" />
|
||||
</p>
|
||||
|
||||
```swift
|
||||
.confettiCannon(counter: $counter, confettis: [.text("❤️"), .text("💙"), .text("💚"), .text("🧡")])
|
||||
```
|
||||
|
||||
#### Endless Configuration
|
||||
|
||||
<p align="center">
|
||||
<img src="./Gifs/constant.gif" width="150" />
|
||||
</p>
|
||||
|
||||
```swift
|
||||
.confettiCannon(counter: $counter, num:1, confettis: [.text("💩")], confettiSize: 20, repetitions: 100, repetitionInterval: 0.1)
|
||||
```
|
||||
|
||||
#### Make-it-Rain Configuration
|
||||
|
||||
<p align="center">
|
||||
<img src="./Gifs/make-it-rain.gif" width="150" />
|
||||
</p>
|
||||
|
||||
```swift
|
||||
.confettiCannon(counter: $counter, num:1, confettis: [.text("💵"), .text("💶"), .text("💷"), .text("💴")], confettiSize: 30, repetitions: 50, repetitionInterval: 0.1)
|
||||
```
|
||||
|
||||
## 👨💻 Contributors
|
||||
|
||||
All issue reports, feature requests, pull requests and GitHub stars are welcomed and much appreciated.
|
||||
|
||||
## ✍️ Author
|
||||
|
||||
Simon Bachmann
|
||||
|
||||
## 📃 License
|
||||
|
||||
`ConfettiSwiftUI` is available under the MIT license. See the [LICENSE](https://github.com/simibac/ConfettiSwiftUI/blob/master/LICENSE) file for more info.
|
||||
|
||||
## 📦 Projects
|
||||
|
||||
The following projects have integrated ConfettiSwiftUI in their App.
|
||||
|
||||
- [Basic Code](https://basiccode.de) avaliable on the [AppStore](https://apps.apple.com/de/app/basiccode/id1562309250)
|
||||
|
||||
- [Basic Code](https://basiccode.de) available on the [AppStore](https://apps.apple.com/de/app/basiccode/id1562309250)
|
||||
|
||||
---
|
||||
|
||||
- [Jump Up](#-overview)
|
||||
|
|
|
@ -19,6 +19,7 @@ public enum ConfettiType:CaseIterable, Hashable {
|
|||
|
||||
case shape(Shape)
|
||||
case text(String)
|
||||
case sfSymbol(symbolName: String)
|
||||
|
||||
public var view:AnyView{
|
||||
switch self {
|
||||
|
@ -32,6 +33,8 @@ public enum ConfettiType:CaseIterable, Hashable {
|
|||
return AnyView(RoundedCross())
|
||||
case let .text(text):
|
||||
return AnyView(Text(text))
|
||||
case .sfSymbol(let symbolName):
|
||||
return AnyView(Image(systemName: symbolName))
|
||||
default:
|
||||
return AnyView(Circle())
|
||||
}
|
||||
|
@ -48,8 +51,8 @@ public struct ConfettiCannon: View {
|
|||
@StateObject private var confettiConfig:ConfettiConfig
|
||||
|
||||
@State var animate:[Bool] = []
|
||||
@State var finishedAnimationCouter = 0
|
||||
@State var firtAppear = false
|
||||
@State var finishedAnimationCounter = 0
|
||||
@State var firstAppear = false
|
||||
@State var error = ""
|
||||
|
||||
/// renders configurable confetti animaiton
|
||||
|
@ -79,7 +82,6 @@ public struct ConfettiCannon: View {
|
|||
radius:CGFloat = 300,
|
||||
repetitions:Int = 0,
|
||||
repetitionInterval:Double = 1.0
|
||||
|
||||
) {
|
||||
self._counter = counter
|
||||
var shapes = [AnyView]()
|
||||
|
@ -113,22 +115,22 @@ public struct ConfettiCannon: View {
|
|||
|
||||
public var body: some View {
|
||||
ZStack{
|
||||
ForEach(finishedAnimationCouter..<animate.count, id:\.self){ i in
|
||||
ForEach(finishedAnimationCounter..<animate.count, id:\.self){ i in
|
||||
ConfettiContainer(
|
||||
finishedAnimationCouter: $finishedAnimationCouter,
|
||||
finishedAnimationCounter: $finishedAnimationCounter,
|
||||
confettiConfig: confettiConfig
|
||||
)
|
||||
}
|
||||
}
|
||||
.onAppear(){
|
||||
firtAppear = true
|
||||
firstAppear = true
|
||||
}
|
||||
.onChange(of: counter){value in
|
||||
if firtAppear{
|
||||
if firstAppear{
|
||||
for i in 0...confettiConfig.repetitions{
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + confettiConfig.repetitionInterval * Double(i)) {
|
||||
animate.append(false)
|
||||
if(value < animate.count){
|
||||
if(value > 0 && value < animate.count){
|
||||
animate[value-1].toggle()
|
||||
}
|
||||
}
|
||||
|
@ -140,7 +142,7 @@ public struct ConfettiCannon: View {
|
|||
|
||||
@available(iOS 14.0, macOS 11.0, watchOS 7, tvOS 14.0, *)
|
||||
struct ConfettiContainer: View {
|
||||
@Binding var finishedAnimationCouter:Int
|
||||
@Binding var finishedAnimationCounter:Int
|
||||
@StateObject var confettiConfig:ConfettiConfig
|
||||
@State var firstAppear = true
|
||||
|
||||
|
@ -153,7 +155,7 @@ struct ConfettiContainer: View {
|
|||
.onAppear(){
|
||||
if firstAppear{
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + confettiConfig.animationDuration) {
|
||||
self.finishedAnimationCouter += 1
|
||||
self.finishedAnimationCounter += 1
|
||||
}
|
||||
firstAppear = false
|
||||
}
|
||||
|
@ -179,13 +181,33 @@ struct ConfettiView: View{
|
|||
let spinDirections:[CGFloat] = [-1.0, 1.0]
|
||||
return spinDirections.randomElement()!
|
||||
}
|
||||
|
||||
func getRandomExplosionTimeVariation() -> CGFloat {
|
||||
CGFloat((0...999).randomElement()!) / 2100
|
||||
}
|
||||
|
||||
func getAnimationDuration() -> CGFloat {
|
||||
return 0.2 + confettiConfig.explosionAnimationDuration + getRandomExplosionTimeVariation()
|
||||
}
|
||||
|
||||
func getAnimation() -> Animation {
|
||||
return Animation.timingCurve(0.1, 0.8, 0, 1, duration: getAnimationDuration())
|
||||
}
|
||||
|
||||
func getDistance() -> CGFloat {
|
||||
return pow(CGFloat.random(in: 0.01...1), 2.0/7.0) * confettiConfig.radius
|
||||
}
|
||||
|
||||
func getDelayBeforeRainAnimation() -> TimeInterval {
|
||||
confettiConfig.explosionAnimationDuration * 0.1
|
||||
}
|
||||
|
||||
var body: some View{
|
||||
ConfettiAnimationView(shape:getShape(), color:getColor(), spinDirX: getSpinDirection(), spinDirZ: getSpinDirection())
|
||||
.offset(x: location.x, y: location.y)
|
||||
.opacity(opacity)
|
||||
.onAppear(){
|
||||
withAnimation(Animation.timingCurve(0.61, 1, 0.88, 1, duration: confettiConfig.explosionAnimationDuration)) {
|
||||
withAnimation(getAnimation()) {
|
||||
opacity = confettiConfig.opacity
|
||||
|
||||
let randomAngle:CGFloat
|
||||
|
@ -195,13 +217,13 @@ struct ConfettiView: View{
|
|||
randomAngle = CGFloat.random(in: CGFloat(confettiConfig.openingAngle.degrees)...CGFloat(confettiConfig.closingAngle.degrees + 360)).truncatingRemainder(dividingBy: 360)
|
||||
}
|
||||
|
||||
let distance = CGFloat.random(in: 0.5...1) * confettiConfig.radius
|
||||
let distance = getDistance()
|
||||
|
||||
location.x = distance * cos(deg2rad(randomAngle))
|
||||
location.y = -distance * sin(deg2rad(randomAngle))
|
||||
}
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + confettiConfig.explosionAnimationDuration) {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + getDelayBeforeRainAnimation()) {
|
||||
withAnimation(Animation.timingCurve(0.12, 0, 0.39, 0, duration: confettiConfig.rainAnimationDuration)) {
|
||||
location.y += confettiConfig.rainHeight
|
||||
opacity = confettiConfig.fadesOut ? 0 : confettiConfig.opacity
|
||||
|
@ -225,9 +247,9 @@ struct ConfettiAnimationView: View {
|
|||
|
||||
|
||||
@State var move = false
|
||||
@State var xSpeed:Double = Double.random(in: 1...2)
|
||||
@State var xSpeed:Double = Double.random(in: 0.501...2.201)
|
||||
|
||||
@State var zSpeed = Double.random(in: 1...2)
|
||||
@State var zSpeed = Double.random(in: 0.501...2.201)
|
||||
@State var anchor = CGFloat.random(in: 0...1).rounded()
|
||||
|
||||
var body: some View {
|
||||
|
@ -260,7 +282,7 @@ class ConfettiConfig: ObservableObject {
|
|||
self.radius = radius
|
||||
self.repetitions = repetitions
|
||||
self.repetitionInterval = repetitionInterval
|
||||
self.explosionAnimationDuration = Double(radius / 1500)
|
||||
self.explosionAnimationDuration = Double(radius / 1300)
|
||||
self.rainAnimationDuration = Double((rainHeight + radius) / 200)
|
||||
}
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
//
|
||||
// View+ConfettiCannon.swift
|
||||
//
|
||||
//
|
||||
// Created by Abdullah Alhaider on 24/03/2022.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
public extension View {
|
||||
|
||||
/// renders configurable confetti animaiton
|
||||
///
|
||||
/// - Usage:
|
||||
///
|
||||
/// ```
|
||||
/// import SwiftUI
|
||||
///
|
||||
/// struct ContentView: View {
|
||||
///
|
||||
/// @State private var counter: Int = 0
|
||||
///
|
||||
/// var body: some View {
|
||||
/// Button("Wow") {
|
||||
/// counter += 1
|
||||
/// }
|
||||
/// .confettiCannon(counter: $counter)
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// - Parameters:
|
||||
/// - counter: on any change of this variable the animation is run
|
||||
/// - num: amount of confettis
|
||||
/// - colors: list of colors that is applied to the default shapes
|
||||
/// - confettiSize: size that confettis and emojis are scaled to
|
||||
/// - rainHeight: vertical distance that confettis pass
|
||||
/// - fadesOut: reduce opacity towards the end of the animation
|
||||
/// - opacity: maximum opacity that is reached during the animation
|
||||
/// - openingAngle: boundary that defines the opening angle in degrees
|
||||
/// - closingAngle: boundary that defines the closing angle in degrees
|
||||
/// - radius: explosion radius
|
||||
/// - repetitions: number of repetitions of the explosion
|
||||
/// - repetitionInterval: duration between the repetitions
|
||||
///
|
||||
@ViewBuilder func confettiCannon(
|
||||
counter: Binding<Int>,
|
||||
num: Int = 20,
|
||||
confettis: [ConfettiType] = ConfettiType.allCases,
|
||||
colors: [Color] = [.blue, .red, .green, .yellow, .pink, .purple, .orange],
|
||||
confettiSize: CGFloat = 10.0,
|
||||
rainHeight: CGFloat = 600.0,
|
||||
fadesOut: Bool = true,
|
||||
opacity: Double = 1.0,
|
||||
openingAngle: Angle = .degrees(60),
|
||||
closingAngle: Angle = .degrees(120),
|
||||
radius: CGFloat = 300,
|
||||
repetitions: Int = 0,
|
||||
repetitionInterval: Double = 1.0
|
||||
) -> some View {
|
||||
ZStack {
|
||||
self
|
||||
ConfettiCannon(
|
||||
counter: counter,
|
||||
num: num,
|
||||
confettis: confettis,
|
||||
colors: colors,
|
||||
confettiSize: confettiSize,
|
||||
rainHeight: rainHeight,
|
||||
fadesOut: fadesOut,
|
||||
opacity: opacity,
|
||||
openingAngle: openingAngle,
|
||||
closingAngle: closingAngle,
|
||||
radius: radius,
|
||||
repetitions: repetitions,
|
||||
repetitionInterval: repetitionInterval
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue