前言
Shimmer是facebook的开源动画库,可以实现闪现文字的效果,在paper
app里用到了,虽然paper
没有火起来,但是这个效果还是很不错的。它的源码只有几百行,很容易学习理解。
源码的github地址在这
1.通过宏来创建属性的getter和setter方法:
#define LAYER_ACCESSOR(accessor, ctype) \
- (ctype)accessor { \
return [__layer accessor]; \
}
![Alt text](./shimmer.gif)
#define LAYER_MUTATOR(mutator, ctype) \
- (void)mutator (ctype)value { \
[__layer mutator value]; \
}
#define LAYER_RW_PROPERTY(accessor, mutator, ctype) \
LAYER_ACCESSOR (accessor, ctype) \
LAYER_MUTATOR (mutator, ctype)
LAYER_RW_PROPERTY(isShimmering, setShimmering:, BOOL)
LAYER_RW_PROPERTY(shimmeringPauseDuration, setShimmeringPauseDuration:, CFTimeInterval)
LAYER_RW_PROPERTY(shimmeringAnimationOpacity, setShimmeringAnimationOpacity:, CGFloat)
LAYER_RW_PROPERTY(shimmeringOpacity, setShimmeringOpacity:, CGFloat)
LAYER_RW_PROPERTY(shimmeringSpeed, setShimmeringSpeed:, CGFloat)
LAYER_RW_PROPERTY(shimmeringHighlightLength, setShimmeringHighlightLength:, CGFloat)
LAYER_RW_PROPERTY(shimmeringDirection, setShimmeringDirection:, FBShimmerDirection)
LAYER_ACCESSOR(shimmeringFadeTime, CFTimeInterval)
LAYER_RW_PROPERTY(shimmeringBeginFadeDuration, setShimmeringBeginFadeDuration:, CFTimeInterval)
LAYER_RW_PROPERTY(shimmeringEndFadeDuration, setShimmeringEndFadeDuration:, CFTimeInterval)
LAYER_RW_PROPERTY(shimmeringBeginTime, setShimmeringBeginTime:, CFTimeInterval)
用宏的方式来实现多个方法,节省代码量。如果你经常看第三方源码的话,会发现这种方式也很常见的。
2.打个差,因为后面会讲到蒙板,先介绍一下mask
这是我写的一个demo:
CALayer *layer1 = [[CALayer alloc] init];
layer1.frame = CGRectMake(50, 100, 120, 120);
layer1.backgroundColor = [UIColor colorWithRed:1.0 green:0 blue:0 alpha:0.2].CGColor;
CALayer *layer2 = [[CALayer alloc] init];
layer2.frame = CGRectMake((120 - 60)/2, (120-60)/2, 60, 60);
layer2.backgroundColor = [UIColor colorWithWhite:1.0 alpha:1.0].CGColor;
layer1.mask = layer2;
[self.view.layer addSublayer:layer1];
测试结果可知:
- 蒙版mask的
颜色值
是不会起作用的,只以设置做任意颜色值,只有它的alpha
起作用。 - 我们将layer的alpha是0.2, maskLayer是1.0,结果的alpha值是0.2。调一下个儿,将layer的alpha是1.0, maskLayer是0.2,结果的alpha值是0.2。可以看来两个alpha值的共同作用是最终的值. 所以得出公式:
alpha = layer的alpha * maskLayer的alpha
3.如何实现中间高亮的效果?
看源码发现里面主要用到二个Layer,
-
_maskLayer
:_contentLayer.mask = _maskLayer, 实现蒙层效果 -
_fadeLayer
:是_maskLayer里的subLayer, 为了实现淡入淡出效果
maskLayer定义:
@interface FBShimmeringMaskLayer : CAGradientLayer
@property (readonly, nonatomic) CALayer *fadeLayer;
@end
疑问1:maskLayer的bound是多少
我本来以为maskLayer的bound大小是白色高度部分,但仔细一想不对,这样的话,非高亮的部分就会被隐藏!我看了一下源码maskLayer的bound大小,原来跟_contentLayer的大小一样,哦那就对了。
疑问2:mask和contentLayer大小一样了,蒙层还有什么用?蒙层不就是为了扣图么?
仔细看maskLayer是继承了CAGradientLayer
,但是做颜色渐变有什么用呢,因为蒙层的颜色值是起不到作用的, 渐变效果也看不出来呀!我又蒙逼了!我们再仔细看源码:
UIColor *maskedColor = [UIColor colorWithWhite:1.0 alpha:_shimmeringOpacity];//alpha: 1.0
UIColor *unmaskedColor = [UIColor colorWithWhite:1.0 alpha:_shimmeringAnimationOpacity];//alpha: 0.5
// Create a gradient from masked to unmasked to masked.
_maskLayer.colors = @[(__bridge id)maskedColor.CGColor, (__bridge id)unmaskedColor.CGColor, (__bridge id)maskedColor.CGColor];
它设了2个渐变颜色,这个颜色的alpha一个是1.0,另一个是0.5, 找到了真相!虽然颜色没什么用,但是alpha有用呀!所以渐变效果还是能展示的!所以最终最终的效果就是两边部分显示正常,中间部分显示高亮!
淡入淡出效果
static CABasicAnimation *fade_animation(CALayer *layer, CGFloat opacity, CFTimeInterval duration)
{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
animation.fromValue = @([(layer.presentationLayer ?: layer) opacity]);
animation.toValue = @(opacity);
animation.fillMode = kCAFillModeBoth;
animation.removedOnCompletion = NO;
animation.duration = duration;
FBShimmeringLayerAnimationApplyDragCoefficient(animation);
return animation;
}
滑动效果
static CABasicAnimation *shimmer_slide_animation(CFTimeInterval duration, FBShimmerDirection direction)
{
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position"];
animation.toValue = [NSValue valueWithCGPoint:CGPointZero];
animation.duration = duration;
animation.repeatCount = HUGE_VALF;
FBShimmeringLayerAnimationApplyDragCoefficient(animation);
if (direction == FBShimmerDirectionLeft ||
direction == FBShimmerDirectionUp) {
animation.speed = -fabsf(animation.speed);
}
return animation;
}
通过动态改变maskLayer的position位置,实现maskLayer从左向右滑动的动态效果。其实苹果开锁的效果也是这样的,原理是相同的!