三、SwiftUI动画

217 阅读5分钟

1、简单实现缩放动画

image.png

import SwiftUI

struct ContentView : View
{
    @State var factor: CGFloat = 1

    var body: some View
    {
        VStack
        {
            Image("couples")
                .scaleEffect(factor)
                .animation(.default)
            
            Divider().fixedSize()
            
            Button(action:
            {
                self.factor += 0.2
            }) {
                Text("Zoom in")
            }
        }
    }
}

2、withAnimation实现缩放和渐隐动画

import SwiftUI

struct ContentView : View
{
    @State var factor: Double = 1
    @State var alpha: Double = 1

    var body: some View
    {
        Image("couples")
            .scaleEffect(CGFloat(factor))
            .opacity(alpha)
            .onTapGesture
            {
                withAnimation(.linear(duration: 1.0))
                {
                    self.factor += 0.1
                    self.alpha -= 0.2
                }
            }
    }
}

3、渐缓时间曲线进行位移动画 -- easeOut

import SwiftUI

struct ContentView : View
{
    @State var distance: Double = 0

    var body: some View
    {
        VStack
        {
            Image("couples")
                .offset(x: 0, y: CGFloat(distance))
                .animation(.easeOut(duration: 2))
            
            Divider().fixedSize()
            
            Button(action:
            {
                self.distance -= 120
            }) {
                Text("Move Effect")
            }
        }
    }
}

4、旋转动画 -- rotationEffect

//
//  ContentView.swift
//  Copyright © www.hdjc8.com
//

import SwiftUI

struct ContentView : View
{
    @State var angle: Double = 0

    var body: some View
    {
        VStack
        {
            Image("couples")
                .rotationEffect(Angle.init(degrees: angle))
                .animation(.spring())
            
            Divider().fixedSize()
            
            Button(action:
            {
                self.angle += 90
            }) {
                Text("Rotation Effect")
            }
        }
    }
}

5、色相和亮度复合动画 -- hueRotation & brightness

import SwiftUI

struct ContentView : View
{
    @State var angle: Double = 0
    @State var brightness: Double = 0

    var body: some View
    {
        VStack
        {
            Image("couples")
                .hueRotation(Angle.init(degrees: angle))
                .brightness(brightness)
                .animation(.linear(duration: 2))
            
            Divider().fixedSize()
            
            Button(action:
            {
                self.angle = 180
                self.brightness = 2
            }) {
                Text("Move Effect")
            }
        }
    }
}

6、动画的播放速度和延迟时间 -- speed & delay

import SwiftUI

struct ContentView : View
{
    @State var factor: Double = 1.0
    
    var animation: Animation
    {
        Animation.linear(duration: 1)
//            .speed(5)
            .delay(1)
    }

    var body: some View
    {
        VStack
        {
            Image("couples")
                .scaleEffect(CGFloat(factor))
                .animation(animation)
            
            Divider().fixedSize()
            
            Button(action:
            {
                self.factor = 0.1
            }) {
                Text("Zoom In Effect")
            }
        }
    }
}

7、循环动画实现和循环次数设置 -- repeatForever & repeatCount

autoreverses: 循环动画默认是有自动反转的特性,当动画结束时会播放动画开始时的状态。

import SwiftUI

struct ContentView : View
{
    @State var angle: Double = 0
    
    var animation: Animation
    {
        Animation.spring()
   //         .repeatForever(autoreverses: false)
              .repeatCount(3)
    }

    var body: some View
    {
        VStack
        {
            Image("couples")
                .rotationEffect(Angle.init(degrees: angle))
                .animation(animation)
            
            Divider().fixedSize()
            
            Button(action:
            {
                self.angle = 45
            }) {
                Text("Repeat Forever Effect")
            }
        }
    }
}

8、动画的方式显示或隐藏指定位图

image.png

import SwiftUI

struct ContentView : View
{
    @State private var showingPassword = false
    @State private var password = ""

    var body: some View
    {
        VStack
        {
            Toggle(isOn: $showingPassword.animation(
                    .linear(duration: 1)))
            {
                Text("Toggle Password")
            }

            if showingPassword
            {
                TextField("Password", text: $password)
                    .padding()
                    .border(Color.green, width: 2)
            }
        }
        .padding()
    }
}

9、过渡动画

image.png

import SwiftUI

struct ContentView : View
{
    @State var showPicture = false

    var body: some View
    {
        VStack
        {
            Button(action:
            {
                withAnimation
                {
                    self.showPicture.toggle()
                }
            }) {
                Text("Show picture")
            }

            if showPicture
            {
//                Image("logo")
                
//                Image("logo")
//                    .transition(.move(edge: .top))
                
//                Image("logo")
//                    .transition(.scale(scale: 0))
                
//                Image("logo")
//                    .transition(.slide)
                
//                Image("logo")
//                    .transition(.asymmetric(insertion: .scale(scale: 0), removal: .slide))
                
                Image("logo")
                    .transition(AnyTransition.scale(scale: 0).combined(with:.slide))
            }
        }
    }
}

