iOS 2D繪圖 (Quartz2D)之陰影和漸變(shadow,Gradient)


原博地址:http://blog.csdn.net/hello_hwc/article/details/49507881

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();
    //填充邊框 當然你可以用layer設置
    CGContextSetStrokeColorWithColor(context, [UIColor darkGrayColor].CGColor);
    CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
    CGContextFillRect(context, rect);
    CGContextStrokeRect(context, rect);
    
    CGContextAddArc(context, 100, 100, 50, 0, M_2_PI, 0);
    CGContextSetLineCap(context, kCGLineCapRound);
    CGContextSetLineWidth(context, 3.0);
    CGContextSetShadow(context, CGSizeMake(4.0, 4.0), 1.0);
    //CGContextSetShadowWithColor(context, CGSizeMake(4.0, 4.0), 1.0, [UIColor blueColor].CGColor);
    
    CGContextStrokePath(context);
}

效果


Gradient 漸變色

漸變無非就是從一種顏色逐漸變換到另一種顏色,Quartz 2D提供了兩種漸變的模型。

Quartz提供了兩個不透明數據odgago創建漸變:CGShadingRef 和 CGGradientRef.我們可以使用任何一個來創建軸向(axial)或徑向(radial)漸變。一個 漸變是從一個顏色到另外一個顏色的填充。

一個軸向漸變(也稱為線性漸變)沿着兩個端點連接的軸線漸變。所有位於垂直於軸線的的某條線上的點都具有相同的顏色值。

一個徑向漸變也是沿着兩個端點連接的軸線漸變,不過路徑通常由兩個圓來定義。

 

1> axial gradient 線性漸變(軸向漸變),使用的時候設置好兩個頂點的顏色 (也可以設置中間過渡色)

例如:

軸向漸變由橙色向黃色漸變 在這個例子中漸變軸相對於遠點傾斜了四十五度角。

Quartz 也允許我們設置一系列的顏色值和位置值,以沿着軸來創建更復雜的軸向漸變。如下圖所示,起始點的顏色設置為紅色(可以看一下Quartz 的坐標系 零點默認就是左下角),結束點的顏色是紫羅蘭色。同時,在軸上有5個位置,它們的顏色分別設置為橙 黃 綠 藍 和 靛藍。我們可以把它看成沿着同一軸線的六段連續的線性漸變。另外軸線的角度 有我們設置的兩個端點來定義。

2> radial gradient(徑向漸變)

這種模式的漸變允許 一個圓到另一個圓的漸變

如下圖所示,他從一個小的明亮的紅色圓漸變到一個大小黑色的圓,

 

 

我們可以把一個圓放置到一個徑向漸變中來創建各種形狀。如果一個圓是另一個的一部分或者完全在另一個的外面,則Quartz創建了圓錐和一個圓柱。徑向漸變的一個通常用法就是創建一個球體陰影

一個單一的點(半徑為0的圓)位於一個大圓以內。

CGShading 和 CGGradient對象的對比

我們有兩個對象類型用於創建漸變,你可能想知道哪一個更好用,看了下面你就會知道了。

CGshadingRef這個不透明的數據類型給我們更多的控制權,以確定如何計算每個端點的顏色,在我們創建CGShading對象之前,我們必須創建一個CGFunction對象(CGFunctionRef),這個對象定義了一個用於計算漸變色的函數。寫一個自定義的函數讓我們創建平滑的漸變。

當我們創建一個CGShading對象時,我們指定其是軸向還是徑向,除了計算函數以外,我們還需要提供一個顏色空間。起始點 結束點 或者是半徑,這取決於繪制軸向還是徑向漸變。在繪制時,我們只是簡單的傳遞CGShading對象以及繪制上下文給CGContextDrawShading函數,Quartz為漸變上的每個點調用漸變計算函數。

一個CGGRadient對象是CGShading對象的子集,其更容易使用,CGGradientRef不透明類型抑郁作用,因為Quartz在漸變的每一點上計算顏色值。我們不需要提供一個漸變計算函數。當創建好一個漸變對象的時候,我們提供一個位置和顏色的數組。quartz使用對應的顏色值來計算每個梯度的漸變。我們可以使用單一的氣勢與結束點來設置一個漸變對象。或者提供一組斷電來創建一個多顏色漸變的效果。使用CGShading對象可以提供多於兩個位置的能力。

