久久精品精选,精品九九视频,www久久只有这里有精品,亚洲熟女乱色综合一区
    分享

    CADisplayLink結(jié)合UIBezierPath的神奇妙用

     ccccshq 2017-05-23

    做過iOS動(dòng)畫的朋友都知道,動(dòng)畫中一大頭疼之處就是彈性、形變之類扭曲的效果。iOS7開始,我們開始可以直接使用UiView的渲染動(dòng)畫API實(shí)現(xiàn)簡(jiǎn)單的彈性效果。

    + (void)animateWithDuration:(NSTimeInterval)duration delay:(NSTimeInterval)delay usingSpringWithDamping:(CGFloat)dampingRatio initialSpringVelocity:(CGFloat)velocity options:(UIViewAnimationOptions)options animations:(void (^)(void))animations completion:(void (^)(BOOL finished))completion NS_AVAILABLE_IOS(7_0);
    

    dampingRatio是阻尼系數(shù),取值范圍0~1,決定彈性效果的明顯程度;

    velocity是初速度。

    除此之外,iOS7又出現(xiàn)了一個(gè)重量級(jí)的家伙:UIKit Dynamics ,可以用很簡(jiǎn)單的代碼實(shí)現(xiàn)非常逼真的物理效果。

    當(dāng)然,更強(qiáng)大的是Facebook開源的Pop這個(gè)介于CAAnimation 和 UIDynamics之間的動(dòng)畫引擎,使用習(xí)慣和CAAnimation基本別無二致,很方便上手,而且動(dòng)畫效果非常出色,幀頻非常高,所以看上去的動(dòng)畫會(huì)很連貫順滑。

    但是以上要實(shí)現(xiàn)那種很Q彈、形變的效果還是有點(diǎn)困難。知道我同時(shí)遇到了CADisplayLink和貝塞爾曲線UIBezierPath。下面就是一些結(jié)合CADisplayLinkUIBezierPath 的案例,并附上了源代碼地址。

    Github地址

    Github地址

    Github地址
    博文

    Github地址

    1、什么是CADisplayLink

    簡(jiǎn)單地說,它就是一個(gè)定時(shí)器,每隔幾毫秒刷新一次屏幕。

    CADisplayLink是一個(gè)能讓我們以和屏幕刷新率相同的頻率將內(nèi)容畫到屏幕上的定時(shí)器。我們?cè)趹?yīng)用中創(chuàng)建一個(gè)新的 CADisplayLink 對(duì)象,把它添加到一個(gè)runloop中,并給它提供一個(gè) targetselector 在屏幕刷新的時(shí)候調(diào)用。

    一但 CADisplayLink 以特定的模式注冊(cè)到runloop之后,每當(dāng)屏幕需要刷新的時(shí)候,runloop就會(huì)調(diào)用CADisplayLink綁定的target上的selector,這時(shí)target可以讀到 CADisplayLink 的每次調(diào)用的時(shí)間戳,用來準(zhǔn)備下一幀顯示需要的數(shù)據(jù)。例如一個(gè)視頻應(yīng)用使用時(shí)間戳來計(jì)算下一幀要顯示的視頻數(shù)據(jù)。在UI做動(dòng)畫的過程中,需要通過時(shí)間戳來計(jì)算UI對(duì)象在動(dòng)畫的下一幀要更新的大小等等。

    在添加進(jìn)runloop的時(shí)候我們應(yīng)該選用高一些的優(yōu)先級(jí),來保證動(dòng)畫的平滑。可以設(shè)想一下,我們?cè)趧?dòng)畫的過程中,runloop被添加進(jìn)來了一個(gè)高優(yōu)先級(jí)的任務(wù),那么,下一次的調(diào)用就會(huì)被暫停轉(zhuǎn)而先去執(zhí)行高優(yōu)先級(jí)的任務(wù),然后在接著執(zhí)行CADisplayLink的調(diào)用,從而造成動(dòng)畫過程的卡頓,使動(dòng)畫不流暢。

    duration屬性:提供了每幀之間的時(shí)間,也就是屏幕每次刷新之間的的時(shí)間。我們可以使用這個(gè)時(shí)間來計(jì)算出下一幀要顯示的UI的數(shù)值。但是 duration只是個(gè)大概的時(shí)間,如果CPU忙于其它計(jì)算,就沒法保證以相同的頻率執(zhí)行屏幕的繪制操作,這樣會(huì)跳過幾次調(diào)用回調(diào)方法的機(jī)會(huì)。

    frameInterval屬性:是可讀可寫的NSInteger型值,標(biāo)識(shí)間隔多少幀調(diào)用一次selector 方法,默認(rèn)值是1,即每幀都調(diào)用一次。如果每幀都調(diào)用一次的話,對(duì)于iOS設(shè)備來說那刷新頻率就是60HZ也就是每秒60次,如果將 frameInterval 設(shè)為2 那么就會(huì)兩幀調(diào)用一次,也就是變成了每秒刷新30次。

    pause屬性:控制CADisplayLink的運(yùn)行。當(dāng)我們想結(jié)束一個(gè)CADisplayLink的時(shí)候,應(yīng)該調(diào)用-(void)invalidate 從runloop中刪除并刪除之前綁定的 targetselector

    另外 CADisplayLink 不能被繼承。

    CADisplayLink 與 NSTimer 有什么不同?

    iOS設(shè)備的屏幕刷新頻率是固定的,CADisplayLink在正常情況下會(huì)在每次刷新結(jié)束都被調(diào)用,精確度相當(dāng)高。

    NSTimer的精確度就顯得低了點(diǎn),比如NSTimer的觸發(fā)時(shí)間到的時(shí)候,runloop如果在阻塞狀態(tài),觸發(fā)時(shí)間就會(huì)推遲到下一個(gè)runloop周期。并且 NSTimer新增了tolerance屬性,讓用戶可以設(shè)置可以容忍的觸發(fā)的時(shí)間的延遲范圍。

    CADisplayLink使用場(chǎng)合相對(duì)專一,適合做UI的不停重繪,比如自定義動(dòng)畫引擎或者視頻播放的渲染。

    NSTimer的使用范圍要廣泛的多,各種需要單次或者循環(huán)定時(shí)處理的任務(wù)都可以使用。

    CADisplayLink使用的例子

    self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateTextColor)];  
    self.displayLink.paused = YES;  
    [self.displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    -(void)updateTextColor{}
    - (void)startAnimation{
       self.beginTime = CACurrentMediaTime();
       self.displayLink.paused = NO;
    }
    - (void)stopAnimation{
      self.displayLink.paused = YES;
      [self.displayLink invalidate];
      self.displayLink = nil;
    }
    

    給非UI對(duì)象添加動(dòng)畫效果

    我們知道動(dòng)畫效果就是一個(gè)屬性的線性變化,比如 UIView 動(dòng)畫的 EasyIn EasyOut 。通過數(shù)值按照不同速率的變化我們能生成更接近真實(shí)世界的動(dòng)畫效果。我們也可以利用這個(gè)特性來使一些其他屬性按照我們期望的曲線變化。比如當(dāng)播放視頻時(shí)關(guān)掉視頻的聲音我可以通過 CADisplayLink 來實(shí)現(xiàn)一個(gè) EasyOut 的漸出效果:先快速的降低音量,在慢慢的漸變到靜音。

    注意

    通常來講:iOS設(shè)備的刷新頻率事60HZ也就是每秒60次。那么每一次刷新的時(shí)間就是1/60秒 大概16.7毫秒。當(dāng)我們的frameInterval 值為1的時(shí)候我們需要保證的是 CADisplayLink調(diào)用的target的函數(shù)計(jì)算時(shí)間不應(yīng)該大于 16.7否則就會(huì)出現(xiàn)嚴(yán)重的丟幀現(xiàn)象。 在mac應(yīng)用中我們使用的不是CADisplayLink而是 CVDisplayLink它是基于C接口的用起來配置有些麻煩但是用起來還是很簡(jiǎn)單的。

    2、Demo

    實(shí)現(xiàn)這個(gè)形變效果的基本思路就是三句話:用CADisplayLink以其自身毫秒級(jí)刷新屏幕的特點(diǎn)去不斷調(diào)用一個(gè)方法,這個(gè)方法里面畫一條貝塞爾曲線,并且貝塞爾曲線的控制點(diǎn)是個(gè)動(dòng)點(diǎn)。

    第一個(gè)gif的實(shí)現(xiàn)思路:

    首先我們需要兩個(gè)輔助視圖,并使用UIView的彈性動(dòng)畫usingSpringWithDamping 實(shí)現(xiàn)類似下面的效果:

    新建 @interface JellyView : UIView

    JellyView.m:

    - (void)drawRect:(CGRect)rect {
    
        CGFloat yOffset = 30.0;
        CGFloat width   = CGRectGetWidth(rect);
        CGFloat height  = CGRectGetHeight(rect);
    
        UIBezierPath *path = [UIBezierPath bezierPath];
        [path moveToPoint:CGPointMake(0.0, yOffset)]; //去設(shè)置初始線段的起點(diǎn)
        CGPoint controlPoint = CGPointMake(width / 2, yOffset + self.sideToCenterDelta);
        [path addQuadCurveToPoint:CGPointMake(width, yOffset) controlPoint:controlPoint];
        [path addLineToPoint:CGPointMake(width, height)];
        [path addLineToPoint:CGPointMake(0.0, height)];
        [path closePath];
    
    
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextAddPath(context, path.CGPath);
        [fillColor set];
        CGContextFillPath(context);
    
    }
    

    上面代碼繪制了一個(gè)封閉的貝塞爾曲線,初始時(shí)刻封閉曲線是一個(gè)四方的矩形,因?yàn)?[path addQuadCurveToPoint:CGPointMake(width, yOffset) controlPoint:controlPoint]; 中的 controlPoint 的縱坐標(biāo)和左右兩個(gè)定點(diǎn)縱坐標(biāo)相等。但這其實(shí)是個(gè)動(dòng)點(diǎn)。注意到 CGPoint controlPoint = CGPointMake(width / 2, yOffset + self.sideToCenterDelta); ,我們可以看到動(dòng)點(diǎn)的縱坐標(biāo)是由yOffset + self.sideToCenterDelta決定的。yOffset是固定的值。self.sideToCenterDelta等于兩個(gè)輔助視圖的高度差。最后通過 CADisplayLink 的實(shí)時(shí)繪制,我們可以就可以看到屏幕上出現(xiàn)的形變效果了。

    ViewController.m:

    先創(chuàng)建一個(gè)實(shí)例 displayLink.

    self.displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkAction:)];  
    [self.displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
    

    實(shí)現(xiàn)刷新器綁定的方法:

    -(void)displayLinkAction:(CADisplayLink *)dis{
        CALayer *sideHelperPresentationLayer   =  (CALayer *)[self.sideHelperView.layer presentationLayer];
        CALayer *centerHelperPresentationLayer =  (CALayer *)[self.centerHelperView.layer presentationLayer];
    
        CGPoint position = [[centerHelperPresentationLayer valueForKeyPath:@"position"]CGPointValue];
    
        CGRect centerRect = [[centerHelperPresentationLayer valueForKeyPath:@"frame"]CGRectValue];
        CGRect sideRect = [[sideHelperPresentationLayer valueForKeyPath:@"frame"]CGRectValue];
    
        NSLog(@"Center:%@",NSStringFromCGRect(centerRect));
        NSLog(@"Side:%@",NSStringFromCGRect(sideRect));
    
        CGFloat newJellyViewTopConstraint      =  position.y - CGRectGetMaxY(self.view.frame);
    
        self.jellyViewTopConstraint.constant = newJellyViewTopConstraint;
        [self.jellyView layoutIfNeeded];
    
        self.jellyView.sideToCenterDelta = centerRect.origin.y - sideRect.origin.y;
    
    }
    

    這里有個(gè)地方花了我好長時(shí)間,就是我們不能直接通過 self.sideHelperView.layerself.centerHelperView.layer 獲取兩個(gè)輔助視圖動(dòng)畫過程中的變化的坐標(biāo),得到的是一個(gè)恒定的終點(diǎn)狀態(tài)的坐標(biāo)。要想獲得動(dòng)畫過程中的每個(gè)狀態(tài)的坐標(biāo),我們需要使用layer的 presentationLayer ,并且通過 valueForKeyPath:@"position"的方式實(shí)時(shí)獲取動(dòng)態(tài)坐標(biāo)。

    !!最后千萬別忘了調(diào)用 [self.jellyView setNeedsDisplay]; ,否則- (void)drawRect:(CGRect)rect不會(huì)called.

    第二個(gè)gif的實(shí)現(xiàn)思路:

    接下來的思路完全大同小異,只不過實(shí)時(shí)刷新的定時(shí)器從CADisplayLink換成了同樣具有實(shí)時(shí)調(diào)用功能的手勢(shì):UIGestureRecognizerStateChanged

    新建 @interface BounceView : UIView

    BounceView.m:
    先準(zhǔn)備好一個(gè)CAShapeLayer,并且填充顏色用來顯示形變的圖形。

    - (void) createLine {
        self.verticalLineLayer = [CAShapeLayer layer];
        self.verticalLineLayer.strokeColor = [[UIColor whiteColor] CGColor];
        self.verticalLineLayer.lineWidth = 1.0;
        self.verticalLineLayer.fillColor = [[UIColor whiteColor] CGColor];
    
        [self.layer addSublayer:self.verticalLineLayer];
    }
    

    當(dāng)手勢(shì)開始變化的時(shí)候,我們讓 self.verticalLineLayer.path 等于變化中的貝塞爾曲線的CGPath,并且把手指的偏移程度的變量CGFloat amountX = [gr translationInView:self].x傳過去;

    self.verticalLineLayer.path = [self getLeftLinePathWithAmount:amountX];  
    

    貝塞爾曲線的變化代碼如下:

    //左邊曲線
    - (CGPathRef) getLeftLinePathWithAmount:(CGFloat)amount {
        UIBezierPath *verticalLine = [UIBezierPath bezierPath];
        CGPoint topPoint = CGPointMake(0, 0);
        CGPoint midControlPoint = CGPointMake(amount, self.bounds.size.height/2);
        CGPoint bottomPoint = CGPointMake(0, self.bounds.size.height);
    
        [verticalLine moveToPoint:topPoint];
        [verticalLine addQuadCurveToPoint:bottomPoint controlPoint:midControlPoint];
        [verticalLine closePath];
    
        return [verticalLine CGPath];
    }
    

    代碼還是大同小異,無非就是改變控制點(diǎn)midControlPoint,只不過這里是改變它的橫坐標(biāo)而已。

    3、總結(jié)

    歸根結(jié)底,要實(shí)現(xiàn)這個(gè)形變的Q彈效果無非就是一個(gè)實(shí)時(shí)調(diào)用一個(gè)繪制貝塞爾曲線的方法,并且這個(gè)貝塞爾曲線的控制點(diǎn)是一個(gè)動(dòng)點(diǎn)。那個(gè)實(shí)時(shí)調(diào)用就有很多實(shí)現(xiàn)的辦法了。各種原生的代理方法,當(dāng)然還包括文中提到了毫秒級(jí)刷新器CADisplayLink。期待你能做出更加動(dòng)感的動(dòng)畫:)


    資料參考:

    CADisplayLink http://www.jianshu.com/p/c35a81c3b9eb

      本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
      轉(zhuǎn)藏 分享 獻(xiàn)花(0

      0條評(píng)論

      發(fā)表

      請(qǐng)遵守用戶 評(píng)論公約

      類似文章 更多

      主站蜘蛛池模板: 人妻中文字幕不卡精品| 在线a级毛片无码免费真人 | 国产一卡2卡三卡4卡免费网站| 久久99精品久久水蜜桃| AV老司机亚洲精品天堂| 亚洲色大成网站WWW永久麻豆| 不卡AV中文字幕手机看| 亚洲国产精品成人无码区| 国产一区二区三区导航| 久久国产成人午夜AV影院| 亚洲熟妇精品一区二区| 久久婷婷综合色丁香五月| 亚洲男女羞羞无遮挡久久丫 | 美女又黄又免费的视频| 性欧美VIDEOFREE高清大喷水| 国产精品色内内在线播放| 日韩中文字幕国产精品| 深夜国产成人福利在线观看| 亚洲精品日本一区二区| 成熟丰满熟妇高潮XXXXX| 免费午夜无码片在线观看影院| 久久婷婷国产剧情内射白浆| 翘臀少妇被扒开屁股日出水爆乳| 国产精品情侣呻吟对白视频| 国产毛1卡2卡3卡4卡免费观看| 中文字幕无线码免费人妻| 亚洲精品成人久久久 | 国产高清在线男人的天堂| 18精品久久久无码午夜福利 | 少妇办公室好紧好爽再浪一点| 体验区试看120秒啪啪免费| 华人在线亚洲欧美精品| 99久久精品国产一区二区蜜芽| 亚洲男女内射在线播放| A毛片终身免费观看网站| 狠狠躁夜夜躁人人躁婷婷| 日韩免费码中文在线观看| 日本一区不卡高清更新二区| 久久超碰97人人做人人爱| 日韩免费码中文在线观看| 亚洲国产欧美一区二区好看电影|