OSXDev BootCamp에서 코어 애니메이션(Core Animation)에 대한 세션을 봤는데, 꽤나 인상적이었다. 마치 웹에서 Script.aculo.us 전후로 세상이 바뀐 것처럼, 데스크탑에는 코어 애니메이션이 새로운 세상을 열어주는 것 같았다.
기회가 되면 코어 애니메이션을 체험해봐야지라고 생각하고 있었는데, 맥루비(MacRuby)와도 익숙해질겸, 간단한 애플리케이션을 만들어봤다.
Jumpy
Jumpy는 Lucas Newman씨가 만들어 공개한 3가지 코어 애니메이션 예제중 하나다. 그 중 Jumpy가 가장 간단해보여서 맥루비로 포팅해보기로 했다. 일단 동영상을 보자.
실행하면 현재 화면을 캡쳐해서 뒤로 통통튀며 사라지는 간단한 효과를 구현한 애플리케이션이다. 이 예제에는 3가지 애니메이션이 복합되어 있다.
크키가 작아진다
점점 흐려진다(Fade Out)
통통 튄다.
창을 만들고 레이어를 구성한다
먼저 메인 윈도우를 만든다.
def main_window
returning(:main_window, NSWindow.alloc.initWithContentRect(NSScreen.mainScreen.frame,
styleMask: ::NSBorderlessWindowMask, backing: ::NSBackingStoreRetained,
defer: false, screen: NSScreen.mainScreen)) do |win|
win.contentView.wantsLayer = true
win.contentView.layer.backgroundColor = CGColorCreateGenericGray(0.0, 1.0)
end
end
returning은 계산 결과를 캐싱하기 위해 만든 함수다. 위에서는 @main_window 인스턴스가 있으면 이 값을 반환하고 없으면 블록을 수행한다. 그래서 NSScreen.mainScreen.frame 사이즈로 검은색 배경의 윈도우를 하나 만들었다.
이 윈도우에 레이어를 추가해보자.
먼저 화면의 스크린샷 이미지를 만든다. CGWindowListCreateImage 메서드를 이용한다.
def snapshot_image
returning :snapshot_image, CGWindowListCreateImage(
::CGRectInfinite, ::KCGWindowListOptionOnScreenOnly,
::KCGNullWindowID, ::KCGWindowImageDefault)
end
이 스냅샷을 담은 레이어의 이름은 스크린 레이어다.
def screen_layer
returning :screen_layer, CALayer.layer do |layer|
layer.frame = cgrect(0.0, 0.0, CGImageGetWidth(snapshot_image), CGImageGetHeight(snapshot_image))
layer.contents = snapshot_image
end
end
그리고 아래에 스크린레이어가 비친 형상의 리플렉션 레이어를 만든다. 웹 2.0 사이트에서 인기있는 바로 그 효과다.
def reflection_layer
returning :reflection_layer, CALayer.layer do |layer|
layer.contents = screen_layer.contents
layer.opacity = 0.4
layer.frame = CGRectOffset(screen_layer.frame, 0.5, -NSScreen.mainScreen.frame.size.height + 0.5)
layer.transform = CATransform3DMakeScale(1.0, -1.0, 1.0) # flip the y-axis
layer.sublayerTransform = reflection_layer.transform
end
end
이제 이 두개의 레이어를 포함하는 컨테이너 레이어다.
def container_layer
returning :container_layer, CALayer.layer do |layer|
container_layer.frame = screen_layer.frame
container_layer.addSublayer(screen_layer)
container_layer.addSublayer(reflection_layer)
end
end
애니메이션 효과
위에서 만든 컨테이너 레이어에 애니메이션 효과를 부여해보자. 먼저 점점 작아지는 애니메이션이다.
def scale_to(val = 0.0)
shirink = CABasicAnimation.animationWithKeyPath('transform.scale')
shirink.toValue = val
shirink.timingFunction = easy_in_timing_function
container_layer.addAnimation shirink, forKey: 'shirinkAnimation'
end
타이밍 펑션은 EasyIn으로 점점 느려지는 것이다.
def easy_in_timing_function
CAMediaTimingFunction.functionWithName(::KCAMediaTimingFunctionEaseIn)
end
꽤 간단한 코드로 작어지는 애니메이션을 구현할 수 있다. 코어 애니메이션의 매력이다.
다음에는 페이드 아웃 효과다.
def fade_to(val = 0.0)
fade = CABasicAnimation.animationWithKeyPath('opacity')
fade.toValue = val
fade.timingFunction = easy_in_timing_function
container_layer.addAnimation fade, forKey: 'fadeAnimation'
end