iOS炫酷动画(二)

O(∩_∩)O哈!又是一个很cool的动画。原作者在这里。画面看着太美好,于是又试着用Swift写了一个。我的在这里

1.gif
动画分解

1、首先是椭圆背景色的渐变,灰色---》淡蓝色。

2、脸的动画。这里又分为脸盘(😄就这么叫吧)和器官(2眼睛+1嘴巴)的动画。

  • 脸盘的动画,比较简单,就是x的位移。
  • 眼睛+嘴巴这个整体的动画,有个回弹的效果。先是向右飞出去,然后到达右边之后,又从左边飞进来,最后回弹一下。

3、嘴巴的动画。off的时候,是个长方形的嘴,切换到on,变成了笑脸嘴,并且有个逐渐张开的过程。

AIYA,还真是难描述。

元素分解

知道了动画步骤之后,元素就比较好分了。脸盘是1个layer,2眼睛+嘴巴==1个layer,椭圆背景=1view。

开工开工
嘴巴

首先注意到的是嘴巴的绘制,on和off的状态其实是2种不同的曲线,长方形很好画,那么张开的嘴呢?当然都是用万能的贝塞尔曲线啦。其实看了代码,都觉得简单o(>﹏<)o。

闭嘴的frame=(0.25w, 0.7h, 0.5w, 0.1h)
张嘴的,x,y,w都是一样的,只不过是控制2个control point的位置。

private func mouthPath() -> UIBezierPath {
        
        if isOn {
            let path = UIBezierPath()

            path.moveToPoint(CGPointMake(mouthRect().origin.x, mouthRect().origin.y))
            path.addCurveToPoint(CGPointMake(mouthLenth() + mouthRect().origin.x, mouthY()), controlPoint1: CGPointMake(mouthRect().origin.x + mouthOffset / 4, mouthY() + mouthOffset / 2),
                                 controlPoint2: CGPointMake(mouthRect().origin.x + mouthOffset * 3 / 4, mouthY() + mouthOffset / 2))
            path.closePath()
            
            return path
        } else {
            let path = UIBezierPath(rect: mouthRect())
            return path
        }
    }

眼睛

眼睛就是2个椭圆,有现成的。frame=(0.2w, 0.25h, 0.4w, 0.6h)

private func rightEyePath() -> UIBezierPath {
        let origin = rightEyeOrigin()
        let size = eyeSize()
        let path = UIBezierPath(ovalInRect: CGRectMake(origin.x, origin.y, size.width, size.height))
        
        return path
    }
眼睛+嘴巴

其实就是在layer上面,分别把他们画上去。

override func drawInContext(ctx: CGContext) {
        
        let bezierLeft = leftEyePath()
        let bezierRight = rightEyePath()
        let bezierMouth = mouthPath()
        
        CGContextAddPath(ctx, bezierLeft.CGPath)
        CGContextAddPath(ctx, bezierRight.CGPath)
        CGContextAddPath(ctx, bezierMouth.CGPath)
        CGContextSetFillColorWithColor(ctx, color().CGColor)
        CGContextSetStrokeColorWithColor(ctx, UIColor.clearColor().CGColor)
        CGContextFillPath(ctx)
    }

脸盘/椭圆背景

这个更简单了,用cornerRadius或者bezierPath都行。

嘴张开的动画

因为嘴是用bezier曲线画的,有2个control point。所以主要是操控control point,来实现张开的效果。

为了支持自定义属性动画,需要用到needsDisplayForKey,改变属性后,可以引发drawInContext重绘。mouthOffset最后其实是等于mounLength的,所以只需要将mouthOffset从0变到mounLength即可。

internal override class func needsDisplayForKey(key: String) -> Bool {
        if key == "mouthOffset" {
            return true
        }
        
        return super.needsDisplayForKey(key)
    }
    
动画顺序

拿从off到on的过程来说。
1、背景渐变+脸盘移动+器官向右飞出
2、脸盘移动的动画结束后,器官从左往右飞出,并回弹。同时嘴巴开始张开动画。
3、回弹动画结束,整个动画结束。

动画注意

在动画结束之后,要将对应值设成动画之后的值。

override func animationDidStop(anim: CAAnimation, finished flag: Bool) {
        if flag {
            if let type = anim.valueForKey("animation") as? String {
                // 脸盘移动到端点
                if type == "faceBgLayerAnimation" {
                    
                    faceBgLayer.removeAllAnimations()
                    faceBgLayer.position = positionOfFaceView(!isOn)
                    
                    faceLayer.isOn = !isOn
                    faceLayer.mouthOffset = !isOn ? faceLayer.bounds.size.width / 2 : 0
                    faceLayer.setNeedsDisplay()
                
                    if (!isOn) {
                        mouthAnimation(isOn, offset: faceLayer.bounds.size.width / 2)
                        moveRightBackAnimation()
                    } else {
                        moveLeftBackAnimation()
                    }
                } else if type == "backgroundAnimation" {
                    
                    self.backgroundColor = isOn ? onColor : offColor
                    
                } else if type == "moveBackAnimation" {
                    
                    faceLayer.removeAllAnimations()

                    isOn = !isOn
                    isAnimating = false
                }
            }
        }
    }

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

推荐阅读更多精彩内容

  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥ios动画全貌。在这里你可以看...
    每天刷两次牙阅读 8,460评论 6 30
  • 转载:http://www.jianshu.com/p/32fcadd12108 每个UIView有一个伙伴称为l...
    F麦子阅读 6,148评论 0 13
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,392评论 25 707
  • 每个UIView有一个伙伴称为layer,一个CALayer。UIView实际上并没有把自己画到屏幕上;它绘制本身...
    shenzhenboy阅读 3,081评论 0 17
  • 我忍不住想睡了 它却一直阻拦我 打开我的眼 却什么都看不见 也不知道 张开眼看见的 和睡了 有何区别
    满岛遥阅读 127评论 0 0