當我們創建一個CGGradient對象時,我們需要設置一個顏色空間、位置、和每個位置對應的顏色值。當使用一個漸變對象繪制上下文時,我們指定Quartz是繪制一個軸向還是徑向漸變。在繪制時,我們指定開始結束點或半徑,這取決於我們是繪制軸向還是徑向漸變。而CGShading的幾何形狀是在創建時定義的,而不是繪制時。

擴展漸變端點外部的顏色

當我們創建一個漸變時,我們可以選擇使用純色來填充漸變端點外部的空間。Quartz使用使用漸變邊界上的顏色作為填充顏色。我們可以擴展漸變起點、終點或兩端的顏色。我們可以擴展使用CGShading對象或CGGradient對象創建的軸向或徑向漸變。

圖演示了一個軸向漸變,它擴展了起點和終點兩側的區域。圖片中的線段顯示了漸變的軸線。我們可以看到,填充顏色與起點和終點的顏色是對應的。

通過 這兩種漸變的嵌套使用 Quartz 2D能繪制出非常漂亮的圖形


漸變的兩種繪制模型

CGShading -使用這種數據類型需要自己定義CFFunction來計算每一個點的漸變色,較為復雜,但是能夠靈活的繪制。

CGGradient-使用這種數據類型只需要制定兩個頂點的顏色,以及繪制模式,其余的Quartz會給繪制,但是漸變的數學模型不靈活。


使用CGGradient對象:

一個CGGradient 對象是一個漸變的抽象定義 他簡單的質地昂了顏色值和位置,但沒有指定任何幾何形狀,我們可以在軸向和徑向幾何形狀中使用這個對象,作為一個軸向定義,CGGradient對象可能比CGShading對象更容易重用。沒有講任何幾何形狀存儲在CGGradient對象中。這樣允許我們使用相同的顏色方案來繪制不同的幾何圖形,而不需要為多個圖形創建多個CGGadient對象。

因為Quartz為我們計算漸變,使用一個CGGradient對象來創建和繪制一個漸變則更直接,只需要以下幾步:

1>創建一個CGGradient對象,提供一個顏色空間,一個飽含兩個或更多顏色組件的數組,一個包含兩個或多個位置的數組,和兩個數組中元素的個數。

2>使用CGContextDrawLinearGradient或者CGContentDrawRadialGradient繪制。

3>釋放CGGradient對象

一個位置是一個值區間在0.0到1.0之間的CGFloat值,他指定了沿着漸變的軸線的標准化距離值0.0的值指定軸線的起點,1.0的值指定了軸線的重點。其他值指定了一個距離的比例。最低限度情況下,Quartz使用兩個位置值。如果我們傳遞NULL值作為位置數組參數,則Quartz使用0作為第一個位置,1作為第二個位置。

每個顏色的顏色組件的數目取決於顏色空間。對於離屏繪制,我們使用一個RGB顏色空間。因為Quartz使用alpha來繪制,每個離屏顏色都有四個組件—紅、綠、藍和alpha。所以,對於離屏繪制,我們提供的顏色組件數組的元素的數目必須是位置數目的4倍。Quartz的RGBA顏色組件可以在0.0到1.0之間改變。

代碼

- (void)drawRect:(CGRect)rect {
    
    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(rect.size.width, rect.size.height); CGContextDrawLinearGradient(context, gradient, startPoint, endPoint, 0); CGColorSpaceRelease(deviceRGB); CGGradientRelease(gradient); }

效果

徑向漸變

