在iOS系统弹性动效的API接口
+ (void)animateWithDuration:(NSTimeInterval)duration
delay:(NSTimeInterval)delay
usingSpringWithDamping:(CGFloat)dampingRatio
initialSpringVelocity:(CGFloat)velocity
options:(UIViewAnimationOptions)options
animations:(void (^)(void))animations
completion:(void (^ __nullable)(BOOL finished))completion API_AVAILABLE(ios(7.0));
参数为duration(时长),dampingRatio(弹性系数),velocity (初始速度);
并没有friction(摩擦力),tension(拉力),mass(质量)参数。
如何将friction,tension转化为duration,dampingRatio,velocity呢?
在著名App动效设计软件Framer的开源库Framer的开源库,找到以下转换代码(CoffeeScript):
epsilon = 0.001
minDuration = 0.01
maxDuration = 10.0
minDamping = Number.MIN_VALUE
maxDamping = 1
# Newton's method
approximateRoot = (func, derivative, initialGuess, times=12) ->
result = initialGuess
for i in [1...times]
result = result - func(result) / derivative(result)
return result
angularFrequency = (undampedFrequency, dampingRatio) ->
undampedFrequency * Math.sqrt(1 - Math.pow(dampingRatio, 2))
exports.computeDampingRatio = computeDampingRatio = (tension, friction, mass = 1) ->
friction / (2 * Math.sqrt(mass * tension))
# Tries to compute the duration of a spring,
# but can't for certain velocities and if dampingRatio >= 1
# In those cases it will return null
exports.computeDuration = (tension, friction, velocity = 0, mass = 1) ->
dampingRatio = computeDampingRatio(tension, friction)
undampedFrequency = Math.sqrt(tension / mass)
# This is basically duration extracted out of the envelope functions
if dampingRatio < 1
a = Math.sqrt(1 - Math.pow(dampingRatio, 2))
b = velocity / (a * undampedFrequency)
c = dampingRatio / a
d = - ((b - c) / epsilon)
if d <= 0
return null
duration = Math.log(d) / (dampingRatio * undampedFrequency)
else
return null
return duration
exports.computeDerivedCurveOptions = (dampingRatio, duration, velocity = 0, mass = 1) ->
dampingRatio = Math.max(Math.min(dampingRatio, maxDamping), minDamping)
duration = Math.max(Math.min(duration, maxDuration), minDuration)
if dampingRatio < 1
envelope = (undampedFrequency) ->
exponentialDecay = undampedFrequency * dampingRatio
currentDisplacement = exponentialDecay * duration
a = (exponentialDecay) - velocity
b = angularFrequency(undampedFrequency, dampingRatio)
c = Math.exp(-currentDisplacement)
return epsilon - (a / b) * c
derivative = (undampedFrequency) ->
exponentialDecay = undampedFrequency * dampingRatio
currentDisplacement = exponentialDecay * duration
d = currentDisplacement * velocity + velocity
e = Math.pow(dampingRatio, 2) * Math.pow(undampedFrequency, 2) * duration
f = Math.exp(-currentDisplacement)
g = angularFrequency(Math.pow(undampedFrequency, 2), dampingRatio)
factor = if (- envelope(undampedFrequency) + epsilon) > 0 then -1 else 1
return factor * ((d - e) * f) / g
else
envelope = (undampedFrequency) ->
a = Math.exp(-undampedFrequency * duration)
b = (undampedFrequency - velocity) * duration + 1
return -epsilon + a * b
derivative = (undampedFrequency) ->
a = Math.exp(-undampedFrequency * duration)
b = (velocity - undampedFrequency) * Math.pow(duration, 2)
return a * b
result =
tension: 100
friction: 10
velocity: velocity
initialGuess = 5 / duration
undampedFrequency = approximateRoot(envelope, derivative, initialGuess)
unless isNaN(undampedFrequency)
result.tension = Math.pow(undampedFrequency, 2) * mass
result.friction = dampingRatio * 2 * Math.sqrt(mass * result.tension)
return result
简化并转化为oc代码后
+ (void)animateWithFriction:(CGFloat)friction
Tension:(CGFloat)tension
delay:(NSTimeInterval)delay
animations:(void (^)(void))animations
completion:(void (^ __nullable)(BOOL finished))completion {
//初始速度默认为1
[UIView animateWithFriction:friction
Tension:tension
mass:1
delay:delay
initialSpringVelocity:0
options:0
animations:animations
completion:completion];
}
+ (void)animateWithFriction:(CGFloat)friction
Tension:(CGFloat)tension
mass:(CGFloat)mass
delay:(NSTimeInterval)delay
initialSpringVelocity:(CGFloat)velocity
options:(UIViewAnimationOptions)options
animations:(void (^)(void))animations completion:(void (^ __nullable)(BOOL finished))completion {
CGFloat damping = friction / sqrt(2 * (1 * tension));
CGFloat undampedFrequency = sqrt(tension / mass);
CGFloat epsilon = 0.001;
NSTimeInterval duration = 0;
if (damping < 1) {
CGFloat a = sqrt(1 - pow(damping, 2));
CGFloat b = velocity / (a * undampedFrequency);
CGFloat c = damping / a;
CGFloat d = -((b - c) / epsilon);
if (d > 0) {
duration = log(d) / (damping * undampedFrequency);
}
}
[UIView animateWithDuration:duration
delay:delay
usingSpringWithDamping:damping
initialSpringVelocity:velocity
options:options
animations:animations
completion:completion];
}
设计输出的pixate文件的动效可以直接方便在iOS上编码实现。