SwiftUI 动画

import SwiftUI

struct Demo01: View {
    @State private var enable = false
    var body: some View {
        demo01
//        demo02
//        demo03
//        demo04
//        demo05
//        demo06
//        demo07
    }
    
    var demo01 : some View{
        VStack(alignment: .center, spacing: 0) {
//            Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
//                .font(.title)
//                .padding()
//            Text("Animation")
//                .font(.title2)
//                .foregroundColor(.red)
//                .fontWeight(.bold)
//                .scaleEffect(enable ? 3.0 : 1.0)
//                .animation(.default.repeatForever())//repeatForever永远重复
//                .padding()
            Image(systemName: "heart.fill")
                .foregroundColor(.red)
                .font(.largeTitle)
                .scaleEffect(enable ? 5.0 : 1.0)
                .animation(
                    .timingCurve(0.76, 0, 0.24, 1,duration: 1)
                        .repeatForever()
                )
        }
        .onAppear {
            enable = true
        }
    }
    //隐式声明动画
    var demo02 : some View{
        VStack(spacing: 50){
            RoundedRectangle(cornerRadius: enable ? 200 : 0)
//                .fill(enable ? .red : .green)
//                .opacity(enable ? 1.0 : 0.3)//透明度
//                .animation(.default)
//                .animation(.linear)
//                .animation(.easeIn)
//                .animation(.easeOut)
//                .animation(.easeInOut)
//                .animation(.spring())
//                .animation(.timingCurve(0.87, 0, 0.13, 1,duration: 1))//时间曲线 easings.net
//                .animation(.interactiveSpring())
                .animation(.interpolatingSpring(mass: 0.7, stiffness: 200, damping: 5, initialVelocity: 4))//弹性动画 kiteapp.co
                .frame(width: 300, height: 300, alignment: .center)
            Button {
                enable.toggle()
            } label: {
                Text("执行动画")
            }

        }
    }
    //显式声明动画
    @State private var state1 = false
    @State private var state2 = false
    @State private var state3 = false
    var demo03 : some View {
        VStack(spacing: 50){
            RoundedRectangle(cornerRadius: state1 ? 200 : 0)
                .fill(state2 ? .red : .green)
                .opacity(state3 ? 1.0 : 0.3)//透明度
                .frame(width: 300, height: 300, alignment: .center)
            Button {
                //显示的声明动画
                withAnimation (.linear) {
                    state3.toggle()
                }
                state2.toggle()
                state1.toggle()
            } label: {
                Text("执行动画")
            }

        }
    }
    
    var demo04 : some View {
        VStack(spacing: 50){
            RoundedRectangle(cornerRadius: enable ? 200 : 0)
                .fill(enable ? .red : .green)
                .opacity(enable ? 1.0 : 0.3)//透明度
                .animation(
                    .linear
//                        .delay(5)//延迟5秒执行动画
//                        .repeatCount(7)//动画执行次数
                        .repeatForever()//永远重复执行
//                        .repeatForever(autoreverses: false)//不返回执行
                        .speed(3)//动画执行速度
                )
                .frame(width: 300, height: 300, alignment: .center)
            
            Button {
                enable.toggle()
            } label: {
                Text("执行动画")
            }

        }
    }
    
