Keep improving dice roll animation logic
This commit is contained in:
parent
582321930b
commit
4ce8accdc5
|
@ -10,8 +10,19 @@ import SwiftUI
|
||||||
import CypherPoetSwiftUIKit_BindingUtils
|
import CypherPoetSwiftUIKit_BindingUtils
|
||||||
|
|
||||||
|
|
||||||
struct VerticalDiceRollView {
|
struct VerticalDiceRollView<Content: View> {
|
||||||
var diceCollection: [Dice] = []
|
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
|
GeometryReader { geometry in
|
||||||
ZStack {
|
ZStack {
|
||||||
ForEach(self.diceCollection.indexed(), id: \.0.self) { (index, dice) in
|
ForEach(self.diceCollection.indexed(), id: \.0.self) { (index, dice) in
|
||||||
DiceView(dice: dice)
|
// EmptyView()
|
||||||
.frame(
|
self.content(
|
||||||
width: self.sideLength(forDiceIn: geometry),
|
index,
|
||||||
height: self.sideLength(forDiceIn: geometry)
|
dice,
|
||||||
|
self.position(forDiceAt: index, in: geometry),
|
||||||
|
self.sideLength(forDiceIn: geometry)
|
||||||
)
|
)
|
||||||
.position(self.position(forDiceAt: index, in: 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(
|
.frame(
|
||||||
|
@ -131,7 +151,14 @@ struct VerticalDiceRollView_Previews: PreviewProvider {
|
||||||
// diceCollection: [.one, .two,]
|
// diceCollection: [.one, .two,]
|
||||||
// diceCollection: [.one]
|
// diceCollection: [.one]
|
||||||
diceCollection: [.one, .two, .three]
|
diceCollection: [.one, .two, .three]
|
||||||
|
) { (index, dice, position, sideLength) in
|
||||||
|
DiceView(dice: dice)
|
||||||
|
.frame(
|
||||||
|
width: sideLength,
|
||||||
|
height: sideLength
|
||||||
)
|
)
|
||||||
|
.position(position)
|
||||||
|
}
|
||||||
|
|
||||||
Spacer()
|
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
|
@Environment(\.verticalSizeClass) var verticalSizeClass
|
||||||
|
|
||||||
private let diceCountRange = 1...6
|
private let diceCountRange = 1...6
|
||||||
|
private let diceRollAnimationDuration = 0.76
|
||||||
|
|
||||||
@ObservedObject var viewModel: ViewModel
|
@ObservedObject var viewModel: ViewModel
|
||||||
let onDiceRolled: ((DiceRoll) -> Void)?
|
let onDiceRolled: ((DiceRoll) -> Void)?
|
||||||
|
|
||||||
// @State private var diceShakeCompletionPercentage: CGFloat = 0.0
|
|
||||||
@State private var hasDiceRolledAtLeastOnce = false
|
|
||||||
@State private var isShakingDice = false
|
@State private var isShakingDice = false
|
||||||
@State private var diceCollectionViewID = 1
|
|
||||||
@State private var isShowingDice = true
|
@State private var isShowingDice = true
|
||||||
|
|
||||||
|
@State private var diceRollCompletion: CGFloat = 1.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,8 +35,6 @@ extension DiceGeneratorView: View {
|
||||||
|
|
||||||
if isShowingDice {
|
if isShowingDice {
|
||||||
diceCollectionSection
|
diceCollectionSection
|
||||||
// .id(self.diceCollectionViewID)
|
|
||||||
.transition(self.diceCollectionTransition)
|
|
||||||
} else {
|
} else {
|
||||||
Color.clear
|
Color.clear
|
||||||
}
|
}
|
||||||
|
@ -61,13 +59,32 @@ extension DiceGeneratorView {
|
||||||
var diceCollectionTransition: AnyTransition {
|
var diceCollectionTransition: AnyTransition {
|
||||||
.asymmetric(
|
.asymmetric(
|
||||||
insertion: AnyTransition
|
insertion: AnyTransition
|
||||||
.move(edge: .bottom).combined(with: .scale(scale: 0, anchor: .top)),
|
.move(edge: .bottom)
|
||||||
|
.combined(with: .scale(scale: 0, anchor: .top)),
|
||||||
|
|
||||||
removal: AnyTransition
|
removal: AnyTransition
|
||||||
.opacity
|
.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
|
// TODO: Improve this logic
|
||||||
func offsetFromShake(in geometry: GeometryProxy) -> CGSize {
|
func offsetFromShake(in geometry: GeometryProxy) -> CGSize {
|
||||||
.init(
|
.init(
|
||||||
|
@ -88,7 +105,25 @@ extension DiceGeneratorView {
|
||||||
if self.isShowingHorizontalDiceLayout {
|
if self.isShowingHorizontalDiceLayout {
|
||||||
HorizontalDiceRollView(diceCollection: self.viewModel.diceCollection)
|
HorizontalDiceRollView(diceCollection: self.viewModel.diceCollection)
|
||||||
} else {
|
} 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))
|
.offset(self.offsetFromShake(in: geometry))
|
||||||
|
@ -100,28 +135,31 @@ extension DiceGeneratorView {
|
||||||
private var rollButton: some View {
|
private var rollButton: some View {
|
||||||
Button(action: {
|
Button(action: {
|
||||||
// self.onDiceRolled?(viewModel.diceRollFromForm)
|
// self.onDiceRolled?(viewModel.diceRollFromForm)
|
||||||
self.hasDiceRolledAtLeastOnce = true
|
|
||||||
|
|
||||||
|
// Shake the current set
|
||||||
withAnimation(
|
withAnimation(
|
||||||
Animation
|
Animation
|
||||||
.timingCurve(0.3, 0.3, 0.7, 2.7, duration: 0.07)
|
.timingCurve(0.3, 0.3, 0.7, 2.7, duration: 0.11)
|
||||||
.repeatCount(10, autoreverses: true)
|
.repeatCount(8, autoreverses: true)
|
||||||
) {
|
) {
|
||||||
self.isShakingDice = true
|
self.isShakingDice = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Roll in the new set
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + (0.07 * 10)) {
|
DispatchQueue.main.asyncAfter(deadline: .now() + (0.11 * 8)) {
|
||||||
// self.diceCollectionViewID += 1
|
|
||||||
self.isShakingDice = false
|
self.isShakingDice = false
|
||||||
self.isShowingDice = false
|
self.isShowingDice = false
|
||||||
|
self.diceRollCompletion = 0.0
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
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")
|
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