This commit is contained in:
2026-09-24 16:25:22 +08:00
commit 7428184f01
1198 changed files with 314515 additions and 0 deletions
@@ -0,0 +1,307 @@
import UIKit
import QuartzCore
//
class AnimationDelegate: NSObject, CAAnimationDelegate {
var start: (() -> Void)?
var end: (() -> Void)?
init(start: (() -> Void)?, end: (() -> Void)?) {
self.start = start
self.end = end
}
func animationDidStart(_ anim: CAAnimation) {
start?()
}
func animationDidStop(_ anim: CAAnimation, finished flag: Bool) {
end?()
}
}
// CGFloat
extension CGFloat {
func toRadians() -> CGFloat {
return self * .pi / 180.0
}
}
class xdAnimation {
//
static func translate(
view: UIView,
duration: TimeInterval = 1.0,
delay: TimeInterval = 0,
translation: Int = 0, // 0x,1y,2x,y
fromLen: CGFloat = 0,
len: CGFloat = 0,
loop: Int = 0,
start: (() -> Void)? = nil,
end: (() -> Void)? = nil
) {
let timingFunction: CAMediaTimingFunction = CAMediaTimingFunction(name: .easeInEaseOut)
//
let animation: CAPropertyAnimation
switch translation {
case 1: // Y
let basicAnimation = CABasicAnimation(keyPath: "transform.translation.y")
basicAnimation.fromValue = fromLen
basicAnimation.toValue = len
animation = basicAnimation
case 2: // XY
let keyframeAnimation = CAKeyframeAnimation(keyPath: "position")
let path = CGMutablePath()
let startPoint = CGPoint(x: view.frame.origin.x + fromLen, y: view.frame.origin.y + fromLen)
let endPoint = CGPoint(x: view.frame.origin.x + len, y: view.frame.origin.y + len)
path.move(to: startPoint)
path.addLine(to: endPoint)
keyframeAnimation.path = path
animation = keyframeAnimation
default: // X
let basicAnimation = CABasicAnimation(keyPath: "transform.translation.x")
basicAnimation.fromValue = fromLen
basicAnimation.toValue = len
animation = basicAnimation
}
//
animation.duration = duration
animation.beginTime = CACurrentMediaTime() + delay
animation.timingFunction = timingFunction // 线
animation.isRemovedOnCompletion = false //
animation.fillMode = .forwards //
//
if loop == -1 {
animation.repeatCount = .infinity
} else {
animation.repeatCount = Float(loop)
}
//
CATransaction.begin()
CATransaction.setCompletionBlock {
end?()
}
start?()
view.layer.add(animation, forKey: "translation")
CATransaction.commit()
}
static func rotate(view: UIView,
duration: TimeInterval = 1.0,
delay: TimeInterval = 0.0,
fromDeg: CGFloat = 0.0,
deg: CGFloat = 360.0,
loop: Int = 0,
start: (() -> Void)? = nil,
end: (() -> Void)? = nil) {
//
let timingFunction: CAMediaTimingFunction = CAMediaTimingFunction(name: .linear)
let rotation = CABasicAnimation(keyPath: "transform.rotation")
rotation.fromValue = fromDeg * .pi / 180.0 //
rotation.toValue = deg * .pi / 180.0 //
rotation.duration = duration
rotation.beginTime = CACurrentMediaTime() + delay
rotation.isCumulative = true
rotation.timingFunction = timingFunction // 线
rotation.isRemovedOnCompletion = false //
rotation.fillMode = .forwards //
//
if loop == -1 {
rotation.repeatCount = .infinity
} else {
rotation.repeatCount = Float(loop)
}
//
if let startAction = start {
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
startAction()
}
}
// 使 CATransaction
CATransaction.begin()
//
CATransaction.setCompletionBlock {
end?()
}
//
view.layer.add(rotation, forKey: "rotationAnimation")
//
CATransaction.commit()
}
//
static func scale(view: UIView,
duration: TimeInterval = 1.0,
delay: TimeInterval = 0.0,
direction: Int = 0,
fromScale: CGFloat = 0.0,
scale: CGFloat = 1.0,
loop: Int = 0,
start: (() -> Void)? = nil,
end: (() -> Void)? = nil) {
//
var scaleAnimations = [CABasicAnimation]()
switch direction {
case 1: // (Y)
let scaleY = CABasicAnimation(keyPath: "transform.scale.y")
scaleY.fromValue = fromScale
scaleY.toValue = scale
scaleAnimations = [scaleY]
case 2: // XY
let scaleX = CABasicAnimation(keyPath: "transform.scale.x")
scaleX.fromValue = fromScale
scaleX.toValue = scale
let scaleY = CABasicAnimation(keyPath: "transform.scale.y")
scaleY.fromValue = fromScale
scaleY.toValue = scale
scaleAnimations = [scaleX, scaleY]
default: // (X)
let scaleX = CABasicAnimation(keyPath: "transform.scale.x")
scaleX.fromValue = fromScale
scaleX.toValue = scale
scaleAnimations = [scaleX]
}
//
for animation in scaleAnimations {
animation.duration = duration
animation.beginTime = CACurrentMediaTime() + delay
animation.isCumulative = true
animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) // 线
animation.isRemovedOnCompletion = false //
animation.fillMode = .forwards //
if loop == -1 {
animation.repeatCount = .infinity
} else {
animation.repeatCount = Float(loop)
}
}
// 使 CATransaction
CATransaction.begin()
//
if let startAction = start {
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
startAction()
}
}
//
CATransaction.setCompletionBlock {
end?()
}
//
var i=0
for animation in scaleAnimations {
view.layer.add(animation, forKey: "scaleAnimation"+i.toString())
i+=1;
}
//
CATransaction.commit()
}
private func deg2rad(_ degree: CGFloat) -> CGFloat {
return degree * .pi / 180
}
// 3D
static func flip3D(
view: UIView,
duration: TimeInterval = 1.0,
delay: TimeInterval = 0.0,
axis: Int = 1,
fromDeg: CGFloat = 0.0, //
deg: CGFloat = 180.0, //
loop: Int = 0,
cameraDistance: CGFloat = 8.0,
scaleZ: CGFloat = 0.8,
start: (() -> Void)? = nil,
end: (() -> Void)? = nil) {
let adjustedDeg: CGFloat = deg
// 3D
var transform = CATransform3DIdentity
transform.m34 = -1.0 / cameraDistance
view.layer.transform = transform
//
var rotationTransform: CATransform3D
if axis == 1 { // Y
rotationTransform = CATransform3DMakeRotation(fromDeg.toRadians(), 0, 1, 0)
} else { // X
rotationTransform = CATransform3DMakeRotation(fromDeg.toRadians(), 1, 0, 0)
}
//
view.layer.transform = rotationTransform
//
UIView.animate(
withDuration: duration,
delay: delay,
options: [.curveLinear],
animations: {
start?() //
//
let finalTransform: CATransform3D
if axis == 1 { // Y
finalTransform = CATransform3DMakeRotation(adjustedDeg.toRadians(), 0, 1, 0)
} else { // X
finalTransform = CATransform3DMakeRotation(adjustedDeg.toRadians(), 1, 0, 0)
}
// Z
let scaleTransform = CATransform3DScale(finalTransform, 1, 1, scaleZ)
view.layer.transform = scaleTransform
},
completion: { finished in
if loop > 1 {
//
flip3D(
view: view,
duration: duration,
delay: delay,
axis: axis,
fromDeg: adjustedDeg,
deg: fromDeg,
loop: loop - 1,
cameraDistance: cameraDistance,
scaleZ: scaleZ,
start: start,
end: end
)
} else {
end?() //
}
}
)
}
}