Macos 使用核心动画层创建可设置动画的半透明覆盖
我正在创建一个聚光灯,它可以覆盖我应用程序中的内容,如下所示: 在示例应用程序(如上所示)中,背景层是蓝色的,我在它上面有一个层,使所有的背景层都变暗,除了一个正常显示的圆圈。我已经做到了(你可以在下面的代码中看到)。在我真正的应用程序中,其他Calayer中有实际内容,而不仅仅是蓝色 我的问题是:它没有动画。我正在使用Macos 使用核心动画层创建可设置动画的半透明覆盖,macos,cocoa,drawing,core-animation,calayer,Macos,Cocoa,Drawing,Core Animation,Calayer,我正在创建一个聚光灯,它可以覆盖我应用程序中的内容,如下所示: 在示例应用程序(如上所示)中,背景层是蓝色的,我在它上面有一个层,使所有的背景层都变暗,除了一个正常显示的圆圈。我已经做到了(你可以在下面的代码中看到)。在我真正的应用程序中,其他Calayer中有实际内容,而不仅仅是蓝色 我的问题是:它没有动画。我正在使用CGContextdrawing来创建圆(这是黑色图层中的一个空白点)。当你在我的示例应用程序中单击按钮时,我会在不同的位置以不同的大小绘制圆圈 我希望它能够平滑地转换和缩放,
CGContext
drawing来创建圆(这是黑色图层中的一个空白点)。当你在我的示例应用程序中单击按钮时,我会在不同的位置以不同的大小绘制圆圈
我希望它能够平滑地转换和缩放,而不是像现在那样跳跃。它可能需要一种不同的方法来创建聚光灯效果,或者可能有一种我不知道的方法来隐式设置-drawLayer:inContext:
调用的动画
创建示例应用程序很容易:
SpotlightView.h
,因为我在SpotlightView.m中包含了它的内容-moveSpotlight:
操作掩码
属性)
我喜欢David Rönnqvist在评论中的建议,即使用暗层的遮罩
属性来切出一个洞,然后我可以独立移动。问题在于,出于某种原因,mask
属性的工作原理与我期望的掩码工作原理相反。当我指定一个圆形遮罩时,所有显示的都是圆形。我希望遮罩以相反的方式工作,用0
alpha遮住该区域
掩蔽看起来是正确的方法,但是如果我必须填充整个层并切出一个洞,那么我也可以按照我最初发布的方式来做。有人知道如何反转-[CALayer mask]
属性,以便从图层图像中剪切绘制的区域吗
/Update
以下是SpotlightView
的代码:
//
// SpotlightView.m
//
#import <Quartz/Quartz.h>
@interface SpotlightView : NSView
- (IBAction)moveSpotlight:(id)sender;
@end
@interface SpotlightView ()
@property (strong) CALayer *spotlightLayer;
@property (assign) CGRect highlightRect;
@end
@implementation SpotlightView
@synthesize spotlightLayer;
@synthesize highlightRect;
- (id)initWithFrame:(NSRect)frame {
if ((self = [super initWithFrame:frame])) {
self.wantsLayer = YES;
self.highlightRect = CGRectNull;
self.spotlightLayer = [CALayer layer];
self.spotlightLayer.frame = CGRectInset(self.layer.bounds, -50, -50);
self.spotlightLayer.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
self.spotlightLayer.opacity = 0.60;
self.spotlightLayer.delegate = self;
CIFilter *blurFilter = [CIFilter filterWithName:@"CIGaussianBlur"];
[blurFilter setValue:[NSNumber numberWithFloat:5.0]
forKey:@"inputRadius"];
self.spotlightLayer.filters = [NSArray arrayWithObject:blurFilter];
[self.layer addSublayer:self.spotlightLayer];
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect {}
- (void)moveSpotlight:(id)sender {
[self.spotlightLayer setNeedsDisplay];
}
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
if (layer == self.spotlightLayer) {
CGContextSaveGState(ctx);
CGColorRef blackColor = CGColorCreateGenericGray(0.0, 1.0);
CGContextSetFillColorWithColor(ctx, blackColor);
CGColorRelease(blackColor);
CGContextClearRect(ctx, layer.bounds);
CGContextFillRect(ctx, layer.bounds);
// Causes the toggling
if (CGRectIsNull(self.highlightRect) || self.highlightRect.origin.x != 25) {
self.highlightRect = CGRectMake(25, 25, 100, 100);
} else {
self.highlightRect = CGRectMake(NSMaxX(self.layer.bounds) - 50,
NSMaxY(self.layer.bounds) - 50,
25, 25);
}
CGRect drawnRect = [layer convertRect:self.highlightRect
fromLayer:self.layer];
CGMutablePathRef highlightPath = CGPathCreateMutable();
CGPathAddEllipseInRect(highlightPath, NULL, drawnRect);
CGContextAddPath(ctx, highlightPath);
CGContextSetBlendMode(ctx, kCGBlendModeClear);
CGContextFillPath(ctx);
CGPathRelease(highlightPath);
CGContextRestoreGState(ctx);
}
else {
CGColorRef blueColor = CGColorCreateGenericRGB(0, 0, 1.0, 1.0);
CGContextSetFillColorWithColor(ctx, blueColor);
CGContextFillRect(ctx, layer.bounds);
CGColorRelease(blueColor);
}
}
@end
//
//SpotlightView.m
//
#进口
@接口SpotlightView:NSView
-(iAction)移动聚光灯:(id)发送方;
@结束
@接口SpotlightView()
@属性(强)CALayer*spotlightLayer;
@属性(分配)CGRect highlightRect;
@结束
@实现SpotlightView
@合成聚光灯;
@合成highlightRect;
-(id)initWithFrame:(NSRect)帧{
if((self=[super initWithFrame:frame])){
self.wantsLayer=是;
self.highlightRect=CGRectNull;
self.spotlightLayer=[CALayer layer];
self.spotlightLayer.frame=CGRectInset(self.layer.bounds,-50,-50);
self.spotlightLayer.autoresizingMask=kCALayerWidthSizable | kCALayerHeightSizable;
self.spotlightLayer.opacity=0.60;
self.spotlightLayer.delegate=self;
CIFilter*模糊过滤器=[CIFilter过滤器名称:@“CIGaussianBlur”];
[blurFilter设置值:[NSNumber numberWithFloat:5.0]
福基:@“inputRadius”];
self.spotlightLayer.filters=[NSArray arrayWithObject:blurFilter];
[self.layer addSublayer:self.spotlightLayer];
}
回归自我;
}
-(void)drawRect:(NSRect)dirtyRect{}
-(无效)移动聚光灯:(id)发送者{
[self.spotlightLayer设置需要显示];
}
-(void)drawLayer:(CALayer*)层inContext:(CGContextRef)ctx{
如果(层==自发光层){
CGContextSaveGState(ctx);
CGColorRef blackColor=CGColorCreateGenericGray(0.0,1.0);
CGContextSetFillColorWithColor(ctx,黑色);
CGColorRelease(黑色);
CGContextClearRect(ctx,layer.bounds);
CGContextFillRect(ctx,layer.bounds);
//导致切换
if(CGRectIsNull(self.highlightRect)| self.highlightRect.origin.x!=25){
self.highlightRect=CGRectMake(25,25100100);
}否则{
self.highlightRect=CGRectMake(NSMaxX(self.layer.bounds)-50,
NSMaxY(self.layer.bounds)-50,
25, 25);
}
CGRect drawnRect=[layer convertRect:self.highlightRect
fromLayer:self.layer];
CGMutablePathRef highlightPath=CGPathCreateMutable();
CGPathAddEllipseInRect(highlightPath,NULL,drawnRect);
CGContextAddPath(ctx,highlightPath);
CGContextSetBlendMode(ctx、kCGBlendModeClear);
CGContextFillPath(ctx);
CGPathRelease(highlightPath);
CGContextRestoreGState(ctx);
}
否则{
CGColorRef blueColor=CGColorCreateGenericRGB(0,0,1.0,1.0);
CGContextSetFillColorWithColor(ctx,蓝色);
CGContextFillRect(ctx,layer.bounds);
CGColorRelease(蓝色);
}
}
@结束
我终于拿到了。促使我回答这个问题的是听到了全班的情况。起初,我认为绘制图层内容比绘制和清除标准CALayer
的内容更简单。但是我阅读了CAShapeLayer
的path
属性的文档,其中说明它可以设置动画,但不能隐式设置
虽然图层蒙版可能更直观、更优雅,但似乎不可能使用蒙版来隐藏所有者图层的一部分,而不是显示一部分,因此我无法使用它。我对这个解决方案很满意,因为很清楚发生了什么。我希望它使用隐式动画,但动画代码只有几行
下面,我修改了问题中的示例代码以添加平滑动画。(我删除了CIFilter
代码,因为它是extrane
//
// SpotlightView.m
//
#import <Quartz/Quartz.h>
@interface SpotlightView : NSView
- (IBAction)moveSpotlight:(id)sender;
@end
@interface SpotlightView ()
@property (strong) CAShapeLayer *spotlightLayer;
@property (assign) CGRect highlightRect;
@end
@implementation SpotlightView
@synthesize spotlightLayer;
@synthesize highlightRect;
- (id)initWithFrame:(NSRect)frame {
if ((self = [super initWithFrame:frame])) {
self.wantsLayer = YES;
self.highlightRect = CGRectNull;
self.spotlightLayer = [CAShapeLayer layer];
self.spotlightLayer.frame = self.layer.bounds;
self.spotlightLayer.autoresizingMask = kCALayerWidthSizable | kCALayerHeightSizable;
self.spotlightLayer.fillRule = kCAFillRuleEvenOdd;
CGColorRef blackoutColor = CGColorCreateGenericGray(0.0, 0.60);
self.spotlightLayer.fillColor = blackoutColor;
CGColorRelease(blackoutColor);
[self.layer addSublayer:self.spotlightLayer];
}
return self;
}
- (void)drawRect:(NSRect)dirtyRect {}
- (CGPathRef)newSpotlightPathInRect:(CGRect)containerRect
withHighlight:(CGRect)spotlightRect {
CGMutablePathRef shape = CGPathCreateMutable();
CGPathAddRect(shape, NULL, containerRect);
if (!CGRectIsNull(spotlightRect)) {
CGPathAddEllipseInRect(shape, NULL, spotlightRect);
}
return shape;
}
- (void)moveSpotlight {
CGPathRef toShape = [self newSpotlightPathInRect:self.spotlightLayer.bounds
withHighlight:self.highlightRect];
CABasicAnimation *pathAnimation = [CABasicAnimation animationWithKeyPath:@"path"];
pathAnimation.fromValue = (__bridge id)self.spotlightLayer.path;
pathAnimation.toValue = (__bridge id)toShape;
[self.spotlightLayer addAnimation:pathAnimation forKey:@"path"];
self.spotlightLayer.path = toShape;
CGPathRelease(toShape);
}
- (void)moveSpotlight:(id)sender {
if (CGRectIsNull(self.highlightRect) || self.highlightRect.origin.x != 25) {
self.highlightRect = CGRectMake(25, 25, 100, 100);
} else {
self.highlightRect = CGRectMake(NSMaxX(self.layer.bounds) - 50,
NSMaxY(self.layer.bounds) - 50,
25, 25);
}
[self moveSpotlight];
}
- (void)drawLayer:(CALayer *)layer inContext:(CGContextRef)ctx {
CGColorRef blueColor = CGColorCreateGenericRGB(0, 0, 1.0, 1.0);
CGContextSetFillColorWithColor(ctx, blueColor);
CGContextFillRect(ctx, layer.bounds);
CGColorRelease(blueColor);
}
@end