Как я могу анимировать вращение UIView только в определенном диапазоне градусов?
В моем приложении у меня есть симулятор аналогового индикатора, который вы можете найти на приборной панели автомобиля или на панели управления. Запустите следующий фрагмент кода для примера того, что я имею в виду:
svg { height: 100vh; }
#dial {
fill: white;
stroke: #CCCCCC;
stroke-dasharray: 405;
transform: rotate(135deg);
transform-origin: center;
}
#pointer {
fill: #FF0000;
animation: 2s infinite alternate dialrotate;
transform-origin: bottom center;
animation-timing-function: ease-in-out;
}
@keyframes dialrotate {
from {
transform: rotate(-135deg);
}
to {
transform: rotate(135deg);
}
}
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
<circle id="dial" cx="99.5" cy="100.5" r="86"/>
<polygon id="pointer" points="100.1,25.5 98.2,100 102,100"/>
</svg>
(Я не использую SVG в своем приложении - я просто собрал пример, используя SVG, потому что я в первую очередь веб-разработчик)
Вот как я сейчас оживляю движение циферблата:
UIView.animate(
withDuration: 0.1,
delay: 0.0,
options: .curveEaseOut,
animations: {
self.dialPointer.transform = CGAffineTransform.init(rotationAngle: radians);
},
completion: nil
)
Я хочу анимировать направление указателя между двумя крайностями (270 градусов). Но я не хочу, чтобы указатель вращался по более короткому пути на 90 градусов за пределы циферблата; то есть это НЕПРАВИЛЬНО:
svg { height: 100vh; }
#dial {
fill: white;
stroke: #CCCCCC;
stroke-dasharray: 405;
transform: rotate(135deg);
transform-origin: center;
}
#pointer {
fill: #FF0000;
animation: 2s infinite alternate dialrotate;
transform-origin: bottom center;
animation-timing-function: ease-in-out;
}
@keyframes dialrotate {
from {
transform: rotate(225deg);
}
to {
transform: rotate(135deg);
}
}
<?xml version="1.0" encoding="utf-8"?>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200">
<circle id="dial" cx="99.5" cy="100.5" r="86"/>
<polygon id="pointer" points="100.1,25.5 98.2,100 102,100"/>
</svg>
Как я могу заставить вращение UIView вращаться по длинному пути, вместо того, чтобы вращаться по кратчайшему направлению?
2 ответа
Как вы обнаружили, по умолчанию анимация UIView использует кратчайший путь при интерполяции значений углов. У вас есть несколько вариантов:
- Вы можете разбить анимацию на несколько более коротких шагов или выделить ее по ключевым кадрам.
- Вместо этого вы можете использовать CABasicAnimation и анимировать преобразование вашего слоя с помощью накопительной анимации.
Пример детской площадки:
import PlaygroundSupport
import UIKit
let rect = CGRect(x: 0, y: 0, width: 300, height: 300)
let view = UIView(frame: rect)
let bar = UIView(frame: rect.insetBy(dx: 40, dy: 140))
bar.backgroundColor = .red
view.addSubview(bar)
let animation = CABasicAnimation(keyPath: "transform.rotation.z")
animation.toValue = 1.25 * CGFloat.pi
animation.isCumulative = true
animation.duration = 5
bar.layer.add(animation, forKey: "spin")
bar.layer.transform = CATransform3DMakeRotation(1.25 * CGFloat.pi, 0, 0, 1)
PlaygroundPage.current.liveView = view
Вы можете сделать это так
UIView.animate(
withDuration: 0.1,
delay: 0.0,
options: .curveEaseOut,
animations: {
if radians > CGFloat.pi {
self.dialPointer.transform = CGAffineTransform.init(rotationAngle: CGFloat.pi);
}
self.dialPointer.transform = CGAffineTransform.init(rotationAngle: radians);
},
completion: nil
)