    @State private var isShow = true
    var demo05 : some View{
        Form{
            HStack {
                if isShow{
                    Text("标题1")
                }
                Spacer()
                Toggle(isOn: $isShow) {
                    Text("")
                }
                .onChange(of: isShow) { newValue in
                    withAnimation {
                        isShow.toggle()
                    }
                }
                .labelsHidden()
            }
            HStack {
                if isShow{
                    Text("标题2")
                }
                Spacer()
                Toggle(isOn: $isShow.animation(.linear)) {
                    Text("")
                }
                .labelsHidden()
            }
        }
    }
    
    
    @State private var state4 = false
    var width : CGFloat {
        state4 ? 100 : 50
    }
    var demo06 : some View {
        VStack(spacing : 50) {
            HStack {
                Text("color")
                Spacer()
                Rectangle()
                    .fill(state4 ? .red : .green)
                    .frame(width: 100, height: 100)
            }
            
            HStack {
                Text("size")
                Spacer()
                Rectangle()
                    .fill(.purple)
                    .frame(width: width, height: width)
                Rectangle()
                    .fill(.yellow)
                    .frame(width: 100, height: 100)
                    .scaleEffect(state4 ? 1 : 0.5)
            }
            HStack {
                Text("opacity")
                Spacer()
                Rectangle()
                    .fill(.blue)
                    .frame(width: 100, height: 100)
                    .opacity(state4 ? 1.0 : 0)
            }
            HStack {
                Text("border")
                Spacer()
                Rectangle()
                    .fill(.orange)
                    .frame(width: 100, height: 100, alignment: .center)
                    .border(.green,width: state4 ? 10 : 2)
                Circle()
                    .fill(.orange)
                    .frame(width: 100, height: 100, alignment: .center)
                    .overlay(
                        Circle()
                            .strokeBorder(.blue,lineWidth: state4 ? 10 : 2)
                    )
            }
            HStack {
                Text("opacity")
                Spacer()
                Rectangle()
                    .fill(.blue)
                    .frame(width: 100, height: 100)
                    .cornerRadius(state4 ? 30 : 0)
            }
            HStack {
                Text("opacity")
                Spacer()
                Rectangle()
                    .fill(.red)
                    .frame(width: 100, height: 100)
                    .rotationEffect(.degrees(state4 ? 360 : 0))
                    
            }
            
        }
        .animation(.linear(duration: 2).repeatForever())
        .padding()
        .font(.title)
        .onAppear {
            state4 = true
        }
    }
    
    let letters = Array("阿巴阿巴阿巴阿巴阿巴")
    @State private var dragPosition : CGSize = .zero
    var demo07 : some View {
        HStack {
            ForEach(0..<letters.count){
                Text(String(letters[$0]))
                    .padding(5)
                    .font(.title)
                    .textCase(.uppercase)
                    .background(state4 ? Color.blue : Color.red)
                    .offset(dragPosition)
                    .animation(.linear.delay(Double($0)/20))
            }
        }
        .gesture(
                DragGesture()
                    .onChanged({ po in
                        self.dragPosition = po.translation
                    })
                    .onEnded({ _ in
                        self.dragPosition = .zero
                        self.state4.toggle()
                    })
//                    .onChanged{
//                        self.dragPosition = $0.translation
//                    }
//                    .onEnded{_ in
//                        self.dragPosition = .zero
//                        self.state4.toggle()
//                    }
        )
    }
}


struct Demo02: View {
    var body: some View {
//        demo01//水波纹开始按钮
//        demo02//点赞红心效果
//        demo03//圆圈加载loading
//        demo04//圆形进度条
//        demo05//条形进度条
//        demo06//跑马灯
//        demo07//Transition通过combined组合动画(preview效果不如模拟器效果好)
        demo08//Transition自定义扩展动画
    }
    
    
    @State private var state1 = false
    var demo01 : some View {
        Button("开始") {
            state1 = true
        }
        .padding(50)
        .background(Color.red)
        .foregroundColor(Color.white)
        .clipShape(Circle())
        .overlay(
            Circle()
                .stroke(Color.red)
                .scaleEffect(state1 ? 2.0 : 1.0)
                .opacity(state1 ? 0 : 1)
                .animation(.easeInOut(duration: 1).repeatForever(autoreverses: false))
        )
    }
    
    var demo02 : some View {
        Image(systemName: "heart.fill")
            .font(.title)
            .foregroundColor(.red)
            .scaleEffect(state1 ? 3 : 1)
            .opacity(state1 ? 1 : 0.2)
            .animation(.interpolatingSpring(mass: 0.7, stiffness: 200, damping: 10, initialVelocity: 4).repeatForever(autoreverses: false))
            .onAppear {
                state1 = true
            }
    }
    