10、模拟手机解锁色彩变换动画 -- hueRotation

image.png

import SwiftUI

struct ContentView : View
{
    @State private var hueShift: Bool = false

    var body: some View
    {
        ZStack
        {
            Text("Slide to unlock")
                .font(.largeTitle)
                .foregroundColor(.purple)
                .brightness(-0.2)
                .hueRotation(.degrees(hueShift ? 0 : 720))
                .animation(Animation.easeInOut(duration: 4))
                .onTapGesture
                {
                    self.hueShift = true
                }
            
            Rectangle()
                .frame(maxWidth: 220, maxHeight: 40)
                .foregroundColor(.white)
                .opacity(0.5)
                .rotationEffect(.degrees(0), anchor: .trailing)
                .scaleEffect(x:hueShift ? 0 : 1, y:1, anchor: .trailing)
                .animation(Animation.easeInOut(duration: 4))
        }
    }
}

11、三维旋转、缩放和偏移复合动画 -- rotation3DEffect

image.png

import SwiftUI

struct ContentView : View
{
    @State var angle: Double = 0
    @State var scale: Double = 0
    @State var offsetY: Double = -200

    var body: some View
    {
        VStack
        {
            Image("bitcoin")
                .offset(x: 0, y: CGFloat(offsetY))
                .scaleEffect(CGFloat(scale))
                .rotation3DEffect(.degrees(angle), axis: (x: 0, y: 1, z: 0))
                .animation(.interpolatingSpring(stiffness: 100, damping: 10))
            
            Divider().fixedSize()
            
            Button(action:
            {
                if(self.angle == 0)
                {
                    self.offsetY = 0
                    self.scale = 1
                    self.angle = 360*10
                }
                else
                {
                    self.offsetY = -200
                    self.scale = 0
                    self.angle = 0
                }
            }) {
                Text("Rotation Effect")
            }
        }
    }
}

12、色轮旋转动画

image.png

import SwiftUI

struct ContentView : View
{
    @State var shouldRotate = false

    var body: some View
    {
        ZStack
        {
            Circle()
                .stroke(style: .init(lineWidth: 50))
                .fill(AngularGradient(
                    gradient: Gradient(colors: [.red, .orange, .yellow, .blue, .green, .purple, .red]),
                    center: .center))
                .padding(40)
                .rotationEffect(.degrees(shouldRotate ? 720 : 0))
                .animation(.easeInOut(duration: 4))
            
            Button(action:
            {
                self.shouldRotate.toggle()
            }) {
                Text("Rock and roll")
            }
        }
    }
}

13、聚焦动画

image.png

import SwiftUI

struct ContentView : View
{
    @State var isAnimating: Bool = false

    var body: some View
    {
        VStack
        {
            Image("Picture")
                .clipShape(
                    Circle()
                        .inset(by: isAnimating ? 120 : 0)
                        .offset(x: isAnimating ? 30 : 0, y: isAnimating ? -100 : 0)
                )
                .animation(.easeOut(duration: 2))
            
            Divider().fixedSize()
            
            Button(action:
            {
                self.isAnimating.toggle()
            }) {
                Text("Start animation")
            }
        }
    }
}

14、探照灯滚动扫描动画

image.png

import SwiftUI

struct ContentView : View
{
    @State var isAnimating: Bool = false

    var body: some View
    {
        ZStack
        {
            Image("Picture")
                .blur(radius: 6)
                .brightness(0.4)
            
            Image("Picture")
                .clipShape(
                    Rectangle()
                    .size(CGSize(width: 340, height: 100))
                    .offset(x: 0, y: isAnimating ? 590 : -100)
                )
                .animation(.linear(duration: 2))
                .onTapGesture
                {
                    self.isAnimating.toggle()
                }
        }
    }
}

15、花朵旋转动画

image.png

import SwiftUI

struct ContentView : View {
    
    @State private var shouldRotate: Bool = false
    @State private var shouldScale: Bool = false

    var body: some View {
        
        FlowerCapsule()
            .scaleEffect(shouldScale ? 0.2 : 1)
            .rotationEffect(.degrees(shouldRotate ? 0 : 360), anchor: .center)
            .animation(.easeInOut(duration: 4))
            .onTapGesture
            {
                self.shouldRotate.toggle()
                self.shouldScale.toggle()
            }
    }
}

struct NewCapsule: View
{
    var color : Color
    var degree : Double
    
    var body: some View
    {
        Capsule()
            .foregroundColor(color)
            .frame(width : 60, height: 90)
            .offset(x: 0, y: 60)
            .opacity(0.75)
            .rotationEffect(.degrees(degree))
    }
}

struct FlowerCapsule: View
{
    var body: some View
    {
        ZStack
        {
            NewCapsule(color: .red, degree: 0)
            NewCapsule(color: .red, degree: 45)
            NewCapsule(color: .yellow, degree: 90)
            NewCapsule(color: .yellow, degree: 135)
            NewCapsule(color: .blue, degree: 180)
            NewCapsule(color: .blue, degree: 225)
            NewCapsule(color: .green, degree: 270)
            NewCapsule(color: .green, degree: 315)
        }
    }
}

