效果处理(内阴影、外阴影、外发光、内发光、投影)

前言

  • 最近在做效果处理,其中遇见了一些问题,写篇文章记录一下之前遇见的问题,这里提供两种思路来处理

第一种:这种思路主要是采用Layer方式处理,采用偏移的方式达到内外阴影效果

内阴影:通过偏移XY
内发光:采用两个layer偏移处理
外发光、外阴影:采用4个layer向4个方向偏移

拷贝效果

- (instancetype)copyWithZone:(NSZone *)zone {
    KJShadowLayer *layer = [[KJShadowLayer allocWithZone:zone] init];
    layer.frame = self.frame;
    layer.kj_path = self.kj_path;
    layer.kj_color = self.kj_color;
    layer.kj_offset = self.kj_offset;
    layer.kj_radius = self.kj_radius;
    layer.kj_opacity = self.kj_opacity;
    layer.kj_shadowType = self.kj_shadowType;
    return layer;
}
复制代码

绘制Layer

- (void)drawInContext:(CGContextRef)context {
    CGRect rect = self.bounds;
    if (self.borderWidth != 0) {
        rect = CGRectInset(rect, self.borderWidth, self.borderWidth);
    }
    CGContextSaveGState(context);
    if (self.kj_shadowType == KJShadowTypeInner || 
        self.kj_shadowType == KJShadowTypeInnerShine) {
        CGContextAddPath(context, self.kj_path.CGPath);
        CGContextClip(context);
        CGMutablePathRef outer = CGPathCreateMutable();
        CGPathAddRect(outer, NULL, CGRectInset(rect, -1 * rect.size.width, -1 * rect.size.height));
        CGPathAddPath(outer, NULL, self.kj_path.CGPath);
        CGPathCloseSubpath(outer);
        CGContextAddPath(context, outer);
        CGPathRelease(outer);
    } else {
        CGContextAddPath(context, self.kj_path.CGPath);
    }
    UIColor *color = [self.kj_color colorWithAlphaComponent:self.kj_opacity];
    CGContextSetShadowWithColor(context, self.kj_offset, self.kj_radius, color.CGColor);
    if (self.kj_shadowType == KJShadowTypeOuterShine || 
        self.kj_shadowType == KJShadowTypeOuter) {
        CGContextDrawPath(context, kCGPathEOFill);
    } else {
        CGContextDrawPath(context, kCGPathEOFillStroke);
    }
    CGContextRestoreGState(context);
}
复制代码

设置属性

self.kj_path = self.kj_shadowPath;
self.kj_color = self.kj_shadowColor;
self.kj_radius = self.kj_shadowRadius;
self.kj_opacity = self.kj_shadowOpacity;
self.kj_offset = CGSizeMake(self.kj_shadowDiffuse, self.kj_shadowDiffuse);
[self setNeedsDisplay];
复制代码
image image

第二种:主要操作图片的方式来处理,新建ImageView来承载投影、阴影等效果

1、投影 - 核心思路

1.1 - 归档复制要投影的视图(因为我只需要上面的图片,所以采用归档的方式复制一份再截图处理)

/// 复制UIView
- (UIView *)kj_copyView:(UIView *)view{
    NSData *data = [NSKeyedArchiver archivedDataWithRootObject:view];
    return [NSKeyedUnarchiver unarchiveObjectWithData:data];
}
复制代码

1.2 - 截图并修改图片颜色

/// 获取截图
- (UIImage *)kj_captureView:(UIView *)view{
    UIGraphicsBeginImageContext(view.bounds.size);
    CGContextRef ctx = UIGraphicsGetCurrentContext();
    [view.layer renderInContext:ctx];
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}
/// 改变图片颜色
- (UIImage *)kj_changeColor:(UIColor *)color image:(UIImage *)image{
    UIGraphicsBeginImageContextWithOptions(image.size, NO, image.scale);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextTranslateCTM(context, 0, image.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);
    CGContextSetBlendMode(context, kCGBlendModeNormal);
    CGRect rect = CGRectMake(0, 0, image.size.width, image.size.height);
    CGContextClipToMask(context, rect, image.CGImage);
    [color setFill];
    CGContextFillRect(context, rect);
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newImage;
}
复制代码

