我正在尝试仅使用图层功能在两个UIView上实现一种折纸转换。我们的想法是用透视效果折叠两个视图。两个视图都有透视,过渡由每个视图上的旋转定义,以及一个视图上的翻译,使得该视图似乎附加到另一个视图。
问题是视图在转换过程中彼此重叠。我不想使用zPosition来直观地避免这种重叠,我真的希望这两个视图就好像它们被共享端绑定在一起一样。以下是转换的代码。
任何想法或任何其他解决方案?
- (void)animateWithPerspective
{
CGFloat rotationAngle = 90;
CATransform3D transform = CATransform3DIdentity;
UIView *topView;
UIView *bottomView;
UIView *mainView;
CGRect frame;
CGFloat size = 200;
mainView = [[UIView alloc] initWithFrame:CGRectMake(10,10, size, size*2)];
[self.view addSubview:mainView];
bottomView = [[UIView alloc] initWithFrame:CGRectZero];
bottomView.layer.anchorPoint = CGPointMake(0.5, 1);
bottomView.frame = CGRectMake(0, size, size, size);
bottomView.backgroundColor = [UIColor blueColor];
[mainView addSubview:bottomView];
topView = [[UIView alloc] initWithFrame:CGRectZero];
topView.layer.anchorPoint = CGPointMake(0.5, 0);
topView.frame = CGRectMake(0, 0, size, size);
topView.backgroundColor = [UIColor redColor];
[mainView addSubview:topView];
transform.m34 = 1.0/700.0;
topView.layer.transform = transform;
bottomView.layer.transform = transform;
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:2];
[UIView setAnimationRepeatAutoreverses:YES];
[UIView setAnimationRepeatCount:INFINITY];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
frame = bottomView.frame;
frame.origin.y = bottomView.frame.origin.y - bottomView.frame.size.height - topView.frame.size.height;
bottomView.frame = frame;
topView.layer.transform = CATransform3DRotate(transform, rotationAngle * M_PI/180, 1, 0, 0);
bottomView.layer.transform = CATransform3DRotate(transform, -rotationAngle * M_PI/180, 1, 0, 0);
[UIView commitAnimations];
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self animate];
}
为了简化问题,让我们摆脱任何透视变换。这是一个更简单的代码,具有相同的问题:
- (void)animateWithoutPerspective
{
CGFloat rotationAngle = 90;
UIView *topView;
UIView *bottomView;
UIView *mainView;
CGRect frame;
CGFloat size = 200;
mainView = [[UIView alloc] initWithFrame:CGRectMake(10,10, size, size*2)];
[self.view addSubview:mainView];
bottomView = [[UIView alloc] initWithFrame:CGRectMake(0, size, size, size)];
bottomView.backgroundColor = [UIColor blueColor];
[mainView addSubview:bottomView];
topView = [[UIView alloc] initWithFrame:CGRectZero];
topView.layer.anchorPoint = CGPointMake(0.5, 0);
topView.frame = CGRectMake(10, 0, size-20, size);
topView.backgroundColor = [UIColor redColor];
[mainView addSubview:topView];
[UIView beginAnimations:nil context:nil];
[UIView setAnimationDuration:2];
[UIView setAnimationRepeatAutoreverses:YES];
[UIView setAnimationRepeatCount:INFINITY];
[UIView setAnimationCurve:UIViewAnimationCurveLinear];
frame = bottomView.frame;
frame.origin.y = bottomView.frame.origin.y - bottomView.frame.size.height;
bottomView.frame = frame;
topView.layer.transform = CATransform3DMakeRotation(rotationAngle * M_PI/180, 1, 0, 0);
[UIView commitAnimations];
}
答案 0 :(得分:21)
最后,这里有一个三袖动画的解决方案,添加了简单的阴影。解决这种动画的关键是使用几个组织良好的子层,还有一些CATransformLayer
。
- (void)animate
{
CATransform3D transform = CATransform3DIdentity;
CALayer *topSleeve;
CALayer *middleSleeve;
CALayer *bottomSleeve;
CALayer *topShadow;
CALayer *middleShadow;
UIView *mainView;
CGFloat width = 300;
CGFloat height = 150;
CALayer *firstJointLayer;
CALayer *secondJointLayer;
CALayer *perspectiveLayer;
mainView = [[UIView alloc] initWithFrame:CGRectMake(50, 50, width, height*3)];
mainView.backgroundColor = [UIColor yellowColor];
[self.view addSubview:mainView];
perspectiveLayer = [CALayer layer];
perspectiveLayer.frame = CGRectMake(0, 0, width, height*2);
[mainView.layer addSublayer:perspectiveLayer];
firstJointLayer = [CATransformLayer layer];
firstJointLayer.frame = mainView.bounds;
[perspectiveLayer addSublayer:firstJointLayer];
topSleeve = [CALayer layer];
topSleeve.frame = CGRectMake(0, 0, width, height);
topSleeve.anchorPoint = CGPointMake(0.5, 0);
topSleeve.backgroundColor = [UIColor redColor].CGColor;
topSleeve.position = CGPointMake(width/2, 0);
[firstJointLayer addSublayer:topSleeve];
topSleeve.masksToBounds = YES;
secondJointLayer = [CATransformLayer layer];
secondJointLayer.frame = mainView.bounds;
secondJointLayer.frame = CGRectMake(0, 0, width, height*2);
secondJointLayer.anchorPoint = CGPointMake(0.5, 0);
secondJointLayer.position = CGPointMake(width/2, height);
[firstJointLayer addSublayer:secondJointLayer];
middleSleeve = [CALayer layer];
middleSleeve.frame = CGRectMake(0, 0, width, height);
middleSleeve.anchorPoint = CGPointMake(0.5, 0);
middleSleeve.backgroundColor = [UIColor blueColor].CGColor;
middleSleeve.position = CGPointMake(width/2, 0);
[secondJointLayer addSublayer:middleSleeve];
middleSleeve.masksToBounds = YES;
bottomSleeve = [CALayer layer];
bottomSleeve.frame = CGRectMake(0, height, width, height);
bottomSleeve.anchorPoint = CGPointMake(0.5, 0);
bottomSleeve.backgroundColor = [UIColor grayColor].CGColor;
bottomSleeve.position = CGPointMake(width/2, height);
[secondJointLayer addSublayer:bottomSleeve];
firstJointLayer.anchorPoint = CGPointMake(0.5, 0);
firstJointLayer.position = CGPointMake(width/2, 0);
topShadow = [CALayer layer];
[topSleeve addSublayer:topShadow];
topShadow.frame = topSleeve.bounds;
topShadow.backgroundColor = [UIColor blackColor].CGColor;
topShadow.opacity = 0;
middleShadow = [CALayer layer];
[middleSleeve addSublayer:middleShadow];
middleShadow.frame = middleSleeve.bounds;
middleShadow.backgroundColor = [UIColor blackColor].CGColor;
middleShadow.opacity = 0;
transform.m34 = -1.0/700.0;
perspectiveLayer.sublayerTransform = transform;
CABasicAnimation* animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.x"];
[animation setDuration:2];
[animation setAutoreverses:YES];
[animation setRepeatCount:INFINITY];
[animation setFromValue:[NSNumber numberWithDouble:0]];
[animation setToValue:[NSNumber numberWithDouble:-90*M_PI/180]];
[firstJointLayer addAnimation:animation forKey:nil];
animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.x"];
[animation setDuration:2];
[animation setAutoreverses:YES];
[animation setRepeatCount:INFINITY];
[animation setFromValue:[NSNumber numberWithDouble:0]];
[animation setToValue:[NSNumber numberWithDouble:180*M_PI/180]];
[secondJointLayer addAnimation:animation forKey:nil];
animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.x"];
[animation setDuration:2];
[animation setAutoreverses:YES];
[animation setRepeatCount:INFINITY];
[animation setFromValue:[NSNumber numberWithDouble:0]];
[animation setToValue:[NSNumber numberWithDouble:-90*M_PI/180]];
[bottomSleeve addAnimation:animation forKey:nil];
animation = [CABasicAnimation animationWithKeyPath:@"bounds.size.height"];
[animation setDuration:2];
[animation setAutoreverses:YES];
[animation setRepeatCount:INFINITY];
[animation setFromValue:[NSNumber numberWithDouble:perspectiveLayer.bounds.size.height]];
[animation setToValue:[NSNumber numberWithDouble:0]];
[perspectiveLayer addAnimation:animation forKey:nil];
animation = [CABasicAnimation animationWithKeyPath:@"position.y"];
[animation setDuration:2];
[animation setAutoreverses:YES];
[animation setRepeatCount:INFINITY];
[animation setFromValue:[NSNumber numberWithDouble:perspectiveLayer.position.y]];
[animation setToValue:[NSNumber numberWithDouble:0]];
[perspectiveLayer addAnimation:animation forKey:nil];
animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
[animation setDuration:2];
[animation setAutoreverses:YES];
[animation setRepeatCount:INFINITY];
[animation setFromValue:[NSNumber numberWithDouble:0]];
[animation setToValue:[NSNumber numberWithDouble:0.5]];
[topShadow addAnimation:animation forKey:nil];
animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
[animation setDuration:2];
[animation setAutoreverses:YES];
[animation setRepeatCount:INFINITY];
[animation setFromValue:[NSNumber numberWithDouble:0]];
[animation setToValue:[NSNumber numberWithDouble:0.5]];
[middleShadow addAnimation:animation forKey:nil];
}
答案 1 :(得分:1)
起初我认为Y位置的线性变换并不意味着旋转的线性变换,但似乎是这种情况。
错误非常简单,透视值错误,透视图通过在Z轴上以负距离定位天文台来建模。那么你需要否定透视值:
transform.m34 = 1.0/(-700.0);
它确实像预期的那样工作。
仅为记录,角度的变换不是线性的。但是这个工件被zbuffer隐藏了。
在中间路径,角度为60度,但线性动画得到45度。但从右侧看,从负Z轴位置看,缓冲区隐藏了平面交叉点。
答案 2 :(得分:0)
说明答案。
我没有将所有动画和透视投影(perspectiveLayer.sublayerTransform
放在其CATransformLayer
子图层上)。使用投影矩阵m34字段值来观察它如何影响消失点。
答案 3 :(得分:0)
Vojtec答案的更新版本。
func animate() {
var transform:CATransform3D = CATransform3DIdentity;
var topSleeve:CALayer
var middleSleeve:CALayer
var bottomSleeve:CALayer
var topShadow:CALayer
var middleShadow:CALayer
var mainView:UIView
let width:CGFloat = 300
let height:CGFloat = 150
var firstJointLayer:CALayer
var secondJointLayer:CALayer
var perspectiveLayer:CALayer
mainView = UIView(frame:CGRect(x: 50, y: 50, width: width, height: height*3))
mainView.backgroundColor = UIColor.yellow
view.addSubview(mainView)
perspectiveLayer = CALayer()
perspectiveLayer.frame = CGRect(x: 0, y: 0, width: width, height: height*2)
mainView.layer.addSublayer(perspectiveLayer)
firstJointLayer = CATransformLayer()
firstJointLayer.frame = mainView.bounds;
perspectiveLayer.addSublayer(firstJointLayer)
topSleeve = CALayer()
topSleeve.frame = CGRect(x: 0, y: 0, width: width, height: height)
topSleeve.anchorPoint = CGPoint(x: 0.5, y: 0)
topSleeve.backgroundColor = UIColor.red.cgColor
topSleeve.position = CGPoint(x: width/2, y: 0)
firstJointLayer.addSublayer(topSleeve)
topSleeve.masksToBounds = true
secondJointLayer = CATransformLayer()
secondJointLayer.frame = mainView.bounds;
secondJointLayer.frame = CGRect(x: 0, y: 0, width: width, height: height*2)
secondJointLayer.anchorPoint = CGPoint(x: 0.5, y: 0)
secondJointLayer.position = CGPoint(x: width/2, y: height)
firstJointLayer.addSublayer(secondJointLayer)
middleSleeve = CALayer()
middleSleeve.frame = CGRect(x: 0, y: 0, width: width, height: height);
middleSleeve.anchorPoint = CGPoint(x: 0.5, y: 0)
middleSleeve.backgroundColor = UIColor.blue.cgColor
middleSleeve.position = CGPoint(x: width/2, y: 0)
secondJointLayer.addSublayer(middleSleeve)
middleSleeve.masksToBounds = true
bottomSleeve = CALayer()
bottomSleeve.frame = CGRect(x: 0, y: height, width: width, height: height)
bottomSleeve.anchorPoint = CGPoint(x: 0.5, y: 0)
bottomSleeve.backgroundColor = UIColor.gray.cgColor
bottomSleeve.position = CGPoint(x: width/2, y: height)
secondJointLayer.addSublayer(bottomSleeve)
firstJointLayer.anchorPoint = CGPoint(x: 0.5, y: 0)
firstJointLayer.position = CGPoint(x: width/2, y: 0)
topShadow = CALayer()
topSleeve.addSublayer(topShadow)
topShadow.frame = topSleeve.bounds
topShadow.backgroundColor = UIColor.black.cgColor
topShadow.opacity = 0
middleShadow = CALayer()
middleSleeve.addSublayer(middleShadow)
middleShadow.frame = middleSleeve.bounds
middleShadow.backgroundColor = UIColor.black.cgColor
middleShadow.opacity = 0
transform.m34 = -1/700
perspectiveLayer.sublayerTransform = transform;
var animation = CABasicAnimation(keyPath: "transform.rotation.x")
animation.duration = 2
animation.autoreverses = true
animation.repeatCount = 1000
animation.fromValue = 0
animation.toValue = -90*Double.pi/180
firstJointLayer.add(animation, forKey: nil)
animation = CABasicAnimation(keyPath: "transform.rotation.x")
animation.duration = 2
animation.autoreverses = true
animation.repeatCount = 1000
animation.fromValue = 0
animation.toValue = 180*Double.pi/180
secondJointLayer.add(animation, forKey: nil)
animation = CABasicAnimation(keyPath: "transform.rotation.x")
animation.duration = 2
animation.autoreverses = true
animation.repeatCount = 1000
animation.fromValue = 0
animation.toValue = -90*Double.pi/180
bottomSleeve.add(animation, forKey: nil)
animation = CABasicAnimation(keyPath: "bounds.size.height")
animation.duration = 2
animation.autoreverses = true
animation.repeatCount = 1000
animation.fromValue = perspectiveLayer.bounds.size.height
animation.toValue = 0
perspectiveLayer.add(animation, forKey: nil)
animation = CABasicAnimation(keyPath: "position.y")
animation.duration = 2
animation.autoreverses = true
animation.repeatCount = 1000
animation.fromValue = perspectiveLayer.position.y
animation.toValue = 0
perspectiveLayer.add(animation, forKey: nil)
animation = CABasicAnimation(keyPath: "opacity")
animation.duration = 2
animation.autoreverses = true
animation.repeatCount = 1000
animation.fromValue = 0
animation.toValue = 0.5
topShadow.add(animation, forKey: nil)
animation = CABasicAnimation(keyPath: "opacity")
animation.duration = 2
animation.autoreverses = true
animation.repeatCount = 1000
animation.fromValue = 0
animation.toValue = 0.5
middleShadow.add(animation, forKey: nil)
}
}