• 媒体层图形技术之Core Animation 学习笔记


    1.CADisplayLink

    //自行定義的函式,用來設定使用CADisplayLink的相關參數
    -(void)initializeTimer {
     
        //theTimer是CADisplayLink型態的指標,用來存放當前的設定狀態
        theTimer = [CADisplayLink displayLinkWithTarget:self selector:@selector(countTotalFrames)];
     
        //CADisplayLink內定值就是每秒60張(參數=1),參數=2就是每秒30張,以此類推
        double fps = 60 / theTimer.frameInterval;
        fpsLabel.text = [NSString stringWithFormat:@"%0.1f" , fps];
     
        //設定執行狀態並啟動theTimer
        [theTimer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
    }
    
    如同 Timer / 計時器一樣,selector 是設定每次觸發時所需要呼叫的函式,其寫法如下。
    
    -(void)countTotalFrames {
        frameCount ++;
        framesLabel.text = [NSString stringWithFormat:@"%d", frameCount];
    }

    2.CAScrollLayer
    使用cascrolllayer可以实现:通过改变一个layer的origin坐标,显示它的一部分内容.它的maskstobounds属性一般设为yes,这样就只能看见它的bounds内的内容.可以通过调用cascrolllayer本身的函数或者调用它的sublayer函数实现layer的滚动功能

    3.CATextLayer可以直接支持NSAttributedString!

    CATextLayer代码:
    CATextLayer *lary = [CATextLayer layer];
        lary.string = @"dasfasa";
        lary.bounds = CGRectMake(0, 0, 320, 20);
           lary.font = @"HiraKakuProN-W3"; //字体的名字 不是 UIFont
        lary.fontSize = 12.f; //字体的大小
           lary.alignmentMode = kCAAlignmentCenter;//字体的对齐方式
        lary.position = CGPointMake(160, 410);
           lary.foregroundColor = [UIColor redColor].CGColor;//字体的颜色
        [self.view.layer addSublayer:lary];

    4.CATiledLayer

    demon:http://www.cocoachina.com/bbs/read.php?tid=31132

    levelsOfDetail是指,从UIScrollView的1倍zoomScale开始,能够支持细节刷新的缩小级数。每一级是上一级的1/2,所以假设levelsOfDetail = n,levelsOfDetailBias不指定的话,CATiledLayer将会在UIScrollView的zoomScale为以下数字时重新drawLayer
    2^-1 -> 2^-2 -> ... -> 2^-n
    也就是
    1/2, 1/4, 1/8, 1/16, ... , 1/2^n

    在levelsOfDetailBias不指定的情况下,zoomScale大于0.5后就不会再drawLayer,所以若继续放大UIScrollView的话,画面将越来越模糊。

    这个时候levelsOfDetailBias就有用了。
    levelsOfDetailBias = m表示,将原来的1/2,移到2^m倍的位置。
    假设levelsOfDetail = n,levelsOfDetailBias = m的话,会有如下队列:
    2^m * 2^-1 -> 2^m * 2^-2 -> ... -> 2^m * 2^-n
    简化一下即
    2^(m - 1) -> 2^(m - 2) -> 2^(m - 3) ->... -> 2^(m - n)

    举例,levelsOfDetail = 3,levelsOfDetailBias = 3,则你的UIScrollView将会在以下zoomScale时drawLayer
    2^(3 - 1) -> 2^(3 - 2) -> 2^(3 - 3)
    即4 -> 2 -> 1

    特例是,levelsOfDetailBias > levelsOfDetail时,则每相差2倍就会drawLayer一下。

    可以简单理解成:
    levelsOfDetail表示一共有多少个drawLayer的位置
    levelsOfDetailBias表示比1大的位置里有多少个drawLayer的位置(包括1)

    CAShapeLayer

    利用CAShapeLayer可以制作出任意的几何图形,其一是把它作为UIImageView的遮罩,达到把图片做成圆形效果。

     

     //创建个人主页头部的用户头像
    
            self.userHead = [[UIImageView alloc]initWithFrame:CGRectMake(10, 35, 80, 80)];
            self.userHead.image = [UIImage imageNamed:@"start.jpg"];
            
            //创建圆形遮罩,把用户头像变成圆形
            UIBezierPath* path = [UIBezierPath bezierPathWithArcCenter:CGPointMake(40, 40) radius:40 startAngle:0 endAngle:2*M_PI clockwise:YES];
            CAShapeLayer* shape = [CAShapeLayer layer];
            shape.path = path.CGPath;
            self.userHead.layer.mask = shape;
            [self addSubview:self.userHead];

    6.CAReplicatorLayer

    使用CAReplicatorLayer重建动态的倒影

    它 自己能够重建包括自己在内的n个copies,这些copies是原layer中的所有sublayers,并且任何对原layer的sublayers 设置的transform是可以积累的(accumulative). 基本上这样的一个关系:我们首先会重建一个CAReplicatorLayer实例,作为我们的sourceLayer, 这个sourceLayer我们需要一份copy,那包括自己在内就是2; 所以我们设置了它的instantCount = 2;这个是包括自己在内总共为2. 然后我们将SourceLayer的宽度设置为image的宽度,但是将其高度设置为image.size.height * 1.5; 并且在sourcelayer上加上masksToBounds为true的属性,这样一来我们可以保证超出的倒影部分会cut调一半,加上 sourceLayer上正常的image,刚刚好组成了我们的完整的倒影。我们sourceLayer的sublayer就是 _imageLayer,。但是我们只是单纯设置instantCount = 2的话, 那个_imageReplicatorLayer(这个就指代的是copy过来的第一个变量)是会继承sourceLayers中_imagelayer 的Geometry,所以它两是重合的。那么我们必须做出transform, CARepliatorLayer有一个属性叫做instantTransform,这个属性指定了除了原来copy之外所有replication layer的trasnform规则,重要的是它是递增的。比如我们这里需要将imageReplicatorLayer应该往下移动image的高度, 这一样来可以保证它是刚刚好在原来imagelayer的正下方,就跟倒影一样。 但是不一样的地方是:这个复制的imageReplicatorLayer它不是正常的,它是需要倒过来的,所以我们在transform上使用了 transform = CATransform3DScale(transform, 1.0, -1.0, 1.0); 这一个的意思是大小不变,但是y轴倒过来,这个应用到imageReplicatorLayer的坐标系是y轴朝上。 这样以来你就不能单纯是向原来一样移动image.height了, 因为y轴反了过来,所以你应该是 -2 * image.size.height 这样以来就搞定了。 最后我们给它加了一个渐变层,让它看起来更接近倒影的感觉。

     

    - (void)viewDidLoad
    {
        [super viewDidLoad];
    
        UIView *view = [self view];
    
        //
        // Yes, this is a very long method. I find it easier to explain what is happening by 
        // keeping the code in a single location (instead of breaking it up over multiple methods).
        // 
    
        // Load the image to "reflect"
        UIImage *image = [UIImage imageNamed:@"american-flag"];
    
        // The replicator layer is where all of the magic happens
        CAReplicatorLayer *replicatorLayer = [CAReplicatorLayer layer];
        [replicatorLayer setContentsScale:[[UIScreen mainScreen] scale]];
    
        // The replicator layer's height is 1.5 times the size of the image. This means that 
        // we will effectively clip/ fade out our "reflection".
        //
        // ********    <- any y-flipping is performed along this plane
        // *  %%  *
        // *      *    image height
        // *  ^^  *
        // ********
        // ========
        // =  ^^  =  + half height (mask to bounds effectively clips the reflected image)
        // =      =
        // 
        //           = total height of layer
        [replicatorLayer setBounds:CGRectMake(0.0, 0.0, [image size].width, [image size].height * 1.5)];
    
        // This ensures that the replicated image is clipped to the replicator's height
        [replicatorLayer setMasksToBounds:YES];
    
        // Position the replicator layer at the top of the view
        // - use the x center point to make the math easy (place in center of view)
        // - use the y upper point to position the layer 10 points below the top of the view (just a little bit of padding)
        [replicatorLayer setAnchorPoint:CGPointMake(0.5, 0.0)];
    
        [replicatorLayer setPosition:CGPointMake(view.frame.size.width / 2.0, 80.0)];
    
        // We need two instances: 
        //   1) the main image layer 
        //   2) a replicated layer for the reflection
        [replicatorLayer setInstanceCount:2];
    
        // Create a transform used by the replicator layer to "flip" and position our reflected layer below the original image
        //
        // I will see if I can explain this.
        // For clarity... we start with an identity
        CATransform3D transform = CATransform3DIdentity;
    
        // 
        // @*******    <- this is the top of the replicator layer (the X,Y origin is at the @, with Y going down)
        // *  %%  *
        // *      *    
        // *  ^^  *
        // ********    
        //
        //
        // Apply a negative scale to y (effectively flips)
        //
        // For example, hold your right hand in front of your face (knuckles facing you, thumb down). Now flip your 
        // hand up keeping your pinky finger in place (i.e. your pinky is a hinge). 
        //
        // ========    <- will draw a flipped version up here
        // =  ^^  =
        // =      =
        // =  %%  =
        // ========
        // ********    <- the "flip" is performed along here
        // *  %%  *
        // *      *    image height
        // *  ^^  *
        // ********    
        transform = CATransform3DScale(transform, 1.0, -1.0, 1.0);
    
        // translate down by 2x height to position the "flipped" layer below the main layer
        // - 2x moves the flipped image under the main image giving us the "reflection"
        //
        // ********    <- y plane (any flipping is performed along this plane)
        // *  %%  *
        // *      *    image height
        // *  ^^  *
        // ********    
        // ========      
        // =  ^^  =
        // =      =    <-- Remember: only half of the "relection" layer renders because the replicator layer clips to bounds.
        // =  %%  =
        // ========  
        transform = CATransform3DTranslate(transform, 0.0, -[image size].height * 2, 1.0);
    
        [replicatorLayer setInstanceTransform:transform];
    
        // Next we create a layer that displays the American flag image.
        _imageLayer = [CALayer layer];
        [_imageLayer setContentsScale:[[UIScreen mainScreen] scale]];
        [_imageLayer setContents:(__bridge id)[image CGImage]];
        [_imageLayer setBounds:CGRectMake(0.0, 0.0, [image size].width, [image size].height)];
        [_imageLayer setAnchorPoint:CGPointMake(0.0, 0.0)];
    
        [replicatorLayer addSublayer:_imageLayer];
    
        // Finally overlay a gradient layer on top of the "reflection" layer. 
        CAGradientLayer *gradientLayer = [CAGradientLayer layer];
        [gradientLayer setContentsScale:[[UIScreen mainScreen] scale]];
        [gradientLayer setColors:@[
            (__bridge id)[[[UIColor whiteColor] colorWithAlphaComponent:0.25] CGColor],
            (__bridge id)[[UIColor whiteColor] CGColor]
        ]];
    
        // Remember that the reflected layer is half the size, which is why the height of the gradient layer is cut in half.
        [gradientLayer setBounds:CGRectMake(0.0, 0.0, replicatorLayer.frame.size.width, [image size].height * 0.5 + 1.0)];
        [gradientLayer setAnchorPoint:CGPointMake(0.5, 0.0)];
        [gradientLayer setPosition:CGPointMake(view.frame.size.width / 2, [image size].height + 80.0)];
        [gradientLayer setZPosition:1]; // make sure the gradient is placed on top of the reflection.
    
        [[view layer] addSublayer:replicatorLayer];
        [[view layer] addSublayer:gradientLayer];
    
        // One final (and fun step): 
        //   Create a text layer that is a sublayer of the image layer.
        //   Core Animation will animate the text in all replicated layers. VERY COOL!!
        CATextLayer *textLayer = [CATextLayer layer];
        [textLayer setContentsScale:[[UIScreen mainScreen] scale]];
        [textLayer setString:@"U.S.A."];
        [textLayer setAlignmentMode:kCAAlignmentCenter];
    
        CGFloat height = [(UIFont *)[textLayer font] lineHeight];
        [textLayer setBounds:CGRectMake(0.0, 0.0, [_imageLayer frame].size.width, height)];
        [textLayer setPosition:CGPointMake([_imageLayer frame].size.width / 2.0, [_imageLayer frame].size.height - 25.0)];
        [textLayer setAnchorPoint:CGPointMake(0.5, 0.5)];
        [_imageLayer addSublayer:textLayer];
    
        // When the user taps, start _animating the image's text layer up and down.
        [view setUserInteractionEnabled:YES];
        [view setMultipleTouchEnabled:YES];
        [view addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(animateTextLayer:)]];
    }
    
    - (void)animateTextLayer:(UIGestureRecognizer *)recognizer
    {
        CALayer *textLayer = (CALayer *)[[_imageLayer sublayers] objectAtIndex:0];
    
        CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"position.y"];
    
        CGFloat halfBoxHeight = [textLayer frame].size.height / 2.0;
        [animation setFromValue:@([textLayer frame].origin.y + halfBoxHeight)];
        [animation setToValue:@(halfBoxHeight)];
        [animation setDuration:3.0];
        [animation setRepeatCount:MAXFLOAT];
        [animation setAutoreverses:YES];
    
        [textLayer addAnimation:animation forKey:nil];
    }
    


    7. 区分隐式动画和隐式事务:隐式动画通过隐式事务实现动画 。

    区分显式动画和显式事务:显式动画有多种实现方式,显式事务是一种实现显式动画的方式。


    ->隐式事务
    除显式事务外,任何对于CALayer属性的修改,都是隐式事务.这样的事务会在run-loop中被提交.

     

    - (void)viewDidLoad {
        //初始化一个layer,添加到主视图
        layer=[CALayer layer];
        layer.bounds = CGRectMake(0, 0, 200, 200);
        layer.position = CGPointMake(160, 250);
        layer.backgroundColor = [UIColor redColor].CGColor;
        layer.borderColor = [UIColor blackColor].CGColor;
        layer.opacity = 1.0f;
        [self.view.layer addSublayer:layer];    
        [super viewDidLoad];
    }
    
    -(IBAction)changeLayerProperty
    {
        //设置变化动画过程是否显示,默认为YES不显示
        [CATransaction setDisableActions:NO];
        //设置圆角
        layer.cornerRadius = (layer.cornerRadius == 0.0f) ? 30.0f : 0.0f;
        //设置透明度
        layer.opacity = (layer.opacity == 1.0f) ? 0.5f : 1.0f;
    }  

    ->显式事务

     

    通过明确的调用begin,commit来提交动画

    修改执行时间
    
     [CATransaction begin];
    
    //显式事务默认开启动画效果,kCFBooleanTrue关闭
    
    [CATransaction setValue:(id)kCFBooleanFalse
    
    forKey:kCATransactionDisableActions];
    
    //动画执行时间
    
    [CATransaction setValue:[NSNumber numberWithFloat:5.0f] forKey:kCATransactionAnimationDuration];
    
        //[CATransaction setAnimationDuration:[NSNumber numberWithFloat:5.0f]];
    
    anotherLayer.cornerRadius = (anotherLayer.cornerRadius == 0.0f) ? 30.0f : 0.0f;
    
    layer.opacity = (layer.opacity == 1.0f) ? 0.5f : 1.0f;
    
    [CATransaction commit];

    ->事物嵌套

     

    事务嵌套
            [CATransaction begin];
         [CATransaction begin];
        [CATransaction setDisableActions:YES];
         layer.cornerRadius = (layer.cornerRadius == 0.0f) ? 30.0f : 0.0f;
        [CATransaction commit];
        //上面的动画并不会立即执行,需要等最外层的commit
        [NSThread sleepForTimeInterval:10];
        //显式事务默认开启动画效果,kCFBooleanTrue关闭
        [CATransaction setValue:(id)kCFBooleanFalse
                         forKey:kCATransactionDisableActions];
        //动画执行时间
        [CATransaction setValue:[NSNumber numberWithFloat:10.0f] forKey:kCATransactionAnimationDuration];
        //[CATransaction setAnimationDuration:[NSNumber numberWithFloat:5.0f]];
        anotherLayer.cornerRadius = (anotherLayer.cornerRadius == 0.0f) ? 30.0f : 0.0f;
    
        
        [CATransaction commit];

    8.CATransform3D

     

    {
    • ;
    • CGFloat m21(x切变), m22(y缩放), m23(), m24();
    1. ;
    2. CGFloat m41(x平移), m42(y平移), m43(z平移), m44();
    • };

    首先要实现view(layer)的透视效果(就是近大远小),是通过设置m34的:

     

    CABasicAnimation *pulseAnimation = [CABasicAnimation animationWithKeyPath:@"transform"];
        [pulseAnimation setDuration:_animationDuration];
        [pulseAnimation setRepeatCount:MAXFLOAT];
    
        // The built-in ease in/ ease out timing function is used to make the animation look smooth as the layer
        // animates between the two scaling transformations.
        [pulseAnimation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut]];
    
        // Scale the layer to half the size
        //CATransform3D transform_1 = CATransform3DIdentity;//CATransform3DMakeScale(1.5, 1.5, 1.0);
    
        CATransform3D transform = CATransform3DMakeRotation((M_PI/180*180), 1, 0, 1);
        
        transform.m34 = 0.5;
        
        
        
        // Tell CA to interpolate to this transformation matrix
        [pulseAnimation setToValue:[NSValue valueWithCATransform3D:transform]];
    
        // Tells CA to reverse the animation (e.g. animate back to the layer's transform)
        [pulseAnimation setAutoreverses:_autoreverses];
    
        // Finally... add the explicit animation to the layer... the animation automatically starts.
        [_layer addAnimation:pulseAnimation forKey:kBTSPulseAnimation];



    9.CAValueFunction

    动画类型:平移、缩放、旋转

    如把一个对象旋转180度可以使用一下方式:

     

    CABasicAnimation * rotationAni = [CAAnimation animation];
        CAValueFunction * valuFunction = [CAValueFunction functionWithName:kCAValueFunctionRotateZ];
        [rotationAni setFromValue:0];
        [rotationAni setToValue:@(M_PI)];
        [rotationAni setValueFunction:valuFunction];

     

    10.CAEmitterCell、CAEmitterLayer 实现粒子效果

    利用Core Animation、CAEmitterCell 以及 CAEmitterLayer在iOS5中实现各种粒子动画效果,包括雪花、火焰、烟雾、飘动的花瓣、爆炸等效果。

    属性详解请参考:http://guxiaojje.blog.163.com/blog/static/1409422912012813104917788/

    - (void) viewDidLoad
    {
        [super viewDidLoad];
    	
    	// Configure the particle emitter
    	self.heartsEmitter = [CAEmitterLayer layer];
    	self.heartsEmitter.emitterPosition = CGPointMake(likeButton.frame.origin.x + likeButton.frame.size.width/2.0, 
    													 likeButton.frame.origin.y + likeButton.frame.size.height/2.0);//放射源的位置
    	self.heartsEmitter.emitterSize = likeButton.bounds.size;//放射源的大小
    	
    	// Spawn points for the hearts are within the area defined by the button frame
    	self.heartsEmitter.emitterMode = kCAEmitterLayerVolume;//放射源的模式
    	self.heartsEmitter.emitterShape = kCAEmitterLayerRectangle;//放射源的形状
    	self.heartsEmitter.renderMode = kCAEmitterLayerAdditive;//放射源的渲染模式
    	
    	// Configure the emitter cell
    	CAEmitterCell *heart = [CAEmitterCell emitterCell];//发射的粒子
    	heart.name = @"heart";
    	
    	heart.emissionLongitude = M_PI/2.0; // up 粒子在xy平面内发射的方向
    	heart.emissionRange = 0.55 * M_PI;  // in a wide spread 发射的水平方向的范围
    	heart.birthRate		= 0.0;			// emitter is deactivated for now 粒子再生的速度,0就是不会再生
    	heart.lifetime		= 10.0;			// hearts vanish after 10 seconds
    
    	heart.velocity		= -120;			// particles get fired up fast 初始速度是-120 向上
    	heart.velocityRange = 60;			// with some variation
    	heart.yAcceleration = 20;			// but fall eventually
    	
    	heart.contents		= (id) [[UIImage imageNamed:@"DazHeart"] CGImage];
    	heart.color			= [[UIColor colorWithRed:0.5 green:0.0 blue:0.5 alpha:0.5] CGColor];
    	heart.redRange		= 0.3;			// some variation in the color
    	heart.blueRange		= 0.3;
    	heart.alphaSpeed	= -0.5 / heart.lifetime;  // fade over the lifetime
    	
    	heart.scale			= 0.15;			// let them start small
    	heart.scaleSpeed	= 0.5;			// but then 'explode' in size
    	heart.spinRange		= 2.0 * M_PI;	// and send them spinning from -180 to +180 deg/s
    	
    	// Add everything to our backing layer
    	self.heartsEmitter.emitterCells = [NSArray arrayWithObject:heart];
    	[self.view.layer addSublayer:heartsEmitter];
    }
    
    
    - (void) viewWillUnload
    {
    	[super viewWillUnload];
    	[self.heartsEmitter removeFromSuperlayer];
    	self.heartsEmitter = nil;
    }
    
    
    - (BOOL) shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
    {
    	return (interfaceOrientation == UIDeviceOrientationPortrait);
    }
    
    
    // ---------------------------------------------------------------------------------------------------------------
    #pragma mark -
    #pragma mark Interaction
    // ---------------------------------------------------------------------------------------------------------------
    
    - (IBAction) likeButtonPressed:(id)sender 
    {
    	// Fires up some hearts to rain on the view
    	CABasicAnimation *heartsBurst = [CABasicAnimation animationWithKeyPath:@"emitterCells.heart.birthRate"];
    	heartsBurst.fromValue		= [NSNumber numberWithFloat:150.0];//喷出的粒子的数量
    	heartsBurst.toValue			= [NSNumber numberWithFloat:  0.0];
    	heartsBurst.duration		= 5.0;
    	heartsBurst.timingFunction	= [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
    	
    	[self.heartsEmitter addAnimation:heartsBurst forKey:@"heartsBurst"]; 
    }
    




  • 相关阅读:
    奇怪的问题:Linux执行脚本碰到Permission denied问题(记录)
    MySQL中的显式锁---MySQL用户级锁函数
    使用Python的tenacity库实现异常重试机制
    json.net payload
    Eclipse Plugin
    MyEclipse web项目转换为eclipse web项目
    Nginx The system cannot find the path specified
    Spring Boot实战:静态资源无法访问
    WebService rwsp:NoRunningCommunicationPointAvailable
    Intellij IDEA 自动生成 serialVersionUID
  • 原文地址:https://www.cnblogs.com/allanliu/p/4223427.html
Copyright © 2020-2023  润新知