mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-10-25 17:45:36 +00:00
Move css animation creation & frame updaters to CPViewAnimator
Reason: • consistency : animations handled by animator. • Views can now animate several DOM elements. For example a view with a nine part image need to animate its sub divs. • Do not create any css animation when the start and end values are equal and there is no intermediate values. + This commit adds an optional method to CPView - (CGRect)frameRectOfView:(CPView)aView inSuperviewSize:(CGSize)aSize Returns the frame of a view given its superview frame. This will allow views implementing -layoutSubviews to gain an optional declarative layout. The animation system will use this method to compute the end value of an animation.
This commit is contained in:
1 parent
cebd51aac1
commit
6bf2d6c1ab
2 files changed
+159
-122
No files matched your search
@@ -3,12 +3,12 @@
|
||||
@import "CPView.j"
|
||||
|
||||
@import <Foundation/CPTimer.j>
|
||||
@import <Foundation/CPRunLoop.j>
|
||||
|
||||
@typedef Map;
|
||||
|
||||
var _CPAnimationContextStack = nil,
|
||||
_animationFlushingObserver = nil,
|
||||
_animationFrameUpdaters = {};
|
||||
_animationFlushingObserver = nil;
|
||||
|
||||
@implementation CPAnimationContext : CPObject
|
||||
{
|
||||
@@ -146,8 +146,8 @@ var _CPAnimationContextStack = nil,
|
||||
values,
|
||||
keyTimes,
|
||||
timingFunctions,
|
||||
objectId;
|
||||
needsPeriodicFrameUpdates,
|
||||
objectId = [anObject UID];
|
||||
|
||||
if (!aKeyPath || !anObject || !(animation = [anObject animationForKey:aKeyPath]) || ![animation isKindOfClass:[CAAnimation class]])
|
||||
return nil;
|
||||
@@ -158,13 +158,12 @@ var _CPAnimationContextStack = nil,
|
||||
if (_completionHandlerAgent)
|
||||
_completionHandlerAgent.increment();
|
||||
|
||||
var animatorClass = [[anObject class] animatorClass];
|
||||
|
||||
var completionFunction = function()
|
||||
{
|
||||
if (needsPeriodicFrameUpdates)
|
||||
{
|
||||
[self stopFrameUpdaterWithIdentifier:objectId];
|
||||
|
||||
}
|
||||
[animatorClass stopUpdaterWithIdentifier:objectId];
|
||||
|
||||
if (animationCompletion)
|
||||
animationCompletion();
|
||||
@@ -266,67 +265,50 @@ var _CPAnimationContextStack = nil,
|
||||
|
||||
- (void)getAnimations:(CPArray)cssAnimations getTimers:(CPArray)timers usingAction:(Object)anAction cssAnimate:(BOOL)needsCSSAnimation
|
||||
{
|
||||
isFrameKeyPath = (keyPath == @"frame" || keyPath == @"frameSize"),
|
||||
customLayout = [aTargetView hasCustomLayoutSubviews],
|
||||
customDrawing = [aTargetView hasCustomDrawRect],
|
||||
var values = anAction.values;
|
||||
|
||||
if (values.length == 2)
|
||||
{
|
||||
var start = values[0],
|
||||
end = values[1];
|
||||
|
||||
if (anAction.keypath == @"frame" && CGRectEqualToRect(start, end)
|
||||
|| anAction.keypath == @"frameSize" && CGSizeEqualToSize(start, end)
|
||||
|| anAction.keypath == @"frameOrigin" && CGPointEqualToPoint(start, end))
|
||||
return;
|
||||
}
|
||||
|
||||
var targetView = anAction.object,
|
||||
keyPath = anAction.keypath,
|
||||
isFrameKeyPath = (keyPath == @"frame" || keyPath == @"frameSize"),
|
||||
customLayout = [targetView hasCustomLayoutSubviews],
|
||||
customDrawing = [targetView hasCustomDrawRect],
|
||||
declarative_subviews_layout = (!customLayout || [targetView implementsSelector:@selector(frameRectOfView:inSuperviewSize:)]),
|
||||
needsPeriodicFrameUpdates = [[targetView animator] needsPeriodicFrameUpdatesForKeyPath:keyPath],
|
||||
timer = nil,
|
||||
animatorClass = [[targetView class] animatorClass];
|
||||
|
||||
if (needsCSSAnimation)
|
||||
{
|
||||
var identifier = [aTargetView UID],
|
||||
duration = anAction.duration,
|
||||
timingFunctions = anAction.timingfunctions,
|
||||
properties = [],
|
||||
valueFunctions = [],
|
||||
cssAnimation = nil;
|
||||
|
||||
[cssAnimations enumerateObjectsUsingBlock:function(anim, idx, stop)
|
||||
{
|
||||
if (anim.identifier == identifier)
|
||||
{
|
||||
cssAnimation = anim;
|
||||
stop(YES);
|
||||
}
|
||||
}];
|
||||
|
||||
if (cssAnimation == nil)
|
||||
{
|
||||
var domElement = [aTargetView DOMElementForKeyPath:keyPath];
|
||||
cssAnimation = new CSSAnimation(domElement, identifier);
|
||||
cssAnimations.push(cssAnimation);
|
||||
}
|
||||
|
||||
var css_mapping = [[aTargetView class] _cssPropertiesForKeyPath:keyPath];
|
||||
|
||||
[css_mapping enumerateObjectsUsingBlock:function(aDict, anIndex, stop)
|
||||
{
|
||||
var completionFunction = (anIndex == 0) ? anAction.completion : null;
|
||||
var property = [aDict objectForKey:@"property"],
|
||||
getter = [aDict objectForKey:@"value"];
|
||||
var targetView = anAction.object,
|
||||
keyPath = anAction.keypath,
|
||||
needsPeriodicFrameUpdates = [[targetView animator] needsPeriodicFrameUpdatesForKeyPath:keyPath],
|
||||
|
||||
cssAnimation.addPropertyAnimation(property, getter, duration, anAction.keytimes, anAction.values, timingFunctions, completionFunction);
|
||||
}];
|
||||
[animatorClass addAnimations:cssAnimations forAction:anAction];
|
||||
}
|
||||
|
||||
if (needsPeriodicFrameUpdates)
|
||||
{
|
||||
var timer = [self addFrameUpdaterWithIdentifier:[rootView UID] forView:aTargetView keyPath:keyPath duration:anAction.duration];
|
||||
|
||||
if (timer)
|
||||
timers.push(timer);
|
||||
[animatorClass addFrameUpdaters:timers forAction:anAction];
|
||||
}
|
||||
|
||||
var subviews = [aTargetView subviews],
|
||||
count = [subviews count],
|
||||
customLayout = [aTargetView hasCustomLayoutSubviews];
|
||||
var subviews = [targetView subviews],
|
||||
count = [subviews count];
|
||||
|
||||
if (count && isFrameKeyPath)
|
||||
{
|
||||
[subviews enumerateObjectsUsingBlock:function(aSubview, idx, stop)
|
||||
{
|
||||
var action = [self actionFromAction:anAction forAnimatedSubview:aSubview],
|
||||
{
|
||||
if (!declarative_subviews_layout && [aSubview autoresizingMask] == 0)
|
||||
return;
|
||||
|
||||
var action = [self actionFromAction:anAction forAnimatedSubview:aSubview],
|
||||
targetFrame = [action.values lastObject];
|
||||
|
||||
if (CGRectEqualToRect([aSubview frame], targetFrame))
|
||||
@@ -354,14 +336,15 @@ var _CPAnimationContextStack = nil,
|
||||
- (Object)actionFromAction:(Object)anAction forAnimatedSubview:(CPView)aView
|
||||
{
|
||||
var targetValue = [anAction.values lastObject],
|
||||
startFrame = [aView frame],
|
||||
endFrame,
|
||||
values;
|
||||
|
||||
if (anAction.keypath == "frame")
|
||||
targetValue = targetValue.size;
|
||||
|
||||
endFrame = [aView frameWithNewSuperviewSize:targetValue];
|
||||
values = [[aView frame], endFrame];
|
||||
endFrame = [[aView superview] frameRectOfView:aView inSuperviewSize:targetValue];
|
||||
values = [startFrame, endFrame];
|
||||
|
||||
return {
|
||||
object:aView,
|
||||
@@ -374,36 +357,6 @@ var _CPAnimationContextStack = nil,
|
||||
};
|
||||
}
|
||||
|
||||
- (Function)addFrameUpdaterWithIdentifier:(CPString)anIdentifier forView:(CPView)aView keyPath:(CPString)aKeyPath duration:(float)aDuration
|
||||
{
|
||||
var frameUpdater = _animationFrameUpdaters[anIdentifier],
|
||||
result = nil;
|
||||
|
||||
if (frameUpdater == null)
|
||||
{
|
||||
frameUpdater = new FrameUpdater(anIdentifier);
|
||||
_animationFrameUpdaters[anIdentifier] = frameUpdater;
|
||||
result = frameUpdater;
|
||||
}
|
||||
|
||||
frameUpdater.addTarget(aView, aKeyPath, aDuration);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
- (void)stopFrameUpdaterWithIdentifier:(CPString)anIdentifier
|
||||
{
|
||||
var frameUpdater = _animationFrameUpdaters[anIdentifier];
|
||||
|
||||
if (frameUpdater)
|
||||
{
|
||||
frameUpdater.stop();
|
||||
delete _animationFrameUpdaters[anIdentifier];
|
||||
}
|
||||
else
|
||||
CPLog.warn("Could not find FrameUpdater with identifier " + anIdentifier);
|
||||
}
|
||||
|
||||
- (void)setCompletionHandler:(Function)aCompletionHandler
|
||||
{
|
||||
if (_completionHandlerAgent)
|
||||
@@ -424,11 +377,15 @@ var _CPAnimationContextStack = nil,
|
||||
|
||||
@implementation CPView (CPAnimationContext)
|
||||
|
||||
- (CGRect)frameRectOfView:(CPView)aView inSuperviewSize:(CGSize)aSize
|
||||
{
|
||||
return [aView frameWithNewSuperviewSize:aSize];
|
||||
}
|
||||
|
||||
- (CGRect)frameWithNewSuperviewSize:(CGSize)newSize
|
||||
{
|
||||
var mask = [self autoresizingMask];
|
||||
|
||||
|
||||
if (mask == CPViewNotSizable)
|
||||
return _frame;
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@
|
||||
@import "_CPObjectAnimator.j"
|
||||
@import "CPView.j"
|
||||
@import "CPCompatibility.j"
|
||||
@import "CSSAnimation.j"
|
||||
|
||||
var DEFAULT_CSS_PROPERTIES = nil,
|
||||
FRAME_UPDATERS = {};
|
||||
|
||||
@implementation CPViewAnimator : _CPObjectAnimator
|
||||
{
|
||||
@@ -87,6 +91,97 @@
|
||||
}
|
||||
}
|
||||
|
||||
+ (CPDictionary)_defaultCSSProperties
|
||||
{
|
||||
if (DEFAULT_CSS_PROPERTIES == nil)
|
||||
{
|
||||
var transformProperty = "transform";
|
||||
|
||||
DEFAULT_CSS_PROPERTIES = @{
|
||||
"backgroundColor" : [@{"property":"background", "value":function(sv, val){return [val cssString];}}],
|
||||
"alphaValue" : [@{"property":"opacity"}],
|
||||
"frame" : [@{"property":transformProperty, "value":frameToCSSTranslationTransformMatrix},
|
||||
@{"property":"width", "value":transformFrameToWidth},
|
||||
@{"property":"height", "value":transformFrameToHeight}],
|
||||
"frameOrigin" : [@{"property":transformProperty, "value":frameOriginToCSSTransformMatrix}],
|
||||
"frameSize" : [@{"property":"width", "value":transformSizeToWidth},
|
||||
@{"property":"height", "value":transformSizeToHeight}]
|
||||
};
|
||||
}
|
||||
|
||||
return DEFAULT_CSS_PROPERTIES;
|
||||
}
|
||||
|
||||
+ (void)addAnimations:(CPArray)animations forAction:(id)anAction
|
||||
{
|
||||
var target = anAction.object;
|
||||
|
||||
return [self _addAnimations:animations forAction:anAction domElement:[target _DOMElement] identifier:[target UID]];
|
||||
}
|
||||
|
||||
+ (void)_addAnimations:(CPArray)animations forAction:(id)anAction domElement:(Object)aDomElement identifier:(CPString)anIdentifier
|
||||
{
|
||||
var animation = [animations objectPassingTest:function(anim, idx, stop)
|
||||
{
|
||||
return anim.identifier == anIdentifier;
|
||||
}];
|
||||
|
||||
if (animation == nil)
|
||||
{
|
||||
animation = new CSSAnimation(aDomElement, anIdentifier, [anAction.object debug_description]);
|
||||
[animations addObject:animation];
|
||||
}
|
||||
|
||||
var css_mapping = [self _cssPropertiesForKeyPath:anAction.keypath];
|
||||
|
||||
[css_mapping enumerateObjectsUsingBlock:function(aDict, anIndex, stop)
|
||||
{
|
||||
var completionFunction = (anIndex == 0) ? anAction.completion : null,
|
||||
property = [aDict objectForKey:@"property"],
|
||||
getter = [aDict objectForKey:@"value"];
|
||||
|
||||
animation.addPropertyAnimation(property, getter, anAction.duration, anAction.keytimes, anAction.values, anAction.timingfunctions, completionFunction);
|
||||
}];
|
||||
}
|
||||
|
||||
+ (CPArray)_cssPropertiesForKeyPath:(CPString)aKeyPath
|
||||
{
|
||||
return [[self _defaultCSSProperties] objectForKey:aKeyPath];
|
||||
}
|
||||
|
||||
+ (void)addFrameUpdaters:(CPArray)frameUpdaters forAction:(id)anAction
|
||||
{
|
||||
var rootIdentifier = [anAction.root UID];
|
||||
|
||||
var frameUpdater = [frameUpdaters objectPassingTest:function(updater, idx, stop)
|
||||
{
|
||||
// There is one timer, linked to the top view, that updates the whole hierarchy.
|
||||
return updater.identifier() == rootIdentifier;
|
||||
}];
|
||||
|
||||
if (frameUpdater == nil)
|
||||
{
|
||||
frameUpdater = new FrameUpdater(rootIdentifier);
|
||||
[frameUpdaters addObject:frameUpdater];
|
||||
FRAME_UPDATERS[rootIdentifier] = frameUpdater;
|
||||
}
|
||||
|
||||
frameUpdater.addTarget(anAction.object, anAction.keypath, anAction.duration);
|
||||
}
|
||||
|
||||
+ (void)stopUpdaterWithIdentifier:(CPString)anIdentifier
|
||||
{
|
||||
var frameUpdater = FRAME_UPDATERS[anIdentifier];
|
||||
|
||||
if (frameUpdater)
|
||||
{
|
||||
frameUpdater.stop();
|
||||
delete FRAME_UPDATERS[anIdentifier];
|
||||
}
|
||||
else
|
||||
CPLog.warn("Could not find FrameUpdater with identifier " + anIdentifier);
|
||||
}
|
||||
|
||||
- (BOOL)needsPeriodicFrameUpdatesForKeyPath:(CPString)aKeyPath
|
||||
{
|
||||
return ((aKeyPath == @"frame" || aKeyPath == @"frameSize") &&
|
||||
@@ -136,36 +231,8 @@ var frameToCSSTranslationTransformMatrix = function(start, current)
|
||||
return CSSStringFromCGAffineTransform(affine);
|
||||
};
|
||||
|
||||
var DEFAULT_CSS_PROPERTIES = nil;
|
||||
|
||||
@implementation CPView (CPAnimatablePropertyContainer)
|
||||
|
||||
+ (CPDictionary)_defaultCSSProperties
|
||||
{
|
||||
if (DEFAULT_CSS_PROPERTIES == nil)
|
||||
{
|
||||
var transformProperty = CPBrowserCSSProperty("transform");
|
||||
|
||||
DEFAULT_CSS_PROPERTIES = @{
|
||||
"backgroundColor" : [@{"property":"background", "value":function(sv, val){return [val cssString];}}],
|
||||
"alphaValue" : [@{"property":"opacity"}],
|
||||
"frame" : [@{"property":transformProperty, "value":frameToCSSTranslationTransformMatrix},
|
||||
@{"property":"width", "value":transformFrameToWidth},
|
||||
@{"property":"height", "value":transformFrameToHeight}],
|
||||
"frameOrigin" : [@{"property":transformProperty, "value":frameOriginToCSSTransformMatrix}],
|
||||
"frameSize" : [@{"property":"width", "value":transformSizeToWidth},
|
||||
@{"property":"height", "value":transformSizeToHeight}]
|
||||
};
|
||||
}
|
||||
|
||||
return DEFAULT_CSS_PROPERTIES;
|
||||
}
|
||||
|
||||
+ (CPArray)_cssPropertiesForKeyPath:(CPString)aKeyPath
|
||||
{
|
||||
return [[self _defaultCSSProperties] objectForKey:aKeyPath];
|
||||
}
|
||||
|
||||
+ (Class)animatorClass
|
||||
{
|
||||
var anim_class = CPClassFromString(CPStringFromClass(self) + "Animator");
|
||||
@@ -184,11 +251,6 @@ var DEFAULT_CSS_PROPERTIES = nil;
|
||||
return _animator;
|
||||
}
|
||||
|
||||
- (id)DOMElementForKeyPath:(CPString)aKeyPath
|
||||
{
|
||||
return _DOMElement;
|
||||
}
|
||||
|
||||
+ (CAAnimation)defaultAnimationForKey:(CPString)aKey
|
||||
{
|
||||
// TODO: remove when supported.
|
||||
@@ -198,7 +260,7 @@ var DEFAULT_CSS_PROPERTIES = nil;
|
||||
return nil;
|
||||
}
|
||||
|
||||
if ([self _cssPropertiesForKeyPath:aKey] !== nil)
|
||||
if ([[self animatorClass] _cssPropertiesForKeyPath:aKey] !== nil)
|
||||
return [CAAnimation animation];
|
||||
|
||||
return nil;
|
||||
@@ -227,6 +289,11 @@ var DEFAULT_CSS_PROPERTIES = nil;
|
||||
_animationsDictionary = [animationsDict copy];
|
||||
}
|
||||
|
||||
- (Object)_DOMElement
|
||||
{
|
||||
return _DOMElement;
|
||||
}
|
||||
|
||||
- (CPString)debug_description
|
||||
{
|
||||
return [self identifier] || [self className];
|
||||
@@ -234,6 +301,19 @@ var DEFAULT_CSS_PROPERTIES = nil;
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPArray (Additions)
|
||||
|
||||
- (CPArray)objectPassingTest:(Function)aFunction
|
||||
{
|
||||
var idx = [self indexOfObjectPassingTest:aFunction];
|
||||
|
||||
if (idx !== CPNotFound)
|
||||
return [self objectAtIndex:idx];
|
||||
|
||||
return nil;
|
||||
}
|
||||
@end
|
||||
|
||||
var FrameUpdater = function(anIdentifier)
|
||||
{
|
||||
this._identifier = anIdentifier;
|
||||
|
||||
Reference in new issue
Block a user