前言
昨天刚做完项目的新版本、除了尝试一些新的架构之外、功能方面并没什么特别的地方。
但是顺手搞了一些还算好玩的东西、其一就是这个导航栏的动画。
感觉还算简单易懂、分享一下(其实更多是最近攒了好多封面、不贴出来憋得人难受)
。
思路
先介绍CA的两个方法:
- 基于原始状态的位移
CG_EXTERN CGAffineTransform CGAffineTransformMakeTranslation(CGFloat tx,
CGFloat ty) CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
- 基于原始状态的形变
CG_EXTERN CGAffineTransform CGAffineTransformScale(CGAffineTransform t,
CGFloat sx, CGFloat sy) CG_AVAILABLE_STARTING(__MAC_10_0, __IPHONE_2_0);
知道了这两个方法、剩下就是如何使用形变和位移以达到想要的效果了。
这里、需要分两部分来看、一部分是位移、一部分是形变
而且、如果将一整个动画以及翻页动作看成1。那么位移和形变又需要分成两部分来看:0到0.5以及0.5到1。
其实一点都不难、画个图看一眼。连一元方程都不算、完全是小学算术题~甚至连奥数都算不上。
0到0.5区间内
-
先看形变
在0.5时、我们需要将线条长度从left_width
形变成max_width
-
再看位移
与此同时如果想让线条在左侧看起来并没有移动、就需要将线条的x轴一点点向右移动。这个移动的值呢?
由于形变是双向的、右侧和左侧都会变长。那么对于左侧最终将是中间时最终形变差值
的一半。
具体到数值:
最左侧到中心点时x轴的位移 = 最左侧到中心点时形变的差值/2 = (max_width - left_width)/2
0.5到1区间内
-
依旧先看形变
和之前的形变相同
-
然后来看位移
同理、如果我的长度减少了X、那么我如果想保证看起来右侧位置不会改变、考虑到形变是左右同时发生、就需要向右移动X/2。
具体到数值:
中心点到最右侧时x轴的位移 = 中心点到最右时形变的差值/2 = (max_width - right_width)/2
具体函数
以我项目里两个按钮(self.titleBtn1
/self.titleBtn2
)为例
if (0 <= offsetRate && offsetRate <= 0.5) {
/*
* 左侧与中间相互移动
*/
//x轴位移 :: 中间时位移 * 偏移比例系数
CGFloat translationOfX = _translationofX_center * offsetRate * 2;
self.line.transform = CGAffineTransformMakeTranslation(translationOfX, 0);
//x轴形变 :: 1 + (最大时相对形变) * 偏移比例系数
CGFloat flagScale = 1 + (_flagScale_center - 1) * (offsetRate * 2);
self.line.transform = CGAffineTransformScale(self.line.transform, flagScale, 1);
}else if (0.5 < offsetRate && offsetRate <= 1) {
/*
* 中间与右侧相互移动
*/
//x轴位移 :: 中间时位移 + 最终位移 * 偏移比例系数
CGFloat translationOfX = _translationofX_center + _translationofX_right * (offsetRate - 0.5) * 2;
self.line.transform = CGAffineTransformMakeTranslation(translationOfX, 0);
//x轴形变 :: 最大时形变 - (最大形变 - 最终形变) * 偏移比例系数
CGFloat flagScale = _flagScale_center - (_flagScale_center - _btn2Width/_btn1Width) * (offsetRate - 0.5) * 2;
self.line.transform = CGAffineTransformScale(self.line.transform, flagScale, 1);
}
其中的某些参数的意义以及取值:
{
//整体最大宽度
CGFloat _maxWidth;
//位于左侧时宽度
CGFloat _btn1Width;
//位于右侧时宽度
CGFloat _btn2Width;
//从左侧移到中心时x轴位移
CGFloat _translationofX_center;
//从中心移到右侧时x轴位移
//所以总位移就是_translationofX_center+_translationofX_right
CGFloat _translationofX_right;
//位于中心时形变
CGFloat _flagScale_center;
}
{
_btn1Width = self.titleBtn1.width;
_btn2Width = self.titleBtn2.width;
_maxWidth = self.titleBtn2.right - self.titleBtn1.left;
_translationofX_center = (_maxWidth - _btn1Width)/2;
_translationofX_right = (_maxWidth - _btn2Width)/2;
_flagScale_center = _maxWidth/_btn1Width;
self.line.frame = CGRectMake(self.titleBtn1.left, self.height, self.titleBtn1.width, 1);
}
将屏幕滑动的偏移量传递进来
#pragma mark UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
CGFloat offsetX = scrollView.contentOffset.x;
CGFloat offsetRate = offsetX/BSScreen_Width;
//将偏移率传递给navView
[self.navView configLingWithOffsetRate:offsetRate];
}
Demo
上面的例子的代码其实都已经列出来了、没什么再单独传Demo的必要。
但是毕竟开了个帖子、连个Demo都没有不好看。于是上午干脆封装了一个小工具出来。
但是只封装了移动的动画、并没封装按钮变色等等一系列功能。
用起来也挺简单的、只要将按钮的数组
、下方横线
交付、在屏幕滑动的时候把偏移量
传递进去、就能自动工作了。宽度啊什么都会自己算:
@interface KTNavScrollTool : NSObject
/**
初始化
@param titleArr 按钮数组
@param line 需要移动的横线
@return 实例对象
*/
- (instancetype)initWithTitleArr:(NSArray *)titleArr line:(UIView *)line;
/**
移动的函数
@param offsetRate scrollview滚动式的偏移量的比例
*/
- (void)configLingWithOffsetRate:(CGFloat)offsetRate;
@end