You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@weex.apache.org by ac...@apache.org on 2017/08/15 02:49:12 UTC

[2/7] incubator-weex git commit: * [ios] WXTransition

* [ios] WXTransition


Project: http://git-wip-us.apache.org/repos/asf/incubator-weex/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-weex/commit/741ee207
Tree: http://git-wip-us.apache.org/repos/asf/incubator-weex/tree/741ee207
Diff: http://git-wip-us.apache.org/repos/asf/incubator-weex/diff/741ee207

Branch: refs/heads/0.16-dev
Commit: 741ee207ec66508d9124cb99a88e530a1458733f
Parents: 6bd86e5
Author: doumafang <do...@gmail.com>
Authored: Thu Aug 10 21:34:48 2017 +0800
Committer: doumafang <do...@gmail.com>
Committed: Thu Aug 10 21:34:48 2017 +0800

----------------------------------------------------------------------
 .../Sources/Component/WXComponent_internal.h    |  38 +--
 .../WeexSDK/Sources/Layout/WXComponent+Layout.m | 280 -----------------
 ios/sdk/WeexSDK/Sources/Model/WXComponent.m     |  34 ++-
 .../WeexSDK/Sources/Module/WXAnimationLayout.h  |  49 ---
 .../WeexSDK/Sources/Module/WXAnimationLayout.m  | 115 -------
 .../WeexSDK/Sources/Module/WXAnimationModule.h  |   2 +
 .../WeexSDK/Sources/Module/WXAnimationModule.m  |  46 ++-
 ios/sdk/WeexSDK/Sources/Module/WXTransition.h   |  38 +++
 ios/sdk/WeexSDK/Sources/Module/WXTransition.m   | 299 +++++++++++++++++++
 9 files changed, 380 insertions(+), 521 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/741ee207/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h b/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h
index d2b7d54..49db7a9 100644
--- a/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h
+++ b/ios/sdk/WeexSDK/Sources/Component/WXComponent_internal.h
@@ -21,9 +21,9 @@
 #import "WXComponent.h"
 #import "WXConvert.h"
 #import "WXTransform.h"
+#import "WXTransition.h"
 @class WXTouchGestureRecognizer;
 @class WXThreadSafeCounter;
-@class WXLayoutAnimationInfo;
 
 
 /**
@@ -45,34 +45,8 @@
     WXPositionType _positionType;
     
     
-    
-    //LayoutAnimation
-    WXLayoutAnimationInfo *_heightInfo;
-    WXLayoutAnimationInfo *_widthInfo;
-    WXLayoutAnimationInfo *_topInfo;
-    WXLayoutAnimationInfo *_rightInfo;
-    WXLayoutAnimationInfo *_leftInfo;
-    WXLayoutAnimationInfo *_bottomInfo;
-    double ax;
-    double bx;
-    double cx;
-    
-    double ay;
-    double by;
-    double cy;
-    
-    
-    float _layoutAnimationDuration;
-    float _layoutAnimationDelay;
-    CAMediaTimingFunction *_layoutAnimationTimingFunction;
-    NSUInteger _layoutAnimationCount;
-    
-    NSMutableDictionary *_toStyles;
-    NSMutableDictionary *_fromStyles;
-    NSMutableDictionary *_addStyles;
-    CADisplayLink *_layoutAnimationDisplayLink;
-
-    
+    //Transition
+    WXTransition *_transition;
     
     /**
      *  View
@@ -252,10 +226,4 @@
 
 @end
 
-@interface WXLayoutAnimationInfo : NSObject
-@property (nonatomic, strong) id fromValue;
-@property (nonatomic, strong) id toValue;
-@property (nonatomic, strong) id perValue;
-@property (nonatomic, assign) BOOL isAnimated;
-@end
 

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/741ee207/ios/sdk/WeexSDK/Sources/Layout/WXComponent+Layout.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Layout/WXComponent+Layout.m b/ios/sdk/WeexSDK/Sources/Layout/WXComponent+Layout.m
index 14034a6..4eb57f5 100644
--- a/ios/sdk/WeexSDK/Sources/Layout/WXComponent+Layout.m
+++ b/ios/sdk/WeexSDK/Sources/Layout/WXComponent+Layout.m
@@ -17,8 +17,6 @@
  * under the License.
  */
 
-#define SOLVE_EPS(dur) (1. / (1000. * (dur)))
-
 
 #import "WXComponent+Layout.h"
 #import "WXComponent_internal.h"
@@ -116,210 +114,6 @@
 }
 
 
