序
UIView的动画,没法半路取消,逆向,有时候确实有这样的需求.
来源
写了个swift版本的AnimationController,用于控制动画前进后退暂停.
//
// AnimationController.swift
// SwiftuiCli
//
// Created by hccc on 2023/12/11.
//
import Foundation
import RxCocoa
import RxSwift
private func clampValue(x: CGFloat, min: CGFloat, max: CGFloat) -> CGFloat {
// assert(min <= max && !max.isNaN && !min.isNaN);
if x < min {
return min
}
if x > max {
return max
}
return x
}
class AnimationController: NSObject {
enum AnimationDirection {
case forward
case reverse
}
enum AnimationStatus {
case dismissed
case forward
case reverse
case completed
}
struct AnimationProgress {
// 0~1进度
var progress: CGFloat
// 当前进度时间
var currentDuration: CGFloat
// 当前状态
var status: AnimationStatus
// 当前值
var value: CGFloat
}
static let kDefault: Int = 1000 / 25
/// 毫秒step值
var step: Int = kDefault
/// 毫秒结算
var duration: CGFloat = 1000
var timer: Disposable?
var value: CGFloat = 0
var lowerBound: CGFloat = 0
var upperBound: CGFloat = 1
var curve: CurveProtocol = Curves.linear
var processListener = PublishSubject<AnimationProgress>()
private var direction: AnimationDirection = .forward
private var progress: CGFloat = 0
private var status: AnimationStatus = .dismissed
let disposeBag = DisposeBag()
var isAniamting: Bool { timer != nil }
init(duration: CGFloat,
lowerBound: CGFloat = 0,
upperBound: CGFloat = 1,
curve: CurveProtocol = Curves.linear)
{
super.init()
self.duration = duration
self.lowerBound = lowerBound
self.upperBound = upperBound
self.curve = curve
}
func forward() {
direction = .forward
_startTimer()
}
func reverser() {
direction = .reverse
_startTimer()
}
func pause() {
stop()
}
func resume() {
_startTimer()
}
private func _startTimer() {
stop()
print(Date().timeIntervalSince1970)
timer = Observable<Int>
.interval(.milliseconds(step), scheduler: MainScheduler.asyncInstance)
.take(until: rx.deallocated)
.subscribe(onNext: { [weak self] _ in
self?._doStep()
})
}
private func _doStep() {
switch direction {
case .forward:
progress = progress + (CGFloat(step) / duration)
case .reverse:
progress = progress - (CGFloat(step) / duration)
}
// 1进度
progress = clampValue(x: progress, min: 0, max: 1)
value = curve.transform(startValue: lowerBound, endValue: upperBound, time: progress)
// 2当前时间
// value = InterpolationDuration(
// begin: lowerBound,
// end: upperBound
// )
// .clamp(process: progress)
// 3状态
let status: AnimationStatus
if value == lowerBound {
status = .dismissed
} else if value == upperBound {
status = .completed
} else {
if direction == .forward {
status = .forward
} else {
status = .reverse
}
}
// print(progress, value)
// 进度
processListener.onNext(
AnimationProgress(
progress: progress,
currentDuration: progress * duration,
status: status,
value: value
)
)
if value >= upperBound || value <= lowerBound {
stop()
print(Date().timeIntervalSince1970)
}
}
func stop() {
if isAniamting {
timer?.dispose()
timer = nil
}
}
}
protocol CurveProtocol {
func transform(startValue: CGFloat, endValue: CGFloat, time: CGFloat) -> CGFloat
}
enum Curves: CurveProtocol {
case linear
case easyIn
case easyOut
case easyInOut
func transform(startValue: CGFloat, endValue: CGFloat, time: CGFloat) -> CGFloat {
switch self {
case .linear:
return startValue + (endValue - startValue) * time
case .easyIn:
return startValue + (endValue - startValue) * pow(time, 3)
case .easyOut:
let t = 1 - time
return startValue + (endValue - startValue) * (1 - pow(t, 3))
case .easyInOut:
return startValue + (endValue - startValue) * (0.5 - 0.5 * cos(time * .pi))
}
}
}
前段时间写的,代码也是十分的浅显易懂,当然也有一定的优化空间,比如timer的执行队列等等,有需要的人自取即可.
PS
这种什么都不用说明,直接水一篇文章的感觉真爽.
结尾
后面开始不会太过频繁更新,准备写一个flutter工具集,做个自己的模板项目,收拾心情准备找工作.