这是我参与8月更文挑战的第8天,活动详情查看:8月更文挑战
有时候 UI 上需要突出重点或者添加逐渐显示的效果,看起来就像聚光灯打在了图片上。
我们先来分析一下需要做哪些准备:
- 要想显示一个图像的部分内容,可以使用 CALayer 的 mask 属性设置遮罩,让 layer 显示 mask 遮住(非透明)的部分;
- 我们需要一个像聚光灯一样的遮罩,透明度从中间向外逐渐变高,即中间的透明度 alpha 为 1,四周的 0。
聚光灯遮罩
首先我们来看怎么绘制聚光灯效果的遮罩。模仿聚光灯的效果需要用到渐变。
渐变分为两种:
- 线性渐变:图形以直线的方式,朝着一个方向进行发散,发散后呈现矩形;
- 径向渐变:从指定的半径的大小开始,由往外或往内发散,发散后呈现出圆形。
在这里我们就是需要使用径向渐变:
/// 聚光灯效果的遮罩
class SpotlightFilterMaskLayer : CALayer {
/// 坡度的宽度
var gradientWidth: CGFloat = 40.0
/// 直径
var diameter: CGFloat = 0 {
didSet {
setNeedsDisplay()
}
}
override func draw(in ctx: CGContext) {
let origin: CGPoint = CGPoint(x: self.bounds.midX, y: self.bounds.midY)
let clearRegionRadius: CGFloat = self.diameter * 0.5
let blurRegionRadius: CGFloat = clearRegionRadius + gradientWidth
// 创建颜色空间
let baseColorSpace = CGColorSpaceCreateDeviceRGB();
let colours : [CGFloat] = [0.0, 0.0, 0.0, 1,
0.0, 0.0, 0.0, 0.2]
let colourLocations : [CGFloat] = [0, 1]
// 创建渐变对象
// - colorSpace: 颜色空间
// - colorComponents: 颜色分量的强度值数组
// - locations: 渐变系数数组
// - count: 设置的渐变系数数组的元素个数
guard let gradient = CGGradient(colorSpace: baseColorSpace,
colorComponents: colours,
locations: colourLocations,
count: 2) else {
return
}
// 绘制渐变
// - gradient: 渐变对象
// - startCenter: 起点
// - startRadius: 径向半径
// - endCenter: 终点
// - endRadius: 径向半径
// - options: 渐变模式
// - .drawsBeforeStartLocation 表示向里发散
// - .drawsAfterEndLocation 表示向外发散
ctx.drawRadialGradient(gradient,
startCenter: origin,
startRadius: clearRegionRadius,
endCenter: origin,
endRadius: blurRegionRadius,
options: .drawsBeforeStartLocation);
ctx.drawPath(using: .fill)
}
}
设置遮罩
直接通过 layer.mask 属性设置遮罩:
imageView.layer.mask = maskLayer
添加动画
给 maskLayer 添加一个无限循环的 bounds 变化的动画。
func begin() {
let fromValue = imageView.bounds
let animation = CABasicAnimation(keyPath: "bounds")
animation.fromValue = fromValue
animation.toValue = CGRect(x: fromValue.origin.x - 100,
y: fromValue.origin.y - 100,
width: fromValue.size.width + 200,
height: fromValue.size.height + 200)
animation.duration = 1
animation.autoreverses = true
animation.repeatCount = MAXFLOAT
animation.isRemovedOnCompletion = false
maskLayer.add(animation, forKey: nil)
}
完整的代码
感兴趣可以查看完整的代码:SpotlightAnimation