IOS анимирует/изменяет форму от круга к квадрату
Я пытаюсь изменить круг на квадрат и наоборот, и я почти на месте. Однако это не оживляет, как ожидалось. Мне бы хотелось, чтобы все углы квадрата были анимированы/трансформированы одновременно, но я получаю следующее:
![enter image description here]()
Я использую CAShapeLayer
и CABasicAnimation
, чтобы анимировать свойство формы.
Вот как я создаю круговую дорожку:
- (UIBezierPath *)circlePathWithCenter:(CGPoint)center radius:(CGFloat)radius
{
UIBezierPath *circlePath = [UIBezierPath bezierPath];
[circlePath addArcWithCenter:center radius:radius startAngle:0 endAngle:M_PI/2 clockwise:YES];
[circlePath addArcWithCenter:center radius:radius startAngle:M_PI/2 endAngle:M_PI clockwise:YES];
[circlePath addArcWithCenter:center radius:radius startAngle:M_PI endAngle:3*M_PI/2 clockwise:YES];
[circlePath addArcWithCenter:center radius:radius startAngle:3*M_PI/2 endAngle:M_PI clockwise:YES];
[circlePath closePath];
return circlePath;
}
Вот квадратный путь:
- (UIBezierPath *)squarePathWithCenter:(CGPoint)center size:(CGFloat)size
{
CGFloat startX = center.x-size/2;
CGFloat startY = center.y-size/2;
UIBezierPath *squarePath = [UIBezierPath bezierPath];
[squarePath moveToPoint:CGPointMake(startX, startY)];
[squarePath addLineToPoint:CGPointMake(startX+size, startY)];
[squarePath addLineToPoint:CGPointMake(startX+size, startY+size)];
[squarePath addLineToPoint:CGPointMake(startX, startY+size)];
[squarePath closePath];
return squarePath;
}
Я применяю круговую траекторию к одному из моих слоев просмотра, устанавливаю заливку и т.д. Он отлично отрисовывается.
Затем в моем селекторе жеста Recognizer я создаю и запускаю следующую анимацию:
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"path"];
animation.duration = 1;
animation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
animation.fromValue = (__bridge id)(self.stateLayer.path);
animation.toValue = (__bridge id)(self.stopPath.CGPath);
self.stateLayer.path = self.stopPath.CGPath;
[self.stateLayer addAnimation:animation forKey:@"animatePath"];
Как вы можете заметить в circlePathWithCenter:radius:
и squarePathWithCenter:size:
, я следую предложению отсюда (чтобы иметь одинаковое количество сегментов и контрольных точек):
Плавная анимация смещения формы
Анимация выглядит лучше, чем в сообщении сверху, но она все еще не та, которую я хочу достичь :(
Я знаю, что могу сделать это с помощью простого CALayer
, а затем установить соответствующий уровень cornerRadius
, чтобы сделать круг из квадрата/прямоangularьника, и после этого свойства анимировать cornerRadius
, чтобы изменить его с круга на квадрат, но я по-прежнему крайне любопытно, возможно ли это сделать с помощью анимации CAShapeLayer
и path
.
Заранее спасибо за помощь!
Ответы
Ответ 1
Немного поиграв с ним на игровой площадке, я заметил, что каждая дуга добавляет к безье пути два сегмента, а не один. Более того, вызов closePath
добавляет еще два. Поэтому, чтобы количество сегментов оставалось неизменным, я добавил фальшивые сегменты к своему квадратному пути. Код написан на Swift, но я думаю, что это не имеет значения.
func circlePathWithCenter(center: CGPoint, radius: CGFloat) -> UIBezierPath {
let circlePath = UIBezierPath()
circlePath.addArcWithCenter(center, radius: radius, startAngle: -CGFloat(M_PI), endAngle: -CGFloat(M_PI/2), clockwise: true)
circlePath.addArcWithCenter(center, radius: radius, startAngle: -CGFloat(M_PI/2), endAngle: 0, clockwise: true)
circlePath.addArcWithCenter(center, radius: radius, startAngle: 0, endAngle: CGFloat(M_PI/2), clockwise: true)
circlePath.addArcWithCenter(center, radius: radius, startAngle: CGFloat(M_PI/2), endAngle: CGFloat(M_PI), clockwise: true)
circlePath.closePath()
return circlePath
}
func squarePathWithCenter(center: CGPoint, side: CGFloat) -> UIBezierPath {
let squarePath = UIBezierPath()
let startX = center.x - side / 2
let startY = center.y - side / 2
squarePath.moveToPoint(CGPoint(x: startX, y: startY))
squarePath.addLineToPoint(squarePath.currentPoint)
squarePath.addLineToPoint(CGPoint(x: startX + side, y: startY))
squarePath.addLineToPoint(squarePath.currentPoint)
squarePath.addLineToPoint(CGPoint(x: startX + side, y: startY + side))
squarePath.addLineToPoint(squarePath.currentPoint)
squarePath.addLineToPoint(CGPoint(x: startX, y: startY + side))
squarePath.addLineToPoint(squarePath.currentPoint)
squarePath.closePath()
return squarePath
}
![enter image description here]()
Ответ 2
Я знаю, что это старый вопрос, но это может кому-то помочь.
Я думаю, что лучше анимировать радиус угла, чтобы получить этот эффект.
let v1 = UIView(frame: CGRect(x: 100, y: 100, width: 50, height: 50))
v1.backgroundColor = UIColor.green
view.addSubview(v1)
анимация:
let animation = CABasicAnimation(keyPath: "cornerRadius")
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animation.fillMode = kCAFillModeForwards
animation.isRemovedOnCompletion = false
animation.fromValue = v1.layer.cornerRadius
animation.toValue = v1.bounds.width/2
animation.duration = 3
v1.layer.add(animation, forKey: "cornerRadius")
Ответ 3
Я настоятельно рекомендую библиотеку CanvasPod, она также имеет предопределенную анимацию морфинга и легко интегрируется. Вы также можете ознакомиться с примерами на сайте canvaspod.io.
Ответ 4
Основываясь на ответе @Danchoys, это для Objective-C.
info - это кнопка размером 60px, а infoVC - следующий нажатый ViewController:
UIBezierPath * maskPath = [UIBezierPath new];
[maskPath addArcWithCenter:info.center radius:15.0f startAngle:DEGREES_TO_RADIANS(180) endAngle:DEGREES_TO_RADIANS(270) clockwise:true];
[maskPath addArcWithCenter:info.center radius:15.0f startAngle:DEGREES_TO_RADIANS(270) endAngle:DEGREES_TO_RADIANS(0) clockwise:true];
[maskPath addArcWithCenter:info.center radius:15.0f startAngle:DEGREES_TO_RADIANS(0) endAngle:DEGREES_TO_RADIANS(90) clockwise:true];
[maskPath addArcWithCenter:info.center radius:15.0f startAngle:DEGREES_TO_RADIANS(90) endAngle:DEGREES_TO_RADIANS(180) clockwise:true];
[maskPath closePath];
UIBezierPath * endPath = [UIBezierPath new];
[endPath moveToPoint:CGPointMake(0,0)];
[endPath addLineToPoint:endPath.currentPoint];
[endPath addLineToPoint:CGPointMake(w, 0)];
[endPath addLineToPoint:endPath.currentPoint];
[endPath addLineToPoint:CGPointMake(w, h)];
[endPath addLineToPoint:endPath.currentPoint];
[endPath addLineToPoint:CGPointMake(0, h)];
[endPath addLineToPoint:endPath.currentPoint];
[endPath closePath];
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = info.bounds;
maskLayer.path = maskPath.CGPath;
_infoVC.view.layer.mask = maskLayer;
CABasicAnimation * animation = [CABasicAnimation animationWithKeyPath:@"path"];
animation.fromValue = (id)maskPath.CGPath;
animation.toValue = (id)endPath.CGPath;
animation.duration = 0.3f;
[maskLayer addAnimation:animation forKey:nil];
[maskLayer setPath:endPath.CGPath];