Created
August 26, 2014 20:23
-
-
Save jessecurry/e68bfd7f0a4708e680f4 to your computer and use it in GitHub Desktop.
Parallax NSImageView for OS X
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// BFParallaxImageView.h | |
// bout-osx | |
// | |
// Created by Jesse Curry on 8/26/14. | |
// Copyright (c) 2014 Bout Fitness, LLC. All rights reserved. | |
// | |
#import <Cocoa/Cocoa.h> | |
@interface BFParallaxImageView : NSView | |
- (void)setImage: (NSImage*)image; | |
@end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// BFParallaxImageView.m | |
// bout-osx | |
// | |
// Created by Jesse Curry on 8/26/14. | |
// Copyright (c) 2014 Bout Fitness, LLC. All rights reserved. | |
// | |
#import "BFParallaxImageView.h" | |
#define USE_POP_FOR_ANIMATION 1 | |
#if USE_POP_FOR_ANIMATION | |
#import <pop/POP.h> | |
#endif | |
static const CGFloat kImageOverscan = 1.2; // 20% larger image | |
@interface BFParallaxImageView () | |
@property (nonatomic, weak) NSTrackingArea* trackingArea; | |
@property (nonatomic, weak) NSImageView* imageView; | |
@end | |
//////////////////////////////////////////////////////////////////////////////////////////////////// | |
@implementation BFParallaxImageView | |
- (instancetype)initWithFrame: (NSRect)frameRect | |
{ | |
self = [super initWithFrame: frameRect]; | |
if ( self ) | |
{ | |
[self setupView]; | |
} | |
return self; | |
} | |
- (instancetype)initWithCoder: (NSCoder*)coder | |
{ | |
self = [super initWithCoder: coder]; | |
if ( self ) | |
{ | |
[self setupView]; | |
} | |
return self; | |
} | |
- (void)setupView | |
{ | |
self.wantsLayer = YES; | |
// Add Image View | |
NSImageView* imageView = [[NSImageView alloc] initWithFrame: self.bounds]; | |
imageView.imageScaling = NSImageScaleProportionallyUpOrDown; | |
[self addSubview: imageView]; | |
self.imageView = imageView; | |
// Start Tracking | |
NSTrackingAreaOptions options = (NSTrackingMouseMoved | NSTrackingActiveInActiveApp); | |
NSTrackingArea* trackingArea = [[NSTrackingArea alloc] initWithRect: self.bounds | |
options: options | |
owner: self | |
userInfo: nil]; | |
[self addTrackingArea: trackingArea]; | |
self.trackingArea = trackingArea; | |
} | |
#pragma mark - | |
- (void)setImage: (NSImage*)image | |
{ | |
CGSize viewSize = self.bounds.size; | |
NSSize imageSize = image.size; | |
if ( imageSize.height > 0 && imageSize.width > 0 ) | |
{ | |
CGFloat wRatio = (viewSize.width / imageSize.width); | |
CGFloat hRatio = (viewSize.height / imageSize.height); | |
CGFloat scale = MAX(wRatio, hRatio); | |
scale *= kImageOverscan; // image should be 20% larger than this view to allow parallax | |
CGSize scaledSize = CGSizeMake(imageSize.width * scale, imageSize.height * scale); | |
CGFloat dX = (scaledSize.width - viewSize.width); | |
CGFloat dY = (scaledSize.height - viewSize.height); | |
self.imageView.frame = CGRectMake(-0.5 * dX, -0.5 * dY, scaledSize.width, scaledSize.height); | |
self.imageView.image = image; | |
} | |
else | |
{ | |
self.imageView.bounds = CGRectMake(0, 0, viewSize.width, viewSize.height); | |
self.imageView.image = nil; | |
} | |
} | |
#pragma mark - Tracking | |
- (void)mouseMoved: (NSEvent*)theEvent | |
{ | |
NSPoint eyeCenter = [self convertPoint: [theEvent locationInWindow] | |
fromView: nil]; | |
CGSize viewSize = self.bounds.size; | |
CGSize imageSize = self.imageView.bounds.size; | |
CGFloat wR = MIN(1.0, (eyeCenter.x / viewSize.width)); | |
CGFloat hR = MIN(1.0, (eyeCenter.y / viewSize.height)); | |
CGFloat dX = (imageSize.width - viewSize.width); | |
CGFloat dY = (imageSize.height - viewSize.height); | |
CGFloat x = -(wR * dX); | |
CGFloat y = -(hR * dY); | |
#if USE_POP_FOR_ANIMATION | |
static NSString* const kPosAnimKey = @"positionAnimation"; | |
POPSpringAnimation* posAnim = [self.imageView.layer pop_animationForKey: kPosAnimKey]; | |
if ( posAnim == nil ) | |
{ | |
posAnim = [POPSpringAnimation animationWithPropertyNamed: kPOPLayerPosition]; | |
posAnim.springBounciness = 10; | |
posAnim.springSpeed = 15; | |
posAnim.fromValue = [NSValue valueWithCGPoint: self.imageView.layer.position]; | |
[self.imageView.layer pop_addAnimation: posAnim | |
forKey: kPosAnimKey]; | |
} | |
posAnim.toValue = [NSValue valueWithCGPoint: CGPointMake(x, y)]; | |
#else | |
self.imageView.layer.position = CGPointMake(x, y); | |
#endif | |
} | |
@end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Created a simple parallax image view for OS X.
By default, this will use the pop library from Facebook to add a spring animation to the parallax motion, but you may disable that by changing
USE_POP_FOR_ANIMATION 1
toUSE_POP_FOR_ANIMATION 0
.Enabling bindings is probably my next step.