同时匹配GeometryEffect和rotation3DEffect
Simultaneous matchedGeometryEffect and rotation3DEffect
我想制作一张卡片的动画,它从屏幕的上半部分飞到屏幕的下半部分,并在飞行过程中翻转。
我使用自定义 .cardify
修饰符控制翻转逻辑。它似乎 单独工作 ,例如当我按 onTapGesture { withAnimation { ... } }
.
翻牌时
我还使用 matchedGeometryEffect
使卡片从屏幕顶部飞到底部,反之亦然。
但是,当我点击卡片时,它没有旋转地飞行。
我尝试为两个 if 分支应用 .transition(.assymetric(...))
(请参阅下面的代码),但没有帮助。
所以,代码
import SwiftUI
struct ContentView: View {
@State var cardOnTop = true
@State var theCard = Card(isFaceUp: true)
@Namespace private var animationNameSpace
var body: some View {
VStack {
if cardOnTop {
CardView(card: theCard)
.matchedGeometryEffect(id: 1, in: animationNameSpace)
.onTapGesture {
withAnimation {
theCard.toggle()
cardOnTop.toggle() // comment me to test flipping alone
}
}
Color.white
} else {
Color.white
CardView(card: theCard)
.matchedGeometryEffect(id: 1, in: animationNameSpace)
.onTapGesture {
withAnimation {
theCard.toggle()
cardOnTop.toggle()
}
}
}
}
.padding()
}
struct Card {
var isFaceUp: Bool
mutating func toggle() {
isFaceUp.toggle()
}
}
struct CardView: View {
var card: Card
var body: some View {
Rectangle()
.frame(width: 100, height: 50)
.foregroundColor(.red)
.cardify(isFaceUp: card.isFaceUp)
}
}
}
/* Cardify ViewModifier */
struct Cardify: ViewModifier, Animatable {
init(isFaceUp: Bool){
rotation = isFaceUp ? 0 : 180
}
var rotation: Double // in degrees
var animatableData: Double {
get { return rotation }
set { rotation = newValue }
}
func body(content: Content) -> some View {
ZStack {
let shape = RoundedRectangle(cornerRadius: DrawingConstants.cornerRadius)
if rotation < 90 {
shape.fill().foregroundColor(.white)
shape.strokeBorder(lineWidth: DrawingConstants.lineWidth).foregroundColor(.gray)
} else {
shape.fill().foregroundColor(.gray)
}
content
.opacity(rotation < 90 ? 1 : 0)
}
.rotation3DEffect(Angle.degrees(rotation), axis: (0, 1, 0))
}
private struct DrawingConstants {
static let cornerRadius: CGFloat = 15
static let lineWidth: CGFloat = 2
}
}
extension View {
func cardify(isFaceUp: Bool) -> some View {
return self.modifier(Cardify(isFaceUp: isFaceUp))
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
如何让卡片旋转飞起来?
此外,我发现这行得通(如果单独放在 VStack
的正文中,没有 if-branches)
CardView(card: theCard)
.offset(x: 0, y: cardOnTop ? 0 : 100)
.onTapGesture {
withAnimation {
theCard.toggle()
cardOnTop.toggle() // comment me to test flipping alone
}
}
但在我的应用程序中,我有一副向下翻转的卡片,这些卡片被处理到向上翻转的板上。我试图在上面 ContentView
的代码中通过 if-branch 来模拟行为(CardView
s 出现和消失)。
没有触发旋转,因为您取出卡片并在视图中插入另一张卡片,因此只剩下 .matchedGeometryEffect
。
您想要的是一张卡片保持在视野中,这样它可以旋转,但也可以改变它的位置。
你可以做到这一点,例如.offset
。 GeometryReader
只是为了找到正确的偏移值。
var body: some View {
GeometryReader { geo in
VStack {
CardView(card: theCard)
.offset(x: 0, y: cardOnTop ? 0 : geo.size.height / 2)
.onTapGesture {
withAnimation {
cardOnTop.toggle()
theCard.toggle()
}
}
Color.clear
}
}
.padding()
}
这里是一个纯粹使用自定义转换的 .transition
解决方案:
struct ContentView: View {
@State var cardOnTop = true
var body: some View {
VStack(spacing:0) {
if cardOnTop {
RoundedRectangle(cornerRadius: 15)
.fill(.blue)
.transition(.rotate3D(direction: 1).combined(with: .move(edge: .bottom)))
Color.clear
} else {
Color.clear
RoundedRectangle(cornerRadius: 15)
.fill(.green)
.transition(.rotate3D(direction: -1).combined(with: .move(edge: .top)))
}
}
.onTapGesture {
withAnimation(.easeInOut(duration: 2)) {
cardOnTop.toggle()
}
}
.padding()
}
}
/* Transition Modifier */
extension AnyTransition {
static func rotate3D(direction: Double) -> AnyTransition {
AnyTransition.modifier(
active: Rotate3DModifier(value: 1, direction: direction),
identity: Rotate3DModifier(value: 0, direction: direction))
}
}
struct Rotate3DModifier: ViewModifier {
let value: Double
let direction: Double
func body(content: Content) -> some View {
content
.rotation3DEffect(Angle(degrees: 180 * value * direction), axis: (x: 0, y: 1, z: 0))
.opacity(1 - value)
}
}
我想制作一张卡片的动画,它从屏幕的上半部分飞到屏幕的下半部分,并在飞行过程中翻转。
我使用自定义 .cardify
修饰符控制翻转逻辑。它似乎 单独工作 ,例如当我按 onTapGesture { withAnimation { ... } }
.
我还使用 matchedGeometryEffect
使卡片从屏幕顶部飞到底部,反之亦然。
但是,当我点击卡片时,它没有旋转地飞行。
我尝试为两个 if 分支应用 .transition(.assymetric(...))
(请参阅下面的代码),但没有帮助。
所以,代码
import SwiftUI
struct ContentView: View {
@State var cardOnTop = true
@State var theCard = Card(isFaceUp: true)
@Namespace private var animationNameSpace
var body: some View {
VStack {
if cardOnTop {
CardView(card: theCard)
.matchedGeometryEffect(id: 1, in: animationNameSpace)
.onTapGesture {
withAnimation {
theCard.toggle()
cardOnTop.toggle() // comment me to test flipping alone
}
}
Color.white
} else {
Color.white
CardView(card: theCard)
.matchedGeometryEffect(id: 1, in: animationNameSpace)
.onTapGesture {
withAnimation {
theCard.toggle()
cardOnTop.toggle()
}
}
}
}
.padding()
}
struct Card {
var isFaceUp: Bool
mutating func toggle() {
isFaceUp.toggle()
}
}
struct CardView: View {
var card: Card
var body: some View {
Rectangle()
.frame(width: 100, height: 50)
.foregroundColor(.red)
.cardify(isFaceUp: card.isFaceUp)
}
}
}
/* Cardify ViewModifier */
struct Cardify: ViewModifier, Animatable {
init(isFaceUp: Bool){
rotation = isFaceUp ? 0 : 180
}
var rotation: Double // in degrees
var animatableData: Double {
get { return rotation }
set { rotation = newValue }
}
func body(content: Content) -> some View {
ZStack {
let shape = RoundedRectangle(cornerRadius: DrawingConstants.cornerRadius)
if rotation < 90 {
shape.fill().foregroundColor(.white)
shape.strokeBorder(lineWidth: DrawingConstants.lineWidth).foregroundColor(.gray)
} else {
shape.fill().foregroundColor(.gray)
}
content
.opacity(rotation < 90 ? 1 : 0)
}
.rotation3DEffect(Angle.degrees(rotation), axis: (0, 1, 0))
}
private struct DrawingConstants {
static let cornerRadius: CGFloat = 15
static let lineWidth: CGFloat = 2
}
}
extension View {
func cardify(isFaceUp: Bool) -> some View {
return self.modifier(Cardify(isFaceUp: isFaceUp))
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
如何让卡片旋转飞起来?
此外,我发现这行得通(如果单独放在 VStack
的正文中,没有 if-branches)
CardView(card: theCard)
.offset(x: 0, y: cardOnTop ? 0 : 100)
.onTapGesture {
withAnimation {
theCard.toggle()
cardOnTop.toggle() // comment me to test flipping alone
}
}
但在我的应用程序中,我有一副向下翻转的卡片,这些卡片被处理到向上翻转的板上。我试图在上面 ContentView
的代码中通过 if-branch 来模拟行为(CardView
s 出现和消失)。
没有触发旋转,因为您取出卡片并在视图中插入另一张卡片,因此只剩下 .matchedGeometryEffect
。
您想要的是一张卡片保持在视野中,这样它可以旋转,但也可以改变它的位置。
你可以做到这一点,例如.offset
。 GeometryReader
只是为了找到正确的偏移值。
var body: some View {
GeometryReader { geo in
VStack {
CardView(card: theCard)
.offset(x: 0, y: cardOnTop ? 0 : geo.size.height / 2)
.onTapGesture {
withAnimation {
cardOnTop.toggle()
theCard.toggle()
}
}
Color.clear
}
}
.padding()
}
这里是一个纯粹使用自定义转换的 .transition
解决方案:
struct ContentView: View {
@State var cardOnTop = true
var body: some View {
VStack(spacing:0) {
if cardOnTop {
RoundedRectangle(cornerRadius: 15)
.fill(.blue)
.transition(.rotate3D(direction: 1).combined(with: .move(edge: .bottom)))
Color.clear
} else {
Color.clear
RoundedRectangle(cornerRadius: 15)
.fill(.green)
.transition(.rotate3D(direction: -1).combined(with: .move(edge: .top)))
}
}
.onTapGesture {
withAnimation(.easeInOut(duration: 2)) {
cardOnTop.toggle()
}
}
.padding()
}
}
/* Transition Modifier */
extension AnyTransition {
static func rotate3D(direction: Double) -> AnyTransition {
AnyTransition.modifier(
active: Rotate3DModifier(value: 1, direction: direction),
identity: Rotate3DModifier(value: 0, direction: direction))
}
}
struct Rotate3DModifier: ViewModifier {
let value: Double
let direction: Double
func body(content: Content) -> some View {
content
.rotation3DEffect(Angle(degrees: 180 * value * direction), axis: (x: 0, y: 1, z: 0))
.opacity(1 - value)
}
}