• iOS 2D绘图详解(Quartz 2D)之阴影和渐变(Shadow,Gradient)


    前言:这个系列写道这里已经是第五篇了,本文会介绍下阴影和渐变的基础知识,以及一些基本的Demo Code展示,应该还会有两篇,介绍下Bitmap绘制以及Pattern等知识。


    Shadow

    shadow(阴影)的目的是为了使UI更具有立体感,如图

    shadow主要有三个影响因素

    • x off-set 决定阴影沿着x的偏移量
    • y off-set 决定阴影沿着y的偏移量
    • blur value 决定了阴影的边缘区域是不是模糊的

    其中不同的blur效果如图

    注意

    Shadow也是绘制状态相关的,意味着如果仅仅要绘制一个subpath的shadow,要注意save和restore状态。

    相关函数

    CGContextSetShadow
    CGContextSetShadowWithColor//位移区别是设置了阴影颜色

    参数

    • context 绘制画板
    • offset 阴影偏移量,参考context的坐标系
    • blur 非负数,决定阴影的模糊程度
      示例代码
    - (void)drawRect:(CGRect)rect {
        CGContextRef context = UIGraphicsGetCurrentContext();
        CGContextAddArc(context,40, 40, 20, 0,M_2_PI,0);
        CGContextSetLineCap(context, kCGLineCapRound);
        CGContextSetLineWidth(context,3.0);
        CGContextSetShadow(context,CGSizeMake(4.0, 4.0),1.0);
        CGContextStrokePath(context);
    }
    -(instancetype)initWithFrame:(CGRect)frame{
        if(self = [super initWithFrame:frame]){
            self.opaque = NO;
            self.layer.borderColor = [UIColor lightGrayColor].CGColor;
            self.layer.borderWidth =  1.0;
        }
        return self;
    }
    

    效果

    关于状态保存会在最后和gradient一起再讲解一次


    Gradient

    渐变无非就是从一种颜色逐渐变换到另一种颜色,quart提供了两种渐变模型。

    • axial gradient,线性渐变,使用的时候设置好两个顶点的颜色(也可以设置中间过渡色)
      例如
      只设置两个颜色,和顶点

      设置中间过渡色

    • radial gradient
      这种模式的渐变允许,一个圆到另一个圆的渐变

      一个点到一个圆的渐变

    注意,可以对渐变结束或者开始的额外区域使用指定颜色填充
    ,效果

    通过这两种渐变的嵌套使用,Quartz 2D能够绘制出非常漂亮的图形


    渐变的两种绘制模型

    • CGShading - 使用这种数据类型需要自己定义CFFunction来计算每一个点的渐变颜色,较为复杂,但是能够更灵活的绘制。
    • CGGradient- 使用这种数据类型只需要制定两个顶点的颜色,以及绘制模式,其余的Quartz会给绘制,但是渐变的数学模型不灵活。

    CGGradient的例子

    使用步骤

    • 创建一个CGGradient对象,指定颜色域(一般就是RGB),指定颜色变化的数组,指定对应颜色位置的数组,指定每个数组数据的个数
    • 用CGContextDrawLinearGradient或者CGContextDrawRadialGradient绘制
    • 释放CGGradient对象

    代码

       CGContextRef context = UIGraphicsGetCurrentContext();
        //用CGGradient绘制
        CGColorSpaceRef deviceRGB = CGColorSpaceCreateDeviceRGB();
        size_t num_of_locations = 2;
        CGFloat locations[2] = {0.0,1.0};
        CGFloat components[8] = {1.0, 0.0, 0.0, 1.0,  // 红色
                                 0.0, 1.0, 0.0, 1.0};//绿色
        CGGradientRef gradient = CGGradientCreateWithColorComponents(deviceRGB, components, locations,num_of_locations);
        CGPoint startPoint = CGPointMake(0, 0);
        CGPoint endPoint = CGPointMake(100, 100);
        CGContextDrawLinearGradient(context,gradient,startPoint, endPoint,0);
        CGColorSpaceRelease(deviceRGB);
        CGGradientRelease(gradient);
    

    效果

    然后,我们绘制一个RadialGradient,
    代码

     CGContextRef context = UIGraphicsGetCurrentContext();
        //用CGGradient绘制
        CGColorSpaceRef deviceRGB = CGColorSpaceCreateDeviceRGB();
        size_t num_of_locations = 2;
        CGFloat locations[2] = {0.0,1.0};
        CGFloat components[8] = {1.0, 1.0, 1.0, 1.0,  // 白色
                                 0.0, 0.0, 0.0, 1.0};//黑色
        CGGradientRef gradient = CGGradientCreateWithColorComponents(deviceRGB, components, locations,num_of_locations);
        CGPoint startCenter = CGPointMake(30, 30);
        CGPoint endCenter = CGPointMake(50, 50);
        CGFloat startRadius = 0.0;
        CGFloat endRadius = 40.0;
        CGContextDrawRadialGradient(context,gradient, startCenter,startRadius, endCenter, endRadius, 0);
        CGColorSpaceRelease(deviceRGB);
        CGGradientRelease(gradient);
    

    效果


    CGShading绘制

    使用CGShadingCreateAxial或者CGShadingCreateRadial来创建对象,传入的参数如下

    • Color Space,处理的颜色域,在iOS通畅就是device RGB
    • 开始的点和结束的点
    • 对于 radial gradient要传入开始和结束的半径
    • CGFunction 对象来计算每个点的显示值
    • 一个bool值,来确定是否要填充没有被渐变覆盖的区域

    这里面最复杂的就是创建一个CGFunction对象,使用CGFunctionCreate来创建,我们线看看这个函数

    CGFunctionRef _Nullable CGFunctionCreate (
       void * _Nullable info,
       size_t domainDimension,
       const CGFloat * _Nullable domain,
       size_t rangeDimension,
       const CGFloat * _Nullable range,
       const CGFunctionCallbacks * _Nullable callbacks
    );

    乍一看是不是不想再学了?当然,CGGradient对象足矣满足大部分时候的需求,不过有空的话还是耐心下来看看吧。我们先看看参数

    • info 用来传递到callback的数据,注意,它的生命周期有可能不只是方法的生命周期
    • domainDimension 输入的数量,quart中,就是1
    • domain 一组数据,确定输入的有效间隔。quart中是0到1,0表示开始,1表示结束
    • rangeDimension 输出的数量
    • range 输出的有效间隔
    • callbacks 用来计算的实际方法,格式如下void myCalculateShadingValues (void *info, const CGFloat *in, CGFloat *out)

    可能这样讲还是不清楚,看个例子就明白了(例子来自官方文档,我只是翻译过来的)


    首先定义callback来计算实际像素值

    static void myCalculateShadingValues (void *info,
                                const CGFloat *in,
                                CGFloat *out)
    {
    CGFloat v;
        size_t k, components;
        static const CGFloat c[] = {1, 0, 0.5, 0 };
        components = (size_t)info;
        v = *in;
        for (k = 0; k < components -1; k++)
            *out++ = c[k] * v;
         *out++ = 1;
    }

    这里的三个参数,函数很简单out的值(r,g,b,a)分别为(in*1,in*0.in*0.5,1)


    创建一个CGFunction

    static CGFunctionRef myGetFunction (CGColorSpaceRef colorspace) //1 {
    size_t numComponents;
    static const CGFloat input_value_range [2] = { 0, 1 };
    static const CGFloat output_value_ranges [8] = { 0, 1, 0, 1, 0, 1, 0, 1 }; static const CGFunctionCallbacks callbacks = { 0, //2
                                    &myCalculateShadingValues,
                                    NULL };
    numComponents = 1 + CGColorSpaceGetNumberOfComponents (colorspace); //3
     return CGFunctionCreate ((void *) numComponents,  1,  input_value_range,  numComponents, output_value_ranges,  &callbacks); 
    }

    其中,每一行分别为

    1. 以colorspace作为参数
    2. 定义callback函数
    3. 计算颜色域中的颜色组建的个数,例如RGB就是三个,然后加一,表示alpha通道

    用CGShading绘制Axial Gradient

    CGPoint     startPoint,
                endPoint;
    CGFunctionRef myFunctionObject;
    CGShadingRef myShading;
    startPoint = CGPointMake(0,0.5);
    endPoint = CGPointMake(1,0.5);
    colorspace = CGColorSpaceCreateDeviceRGB();
    myFunctionObject = myGetFunction (colorspace);
    myShading = CGShadingCreateAxial (colorspace,
                            startPoint, endPoint,
                            myFunctionObject,
                            false, false)
    CGContextDrawShading (myContext, myShading);
    CGShadingRelease (myShading);
    CGColorSpaceRelease (colorspace);
    CGFunctionRelease (myFunctionObject);

    用CGShading绘制Radial Gradient

    原理类似
    callback

    static void  myCalculateShadingValues (void *info,
                                    const CGFloat *in,
                                    CGFloat *out)
    {
        size_t k, components;
        double frequency[4] = { 55, 220, 110, 0 };
        components = (size_t)info;
        for (k = 0; k < components - 1; k++)
            *out++ = (1 + sin(*in * frequency[k]))/2;
         *out++ = 1; // alpha
    }
    CGPoint startPoint, endPoint;
          CGFloat startRadius, endRadius;
          startPoint = CGPointMake(0.25,0.3);
          startRadius = .1;
          endPoint = CGPointMake(.7,0.7);
          endRadius = .25;
          colorspace = CGColorSpaceCreateDeviceRGB();
          myShadingFunction = myGetFunction (colorspace);
          CGShadingCreateRadial (colorspace,
                          startPoint,
                          startRadius,
                          endPoint,
                          endRadius,
                          myShadingFunction,
                          false,
                          false)
     CGContextDrawShading (myContext, shading);
     CGShadingRelease (myShading);
     CGColorSpaceRelease (colorspace);
     CGFunctionRelease (myFunctionObject);

    效果

  • 相关阅读:
    python学习笔记(一)--之list与tuple
    centos 安装redis3.0为解决数据库频繁插入数据IO性能问题
    Win32汇编木马初探
    Knockout自定义绑定my97datepicker
    iTextSharp给PDF添加水印
    技术资料整理
    css模拟阴影和小三角
    统计一个部门有多少人
    js登录界面带提示
    程序员给女朋友用HTML5制作的3D相册 (www.webhek.com)<转摘>
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/5948992.html
Copyright © 2020-2023  润新知