-
Notifications
You must be signed in to change notification settings - Fork 18
1.1 曲线和弹性动画的定义与开发
设计者非常在意动画的曲线实现问题,因为这是使用感觉和动画体验的核心。
在主流的动画设计中(After Effect、Origami、Principle、FramerJS),由于动画引擎不同的缘故,导致同样的设计效果不同。
然而,其中的贝塞尔插值和函数在开发过程中具有相当的借鉴意义。
贝塞尔的原理可以通过这个视频来学习
贝塞尔插值在动画的设计和开发中得以广泛应用,是自定义动画最常用的方式
在设计过程中,设计者可以获取非常有意义的参数 —— 动画时间、属性变化量、以及贝塞尔插值曲线
获取到贝塞尔插值后和上述参数后,开发者可以实现动画的还原。
iOS开发(利用 Core Animation )
CABasicAnimation *animation = [CABasicAnimation animation];
animation.keyPath = @"position.x";
animation.fromValue = @77;
animation.toValue = @455;
animation.duration = 1;
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = NO;
animation.timingFunction =[CAMediaTimingFunction functionWithControlPoints:0.78:0.36:0.14:1.09];
[myView.layer addAnimation:animation forKey:@"myKey"];
Android开发:
PathInterpolator
前端开发者:
div {
-webkit-transition: all 600ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
transition: all 600ms cubic-bezier(0.25, 0.46, 0.45, 0.94);
}
参考资料
2.Bezier Curve based easing functions – from concept to implementation
有些情况下,设计者和开发者可能需要使用函数来描述动画,一般应用以下几个场景:
- 设计者使用 AE 制作弹性动画时,使用表达式
if (nearestKeyIndex > 0 && currentTime < 1) {
calculatedVelocity = velocityAtTime(key(nearestKeyIndex).time - thisComp.frameDuration / 10);
amplitude = 1.1;
frequency = 2.0;
decay = 4.0;
value + calculatedVelocity * amplitude * Math.sin(frequency * currentTime * 2 * Math.PI) / Math.exp(decay * currentTime);
} else {
value;
}
效果如下:
- 同样的,在 AE 中也可以利用函数描述近似贝塞尔曲线的缓动动画(Ease&Wizz JS 文件下载)
//去
var p = 0.8; // period for elastic
var a = 50; // amplitude for elastic
var s = 1.70158; // overshoot amount for "back"
function outQuart(t, b, c, d, a, p) {
return -c * ((t=t/d-1)*t*t*t - 1) + b;
}
//回
var p = 0.8; // period for elastic
var a = 50; // amplitude for elastic
var s = 1.70158; // overshoot amount for "back"
function inOutQuad(t, b, c, d, a, p) {
if ((t/=d/2) < 1) return c/2*t*t + b;
return -c/2 * ((--t)*(t-2) - 1) + b;
}
效果如下:
那么这种缓动函数效果,iOS 中有 AHEasing 这个第三方库
3.1 案例1:利用函数可以将手势交互变化量和动画属性良好结合,实现真实、细腻的交互效果(案例来自 Kitten Yang 的 《A Guide to iOS animation》 Github地址)
这个跟随 Pan 手势变化的交互效果,代码如下
-(void)panGestureRecognized:(UIPanGestureRecognizer *)pan{
static CGPoint initialPoint;
CGFloat factorOfAngle = 0.0f;
CGFloat factorOfScale = 0.0f;
CGPoint transition = [pan translationInView:self.superview];
if (pan.state == UIGestureRecognizerStateBegan) {
initialPoint = self.center;
}else if(pan.state == UIGestureRecognizerStateChanged){
self.center = CGPointMake(initialPoint.x,initialPoint.y + transition.y);
CGFloat Y =MIN(SCROLLDISTANCE,MAX(0,ABS(transition.y)));
//一个开口向下,顶点(SCROLLDISTANCE/2,1),过(0,0),(SCROLLDISTANCE,0)的二次函数
factorOfAngle = MAX(0,-4/(SCROLLDISTANCE*SCROLLDISTANCE)*Y*(Y-SCROLLDISTANCE));
//一个开口向下,顶点(SCROLLDISTANCE,1),过(0,0),(2*SCROLLDISTANCE,0)的二次函数
factorOfScale = MAX(0,-1/(SCROLLDISTANCE*SCROLLDISTANCE)*Y*(Y-2*SCROLLDISTANCE));
CATransform3D t = CATransform3DIdentity;
t.m34 = 1.0/-1000;
t = CATransform3DRotate(t,factorOfAngle*(M_PI/5), transition.y>0?-1:1, 0, 0);
t = CATransform3DScale(t, 1-factorOfScale*0.2, 1-factorOfScale*0.2, 0);
self.layer.transform = t;
}
}
3.2 案例2:获取 Pan 手势的手指位置后,传递给贝塞尔图形,实现拉窗帘一般的效果。
窗帘代码如下:
func createPulledPath(width:CGFloat,point:CGPoint) -> UIBezierPath{
let height = view.bounds.height
let offset = width + point.x
let path = UIBezierPath()
path.moveToPoint(CGPoint(x: -mtExtenedEdgesOffset, y: -mtExtenedEdgesOffset))
path.addLineToPoint(CGPoint(x: width, y: -mtExtenedEdgesOffset))
// add curve
path.addCurveToPoint(CGPoint(x: offset, y: point.y),
controlPoint1: CGPoint(x: width, y: point.y * mtControlPointRatio),
controlPoint2: CGPoint(x: offset, y: point.y - mtControlPointPulledDistance))
path.addCurveToPoint(CGPoint(x: width, y: height + mtExtenedEdgesOffset),
controlPoint1: CGPoint(x: offset, y: point.y + mtControlPointPulledDistance),
controlPoint2: CGPoint(x: width, y: point.y + (height - point.y) * (1 - mtControlPointRatio)))
path.addLineToPoint(CGPoint(x: -mtExtenedEdgesOffset, y: height + mtExtenedEdgesOffset))
path.closePath()
return path
}
然后利用贝塞尔反解函数,传入手指当前点和贝塞尔图形信息,获取实时状态下,曲线上各个点的位置,然后传给方块,获取方块x、y代码如下:
func usefourmulaGetX(width:CGFloat,point:CGPoint,x0:CGFloat,x1:CGFloat,x2:CGFloat,x3:CGFloat,t:CGFloat) -> CGFloat {
let xValue:CGFloat = x0*(1-t)*(1-t)*(1-t) + 3*x1*t*(1-t)*(1-t) + 3*x2*t*t*(1-t) + x3*t*t*t
return xValue
}
func usefourmulaGetY(width:CGFloat,point:CGPoint,y0:CGFloat,y1:CGFloat,y2:CGFloat,y3:CGFloat,t:CGFloat) -> CGFloat {
let yValue:CGFloat = y0*(1-t)*(1-t)*(1-t) + 3*y1*t*(1-t)*(1-t) + 3*y2*t*t*(1-t) + y3*t*t*t
return yValue
}
由上述案例可见,在制作交互式动画时,函数式动画可以发挥极大的作用,然而对于设计者和开发者来说,成本略大。
而一般情况下,普通的贝塞尔曲线动画就能满足设计者和开发者的需求。
4.安卓开发时,使用 RapidInterpolator ,也可以调节原声插值器动画 以及 easing.net那套Easing函数动画
参考资料
1.缓动函数速查表
3.More Animation Curves than You Can Shake a Stick at
4.AHEasing 开源库 (可以尝试简单修改以便与 Ease & Wizz 的JS函数对接)
一般来说有以下几种弹性动画的制作方法,设计与开发对接的形式也有所不同:
1.使用 UIkit Dynamic 中的SnapBehavior
2.使用 UIView 动画的 Spring 形式
3.使用上文提到的函数来描述一根弹性动画曲线
//UI动力学
self.Cardview.center = CGPointMake(160, 219.5)
self.snapBehavior = UISnapBehavior(item: self.Cardview, snapToPoint: CGPointMake(160, 219.5))
//UIView - Spring
UIView.animateWithDuration(0.5, delay: 0,usingSpringWithDamping: 0.7, initialSpringVelocity: 0, options: .CurveEaseOut, animations: {
self.introbtn.frame.origin.y = 692
}, completion: { finished in
})
这几种方法不一一赘述,当设计者在 AE 中使用弹性函数时,开发者需要解读 AE 表达式,转写到程序中 当开发者使用 UI 动力学和 UIView Spring动画时,设计者需要在开发者的代码中进行参数调试,以便达到最佳效果。
4.使用 Facebook POP 与 Origami
利用 Origami 这一设计工具结合 POP 动画库,可以高度还原设计效果,设计者给出 Bounciness 和 Speed 参数即可
POP动画的写法类似CAAnimation:
POPSpringAnimation *switchFrameAnim = [POPSpringAnimation animation];
switchFrameAnim.property = [POPAnimatableProperty propertyWithName:kPOPViewFrame];
switchFrameAnim.springBounciness = 20;
switchFrameAnim.springSpeed = 10;
switchFrameAnim.toValue = [NSValue valueWithCGRect:CGRectMake(137.5, 450, 100, 100)];
[_myBtn pop_addAnimation:switchFrameAnim forKey:@"switchBtnFrame"];
效果对比:
POP动画可以做 CompletionBlock:
anim.completionBlock = ^(POPAnimation *anim, BOOL finished) {
if(finished){NSLog(@"Animation finished!");}};
}
POP动画可以获取动画的 Progress:
//获取某一动画属性的Progress
POPSpringAnimation *progressAnim = [POPSpringAnimation animation];
progressAnim.springBounciness = smallBounciness;
progressAnim.springSpeed = smallSpeed;
progressAnim.property = [POPAnimatableProperty propertyWithName:@"popAnimationProgress" initializer:^(POPMutableAnimatableProperty *prop) {
prop.readBlock = ^(ViewController *obj, CGFloat values[]) {
values[0] = obj.popAnimationProgress;
};
prop.writeBlock = ^(ViewController *obj, const CGFloat values[]) {
obj.popAnimationProgress = values[0];
};
prop.threshold = 0.001;
}];
[self pop_addAnimation:progressAnim forKey:@"popAnimation"];
可以给任何属性进行自定义动画:
//阴影动画
POPSpringAnimation *shadowAnim = [POPSpringAnimation animation];
//Custom Animation Property
_animateableshadowOpacityProperty = [POPAnimatableProperty propertyWithName:@"shadowOpacity" initializer:^(POPMutableAnimatableProperty *prop) {
// read value
prop.readBlock = ^(CALayer * shadowOpacity , CGFloat values[]) {
values[0] = shadowOpacity.shadowOpacity;
};
// write value
prop.writeBlock = ^(CALayer * shadowOpacity, const CGFloat values[]) {
shadowOpacity.shadowOpacity = values[0];
};
// dynamics threshold
prop.threshold = 0.01;
}];
shadowAnim.property = _animateableshadowOpacityProperty;
shadowAnim.springBounciness = smallBounciness;
shadowAnim.springSpeed = smallSpeed;
[_shadowView.layer pop_addAnimation:shadowAnim forKey:@"shadowOpacity"];
5.转换 spring-rk4 到 Facebook pop bouncy animation Framer JS 的 spring-rk4动画,可以使用ReboundJS中的转化公式,转为POP动画,方便了 iOS 开发 和 Web 开发
convert = (tension, friction) ->
result =
tension : Utils.round (tension - 194.0) / 3.62 + 30.0
friction : Utils.round (friction - 25.0) / 3.0 + 8.0
return result
print convert 600, 60
# » {tension:142, friction:20}
参考资料
1.POP使用指南
2.POP进阶指南
贝塞尔插值 | 缓动类型 | 效果 | 图表 |
---|---|---|---|
bezier(0.33,0,0.25,1) | EaseOut1 | ||
bezier(0.33,0,0.33,1) | EaseOut2 | ||
bezier(0.33,0,0.46,1) | EaseOut3 | ||
bezier(0.55,0,0.1,1) | EaseOut4(SwiftOut) | ||
bezier(0.78,0.36,0.14,1.09) | EaseOut5(Back) | ||
bezier(0.66,0,0.33,1) | EaseInOut1 | ||
bezier(0.33,0,0.66,1) | EaseInOut2 |
以上曲线效果可以通过贝塞尔插值法进行复用,保证了设计效果和开发效果的一致,其他类型曲线效果待定