16、序列动画

序列动画:指一个动画结束时执行另一段动画

image.png

import SwiftUI

struct ContentView: View
{
    @State var offsetX: CGFloat = 0
    
    let colors : [Color] = [.red, .orange, .yellow, .green, .blue]
    let animations : [Animation] =
        [Animation.interpolatingSpring(stiffness: 100, damping: 10),
         Animation.interpolatingSpring(stiffness: 100, damping: 10).delay(0.3),
         Animation.interpolatingSpring(stiffness: 100, damping: 10).delay(0.6),
         Animation.interpolatingSpring(stiffness: 100, damping: 10).delay(0.9),
         Animation.interpolatingSpring(stiffness: 100, damping: 10).delay(1.2)]
    
    var body: some View
    {
        VStack
        {
            List(0..<5)
            { item in
                Rectangle()
                    .fill(self.colors[item])
                    .frame(width:50, height:50)
                    .offset(x: self.offsetX, y: 0)
                    .animation(self.animations[item])
            }
            
            Button(action: {
                self.offsetX += 200
            }) {
                Text("Start animation")
            }
        }
    }
}

17、履带滚动动画 -- dashPhase相位移动

image.png

import SwiftUI

struct ContentView : View
{
    @State private var isAnimating = false
    
    var body: some View
    {
        ZStack
        {
            Capsule()
                .stroke(Color.orange, style: StrokeStyle(lineWidth: 10,
                        lineCap: .butt, lineJoin: .bevel,
                        miterLimit: 10, dash: [20,11],
                        dashPhase: isAnimating ? 200 : 0))
                .frame(width : 300, height:100)
                .animation(.easeInOut(duration: 3))
            
            Button(action: {
                self.isAnimating.toggle()
            }) {
                Text("Rock and roll")
            }
        }
    }
}

18、VideoPlayer播放视频素材

image.png

import SwiftUI
import AVKit

struct ContentView : View
{
    var body: some View
    {
        VideoPlayer(player: AVPlayer(url:
        URL(fileURLWithPath: Bundle.main.path(forResource: "movie", ofType: "mp4")!) as URL))
        {
            Text("Watermark of video")
        }
        .padding()
        .background(Color.black)
        .ignoresSafeArea()
    }
}

19、实现过渡动画 -- matchedGeometryEffect

image.png

import SwiftUI
import AVKit

struct ContentView: View
{
    @Namespace private var animation
    @State private var isZoom = false
    
    var body: some View
    {
        VStack(alignment: .leading, spacing:10)
        {
            if isZoom
            {
                VideoPlayer(player: AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: "movie", ofType: "mp4")!) as URL))
                    .frame(height:300)
                    .matchedGeometryEffect(id: "VideoPlayer", in: animation)
                
                Text("Colorful waterfall")
                    .font(.title)
                    .padding(.leading, 10)
                    .matchedGeometryEffect(id: "VideoTitle", in: animation)
                    .onTapGesture
                    {
                        withAnimation(.easeOut(duration: 0.3))
                        {
                            self.isZoom.toggle()
                        }
                    }
                
                Text("The colorful waterfall is located in the green forest not far from the Zizi Cliff. It plunges from the high granite and flows along the red rock of the scales. \n\nThe layers of water are sprinkled like feathers, reflecting the red rock. \n\nA flowing white silk soft satin is lining the red material, and the color is very rich. \n\nIf you encounter sunlight, you can see the rainbow appear in the mist, making people forget to return.")
                    .padding(.leading, 10)
                    .foregroundColor(.gray)
                    .matchedGeometryEffect(id: "VideoReview", in: animation)
                
                Spacer()
            }
            else
            {
                Spacer()
                
                Text("""
Review of Diamond Waterfalls
Reviewed March 13, 2021\n
Buried in Diamond Botanical Park, Diamond Falls displays the minerals of the upstream sulfur hot springs. The various minerals stain the rock leaving behind reds, ochers, and other earth tones of color. Obviously some visitors did not appreciate it but we found it very interesting and quite photogenic.\n

Date of experience: January 2021\n
""")
                    .padding(.leading, 10)
                    .font(.title2)
                    .foregroundColor(.purple)
                    .matchedGeometryEffect(id: "VideoReview", in: animation)
                
                HStack
                {
                    VideoPlayer(player: AVPlayer(url: URL(fileURLWithPath: Bundle.main.path(forResource: "movie", ofType: "mp4")!) as URL))
                        .frame(width:80, height:50)
                        .matchedGeometryEffect(id: "VideoPlayer", in: animation)
                    
                    Text("Colorful waterfall")
                        .matchedGeometryEffect(id: "VideoTitle", in: animation)
                        .onTapGesture
                        {
                            withAnimation(.easeOut(duration: 0.3))
                            {
                                self.isZoom.toggle()
                            }
                        }
                    
                    Spacer()
                }
                .padding(10)
                .background(Color.gray.opacity(0.3))
            }
        }
    }
}