Keep improving dice roll animation logic
This commit is contained in:
parent
582321930b
commit
4ce8accdc5
|
@ -10,8 +10,19 @@ import SwiftUI
|
|||
import CypherPoetSwiftUIKit_BindingUtils
|
||||
|
||||
|
||||
struct VerticalDiceRollView {
|
||||
var diceCollection: [Dice] = []
|
||||
struct VerticalDiceRollView<Content: View> {
|
||||
var diceCollection: [Dice]
|
||||
|
||||
let content: (Int, Dice, CGPoint, CGFloat) -> Content
|
||||
|
||||
|
||||
init(
|
||||
diceCollection: [Dice] = [],
|
||||
@ViewBuilder content: @escaping ((Int, Dice, CGPoint, CGFloat) -> Content)
|
||||
) {
|
||||
self.diceCollection = diceCollection
|
||||
self.content = content
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -22,12 +33,21 @@ extension VerticalDiceRollView: View {
|
|||
GeometryReader { geometry in
|
||||
ZStack {
|
||||
ForEach(self.diceCollection.indexed(), id: \.0.self) { (index, dice) in
|
||||
DiceView(dice: dice)
|
||||
.frame(
|
||||
width: self.sideLength(forDiceIn: geometry),
|
||||
height: self.sideLength(forDiceIn: geometry)
|
||||
)
|
||||
.position(self.position(forDiceAt: index, in: geometry))
|
||||
// EmptyView()
|
||||
self.content(
|
||||
index,
|
||||
dice,
|
||||
self.position(forDiceAt: index, in: geometry),
|
||||
self.sideLength(forDiceIn: geometry)
|
||||
)
|
||||
// DiceView(dice: dice)
|
||||
// .frame(
|
||||
// width: self.sideLength(forDiceIn: geometry),
|
||||
// height: self.sideLength(forDiceIn: geometry)
|
||||
// )
|
||||
// .position(self.position(forDiceAt: index, in: geometry))
|
||||
// .animation(Animation.linear(duration: 0.3).delay(1.0 * Double(index)))
|
||||
// .animation(Animation.linear.delay(1.0 * index))
|
||||
}
|
||||
}
|
||||
.frame(
|
||||
|
@ -131,7 +151,14 @@ struct VerticalDiceRollView_Previews: PreviewProvider {
|
|||
// diceCollection: [.one, .two,]
|
||||
// diceCollection: [.one]
|
||||
diceCollection: [.one, .two, .three]
|
||||
)
|
||||
) { (index, dice, position, sideLength) in
|
||||
DiceView(dice: dice)
|
||||
.frame(
|
||||
width: sideLength,
|
||||
height: sideLength
|
||||
)
|
||||
.position(position)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
|
|
|
@ -1,73 +0,0 @@
|
|||
//
|
||||
// VerticalDiceSetView.swift
|
||||
// Dicey
|
||||
//
|
||||
// Created by CypherPoet on 2/2/20.
|
||||
// ✌️
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
|
||||
struct VerticalDiceSetView {
|
||||
var diceCount: Int = 1
|
||||
var diceSideLength: CGFloat = 40.0
|
||||
}
|
||||
|
||||
|
||||
// MARK: - View
|
||||
extension VerticalDiceSetView: View {
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { geometry in
|
||||
Rectangle()
|
||||
.fill(Color.purple)
|
||||
.frame(
|
||||
width: (geometry.size.width / CGFloat(self.columnCount)) - (self.columnSpacing * (CGFloat(self.columnCount - 1))),
|
||||
height: (geometry.size.height / CGFloat(self.rowCount)) - (self.rowSpacing * CGFloat((self.rowCount - 1)))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Computeds
|
||||
extension VerticalDiceSetView {
|
||||
var rowSpacing: CGFloat { 16.0 }
|
||||
var columnSpacing: CGFloat { 20.0 }
|
||||
|
||||
var rowCount: Int { ((diceCount - 1) / 2) + 1 }
|
||||
var columnCount: Int { (diceCount / 2) + 1 }
|
||||
// var rowCount: Int { 1 }
|
||||
// var columnCount: Int { 1 }
|
||||
}
|
||||
|
||||
|
||||
// MARK: - View Variables
|
||||
extension VerticalDiceSetView {
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Private Helpers
|
||||
private extension VerticalDiceSetView {
|
||||
|
||||
func diceSideLength(in geometry: GeometryProxy) {
|
||||
let maxDimension = max(geometry.size.width, geometry.size.height)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MARK: - Preview
|
||||
struct DiceSetView_Previews: PreviewProvider {
|
||||
|
||||
static var previews: some View {
|
||||
VStack {
|
||||
VerticalDiceSetView(
|
||||
diceCount: 4
|
||||
)
|
||||
}
|
||||
// .environment(\.managedObjectContext, CurrentApp.coreDataManager.mainContext)
|
||||
}
|
||||
}
|
|
@ -14,15 +14,15 @@ struct DiceGeneratorView {
|
|||
@Environment(\.verticalSizeClass) var verticalSizeClass
|
||||
|
||||
private let diceCountRange = 1...6
|
||||
private let diceRollAnimationDuration = 0.76
|
||||
|
||||
@ObservedObject var viewModel: ViewModel
|
||||
let onDiceRolled: ((DiceRoll) -> Void)?
|
||||
|
||||
// @State private var diceShakeCompletionPercentage: CGFloat = 0.0
|
||||
@State private var hasDiceRolledAtLeastOnce = false
|
||||
@State private var isShakingDice = false
|
||||
@State private var diceCollectionViewID = 1
|
||||
@State private var isShowingDice = true
|
||||
|
||||
@State private var diceRollCompletion: CGFloat = 1.0
|
||||
}
|
||||
|
||||
|
||||
|
@ -35,8 +35,6 @@ extension DiceGeneratorView: View {
|
|||
|
||||
if isShowingDice {
|
||||
diceCollectionSection
|
||||
// .id(self.diceCollectionViewID)
|
||||
.transition(self.diceCollectionTransition)
|
||||
} else {
|
||||
Color.clear
|
||||
}
|
||||
|
@ -61,11 +59,30 @@ extension DiceGeneratorView {
|
|||
var diceCollectionTransition: AnyTransition {
|
||||
.asymmetric(
|
||||
insertion: AnyTransition
|
||||
.move(edge: .bottom).combined(with: .scale(scale: 0, anchor: .top)),
|
||||
.move(edge: .bottom)
|
||||
.combined(with: .scale(scale: 0, anchor: .top)),
|
||||
|
||||
removal: AnyTransition
|
||||
.opacity
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
var diceRollAnimation: Animation {
|
||||
Animation.spring(
|
||||
response: diceRollAnimationDuration,
|
||||
dampingFraction: 0.635,
|
||||
blendDuration: 0.0
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
var diceRollRotation: Angle {
|
||||
.radians(
|
||||
(1.0 - Double(self.diceRollCompletion))
|
||||
* (.pi * Double.random(in: 0...1))
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
// TODO: Improve this logic
|
||||
|
@ -88,7 +105,25 @@ extension DiceGeneratorView {
|
|||
if self.isShowingHorizontalDiceLayout {
|
||||
HorizontalDiceRollView(diceCollection: self.viewModel.diceCollection)
|
||||
} else {
|
||||
VerticalDiceRollView(diceCollection: self.viewModel.diceCollection)
|
||||
VerticalDiceRollView(diceCollection: self.viewModel.diceCollection) { (index, dice, position, sideLength) in
|
||||
DiceView(dice: dice)
|
||||
.frame(
|
||||
width: sideLength,
|
||||
height: sideLength
|
||||
)
|
||||
.position(position)
|
||||
.rotation3DEffect(
|
||||
self.diceRollRotation,
|
||||
axis: (x: 0.0, y: 0.0, z: 1.0)
|
||||
)
|
||||
.offset(
|
||||
x: 0,
|
||||
y: (
|
||||
(-1.0 * (1.0 - self.diceRollCompletion) * CGFloat(geometry.size.height))
|
||||
- ((1.0 - self.diceRollCompletion) * position.y)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
.offset(self.offsetFromShake(in: geometry))
|
||||
|
@ -100,28 +135,31 @@ extension DiceGeneratorView {
|
|||
private var rollButton: some View {
|
||||
Button(action: {
|
||||
// self.onDiceRolled?(viewModel.diceRollFromForm)
|
||||
self.hasDiceRolledAtLeastOnce = true
|
||||
|
||||
// Shake the current set
|
||||
withAnimation(
|
||||
Animation
|
||||
.timingCurve(0.3, 0.3, 0.7, 2.7, duration: 0.07)
|
||||
.repeatCount(10, autoreverses: true)
|
||||
.timingCurve(0.3, 0.3, 0.7, 2.7, duration: 0.11)
|
||||
.repeatCount(8, autoreverses: true)
|
||||
) {
|
||||
self.isShakingDice = true
|
||||
}
|
||||
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + (0.07 * 10)) {
|
||||
// self.diceCollectionViewID += 1
|
||||
// Roll in the new set
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + (0.11 * 8)) {
|
||||
self.isShakingDice = false
|
||||
self.isShowingDice = false
|
||||
self.diceRollCompletion = 0.0
|
||||
|
||||
DispatchQueue.main.async {
|
||||
withAnimation(.linear(duration: 2)) {
|
||||
self.isShowingDice = true
|
||||
self.isShowingDice = true
|
||||
|
||||
// 📝 TODO: Compute and set the new dice values here
|
||||
|
||||
withAnimation(self.diceRollAnimation) {
|
||||
self.diceRollCompletion = 1.0
|
||||
}
|
||||
}
|
||||
// self.isShowingDice = true
|
||||
}
|
||||
}) {
|
||||
Image("roll-dice")
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 501 B |
Binary file not shown.
After Width: | Height: | Size: 1004 B |
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
Loading…
Reference in New Issue