常見的引導頁空心遮罩

今天早上,在群里看到一個同學在問,類似下面這樣的引導頁,鏤空透明看到下面圖層的圈圈怎么實現?

3FEA76D476E1E6AE1F9AEE51E9021606.png

其實這個東西,最簡單最高效的做法,當然是叫UI出圖。但其實,用代碼我們也照樣可以實現,也很簡單,也就幾行代碼而已。

    CGMutablePathRef path = CGPathCreateMutable();
    CGPathAddRect(path, NULL, self.view.bounds); 
    
    CGPathRef subPath = CGPathCreateWithEllipseInRect(CGRectMake(self.view.bounds.size.width * 0.5, self.view.bounds.size.height - 50, 50, 50), NULL);
    CGPathAddPath(path, NULL, subPath);
    CGPathCloseSubpath(path);
    
    CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
    maskLayer.path = path;
    maskLayer.fillColor = [[UIColor blackColor] colorWithAlphaComponent:0.55].CGColor;
    maskLayer.fillRule = kCAFillRuleEvenOdd;
    [self.view.layer addSublayer:maskLayer];

上面這幾行代碼關鍵是layer的fullRule屬性,在文檔可以找到蘋果給我們提供了兩個常量值,kCAFillRuleNonZerokCAFillRuleEvenOdd,引用官方文檔的解釋

kCAFillRuleNonZero

kCAFillRuleNonZero // 非零

Specifies the non-zero winding rule. Count each left-to-right path as +1 and each right-to-left path as -1. If the sum of all crossings is 0, the point is outside the path. If the sum is nonzero, the point is inside the path and the region containing it is filled.

這里的left-to-rightright-to-left可以理解為順時針跟逆時針方向,順時針加1,逆時針減1,如果交叉后的結果為0,則說明某個點不在這個path內,也就意味著不被渲染;反之,結果為非零,就是在這個path內,就被渲染。

這樣說出來其實并不好理解,那么來一段demo,理解起來就會好點了。

    CGPoint arcCenter = CGPointMake(self.view.bounds.size.width * 0.5, self.view.bounds.size.height * 0.5);

    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:arcCenter radius:50 startAngle:0 endAngle:2 * M_PI clockwise:YES]; // 外路徑順時針
    UIBezierPath *subPath = [UIBezierPath bezierPathWithArcCenter:arcCenter radius:25 startAngle:0 endAngle:2 * M_PI clockwise:NO]; // 內路徑逆時針
    [path appendPath:subPath];
    
    CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
    maskLayer.path = path.CGPath;
    maskLayer.fillColor = [UIColor yellowColor].CGColor;
    maskLayer.strokeColor = [UIColor blackColor].CGColor;
    maskLayer.fillRule = kCAFillRuleNonZero; // 非零模式
    [self.view.layer addSublayer:maskLayer];

結果如下:

kCAFillRuleNonZero.png

可以看到,外邊的path是順時針,內部的path是逆時針,那么實際上中間的點的num of crossing就為0,根據上面的描述,就會被放棄渲染,也就鏤空透明了。

kCAFillRuleEvenOdd

** kCAFillRuleEvenOdd** // 奇偶

Specifies the even-odd winding rule. Count the total number of path crossings. If the number of crossings is even, the point is outside the path. If the number of crossings is odd, the point is inside the path and the region containing it should be filled.

奇偶原則實際上非零簡單,它并沒有順/逆時針之分,你可以簡單的理解為路徑的重疊數,number of crossings為偶數,表示在path之外;為奇數,表示在path之內。同樣的,對上面的demo稍作修改

    CGPoint arcCenter = CGPointMake(self.view.bounds.size.width * 0.5, self.view.bounds.size.height * 0.5);
    UIBezierPath *path = [UIBezierPath bezierPathWithArcCenter:arcCenter radius:50 startAngle:0 endAngle:2 * M_PI clockwise:YES]; // 最外路徑
    UIBezierPath *subPath = [UIBezierPath bezierPathWithArcCenter:arcCenter radius:25 startAngle:0 endAngle:2 * M_PI clockwise:NO]; // 第二層路徑
    UIBezierPath *sub2Path = [UIBezierPath bezierPathWithArcCenter:arcCenter radius:10 startAngle:0 endAngle:2 * M_PI clockwise:YES]; // 最內層路徑
    [path appendPath:subPath];
    [path appendPath:sub2Path];
    
    CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
    maskLayer.path = path.CGPath;
    maskLayer.fillColor = [UIColor yellowColor].CGColor;
    maskLayer.strokeColor = [UIColor blackColor].CGColor;
    maskLayer.fillRule = kCAFillRuleEvenOdd;
    [self.view.layer addSublayer:maskLayer];

結果如下:

kCAFillRuleEvenOdd.png

可以看到,subPath內的點的number of crossings為偶數,沒被渲染;sub2Path內的點的number of crossings為奇數,被渲染;

回到文章開頭的例子,因為矩形path是沒有順/逆時針之分,所以我們設置為kCAFillRuleEvenOdd模式,也就達到了圓形空心的效果。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容