Как вращать плоский объект вокруг его центра в перспективе?
Кажется, то, что я пытаюсь сделать, должно быть достаточно простым: я создал двухмерный вид сверху на старый фонограф. Я хочу повернуть его (отложить) по оси X, а затем повернуть вокруг оси Z.
Я прочитал все вопросы, которые содержат CATransform3D в теле, я прочитал статью Стива Бейкера "Матрицы могут быть вашими друзьями", а также книгу Билла Дадни "Базовая анимация для Mac OS X и iPhone", думаю, у Брэда Ларсона. "Трехмерное вращение без трекбола" имеет весь правильный код, но, поскольку он позволяет пользователю настраивать все три оси, мне трудно сжать его код в то, что я воспринимаю как одно измерение (повернутая ось Z).
Вот изображение, с которым я тестирую, а не то, что детали важны для проблемы:
Я выведу это на экран обычным способом: (в подклассе UIView)
- (void)awakeFromNib
{
UIImage *recordImage = [UIImage imageNamed:@"3DgoldRecord"];
if (recordImage) {
recordLayer = [CALayer layer];
[recordLayer setFrame:CGRectMake(0.0, 0.0, 1024, 1024)];
[recordLayer setContents:(id)[[UIImage imageNamed:@"3DgoldRecord"] CGImage]];
[self.layer addSublayer:recordLayer];
}
}
Это первый тест, просто вывод его на экран;-)
Затем, чтобы "отложить его", я применяю преобразование для поворота вокруг оси X слоя, вставляя этот код после установки содержимого слоя в изображение и перед добавлением подслоя:
CATransform3D myRotationTransform =
CATransform3DRotate(recordLayer.transform,
(M_PI_2 * 0.85), //experiment with flatness
1.0, // rotate only across the x-axis
0.0, // no y-axis transform
0.0); // no z-axis transform
recordLayer.transform = myRotationTransform;
Это сработало, как и ожидалось: пластинка неплохо ложится.
И для следующего шага, заставляя запись вращаться, я привязал эту анимацию к событию touchesEnded, хотя после фазы тестирования / обучения это вращение не будет находиться под контролем пользователя:
CATransform3D currentTransform = recordLayer.transform; // to come back to at the end
CATransform3D myRotationTransform =
CATransform3DRotate(currentTransform,
1.0, // go all the way around once
(M_PI_2 * 0.85), // x-axis change
1.00, // y-axis change ?
0.0); // z-axis change ?
recordLayer.transform = myRotationTransform;
CABasicAnimation *myAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation"];
myAnimation.duration = 5.0;
myAnimation.fromValue = [NSNumber numberWithFloat:0.0];
myAnimation.toValue = [NSNumber numberWithFloat:M_PI * 2.0];
myAnimation.delegate = self;
[recordLayer addAnimation:myAnimation forKey:@"transform.rotation"];
Так что я почти уверен, что я зациклен на векторе CATransform3DRotate (поверьте мне: я пробовал простые изменения в этом векторе, чтобы наблюдать за изменениями... то, что там указано сейчас, просто последнее изменение Я старался). Насколько я понимаю, значения для x, y и z в преобразовании, по сути, представляют собой процент от значения, переданного во время анимации, в диапазоне от fromValue до toValue.
Если я на правильном пути, понимая это, возможно ли выразить это в одном преобразовании? Или я должен для каждого эффективного кадра анимации слегка повернуть исходное вертикальное изображение вокруг оси z, а затем положить результат с поворотом оси x? Я видел вопрос / ответ, в котором говорилось о комбинировании преобразований, но это было преобразование масштаба, за которым следовало преобразование вращения. Я возился с преобразованием преобразования, но не делаю то, что, как я думаю, должен получить (т.е. передача результата одного преобразования в аргумент преобразования следующего, казалось, просто выполняет одно полностью, а затем анимирует другое).
2 ответа
Это проще всего, если вы используете CATransformLayer
в качестве родителя слоя представления изображения. Так что вам понадобится пользовательский подкласс вида, который использует CATransformLayer
как его слой. Это тривиально:
@interface TransformView : UIView
@end
@implementation TransformView
+ (Class)layerClass {
return [CATransformLayer class];
}
@end
Положить TransformView
в свой кончик, и добавьте UIImageView
как подпредставление TransformView
, Подключите эти виды к выходам в вашем контроллере вида transformView
а также discView
,
В вашем контроллере представления установите преобразование transformView
применить перспективу (установив m34
) и наклон оси X:
- (void)viewDidLoad
{
[super viewDidLoad];
CATransform3D transform = CATransform3DIdentity;
transform.m34 = -1 / 500.0;
transform = CATransform3DRotate(transform, .85 * M_PI_2, 1, 0, 0);
self.transformView.layer.transform = transform;
}
Добавить анимацию для ключевого пути transform.rotation.z
в discView
в viewWillAppear:
и удалите его в viewDidDisappear:
:
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
animation.fromValue = [NSNumber numberWithFloat:0];
animation.toValue = [NSNumber numberWithFloat:2 * M_PI];
animation.duration = 1.0;
animation.repeatCount = HUGE_VALF;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
[self.discView.layer addAnimation:animation forKey:@"transform.rotation.z"];
}
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
[self.discView.layer removeAllAnimations];
}
Результат:
ОБНОВИТЬ
Вот демонстрация игровой площадки Swift:
import UIKit
import XCPlayground
class TransformView: UIView {
override class func layerClass() -> AnyClass {
return CATransformLayer.self
}
}
let view = UIView(frame: CGRectMake(0, 0, 300, 150))
view.backgroundColor = UIColor.groupTableViewBackgroundColor()
XCPlaygroundPage.currentPage.liveView = view
let transformView = TransformView(frame: view.bounds)
view.addSubview(transformView)
var transform = CATransform3DIdentity
transform.m34 = CGFloat(-1) / transformView.bounds.width
transform = CATransform3DRotate(transform, 0.85 * CGFloat(M_PI_2), 1, 0, 0)
transformView.layer.transform = transform
let image = UIImage(named: "3DgoldRecord")!
let imageView = UIImageView(image: image)
imageView.bounds = CGRectMake(0, 0, 200, 200)
imageView.center = CGPointMake(transformView.bounds.midX, transformView.bounds.midY)
transformView.addSubview(imageView)
let animation = CABasicAnimation(keyPath: "transform.rotation.z")
animation.fromValue = 0
animation.toValue = 2 * M_PI
animation.duration = 1
animation.repeatCount = Float.infinity
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
imageView.layer.addAnimation(animation, forKey: animation.keyPath)
Скопируйте изображение из вопроса в папку "Ресурсы" и назовите его. 3DgoldRecord.png
, Результат:
Вы можете обернуть слой записи слоем суперслоя. Примените вращение оси X к суперслою и добавьте анимацию вращения оси Z в recordLayer.