Bounce experiment

This commit is contained in:
Isaac
2025-10-04 00:44:30 +08:00
parent 1ff56084fe
commit 4b04a6c69e
7 changed files with 165 additions and 19 deletions

View File

@@ -138,6 +138,10 @@ private extension CALayer {
}
}
private func bounceParameters(duration: Double) -> (duration: Double, damping: CGFloat, stiffness: CGFloat) {
return (duration: duration * 1.25, damping: 88.0, stiffness: 750.0)
}
public extension ContainedViewLayoutTransition {
func animation() -> CABasicAnimation? {
switch self {
@@ -469,6 +473,106 @@ public extension ContainedViewLayoutTransition {
}
}
func updatePositionSpring(layer: CALayer, position: CGPoint, completion: ((Bool) -> Void)? = nil) {
if layer.position.equalTo(position) {
completion?(true)
} else {
switch self {
case .immediate:
layer.removeAnimation(forKey: "position")
if let view = layer.delegate as? UIView {
view.center = position
} else {
layer.position = position
}
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
let _ = curve
let previousPosition = layer.position
if let view = layer.delegate as? UIView {
view.center = position
} else {
layer.position = position
}
let params = bounceParameters(duration: duration)
layer.animateSpring(from: NSValue(cgPoint: previousPosition), to: NSValue(cgPoint: position), keyPath: "position", duration: params.duration, stiffness: params.stiffness, damping: params.damping, completion: { flag in
if let completion {
completion(flag)
}
})
}
}
}
func updateScaleSpring(layer: CALayer, scale: CGFloat, completion: ((Bool) -> Void)? = nil) {
let t = layer.transform
let currentScale = sqrt((t.m11 * t.m11) + (t.m12 * t.m12) + (t.m13 * t.m13))
if abs(CGFloat(currentScale) - scale) <= CGFloat(Float.ulpOfOne) {
completion?(true)
} else {
switch self {
case .immediate:
layer.removeAnimation(forKey: "transform.scale")
if let view = layer.delegate as? UIView {
view.transform = CGAffineTransformMakeScale(scale, scale)
} else {
layer.transform = CATransform3DMakeScale(scale, scale, 1.0)
}
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
let _ = curve
if let view = layer.delegate as? UIView {
view.transform = CGAffineTransformMakeScale(scale, scale)
} else {
layer.transform = CATransform3DMakeScale(scale, scale, 1.0)
}
let params = bounceParameters(duration: duration)
layer.animateSpring(from: currentScale as NSNumber, to: scale as NSNumber, keyPath: "transform.scale", duration: params.duration, stiffness: params.stiffness, damping: params.damping, completion: { flag in
if let completion {
completion(flag)
}
})
}
}
}
func updateBoundsSpring(layer: CALayer, bounds: CGRect, completion: ((Bool) -> Void)? = nil) {
if layer.bounds.equalTo(bounds) {
completion?(true)
} else {
switch self {
case .immediate:
layer.removeAnimation(forKey: "bounds")
if let view = layer.delegate as? UIView {
view.bounds = bounds
} else {
layer.bounds = bounds
}
if let completion = completion {
completion(true)
}
case let .animated(duration, curve):
let _ = curve
let previousBounds = layer.bounds
if let view = layer.delegate as? UIView {
view.bounds = bounds
} else {
layer.bounds = bounds
}
let params = bounceParameters(duration: duration)
layer.animateSpring(from: NSValue(cgRect: previousBounds), to: NSValue(cgRect: bounds), keyPath: "bounds", duration: params.duration, stiffness: params.stiffness, damping: params.damping, completion: { result in
if let completion = completion {
completion(result)
}
})
}
}
}
func updateAnchorPoint(layer: CALayer, anchorPoint: CGPoint, force: Bool = false, completion: ((Bool) -> Void)? = nil) {
if layer.anchorPoint.equalTo(anchorPoint) && !force {
completion?(true)