    let strokestyle = StrokeStyle(lineWidth:2,lineCap: .round)
    var demo03 : some View {
        VStack{
//            ProgressView()
            demo03_1
            
        }
        .frame(width: 30, height: 30, alignment: .center)
        .padding()
        .background(Color.white)
        .shadow(radius: 3.0)
    }
    var demo03_1 : some View {
        VStack{
            Circle()
                .trim(from: 0.0, to: 0.7)
                .stroke(
                    AngularGradient(gradient: Gradient(colors: [Color.gray,Color.gray.opacity(0.5)]), center: .center),style: strokestyle)
                .rotationEffect(.degrees(state1 ? 360.0 : 0.0))
                .animation(.linear(duration: 0.7).repeatForever(autoreverses: false))
                .onAppear {
                    state1.toggle()
                }
        }
    }
    
    let timer = Timer.publish(every: 1, on: RunLoop.main, in: .common).autoconnect()
    @State private var progress : CGFloat = 0
    var demo04 : some View{
        ZStack {
            Circle()
                .stroke(Color.gray.opacity(0.3),style: strokestyle)
            Circle()
                .trim(from: 0.0, to: self.progress)
                .stroke(Color.gray,style: strokestyle)
                .animation(.default)
                .rotationEffect(.degrees(-90))
                .onReceive(timer) { _ in
                    self.progress += 0.1
                    if self.progress > 1 {
                        self.progress = 0
                    }
                }
        }
        .frame(width: 30, height: 30, alignment: .center)
        .padding()
        .background(Color.white)
        .shadow(radius: 3.0)
    }
    
    
    @State private var barWidth : CGFloat = 300.0
    @State private var progressWidth : CGFloat = 0.0
    var demo05 : some View {
        ZStack {
            Capsule()
                .stroke(Color.gray)
                .frame(width: barWidth, height: 20)
                .overlay(
                    VStack(alignment: .leading) {
                        Capsule()
                        .fill(Color.gray)
                        .frame(width: progressWidth, height: 15, alignment: .leading)
                        .padding(.horizontal,5)
                        .animation(.linear)
                    }
                        .frame(width: barWidth, height: 20, alignment: .leading)
                )
                .onReceive(timer) { _ in
                    self.progressWidth += 5
                    if self.progressWidth >= barWidth{
                        self.progressWidth = 0.0
                    }
                }
        }
        
    }
    
    @State private var scrollText = false
    var demo06 : some View {
        Text("好难啊,秃头了,阿巴阿巴阿巴")
            .foregroundColor(.red)
            .offset(x: scrollText ? 400 : 0)
            .animation(.linear(duration: 30).speed(10).repeatForever(autoreverses: false))
//            .frame(maxWidth: .infinity, alignment: .leading)
            .onAppear {
                scrollText.toggle()
            }
    }
    
    
    @State private var showText = true
    var demo07 : some View {
        Button {
            withAnimation {
                showText.toggle()
            }
        } label: {
            ZStack {
                Rectangle()
                    .fill(Color.clear)
                    .frame(width: 150, height: 60)
                    .border(.red,width: 4)
                if showText {
                    Text("按钮")
                        .font(.largeTitle)
                        .transition(.move(edge: .top).combined(with: .opacity.combined(with: .scale)))
                }
            }
        }

    }
    
    var demo08 : some View {
        Button {
            withAnimation {
                showText.toggle()
            }
        } label: {
            ZStack {
                Rectangle()
                    .fill(Color.clear)
                    .frame(width: 150, height: 60)
                    .border(.red,width: 4)
                if showText {
                    Text("按钮")
                        .font(.largeTitle)
                        .transition(.moveInScaleOut)
//                        .transition(.moveWithFade)
                }
            }
        }
    }
}

extension AnyTransition {
    static var moveWithFade : AnyTransition {
        return AnyTransition.move(edge: .top).combined(with: .opacity).combined(with: .scale)
    }
}

extension AnyTransition {
    static var moveInScaleOut : AnyTransition {
        let insertion = AnyTransition.move(edge: .top).combined(with: .opacity)
        let removal = AnyTransition.scale.combined(with: .opacity)
        return AnyTransition.asymmetric(insertion: insertion, removal: removal)
    }
}

struct Demo01_Previews: PreviewProvider {
    static var previews: some View {
//        Demo01()
        Demo02()
    }
}


posted @ 2021-07-22 16:05  我不爱吃鱼  阅读(181)  评论(0编辑  收藏  举报
Live2D