iOS动画案例(1) 仿qq账号信息里的一个动画

受人所托,仿一个qq账号信息里的一个动画,感觉挺有意思,也没感觉有多难,就开始做了,结果才发现学的数学知识都还给体育老师了,研究了大半天才做出来。
  先看一下动画效果

QQAnimation.gif

  用到的知识点:
(1)三角函数
(2)CALayer
(3)CATransaction
(4)UIBezierPath
(5)CAKeyframeAnimation
(6)CAAnimationGroup

示意图.png

  如图,这明显是一段圆弧,那么要确定这段一段圆弧的位置,就得确定这段圆弧的圆心和圆心角。我规定圆心在手机屏幕的左顶点,也就是(0,0),圆心角为60°。别问我为什么这么确定,我也是一点点尝试的。我们先设手机屏幕的宽度为 ScreenWidth,圆弧半径为R;那么R = ScreenWidth/cos(60°);知道了这些开始画圆弧。

    // 屏幕的宽度
    CGFloat width = [UIScreen mainScreen].bounds.size.width;
    // 圆半径 
    float r = 2 * width / sqrt(3);
    // 画曲线
    UIColor *color = [UIColor redColor];
    [color set];
    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(0, 0) radius:r startAngle:M_PI / 2 endAngle:M_PI / 6 clockwise:NO];
    path.lineWidth = 1.0;
    path.lineCapStyle = kCGLineCapRound;
    path.lineJoinStyle = kCGLineJoinRound;
    [path stroke];

确定了圆心角和半径就要确定ABCD四个点的坐标了,分别作为四张图片的圆心。圆弧SA和圆弧DE的圆心角一样,设定为7.5°,那么弧AB、弧BC、弧CD的圆心角设定为相等,分别为(60 - 7.5 * 2)/ 3 = 15°。那么A点的坐标就等于(R * sin7.5,R * cos7.5°);B,C,D点的坐标一样用三角函数求,分别为(R * sin22.5,R * cos22.5°),(R * sin37.5,R * cos37.5°),(R * sin52.5,R * cos52.5°)。ABCD其实都是一个按钮,下面开始放按钮。

// 放图片
    for (int i = 0; i < 4; i++) {
    
        // 一共四个按钮 从左到右index分别为0,1,2,3
        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
        button.frame = [self getButtonFrame:i];
        button.tag = i + 1;
        [button addTarget:self action:@selector(buttonClick:) forControlEvents:UIControlEventTouchUpInside];
        [button setImage:[UIImage imageNamed:[NSString stringWithFormat:@"%d",i + 1]] forState:UIControlStateNormal];
        // 设置按钮为圆
        button.layer.cornerRadius = 25;
        button.layer.borderColor = [UIColor greenColor].CGColor;
        button.layer.masksToBounds = YES;
        button.layer.borderWidth = 2.0f;
        [self addSubview:button];
    }
    // 根据Index确定按钮的坐标
    - (CGRect)getButtonFrame: (int) index {
    
    float radians = M_PI * (7.5 + 15 * index) / 180;
    CGFloat width = [UIScreen mainScreen].bounds.size.width;
    float r = 2 * width / sqrt(3);
    CGRect frame = CGRectMake(sin(radians) * r, cos(radians) * r, 50, 50);
    frame.origin.x = frame.origin.x - 25;
    frame.origin.y = frame.origin.y - 25;
    return frame;
 }

头像默认放第一个。

    self.head = [[UIImageView alloc] initWithFrame:[self getButtonFrame:0]];
    self.head.image = [UIImage imageNamed:@"myHead"];
    self.head.layer.borderColor = [UIColor greenColor].CGColor;
    self.head.layer.masksToBounds = YES;
    self.head.layer.cornerRadius = 25;
    self.head.layer.borderWidth = 2.0f;
    [self addSubview:self.head];

之后按钮点击之后,头像移动到按钮点击的地方。

// 按钮点击事件
- (void)buttonClick:(UIButton *)button {
    
    // 原来图片所在按钮的index
    int preIndex = [self getPreviousIndexByFrame:self.head.frame];
    int buttonIndex = (int)button.tag - 1;
    // 点击图片所在按钮 不做任何操作
    if (preIndex == buttonIndex) {
        return;
    }
    CGFloat width = [UIScreen mainScreen].bounds.size.width;
    float r = 2 * width / sqrt(3);
    //加入动画效果
    CALayer *transitionLayer = [[CALayer alloc] init];
    //显式事务默认开启动画效果,kCFBooleanTrue关闭 保证begin和commit 之间的属性修改同时进行
    transitionLayer.contents = self.head.layer.contents;
    transitionLayer.borderColor = [UIColor greenColor].CGColor;
    transitionLayer.masksToBounds = YES;
    transitionLayer.cornerRadius = 25;
    transitionLayer.borderWidth = 2.0f;
    transitionLayer.frame = self.head.frame;
    transitionLayer.backgroundColor=[UIColor blueColor].CGColor;
    [self.layer addSublayer:transitionLayer];
    
    self.head.hidden = YES;
    
    UIBezierPath *movePath;
    //路径曲线 贝塞尔曲线
    if (buttonIndex > preIndex) {
        // 向上滑 逆时针
        movePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(0, 0) radius:r startAngle:[self getAnticlockwiseByIndex:preIndex] endAngle:[self getAnticlockwiseByIndex:buttonIndex] clockwise:NO];
        [movePath moveToPoint:transitionLayer.position];
    }else {
        // 向下滑 顺时针
        movePath = [UIBezierPath bezierPathWithArcCenter:CGPointMake(0, 0) radius:r startAngle:[self getClockwiseAngleByIndex:preIndex] endAngle:[self getClockwiseAngleByIndex:buttonIndex] clockwise:YES];
        [movePath moveToPoint:transitionLayer.position];
    }
    //关键帧动画效果
    CAKeyframeAnimation *positionAnimation = [CAKeyframeAnimation animationWithKeyPath:@"position"];
    // 动画轨迹
    positionAnimation.path = movePath.CGPath;
    // 动画完成之后是否删除动画效果
    positionAnimation.removedOnCompletion = NO;
    // 设置开始的时间
    positionAnimation.beginTime = CACurrentMediaTime();
    CGFloat time =  0.7;
    if (labs(buttonIndex - preIndex) > 1) {
        time = 0.4 * labs(buttonIndex - preIndex);

    }
    //动画总时间
    positionAnimation.duration = time;
    // 动画的方式 淡入淡出
    positionAnimation.timingFunction=[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
    // 执行完之后保存最新的状态
    positionAnimation.fillMode = kCAFillModeForwards;
    // 动画完成之后,是否回到原来的地方
    positionAnimation.autoreverses= NO;
    
    [transitionLayer addAnimation:positionAnimation forKey:@"opacity"];
    [CATransaction setCompletionBlock:^{
        [NSThread sleepForTimeInterval:time];
        self.head.hidden = NO;
        self.head.frame = button.frame;
        [transitionLayer removeFromSuperlayer];
    }];
}
// 根据Index获得顺时针的弧度
- (float)getAnticlockwiseByIndex: (NSInteger)index {
    
    return M_PI * (0.5  - (7.5 + 15 * index) / 180);
}
// 根据Index获得逆时针的弧度
- (float)getClockwiseAngleByIndex: (NSInteger)index {
    
    index = 3 - index;
    return M_PI * (30 + 7.5 + 15 * index) / 180;
}

这个动画的难点其实是确定四个按钮的坐标以及圆弧的半径,主要是学的数学都忘的差不多了,还好重新捡起来还算不难。

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

推荐阅读更多精彩内容