CGContextRef context = UIGraphicsGetCurrentContext();
    //顏色空間
    CGColorSpaceRef deviceRGB = CGColorSpaceCreateDeviceRGB();
    //位置數組和顏色數組包含元素的個數
    size_t num_of_locations = 2;
    CGFloat locations[2] = {0.0,1.0};
    CGFloat components[8] = {0.0,0.0,1.0,1.0,//白色
        0.0,1.0,0.0,1.0};//黑色
    CGGradientRef gradient = CGGradientCreateWithColorComponents(deviceRGB, components, locations, num_of_locations);
    CGPoint startCenter = CGPointMake(100, 100);
    CGPoint endCenter = CGPointMake(125, 125);
    CGFloat startRadius = 0.0;
    CGFloat endRadius = 50.0;
    CGContextDrawRadialGradient(context, gradient, startCenter, startRadius, endCenter, endRadius, 0);
    CGColorSpaceRelease(deviceRGB);
    CGGradientRelease(gradient);


 

使用CGShading對象

我們通過調用函數CGShadingCreateAxial或者CGShadingCreateRadial創建一個CGShading對象來設置一個漸變,調用這些函數需要提供以下參數。

1.CGColorSpace 對象:顏色空間

2.起始點和終點。對於軸向漸變,有軸向的起始點和和終點的坐標,對於徑向漸變,有起始圓和終點圓的中心坐標

3.用於定義漸變區域的圓的啟示半徑和終止半徑

4.一個CGFunction對象,可以通過CGFunctionCreate函數來獲取。這個回調例程必須返回繪制到特定點的顏色值。

5.一個布爾值用於指定是否適用純色來繪制起始點和終點的擴展區域

我們提供給CGShading創建函數的CGFunction對象包含一個回調結構體,及Quartz需要實現這個回調的所有信息。也許設置CGShasing對象的最棘手的部分是創建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的數據(就是指向回調所需要的數據的指針),注意他的生命周期可能不只是方法的生命周期

domainDimesion 輸入的數量,quart中,就是1。(回調輸入值的個數,Quartz要求回調攜帶一個輸入值)

domain 一組數據 確定輸入的有效間隔,在Quartz中是0到1,0表示開始,1表示結束 (一個浮點數的數組。Quartz只會提供數組中的一個元素給回調函數。一個轉入值的范圍是0(漸變的開始點的顏色)到1(漸變的結束點的顏色)。)

rangDimension 輸出的數量:(回調提供的輸出值的數目,對於每一個輸入值,我們的回調必須為每個顏色組件提供一個值,以及一個alpha值來指定透明度,顏色組件值由Quartz提供的顏色空間來解釋,並提供給CGShading創建函數。例如如果我們使用RGB顏色空間,則我們提供4作為輸出值(R,G,B,A)的數目)

rang 輸出的有效間隔 (一個浮點數的數組,用於指定每個顏色組件的值及alpha值)

callbacks 用來計算的實際方法 (一個回調數據結構,包含結構體的版本(設置為0)、生成顏色組件值的回調、一個可選的用於釋放回調中info參數表示的數據。)格式如下 格式如下void myCalculateShadingValues (void *info, const CGFloat *in, CGFloat *out)

 


 

在創建CGShading對象后,如果需要我們可以設置額外的裁減操作。然后調用CGContextDrawShading函數來使用漸變來繪制上下文的裁減區域。當調用這個函數時,Quartz調用回調函數來獲取從起點到終點這個范圍內的顏色值。

當不再需要CGShading對象時,我們調用CGShadingRelease來釋放它。

首先設置CGFunction對象來計算顏色值

我們可以以我們想要的方式來計算顏色值,我們的顏色計算函數包含以下三個參數:

1.void *info 這個值可以為NULL 或者是一個指向傳遞給CGShading創建函數的數據

2.const CGFloat *in  Quartz傳遞in數組給回調。數組中的值必須在為CGFunction對象定義的輸入值范圍內

3.我們的回調函數傳遞out數組給Quartz。它包含用於顏色空間中每個顏色組件的元素及一個alpha值。輸出值應該在CGFunction對象中定義的輸出值的范圍內

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)

創建一個CGFuction

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); 
}

其中,每一行分別為

以colorspace作為參數
定義callback函數
計算顏色域中的顏色組建的個數,例如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);

 

具體效果請看 這里 http://www.tuicool.com/articles/biieum

我的水平還是有點看不懂啊


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM