Swiftgram/submodules/Display/Source/ShakeAnimation.swift
2025-07-19 12:51:42 +01:00

89 lines
3.5 KiB
Swift

import Foundation
import UIKit
// Incuding at least one Objective-C class in a swift file ensures that it doesn't get stripped by the linker
private final class LinkHelperClass: NSObject {
}
public extension CALayer {
func addShakeAnimation(amplitude: CGFloat = 3.0, duration: Double = 0.3, count: Int = 4, decay: Bool = false) {
let k = Float(UIView.animationDurationFactor())
var speed: Float = 1.0
if k != 0 && k != 1 {
speed = Float(1.0) / k
}
let animation = CAKeyframeAnimation(keyPath: "position.x")
var values: [CGFloat] = []
values.append(0.0)
for i in 0 ..< count {
let sign: CGFloat = (i % 2 == 0) ? 1.0 : -1.0
let multiplier = decay ? 1.0 / CGFloat(i + 1) : 1.0
values.append(amplitude * sign * multiplier)
}
values.append(0.0)
animation.values = values.map { ($0 as NSNumber) as AnyObject }
var keyTimes: [NSNumber] = []
for i in 0 ..< values.count {
if i == 0 {
keyTimes.append(0.0)
} else if i == values.count - 1 {
keyTimes.append(1.0)
} else {
keyTimes.append((Double(i) / Double(values.count - 1)) as NSNumber)
}
}
animation.keyTimes = keyTimes
animation.speed = speed
animation.duration = duration
animation.isAdditive = true
self.add(animation, forKey: "shake")
}
func addReorderingShaking() {
func degreesToRadians(_ x: CGFloat) -> CGFloat {
return .pi * x / 180.0
}
let duration: Double = 0.4
let displacement: CGFloat = 1.0
let degreesRotation: CGFloat = 2.0
let negativeDisplacement = -1.0 * displacement
let position = CAKeyframeAnimation.init(keyPath: "position")
position.beginTime = 0.8
position.duration = duration
position.values = [
NSValue(cgPoint: CGPoint(x: negativeDisplacement, y: negativeDisplacement)),
NSValue(cgPoint: CGPoint(x: 0, y: 0)),
NSValue(cgPoint: CGPoint(x: negativeDisplacement, y: 0)),
NSValue(cgPoint: CGPoint(x: 0, y: negativeDisplacement)),
NSValue(cgPoint: CGPoint(x: negativeDisplacement, y: negativeDisplacement))
]
position.calculationMode = .linear
position.isRemovedOnCompletion = false
position.repeatCount = Float.greatestFiniteMagnitude
position.beginTime = CFTimeInterval(Float(arc4random()).truncatingRemainder(dividingBy: Float(25)) / Float(100))
position.isAdditive = true
let transform = CAKeyframeAnimation.init(keyPath: "transform")
transform.beginTime = 2.6
transform.duration = 0.3
transform.valueFunction = CAValueFunction(name: CAValueFunctionName.rotateZ)
transform.values = [
degreesToRadians(-1.0 * degreesRotation),
degreesToRadians(degreesRotation),
degreesToRadians(-1.0 * degreesRotation)
]
transform.calculationMode = .linear
transform.isRemovedOnCompletion = false
transform.repeatCount = Float.greatestFiniteMagnitude
transform.isAdditive = true
transform.beginTime = CFTimeInterval(Float(arc4random()).truncatingRemainder(dividingBy: Float(25)) / Float(100))
self.add(position, forKey: "shaking_position")
self.add(transform, forKey: "shaking_rotation")
}
}