iOS实现一个陌陌聊天里的“震惊”效果

如下,第一次看到陌陌的这个效果,一半惊艳一半懵逼:动画确实有震惊的效果,但是怎么实现的?


陌陌震惊.gif

分解一下:

  1. 抖动
  2. 添加黑白闪烁的半透明蒙层
  3. 用白色折线画闪电
  4. 对UILabel截图,图斜劈成两半,后半部分平移后旋转
  5. 在底部放张碎片的图片

先写出基本的视图,一个UIView对象用于设置圆角,背景颜色等等,一个UILabel对象用于显示内容:

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor lightGrayColor];
    
    self.bgView = [[UIView alloc] init];
    self.bgView.backgroundColor = [UIColor colorWithRGB:0xADCCFF alpha:1];
    self.bgView.left = kMainScreenWidth/2;
    self.bgView.top = 100;
    self.bgView.layer.cornerRadius = 4;
    self.bgView.clipsToBounds = YES;
    [self.view addSubview:self.bgView];
    
    self.messageLabel = [[UILabel alloc] init];
    self.messageLabel.text = @"卧槽震惊";
    [self.messageLabel sizeToFit];
    self.messageLabel.textAlignment = NSTextAlignmentCenter;
    self.bgView.height = self.messageLabel.height + 20;
    self.bgView.width = self.messageLabel.width + 30;
    self.messageLabel.center = CGPointMake(self.bgView.width/2, self.bgView.height/2);
    [self.bgView addSubview:self.messageLabel];
}
图一.png

先看一个最难的,步骤4:

UILabel对象的截取

动画比较难的就是对label截取任意形状的图了。

思路:通过把显示内容的label,截取成前后两个梯形获取对应梯形图片或者view,拼接两个图片,这样动画之前看起来和单个label显示没有区别。当需要动画的时候,后边的梯形先向右平移几个dp,然后向下旋转几度。具体平移和旋转的数字可以根据效果自行调整。

首先利用贝塞尔曲线和CAShapeLayer获得自己想要的形状。得到CAShapeLayer对象,先看看长什么样子:

[self.messageLabel.layer addSublayer:shapeLayer];

添加完CAShapeLayer对象后:


添加后的样子.png

黑色遮盖部分就是CAShapeLayer对象,符合,形状符合预期。但是我想要的是遮盖部分显示,未遮盖的“震惊”部分变透明。这就用到了CALayer的mask属性。
该属性也是一个CALayer对象。 简单理解就是:如果设置了CALayer对象mask属性,那么当前CALayer对象只能显示被mask属性遮盖的部分,其余部分变透明——正好符合我们的预期:

self.messageLabel.layer.mask = shapeLayer;
完美的前半部分梯形.png

通过对现在的UILabel对象截屏得一个只显示前半部分梯形内图像后半部分透明的视图snapshotView1,然后添加到self.bgView上:

UIView *snapshotView1 = [self.messageLabel snapshotViewAfterScreenUpdates:YES];
snapshotView1.center = CGPointMake(self.bgView.width/2, self.bgView.height/2);
[self.bgView addSubview:snapshotView1];

同理可得只显示后半部分内容前半部分透明的视图snapshotView2,再添加到self.bgView上。
实际上snapshotView1,snapshotView2和self.messageLabel的size是一样的,所以将snapshotView1和snapshotView2的位置设置为一样的就能得到和图一一样的视觉效果。

动画时断裂效果代码:

- (void)rupture {
    CGAffineTransform transform = CGAffineTransformIdentity;
    // 向右平移3dp
    transform = CGAffineTransformTranslate(transform, 3, 0);
    // 旋转10度
    transform = CGAffineTransformRotate(transform, M_PI / 180.0 * 10.0);
    self.snapshotView2.layer.affineTransform = transform;
}

抖动

抖动效果的实现就是使用了UIView提供的弹簧效果动画:

+ (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

就是这animations block里设置向上移动5dp,因为弹簧效果,会不停的抖动

- (void)shake {
    [UIView animateWithDuration:1.0 delay:0 usingSpringWithDamping:0.08 initialSpringVelocity:30 options:0 animations:^{
        self.bgView.top = self.bgView.top-5;
    } completion:^(BOOL finished) {
        [UIView animateWithDuration:0.1 animations:^{
            self.bgView.top = 100;
        }];
        // 抖动结束断裂
        [self rupture];
        // 显示断裂碎片
        [self showSplinterView];
    }];
}

黑白闪烁的背景

思路:往self.bgView上添加一个黑色半透明的视图对象,同时循环动画改变视图对象的透明度。这样就有了闪烁效果

# pragma mark - 添加黑白闪烁的背景蒙层
- (void)flashMask {
    [self stopFlashMask];
    self.flashMaskView = [[UIView alloc] initWithFrame:self.bgView.bounds];
    self.flashMaskView.backgroundColor = [UIColor blackColor];
    self.flashMaskView.alpha = 0.1;
    [self.flashMaskView.layer addAnimation:[self opacityForever_Animation:0.1] forKey:nil];
    [self.bgView addSubview:self.flashMaskView];
}

- (void)stopFlashMask {
    if (self.flashMaskView && [self.flashMaskView superview]) {
        [self.flashMaskView removeFromSuperview];
    }
}

- (CABasicAnimation *)opacityForever_Animation:(float)time
{
    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];//必须写opacity才行。
    animation.fromValue = [NSNumber numberWithFloat:0.7f];
    animation.toValue = [NSNumber numberWithFloat:0.2f];//这是透明度。
    animation.autoreverses = YES;
    animation.duration = time;
    animation.repeatCount = 3;
    animation.removedOnCompletion = NO;
    animation.fillMode = kCAFillModeForwards;
    animation.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn];///没有的话是均匀的动画。
    return animation;
}

闪电

思路:闪电就是白色的折线,尽量画的写实一点就行了。
生成一个UIBezierPath对象,然后不停的调用addLineToPoint:方法添加一段一段的线,然后根据折线生成CAShapeLayer对象,给CAShapeLayer对象添加动画方法,使得折线动画出来而不是一下子全部出现

- (void)flash {
    [self removeFlash];
    
    UIBezierPath *path = [[UIBezierPath alloc] init];
    [path moveToPoint:CGPointMake(self.bgView.width*5/9, 0)];
    [path addLineToPoint:CGPointMake(self.bgView.width*5/9 - 3, self.bgView.height/10)];
    [path addLineToPoint:CGPointMake(self.bgView.width*5/9+self.bgView.width/9, self.bgView.height/10 + self.bgView.height/7)];
    [path addLineToPoint:CGPointMake(self.bgView.width*5/9+self.bgView.width/9 - 3, self.bgView.height/10 + self.bgView.height/7 + self.bgView.height/5)];
    [path addLineToPoint:CGPointMake(self.bgView.width*5/9+self.bgView.width/9 - 3 - 4, self.bgView.height/10 + self.bgView.height/7 + self.bgView.height/5 + 2)];
    [path addLineToPoint:CGPointMake(self.bgView.width*5/9+self.bgView.width/9 - 3 - 4 - 1, self.bgView.height/10 + self.bgView.height/7 + self.bgView.height/5 + 2 + 2)];
    [path addLineToPoint:CGPointMake(self.bgView.width*5/9+self.bgView.width/9 - 3 - 4 - 1 + self.bgView.width/20, self.bgView.height/10 + self.bgView.height/7 + self.bgView.height/5 + 2 + 2 + self.bgView.height/7)];
    
    [path addLineToPoint:CGPointMake(self.bgView.width*5/9+self.bgView.width/9 - 3 - 4 - 1 + self.bgView.width/20 - 3, self.bgView.height/10 + self.bgView.height/7 + self.bgView.height/5 + 2 + 2 + self.bgView.height/7 + 2)];
    
    [path addLineToPoint:CGPointMake(self.bgView.width*5/9+self.bgView.width/9 - 3 - 4 - 1 + self.bgView.width/20 , self.bgView.height/10 + self.bgView.height/7 + self.bgView.height/5 + 2 + 2 + self.bgView.height/7 + 3)];
    
    self.flashLayer = [CAShapeLayer layer];
    self.flashLayer.strokeColor = [UIColor whiteColor].CGColor;
    self.flashLayer.fillColor = [UIColor clearColor].CGColor;
    self.flashLayer.path = path.CGPath;
    [self.bgView.layer addSublayer:self.flashLayer];
 
    CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"strokeEnd"];
    pathAnimation.duration = 0.3;
    pathAnimation.fromValue = [NSNumber numberWithFloat:0.0f];
    pathAnimation.toValue = [NSNumber numberWithFloat:1.0f];
    pathAnimation.repeatCount = 1;
    pathAnimation.delegate = self;
    [self.flashLayer addAnimation:pathAnimation forKey:nil];
}

把以上几种效果,适当组合一下就山寨个差不多了。

demo

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容

  • 在进行知行合一挑战的过程中,我们听到最多的三个障碍,分别是:没钱、没时间、没办法。 前两回分别说了“没钱”和“没时...
    大胡子逸舟阅读 231评论 0 0
  • 【感悟】 1、有几条不变的铁律 早上刚被闹钟叫醒 总是觉得离不开床。其实只要立即起床,然后也就没有离不开床这一念头...
    i期待阅读 254评论 0 0
  • 她像一缕喝醉的风在我眼前飘着,火光里没有她的影子。 她张开双臂从我身体穿过去,像一个拥抱,一阵舒适的冷冰。 她在我...
    贝龙阅读 833评论 24 8