-#pragma mark LayoutAnimationDisplayLink
-- (void)_startLayoutAnimationDisplayLink
-{
-    WXAssertComponentThread();
-    if (!_layoutAnimationDisplayLink) {
-        _layoutAnimationDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_handleLayoutAnimationDisplayLink)];
-        [_layoutAnimationDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
-    }
-    else{
-        [self _awakeLayoutAnimationDisplayLink];
-    }
-    
-}
-
-- (void)_stopLayoutAnimationDisplayLink
-{
-    WXAssertComponentThread();
-    if (_layoutAnimationDisplayLink) {
-        [_layoutAnimationDisplayLink invalidate];
-        _layoutAnimationDisplayLink = nil;
-    }
-}
-
-- (void)_suspendLayoutAnimationDisplayLink
-{
-    WXAssertComponentThread();
-    if(_layoutAnimationDisplayLink && !_layoutAnimationDisplayLink.paused)
-    {
-        _layoutAnimationDisplayLink.paused = YES;
-    }
-}
-
-
-- (void)_awakeLayoutAnimationDisplayLink
-{
-    WXAssertComponentThread();
-    if (_layoutAnimationDisplayLink && _layoutAnimationDisplayLink.paused) {
-        _layoutAnimationDisplayLink.paused = NO;
-    }
-}
-
-- (void)_handleLayoutAnimationDisplayLink
-{
-    WXAssertComponentThread();
-    int count = _layoutAnimationDuration * 60 / 1000;
-    if (_layoutAnimationCount >= count) {
-        [self _suspendLayoutAnimationDisplayLink];
-        [self _resetProcessAnimationParameter];
-        return;
-    }
-    else
-    {
-        [self _calculateLayoutAnimationProcessingStyle];
-    }
-    _layoutAnimationCount ++;
-}
-
-
-- (void)_resetProcessAnimationParameter
-{
-    
-    _layoutAnimationCount = 0;
-    _layoutAnimationDuration = 0;
-    _widthInfo = nil;
-    _heightInfo = nil;
-    _leftInfo = nil;
-    _rightInfo = nil;
-    _topInfo = nil;
-    _bottomInfo = nil;
-}
-
-- (void)_handleLayoutAnimationWithStyles:(NSDictionary *)styles
-{
-    [self _suspendLayoutAnimationDisplayLink];
-    
-    if (!_addStyles) {
-        _fromStyles = [NSMutableDictionary dictionaryWithDictionary:self.styles];
-        _addStyles = [NSMutableDictionary dictionaryWithDictionary:styles];
-    }
-    else
-    {
-        [_addStyles addEntriesFromDictionary:styles];
-    }//保证_addStyles是唯一的
-    
-    _toStyles = [NSMutableDictionary dictionaryWithDictionary:_fromStyles];
-    [_toStyles addEntriesFromDictionary:_addStyles];
-    
-    _layoutAnimationDuration = _fromStyles[@"transitionDuration"] ? [WXConvert CGFloat:_fromStyles[@"transitionDuration"]] : 0;
-    _layoutAnimationDelay = _fromStyles[@"transitionDelay"] ? [WXConvert CGFloat:_fromStyles[@"transitionDelay"]] : 0;
-    _layoutAnimationTimingFunction = [WXConvert CAMediaTimingFunction:_fromStyles[@"transitionTimingFunction"]];
-    
-    
-    if (_layoutAnimationDuration == 0) {
-        [self _fillCSSNode:styles];
-        return;//如果duration为零直接关闭动画效果
-    }
-    
-    if (![[NSString stringWithFormat:@"%@",_layoutAnimationTimingFunction] isEqualToString: kCAMediaTimingFunctionLinear]) {
-        float vec[4] = {0.};
-        [_layoutAnimationTimingFunction getControlPointAtIndex:1 values:&vec[0]];
-        [_layoutAnimationTimingFunction getControlPointAtIndex:2 values:&vec[2]];
-        [self unitBezierp1x:vec[0] p1y:vec[1] p2x:vec[2] p2y:vec[3]];
-    }
-    
-    
-    NSString *layoutAnimationProperty = _fromStyles[@"transitionProperty"];
-    if ([layoutAnimationProperty containsString:@"width"]) {
-        _widthInfo = [WXLayoutAnimationInfo new];
-        _widthInfo.isAnimated = YES;
-        _widthInfo.fromValue = @(_fromStyles[@"width"] ? [WXConvert CGFloat:_fromStyles[@"width" ]] : 0);
-        _widthInfo.toValue = @(_toStyles[@"width"] ? [WXConvert CGFloat:_toStyles[@"width"]] : 0 );
-        _widthInfo.perValue = @([_widthInfo.toValue doubleValue] - [_widthInfo.fromValue doubleValue]);
-        
-    }
-    if ([layoutAnimationProperty containsString:@"height"])
-    {
-        _heightInfo = [WXLayoutAnimationInfo new];
-        _heightInfo.isAnimated = YES;
-        _heightInfo.fromValue = @(_fromStyles[@"height"] ? [WXConvert CGFloat:_fromStyles[@"height" ]] : 0);
-        _heightInfo.toValue = @(_toStyles[@"height"] ? [WXConvert CGFloat:_toStyles[@"height"]] : 0 );
-        _heightInfo.perValue = @([_heightInfo.toValue doubleValue] - [_heightInfo.fromValue doubleValue]);
-        
-    }
-    if ([layoutAnimationProperty containsString:@"left"])
-    {
-        _leftInfo = [WXLayoutAnimationInfo new];
-        _leftInfo.isAnimated = YES;
-        _leftInfo.fromValue = @(_fromStyles[@"left"] ? [WXConvert CGFloat:_fromStyles[@"left" ]] : 0);
-        _leftInfo.toValue = @(_toStyles[@"left"] ? [WXConvert CGFloat:_toStyles[@"left"]] : 0 );
-        _leftInfo.perValue = @([_leftInfo.toValue doubleValue] - [_leftInfo.fromValue doubleValue]);
-        
-    }
-    if ([layoutAnimationProperty containsString:@"right"])
-    {
-        _rightInfo = [WXLayoutAnimationInfo new];
-        _rightInfo.isAnimated = YES;
-        _rightInfo.fromValue = @(_fromStyles[@"right"] ? [WXConvert CGFloat:_fromStyles[@"right" ]] : 0);
-        _rightInfo.toValue = @(_toStyles[@"right"] ? [WXConvert CGFloat:_toStyles[@"right"]] : 0 );
-        _rightInfo.perValue = @([_rightInfo.toValue doubleValue] - [_rightInfo.fromValue doubleValue]);
-        
-    }
-    if ([layoutAnimationProperty containsString:@"top"])
-    {
-        _topInfo = [WXLayoutAnimationInfo new];
-        _topInfo.isAnimated = YES;
-        _topInfo.fromValue = @(_fromStyles[@"top"] ? [WXConvert CGFloat:_fromStyles[@"top" ]] : 0);
-        _topInfo.toValue = @(_toStyles[@"top"] ? [WXConvert CGFloat:_toStyles[@"top"]] : 0 );
-        _topInfo.perValue = @([_topInfo.toValue doubleValue] - [_topInfo.fromValue doubleValue]);
-        
-    }
-    if ([layoutAnimationProperty containsString:@"bottom"])
-    {
-        _bottomInfo = [WXLayoutAnimationInfo new];
-        _bottomInfo.isAnimated = YES;
-        _bottomInfo.fromValue = @(_fromStyles[@"bottom"] ? [WXConvert CGFloat:_fromStyles[@"bottom" ]] : 0);
-        _bottomInfo.toValue = @(_toStyles[@"bottom"] ? [WXConvert CGFloat:_toStyles[@"bottom"]] : 0 );
-        _bottomInfo.perValue = @([_widthInfo.toValue doubleValue] - [_bottomInfo.fromValue doubleValue]);
-        
-    }
-    if ([layoutAnimationProperty containsString:@"transform"]) {
-        
-        
-    }
-    
-    
-    [self performSelector:@selector(_startLayoutAnimationDisplayLink) withObject:self afterDelay:_layoutAnimationDelay/1000];
-    //    [self _startLayoutAnimationDisplayLink];
-    
-}
-
-
-- (void)_calculateLayoutAnimationProcessingStyle
-{
-    //linear 在做贝塞尔曲线模型
-    double per = 1000 * (_layoutAnimationCount + 1 ) / (60 * _layoutAnimationDuration);//linear
-    if (![[NSString stringWithFormat:@"%@",_layoutAnimationTimingFunction] isEqualToString: kCAMediaTimingFunctionLinear]) {
-        per = [self solveWithx:((_layoutAnimationCount+2)*16)/_layoutAnimationDuration epsilon:SOLVE_EPS(_layoutAnimationDuration)];
-    }
-    
-    double currentWidth = [_widthInfo.fromValue doubleValue] + [_widthInfo.perValue doubleValue] * per;
-    double currentHeight = [_heightInfo.fromValue doubleValue] + [_heightInfo.perValue doubleValue] * per;
-    double currentLeft = [_leftInfo.fromValue doubleValue] + [_leftInfo.perValue doubleValue] * per;
-    double currentRight = [_rightInfo.fromValue doubleValue] + [_rightInfo.perValue doubleValue] * per;
-    double currentTop = [_topInfo.fromValue doubleValue] + [_topInfo.perValue doubleValue] * per;
-    double currentBottom = [_bottomInfo.fromValue doubleValue] + [_bottomInfo.perValue doubleValue] * per;
-    
-    
-    
-    _widthInfo.isAnimated ? [_fromStyles setObject:@(currentWidth) forKey:@"width"]:0;
-    _heightInfo.isAnimated ? [_fromStyles setObject:@(currentHeight) forKey:@"height"]:0;
-    _leftInfo.isAnimated ? [_fromStyles setObject:@(currentLeft) forKey:@"left"]:0;
-    _rightInfo.isAnimated ? [_fromStyles setObject:@(currentRight) forKey:@"right"]:0;
-    _topInfo.isAnimated ? [_fromStyles setObject:@(currentTop) forKey:@"top"]:0;
-    _bottomInfo.isAnimated ? [_fromStyles setObject:@(currentBottom) forKey:@"bottom"]:0;
-    
-    NSLog(@"%@",_fromStyles);
-    
-    [self _fillCSSNode:_fromStyles];
-    
-}
-
-
-
-
 - (void)_frameDidCalculated:(BOOL)isChanged
 {
     WXAssertComponentThread();
@@ -616,78 +410,4 @@ static css_dim_t cssNodeMeasure(void *context, float width, css_measure_mode_t w
     
     return (css_dim_t){resultSize.width, resultSize.height};
 }
-
-//贝塞尔曲线计算
-- (void)unitBezierp1x:(double)p1x p1y:(double)p1y p2x:(double)p2x p2y:(double)p2y
-{
-    cx = 3.0 * p1x;
-    bx = 3.0 * (p2x - p1x) - cx;
-    ax = 1.0 - cx -bx;
-    
-    cy = 3.0 * p1y;
-    by = 3.0 * (p2y - p1y) - cy;
-    ay = 1.0 - cy - by;
-}
-- (double)sampleCurveX:(double)t
-{
-    return ((ax * t + bx) * t + cx) * t;
-}
-
-- (double)sampleCurveY:(double)t
-{
-    return ((ay * t + by) * t + cy) * t;
-}
-
-- (double)sampleCurveDerivativeX:(double)t
-{
-    return (3.0 * ax * t + 2.0 * bx) * t + cx;
-}
-
-- (double)solveCurveX:(double)x epsilon:(double)epsilon
-{
-    double t0;
-    double t1;
-    double t2;
-    double x2;
-    double d2;
-    int i;
-    
-    for (t2 = x, i = 0; i < 8; i++) {
-        x2 = [self sampleCurveX:t2] - x;
-        if (fabs (x2) < epsilon)
-            return t2;
-        d2 = [self sampleCurveDerivativeX:t2];
-        if (fabs(d2) < 1e-6)
-            break;
-        t2 = t2 - x2 / d2;
-    }
-    t0 = 0.0;
-    t1 = 1.0;
-    t2 = x;
-    
-    if (t2 < t0)
-        return t0;
-    if (t2 > t1)
-        return t1;
-    
-    while (t0 < t1) {
-        x2 = [self sampleCurveX:t2];
-        if (fabs(x2 - x) < epsilon)
-            return t2;
-        if (x > x2)
-            t0 = t2;
-        else
-            t1 = t2;
-        t2 = (t1 - t0) * .5 + t0;
-    }
-    return t2;
-}
-
-- (double)solveWithx:(double)x epsilon:(double)epsilon
-{
-    return [self sampleCurveY:([self solveCurveX:x epsilon:epsilon])];
-}
-
-
-
 @end

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/741ee207/ios/sdk/WeexSDK/Sources/Model/WXComponent.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Model/WXComponent.m b/ios/sdk/WeexSDK/Sources/Model/WXComponent.m
index 929d5a5..b8ae789 100644
--- a/ios/sdk/WeexSDK/Sources/Model/WXComponent.m
+++ b/ios/sdk/WeexSDK/Sources/Model/WXComponent.m
@@ -436,12 +436,13 @@
 
 - (void)_updateStylesOnComponentThread:(NSDictionary *)styles resetStyles:(NSMutableArray *)resetStyles isUpdateStyles:(BOOL)isUpdateStyles
 {
-    
-    //根据当前的style是否含有transitionProperty属性来判断是否有Layout Animation
-    if (_styles[@"transitionProperty"]) {
-        [self _handleLayoutAnimationWithStyles:styles];
+    if ([self _isPropertyTransition]) {
+        if (!_transition) {
+            _transition = [WXTransition new];
+        }
+        [_transition _handleTransitionWithStyles:styles withTarget:self];
     }
-    else//如果没有动画属性标明 直接触发layout
+    else
     {
         styles = [self parseStyles:styles];
         [self _updateCSSNodeStyles:styles];
@@ -449,22 +450,30 @@
     
     if (isUpdateStyles) {
         [self _modifyStyles:styles];
-    }//修改_style
+    }
     
     [self _resetCSSNodeStyles:resetStyles];
 }
 
-- (void)_modifyStyles:(NSDictionary *)styles //主要目的是来更新_style
+- (BOOL)_isPropertyTransition
+{
+    BOOL yesOrNo = false;
+    NSString *property = _styles[@"transitionProperty"];
+    if (property) {
+        if ([property containsString:@"width"]||[property containsString:@"height"]||[property containsString:@"top"]||[property containsString:@"left"]||[property containsString:@"bottom"]||[property containsString:@"transform"]) {
+            yesOrNo = true;
+        }
+    }
+    return yesOrNo;
+}
+
+- (void)_modifyStyles:(NSDictionary *)styles
 {
     pthread_mutex_lock(&_propertyMutex);
     [_styles addEntriesFromDictionary:styles];
     pthread_mutex_unlock(&_propertyMutex);
 }
 
-
-
-
-
 - (void)_updateAttributesOnComponentThread:(NSDictionary *)attributes
 {
     pthread_mutex_lock(&_propertyMutex);
@@ -669,6 +678,3 @@
 }
 
 @end
-@implementation WXLayoutAnimationInfo
-
-@end

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/741ee207/ios/sdk/WeexSDK/Sources/Module/WXAnimationLayout.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Module/WXAnimationLayout.h b/ios/sdk/WeexSDK/Sources/Module/WXAnimationLayout.h
deleted file mode 100644
index 1784498..0000000
--- a/ios/sdk/WeexSDK/Sources/Module/WXAnimationLayout.h
+++ /dev/null
@@ -1,49 +0,0 @@
-///*
-// * Licensed to the Apache Software Foundation (ASF) under one
-// * or more contributor license agreements.  See the NOTICE file
-// * distributed with this work for additional information
-// * regarding copyright ownership.  The ASF licenses this file
-// * to you under the Apache License, Version 2.0 (the
-// * "License"); you may not use this file except in compliance
-// * with the License.  You may obtain a copy of the License at
-// *
-// *   http://www.apache.org/licenses/LICENSE-2.0
-// *
-// * Unless required by applicable law or agreed to in writing,
-// * software distributed under the License is distributed on an
-// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// * KIND, either express or implied.  See the License for the
-// * specific language governing permissions and limitations
-// * under the License.
-// */
-//
-//#import <Foundation/Foundation.h>
-//
-//#import "WXComponent.h"
-//#import "WXComponent_internal.h"
-//#import "NSTimer+Weex.h"
-//
-//@interface WXAnimationLayoutInfo : NSObject
-//
-//@property (nonatomic, strong) NSString *propertyName;
-//@property (nonatomic, strong) id fromValue;
-//@property (nonatomic, strong) id toValue;
-//
-//@end
-//
-//
-//@interface WXAnimationLayout : NSObject
-//
-//@property (nonatomic,strong) NSTimer *updateStyleTimer;
-//@property (nonatomic,strong) WXComponent *targetComponent;
-//@property (nonatomic,strong) NSDate *animationStartDate;
-//@property (nonatomic,strong) WXAnimationLayoutInfo *widthInfo;
-//@property (nonatomic,strong) WXAnimationLayoutInfo *heightInfo;
-//@property (nonatomic,assign) double animationDuration;
-//@property (nonatomic,assign) double animationDelay;
-//@property (nonatomic,strong) NSDictionary *needUpdateStyles;
-//@property (nonatomic, weak) WXSDKInstance *weexInstance;
-//
-//- (void)layoutForAnimation;
-//
-//@end

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/741ee207/ios/sdk/WeexSDK/Sources/Module/WXAnimationLayout.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Module/WXAnimationLayout.m b/ios/sdk/WeexSDK/Sources/Module/WXAnimationLayout.m
deleted file mode 100644
index 341f5d2..0000000
--- a/ios/sdk/WeexSDK/Sources/Module/WXAnimationLayout.m
+++ /dev/null
@@ -1,115 +0,0 @@
-///*
-// * Licensed to the Apache Software Foundation (ASF) under one
-// * or more contributor license agreements.  See the NOTICE file
-// * distributed with this work for additional information
-// * regarding copyright ownership.  The ASF licenses this file
-// * to you under the Apache License, Version 2.0 (the
-// * "License"); you may not use this file except in compliance
-// * with the License.  You may obtain a copy of the License at
-// *
-// *   http://www.apache.org/licenses/LICENSE-2.0
-// *
-// * Unless required by applicable law or agreed to in writing,
-// * software distributed under the License is distributed on an
-// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// * KIND, either express or implied.  See the License for the
-// * specific language governing permissions and limitations
-// * under the License.
-// */
-//
-//#import "WXAnimationLayout.h"
-//#import "WXSDKInstance_private.h"
-//
-//@implementation WXAnimationLayoutInfo
-//
-//@end
-//
-//@implementation WXAnimationLayout
-//
-//- (instancetype)init
-//{
-//    if (self = [super init]) {
-//    
-//    }
-//    return self;
-//}
-//
-//- (void)layoutForAnimation
-//{
-//    self.animationStartDate = [NSDate date];
-//    if (_animationDelay > 0) {
-//        [self performSelector:@selector(startUpdateStyleTimer) withObject:nil afterDelay:_animationDelay/1000];
-//    } else {
-//        [self startUpdateStyleTimer];
-//    }
-//}
-//
-//#pragma mark UpdateStyle Methods
-//
-//- (void)startUpdateStyleTimer
-//{
-//    if (!self.updateStyleTimer || ![self.updateStyleTimer isValid]) {
-//        __weak __typeof__(self) weakSelf = self;
-//        self.updateStyleTimer = [NSTimer wx_scheduledTimerWithTimeInterval:16/1000.0f block:^() {
-//            [weakSelf updateStyleOnTimer];
-//        } repeats:YES];
-//        [[NSRunLoop currentRunLoop] addTimer:self.updateStyleTimer forMode:NSRunLoopCommonModes];
-//    }
-//}
-//
-//- (void)stopUpdateStyleTimer
-//{
-//    if (self.updateStyleTimer && [self.updateStyleTimer isValid]) {
-//        [self.updateStyleTimer invalidate];
-//        self.updateStyleTimer = nil;
-//    }
-//}
-//
-//- (void)updateStyleOnTimer
-//{
-//    NSTimeInterval startMsecond = [_animationStartDate timeIntervalSince1970]*1000;
-//    NSTimeInterval nowMsecond = [[NSDate date] timeIntervalSince1970]*1000;
-//    NSTimeInterval interval = nowMsecond - startMsecond;
-//    if (!(_widthInfo || _heightInfo)) {
-//        [self stopUpdateStyleTimer];
-//        return;
-//    }
-//    if (interval > _animationDuration + _animationDelay) {
-//        [self stopUpdateStyleTimer];
-//        return;
-//    }
-//    CGFloat scaleFactor = self.weexInstance.pixelScaleFactor;
-//    _needUpdateStyles = [[NSMutableDictionary alloc] init];
-//    if (_widthInfo) {
-//        double currentValue = (([_widthInfo.toValue doubleValue] - [_widthInfo.fromValue doubleValue]) * ((interval - _animationDelay) / _animationDuration) + [_widthInfo.fromValue doubleValue]) / scaleFactor;
-//        [_needUpdateStyles setValue:[NSNumber numberWithDouble:currentValue] forKey:@"width"];
-//    }
-//    if (_heightInfo) {
-//        double currentValue = (([_heightInfo.toValue doubleValue] - [_heightInfo.fromValue doubleValue]) * ((interval - _animationDelay) / _animationDuration) + [_heightInfo.fromValue doubleValue]) / scaleFactor;
-//        [_needUpdateStyles setValue:[NSNumber numberWithDouble:currentValue] forKey:@"height"];
-//    }
-//    [self updateStyle:_needUpdateStyles];
-//}
-//
-//- (void)updateStyle:(NSDictionary *)styles
-//{
-//    if ([styles count]>0) {
-//        __weak typeof(self) weakSelf = self;
-//        WXPerformBlockOnComponentThread(^{
-//            WXComponentManager *manager = weakSelf.weexInstance.componentManager;
-//            if (!manager.isValid) {
-//                return;
-//            }
-//            [manager updateStyles:styles forComponent:_targetComponent.ref];
-//            [manager startComponentTasks];
-//        });
-//    }
-//}
-//
-//- (void)dealloc
-//{
-//    [self stopUpdateStyleTimer];
-//    [NSObject cancelPreviousPerformRequestsWithTarget:self];
-//}
-//
-//@end

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/741ee207/ios/sdk/WeexSDK/Sources/Module/WXAnimationModule.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Module/WXAnimationModule.h b/ios/sdk/WeexSDK/Sources/Module/WXAnimationModule.h
index ae7f068..d5a8c43 100644
--- a/ios/sdk/WeexSDK/Sources/Module/WXAnimationModule.h
+++ b/ios/sdk/WeexSDK/Sources/Module/WXAnimationModule.h
@@ -22,6 +22,8 @@
 
 @interface WXAnimationModule : NSObject <WXModuleProtocol>
 
+
 - (void)animation:(WXComponent *)targetComponent args:(NSDictionary *)args callback:(WXModuleCallback)callback;
+- (void)animationModuleProcessAnimationWithArgs:(NSDictionary *)args withWeexInstance:(WXSDKInstance *)instance targetComponent:(WXComponent *)target;
 
 @end

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/741ee207/ios/sdk/WeexSDK/Sources/Module/WXAnimationModule.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Module/WXAnimationModule.m b/ios/sdk/WeexSDK/Sources/Module/WXAnimationModule.m
index 46cf68a..dc01af3 100644
--- a/ios/sdk/WeexSDK/Sources/Module/WXAnimationModule.m
+++ b/ios/sdk/WeexSDK/Sources/Module/WXAnimationModule.m
@@ -24,7 +24,7 @@
 #import "WXTransform.h"
 #import "WXUtility.h"
 #import "WXLength.h"
-#import "WXAnimationLayout.h"
+#import "WXTransition.h"
 
 @interface WXAnimationInfo : NSObject<NSCopying>
 
@@ -36,7 +36,6 @@
 @property (nonatomic, assign) double delay;
 @property (nonatomic, strong) CAMediaTimingFunction *timingFunction;
 @property (nonatomic, assign) CGPoint originAnchorPoint;
-
 @end
 
 @implementation WXAnimationInfo
@@ -137,8 +136,9 @@
 
 @interface WXAnimationModule ()
 
-//@property (nonatomic,strong) WXAnimationLayout *animationLayout;
 @property (nonatomic,assign) BOOL needLayout;
+@property (nonatomic, strong) WXTransition *transition;
+
 @end
 
 @implementation WXAnimationModule
@@ -150,8 +150,6 @@ WX_EXPORT_METHOD(@selector(transition:args:callback:))
 - (void)transition:(NSString *)nodeRef args:(NSDictionary *)args callback:(WXModuleCallback)callback
 {
     _needLayout = NO;
-//    _animationLayout = [[WXAnimationLayout alloc] init];
-//    _animationLayout.weexInstance = self.weexInstance;
     WXPerformBlockOnComponentThread(^{
         WXComponent *targetComponent = [self.weexInstance componentForRef:nodeRef];
         if (!targetComponent) {
@@ -177,9 +175,8 @@ WX_EXPORT_METHOD(@selector(transition:args:callback:))
     double delay = [args[@"delay"] doubleValue] / 1000;
     if (args[@"needLayout"]) {
         _needLayout = [WXConvert BOOL:args[@"needLayout"]];
+        _transition = [WXTransition new];
     }
-//    _animationLayout.animationDuration = duration * 1000;
-//    _animationLayout.animationDelay = delay * 1000;
     CAMediaTimingFunction *timingFunction = [WXConvert CAMediaTimingFunction:args[@"timingFunction"]];
     NSDictionary *styles = args[@"styles"];
     for (NSString *property in styles) {
@@ -276,10 +273,6 @@ WX_EXPORT_METHOD(@selector(transition:args:callback:))
         } else if ([property isEqualToString:@"width"]) {
             if (_needLayout) {
                 [self animationWithLayoutAnimationTarget:target handleProperty:property withDic:args];
-                //                _animationLayout.widthInfo = [[WXAnimationLayoutInfo alloc] init];
-                //                _animationLayout.widthInfo.toValue = info.toValue;
-                //                _animationLayout.widthInfo.fromValue = info.fromValue;
-                //                _animationLayout.widthInfo.propertyName = info.propertyName;
             }
             else
             {
@@ -293,10 +286,6 @@ WX_EXPORT_METHOD(@selector(transition:args:callback:))
         } else if ([property isEqualToString:@"height"]) {
             if (_needLayout) {
                 [self animationWithLayoutAnimationTarget:target handleProperty:property withDic:args];
-                //                _animationLayout.heightInfo = [[WXAnimationLayoutInfo alloc] init];
-                //                _animationLayout.heightInfo.toValue = info.toValue;
-                //                _animationLayout.heightInfo.fromValue = info.fromValue;
-                //                _animationLayout.heightInfo.propertyName = info.propertyName;
             }
             else
             {
@@ -313,14 +302,20 @@ WX_EXPORT_METHOD(@selector(transition:args:callback:))
     return infos;
 }
 
+- (void)animationModuleProcessAnimationWithArgs:(NSDictionary *)args withWeexInstance:(WXSDKInstance *)instance targetComponent:(WXComponent *)target
+{
+    self.weexInstance = instance;
+    [self animation:target args:args callback:nil];
+}
+
 - (void)animationWithLayoutAnimationTarget:(WXComponent *)target handleProperty:(NSString *)property withDic:(NSDictionary *)args
 {
     NSDictionary *styles = args[@"styles"];
-    target->_addStyles = [NSMutableDictionary dictionaryWithDictionary:styles];
-    target->_fromStyles = target->_fromStyles ? :[NSMutableDictionary dictionaryWithDictionary:target.styles] ;
-    [target->_fromStyles setObject:@([args[@"duration"] doubleValue]) forKey:@"transitionDuration"];
-    [target->_fromStyles setObject:@([args[@"delay"] doubleValue]) forKey:@"transitionDelay"];
-    NSString *oldProperty = target->_fromStyles[@"transitionProperty"];
+    _transition.addStyles = [NSMutableDictionary dictionaryWithDictionary:styles];
+    _transition.fromStyles =_transition.fromStyles ? :[NSMutableDictionary dictionaryWithDictionary:target.styles] ;
+    [_transition.fromStyles setObject:@([args[@"duration"] doubleValue]) forKey:@"transitionDuration"];
+    [_transition.fromStyles setObject:@([args[@"delay"] doubleValue]) forKey:@"transitionDelay"];
+    NSString *oldProperty = _transition.fromStyles[@"transitionProperty"];
     NSString *newProperty;
     if (oldProperty) {
         if ([oldProperty containsString:property]) {
@@ -335,14 +330,11 @@ WX_EXPORT_METHOD(@selector(transition:args:callback:))
     {
         newProperty = property;
     }
-    [target->_fromStyles setObject:newProperty forKey:@"transitionProperty"];
-    [target->_fromStyles setObject:args[@"timingFunction"] forKey:@"transitionTimingFunction"];
+    [_transition.fromStyles setObject:newProperty forKey:@"transitionProperty"];
+    [_transition.fromStyles setObject:args[@"timingFunction"] forKey:@"transitionTimingFunction"];
     [target _modifyStyles:styles];
     
 }
-
-
-
 - (void)animation:(WXComponent *)targetComponent args:(NSDictionary *)args callback:(WXModuleCallback)callback
 {
     /**
@@ -364,10 +356,8 @@ WX_EXPORT_METHOD(@selector(transition:args:callback:))
     
     [CATransaction commit];
     if (_needLayout) {
-        //        _animationLayout.targetComponent = targetComponent;
-        //        [_animationLayout layoutForAnimation];
         WXPerformBlockOnComponentThread(^{
-            [targetComponent _handleLayoutAnimationWithStyles:targetComponent->_addStyles];
+            [_transition _handleTransitionWithStyles:_transition.addStyles withTarget:targetComponent];
         });
     }
 }

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/741ee207/ios/sdk/WeexSDK/Sources/Module/WXTransition.h
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Module/WXTransition.h b/ios/sdk/WeexSDK/Sources/Module/WXTransition.h
new file mode 100644
index 0000000..9a0d5a8
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Module/WXTransition.h
@@ -0,0 +1,38 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#import <Foundation/Foundation.h>
+
+@interface WXLayoutAnimationInfo : NSObject
+@property (nonatomic, strong) id fromValue;
+@property (nonatomic, strong) id toValue;
+@property (nonatomic, strong) id perValue;
+@property (nonatomic, strong) NSString *propertyName;
+
+@end
+
+@interface WXTransition : NSObject
+@property(nonatomic,strong) NSMutableDictionary *fromStyles;
+@property(nonatomic,strong) NSMutableDictionary *addStyles;
+@property(nonatomic,strong) NSMutableArray *propertyArray;
+- (void)_handleTransitionWithStyles:(NSDictionary *)styles withTarget:(WXComponent *)targetComponent;
+
+@end
+
+

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/741ee207/ios/sdk/WeexSDK/Sources/Module/WXTransition.m
----------------------------------------------------------------------
diff --git a/ios/sdk/WeexSDK/Sources/Module/WXTransition.m b/ios/sdk/WeexSDK/Sources/Module/WXTransition.m
new file mode 100644
index 0000000..26d9fb9
--- /dev/null
+++ b/ios/sdk/WeexSDK/Sources/Module/WXTransition.m
@@ -0,0 +1,299 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+#define SOLVE_EPS(dur) (1. / (1000. * (dur)))
+
+#import <QuartzCore/CATransaction.h>
+#import <QuartzCore/CADisplayLink.h>
+#import "WXAnimationModule.h"
+#import "WXComponentManager.h"
+#import "WXSDKInstance.h"
+#import "WXComponent+Layout.h"
+#import "WXComponent_internal.h"
+#import "WXTransition.h"
+#import "WXUtility.h"
+#import "WXAssert.h"
+#import "WXSDKInstance_private.h"
+
+@implementation WXLayoutAnimationInfo
+
+@end
+
+@interface WXTransition()
+{
+    WXComponent *_targetComponent;
+    
+    double ax;
+    double bx;
+    double cx;
+    
+    double ay;
+    double by;
+    double cy;
+    
+    float _layoutAnimationDuration;
+    float _layoutAnimationDelay;
+    NSUInteger _layoutAnimationCount;
+    
+    CAMediaTimingFunction *_layoutAnimationTimingFunction;
+    CADisplayLink *_layoutAnimationDisplayLink;
+
+    NSMutableDictionary *_toStyles;
+    NSMutableDictionary *_fromStyles;
+    NSMutableDictionary *_addStyles;
+}
+
+@end
+
+@implementation WXTransition
+
+- (void)_handleTransitionWithStyles:(NSDictionary *)styles withTarget:(WXComponent *)targetComponent
+{
+    [self _suspendLayoutAnimationDisplayLink];
+    _targetComponent = targetComponent;
+    if (!_addStyles) {
+        _fromStyles = [NSMutableDictionary dictionaryWithDictionary:targetComponent.styles];
+        _addStyles = [NSMutableDictionary dictionaryWithDictionary:styles];
+    }
+    else
+    {
+        [_addStyles addEntriesFromDictionary:styles];
+    }
+    
+    _toStyles = [NSMutableDictionary dictionaryWithDictionary:_fromStyles];
+    [_toStyles addEntriesFromDictionary:_addStyles];
+    
+    _layoutAnimationDuration = _fromStyles[@"transitionDuration"] ? [WXConvert CGFloat:_fromStyles[@"transitionDuration"]] : 0;
+    _layoutAnimationDelay = _fromStyles[@"transitionDelay"] ? [WXConvert CGFloat:_fromStyles[@"transitionDelay"]] : 0;
+    _layoutAnimationTimingFunction = [WXConvert CAMediaTimingFunction:_fromStyles[@"transitionTimingFunction"]];
+    
+    if (_layoutAnimationDuration == 0) {
+        [targetComponent _updateCSSNodeStyles:styles];
+        WXPerformBlockOnMainThread(^{
+            [targetComponent _updateViewStyles:styles];
+        });
+        return;
+    }
+    
+    if (![[NSString stringWithFormat:@"%@",_layoutAnimationTimingFunction] isEqualToString: kCAMediaTimingFunctionLinear]) {
+        float vec[4] = {0.};
+        [_layoutAnimationTimingFunction getControlPointAtIndex:1 values:&vec[0]];
+        [_layoutAnimationTimingFunction getControlPointAtIndex:2 values:&vec[2]];
+        [self unitBezierp1x:vec[0] p1y:vec[1] p2x:vec[2] p2y:vec[3]];
+    }
+    
+    NSString *layoutAnimationProperty = _fromStyles[@"transitionProperty"];
+    [self _resloveTransitionProperty:layoutAnimationProperty withStyles:styles];
+    [self performSelector:@selector(_startLayoutAnimationDisplayLink) withObject:self afterDelay:_layoutAnimationDelay/1000];
+}
+
+- (void)_resloveTransitionProperty:(NSString *)propertyNames withStyles:(NSDictionary *)styles
+{
+    NSArray *array = @[@"width",@"height",@"top",@"bottom",@"right",@"left",@"transform",@"backgroundColor",@"opacity"];
+    for (NSString *propertyName in array) {
+        if ([propertyNames containsString:propertyName]) {
+            [self _judgeProperty:propertyName withStyles:styles];
+        }
+    }
+}
+
+- (void)_judgeProperty:(NSString *)singleProperty withStyles:(NSDictionary *)styles
+{
+    if (([singleProperty isEqualToString:@"transform"]&&styles[@"transform"]) || ([singleProperty containsString:@"backgroundColor"]&&styles[@"backgroundColor"]) || ([singleProperty containsString:@"opacity"]&&styles[@"opacity"])) {
+        NSDictionary *args = @{@"delay":@(_layoutAnimationDelay),@"duration":@(_layoutAnimationDuration),@"styles":styles,@"timingFunction":_fromStyles[@"transitionTimingFunction"]};
+        [self _animationModuleHandleTransition:args];
+    }
+    else{
+        WXLayoutAnimationInfo *info = [WXLayoutAnimationInfo new];
+        info.fromValue = @(_fromStyles[singleProperty] ? [WXConvert CGFloat:_fromStyles[singleProperty]] : 0);
+        info.toValue = @(_toStyles[singleProperty] ? [WXConvert CGFloat:_toStyles[singleProperty]] : 0 );
+        info.perValue = @([info.toValue doubleValue] - [info.fromValue doubleValue]);
+        info.propertyName = singleProperty;
+        if (!_propertyArray) {
+            _propertyArray = [NSMutableArray new];
+        }
+        [_propertyArray addObject:info];
+    }
+}
+
+- (void)_calculateLayoutAnimationProcessingStyle
+{
+    double per = 1000 * (_layoutAnimationCount + 1 ) / (60 * _layoutAnimationDuration);//linear
+    if (![[NSString stringWithFormat:@"%@",_layoutAnimationTimingFunction] isEqualToString: kCAMediaTimingFunctionLinear]) {
+        per = [self solveWithx:((_layoutAnimationCount+2)*16)/_layoutAnimationDuration epsilon:SOLVE_EPS(_layoutAnimationDuration)];
+    }
+    for (WXLayoutAnimationInfo *info in _propertyArray) {
+        double currentValue = [info.fromValue doubleValue] + [info.perValue doubleValue] * per;
+        [_fromStyles setObject:@(currentValue) forKey:info.propertyName];
+    }
+    [_targetComponent _updateCSSNodeStyles:_fromStyles];
+    [_targetComponent.weexInstance.componentManager startComponentTasks];
+}
+
+- (void)_startLayoutAnimationDisplayLink
+{
+    WXAssertComponentThread();
+    if (!_layoutAnimationDisplayLink) {
+        _layoutAnimationDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(_handleLayoutAnimationDisplayLink)];
+        [_layoutAnimationDisplayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
+    }
+    else{
+        [self _awakeLayoutAnimationDisplayLink];
+    }
+}
+
+- (void)_stopLayoutAnimationDisplayLink
+{
+    WXAssertComponentThread();
+    if (_layoutAnimationDisplayLink) {
+        [_layoutAnimationDisplayLink invalidate];
+        _layoutAnimationDisplayLink = nil;
+    }
+}
+
+- (void)_suspendLayoutAnimationDisplayLink
+{
+    WXAssertComponentThread();
+    if(_layoutAnimationDisplayLink && !_layoutAnimationDisplayLink.paused)
+    {
+        _layoutAnimationDisplayLink.paused = YES;
+    }
+}
+
+- (void)_awakeLayoutAnimationDisplayLink
+{
+    WXAssertComponentThread();
+    if (_layoutAnimationDisplayLink && _layoutAnimationDisplayLink.paused) {
+        _layoutAnimationDisplayLink.paused = NO;
+    }
+}
+
+- (void)_handleLayoutAnimationDisplayLink
+{
+    WXAssertComponentThread();
+    int count = _layoutAnimationDuration * 60 / 1000;
+    if (_layoutAnimationCount >= count) {
+        [self _suspendLayoutAnimationDisplayLink];
+        [self _resetProcessAnimationParameter];
+        return;
+    }
+    else
+    {
+        [self _calculateLayoutAnimationProcessingStyle];
+    }
+    _layoutAnimationCount ++;
+}
+
+- (void)_resetProcessAnimationParameter
+{
+    _layoutAnimationCount = 0;
+    _layoutAnimationDuration = 0;
+
+}
+
+- (void)_animationModuleHandleTransition:(NSDictionary *)args
+{
+    WXAnimationModule *animation = [WXAnimationModule new];
+    WXPerformBlockOnMainThread(^{
+        [animation animationModuleProcessAnimationWithArgs:args withWeexInstance:_targetComponent.weexInstance targetComponent:_targetComponent];
+    });
+}
+
+- (NSMutableDictionary *)_addStyles
+{
+    return self.addStyles;
+}
+
+- (NSMutableDictionary *)_fromStyles
+{
+    return self.fromStyles;
+}
+
+- (void)unitBezierp1x:(double)p1x p1y:(double)p1y p2x:(double)p2x p2y:(double)p2y
+{
+    cx = 3.0 * p1x;
+    bx = 3.0 * (p2x - p1x) - cx;
+    ax = 1.0 - cx -bx;
+    
+    cy = 3.0 * p1y;
+    by = 3.0 * (p2y - p1y) - cy;
+    ay = 1.0 - cy - by;
+}
+
+- (double)sampleCurveX:(double)t
+{
+    return ((ax * t + bx) * t + cx) * t;
+}
+
+- (double)sampleCurveY:(double)t
+{
+    return ((ay * t + by) * t + cy) * t;
+}
+
+- (double)sampleCurveDerivativeX:(double)t
+{
+    return (3.0 * ax * t + 2.0 * bx) * t + cx;
+}
+
+- (double)solveCurveX:(double)x epsilon:(double)epsilon
+{
+    double t0;
+    double t1;
+    double t2;
+    double x2;
+    double d2;
+    int i;
+    
+    for (t2 = x, i = 0; i < 8; i++) {
+        x2 = [self sampleCurveX:t2] - x;
+        if (fabs (x2) < epsilon)
+            return t2;
+        d2 = [self sampleCurveDerivativeX:t2];
+        if (fabs(d2) < 1e-6)
+            break;
+        t2 = t2 - x2 / d2;
+    }
+    t0 = 0.0;
+    t1 = 1.0;
+    t2 = x;
+    
+    if (t2 < t0)
+        return t0;
+    if (t2 > t1)
+        return t1;
+    
+    while (t0 < t1) {
+        x2 = [self sampleCurveX:t2];
+        if (fabs(x2 - x) < epsilon)
+            return t2;
+        if (x > x2)
+            t0 = t2;
+        else
+            t1 = t2;
+        t2 = (t1 - t0) * .5 + t0;
+    }
+    return t2;
+}
+
+- (double)solveWithx:(double)x epsilon:(double)epsilon
+{
+    return [self sampleCurveY:([self solveCurveX:x epsilon:epsilon])];
+}
+
+@end