1.3 - 实现效果

距离和角度:采用偏移坐标的方式处理

CGFloat x = info.diffuse * sin(info.angle);
CGFloat y = info.diffuse * cos(info.angle);
self.frame = CGRectMake(self.originX+x, self.originY+y, self.width, self.height);
复制代码

模糊:这里采用 Accelerate 框架里面的模糊滤镜处理,
主要函数 box滤镜vImageBoxConvolve_ARGB8888和交换像素通道vImagePermuteChannels_ARGB8888
这里有个细节需要注意:CGImageAlphaInfo 需要使用kCGImageAlphaPremultipliedLast枚举,从而保留透明区域(不变黑)

/// box滤镜(模糊滤镜)
error = vImageBoxConvolve_ARGB8888(&inBuffer,&outBuffer,NULL,0,0,boxSize,boxSize,NULL,kvImageEdgeExtend);
    
/// 交换像素通道从BGRA到RGBA
const uint8_t permuteMap[] = {2, 1, 0, 3};
vImagePermuteChannels_ARGB8888(&outBuffer,&rgbOutBuffer,permuteMap,kvImageNoFlags);

/// kCGImageAlphaPremultipliedLast 保留透明区域
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef ctx = CGBitmapContextCreate(rgbOutBuffer.data,
                                         rgbOutBuffer.width,
                                         rgbOutBuffer.height,
                                         8,
                                         rgbOutBuffer.rowBytes,
                                         colorSpace,
                                         kCGImageAlphaPremultipliedLast);
复制代码

image.png

2、阴影(其实阴影和发光根本原理一样)

2.1 - 生成路径图

/// 生成路径图
- (UIImage *)kj_pathImageWithExtend:(CGFloat)extend color:(UIColor *)color{
    UIGraphicsBeginImageContext(self.superview.size);
    UIBezierPath *path = self.outsidePath;
    path.lineWidth = extend;
    path.lineCapStyle = kCGLineCapRound;
    path.lineJoinStyle = kCGLineCapRound;
    [color set];
    [path stroke];
    
    UIImage *img = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return img;
}
复制代码

2.2 - 模糊处理(处理方式和投影一致)

这里需要注意的就是生成的路径图是包含内外阴影,单独使用的话需要做裁剪处理

2.3 - 裁剪处理

外阴影:将路径内部的裁剪掉

/// 路径内部裁剪,保留路径以外区域
- (UIImage *)kj_outerCaptureImage:(UIImage *)image{
    UIGraphicsBeginImageContextWithOptions(self.superview.bounds.size, NO, image.scale);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetBlendMode(context, kCGBlendModeClear);/// kCGBlendModeClear 裁剪部分透明
    [image drawInRect:self.superview.bounds];
    CGContextAddPath(context, self.outsidePath.CGPath);
    CGContextDrawPath(context, kCGPathEOFill);
    UIImage *newimage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newimage;
}
复制代码

内阴影:同理,将路径以外部分裁剪掉

/// 裁剪掉路径以外区域
- (UIImage *)kj_innerCaptureImage:(UIImage *)image{
    UIGraphicsBeginImageContextWithOptions(self.superview.bounds.size, NO, image.scale);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSetBlendMode(context, kCGBlendModeClear);
    [image drawInRect:self.superview.bounds];
    UIBezierPath *path = ({ /// 镂空
        UIBezierPath *path = [UIBezierPath bezierPathWithRect:self.superview.bounds];
        path.usesEvenOddFillRule = YES;
        [path appendPath:self.outsidePath];
        path;
    });
    CGContextAddPath(context, path.CGPath);
    CGContextDrawPath(context, kCGPathEOFill);
    UIImage *newimage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return newimage;
}
复制代码

image.png

阴影发光都可以是一个独立的图层(UIImageView),因此他们是可以互相共同存在。

备注:本文用到的部分函数方法和Demo,均来自三方库KJCategories

内阴影,外阴影,外发光,内发光,投影介绍就到此完毕,后面有相关再补充,写文章不容易,还请点个**小星星**传送门

猜你喜欢

转载自juejin.im/post/7011309826592473125