iPhone旋转函数和手工数学旋转不一致
我目前正在通过触摸、拖动和释放来旋转 UIImageView。我通过以下函数旋转对象:
-(void)rotateForTime:(CGFloat)time distance:(CGFloat)distance
{
CABasicAnimation* rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotationAnimation.toValue = [NSNumber numberWithFloat:distance];
rotationAnimation.removedOnCompletion = NO;
rotationAnimation.fillMode = kCAFillModeForwards;
rotationAnimation.duration = time;
rotationAnimation.repeatCount = 1.0;
rotationAnimation.timingFunction = [CAMediaTimingFunctionfunctionWithName:
kCAMediaTimingFunctionEaseOut];
rotationAnimation.delegate = self;
[bottle.layer addAnimation:rotationAnimation forKey:@"rotationAnimation"];
}
由于旋转后,矩形图像的边界发生变化,为了判断用户是否触摸图像内部,我通过旋转相同的方式来跟踪图像的角点我旋转图像,然后使用矢量->边缘相交检测方法来检查用户是否触摸了由角点构成的矩形的边界。我使用此函数来旋转角:
-(void) rotateImageBounds:(CGFloat) angle
{
// Translate the center and the corners of the image to the origin and their respective positions.
corner1.x = corner1.x - bottle.center.x;
corner1.y = corner1.y - bottle.center.y;
corner2.x = corner2.x - bottle.center.x;
corner2.y = corner2.y - bottle.center.y;
corner3.x = corner3.x - bottle.center.x;
corner3.y = corner3.y - bottle.center.y;
corner4.x = corner4.x - bottle.center.x;
corner4.y = corner4.y - bottle.center.y;
// Rotates the point around the origin/center.
// x = x1cos(angle) - x1sin(angle)
// y = x1sin(angle) + y1cos(angle)
CGFloat tempX;
tempX = (corner1.x*cos(angle)) - (corner1.y*sin(angle));
corner1.y = (corner1.x*sin(angle)) + (corner1.y*cos(angle));
corner1.x = tempX;
tempX = (corner2.x*cos(angle)) - (corner2.y*sin(angle));
corner2.y = (corner2.x*sin(angle)) + (corner2.y*cos(angle));
corner2.x = tempX;
tempX = (corner3.x*cos(angle)) - (corner3.y*sin(angle));
corner3.y = (corner3.x*sin(angle)) + (corner3.y*cos(angle));
corner3.x = tempX;
tempX = (corner4.x*cos(angle)) - (corner4.y*sin(angle));
corner4.y = (corner4.x*sin(angle)) + (corner4.y*cos(angle));
corner4.x = tempX;
// Translates the points back to the original center of the image.
corner1.x = corner1.x + bottle.center.x;
corner1.y = corner1.y + bottle.center.y;
corner2.x = corner2.x + bottle.center.x;
corner2.y = corner2.y + bottle.center.y;
corner3.x = corner3.x + bottle.center.x;
corner3.y = corner3.y + bottle.center.y;
corner4.x = corner4.x + bottle.center.x;
corner4.y = corner4.y + bottle.center.y;
}
第一次旋转后效果很好,角看起来仍然位于图像的角处。然而,经过多次旋转后,图像和角不再对齐。据我所知,旋转方法是准确的,并且角点的数学是正确的。几天来我一直在试图解决这个问题。任何帮助将不胜感激。
编辑*问题已解决: 我最终让动画在完成后自行删除,并在完成后将图像旋转到正确的位置(这种方式更容易,因为我还需要在鼠标运动期间旋转图像)。 对于旋转函数:(角度是鼠标(或触摸)当前所在的角度,使用 atan2(dy,dx) 获得,其中 dy 是触摸和中心之间的高度,dx 是宽度。距离是它的距离 需要旋转:
-(void)rotateForTime:(CGFloat)time distance:(CGFloat)distance
{
animating = TRUE;
CABasicAnimation* rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotationAnimation.fromValue = [NSNumber numberWithFloat:angle];
rotationAnimation.toValue = [NSNumber numberWithFloat:distance + angle];
rotationAnimation.removedOnCompletion = YES;
rotationAnimation.fillMode = kCAFillModeForwards;
rotationAnimation.duration = time;
rotationAnimation.repeatCount = 1.0;
rotationAnimation.timingFunction = [CAMediaTimingFunction
functionWithName:kCAMediaTimingFunctionEaseOut];
rotationAnimation.delegate = self;
[bottle.layer addAnimation:rotationAnimation forKey:@"rotationAnimation"];
}
然后在动画结束时
-(void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
{
animating = FALSE;
// Rotates the image to the last spot that the animation rotated to.
[bottle.layer setValue:[NSNumber numberWithFloat:angle + distance]
forKeyPath:@"transform.rotation.z"];
// Gives the corners of the image after rotation (Thanks to mustISignUp) check to make sure
// that the touch is within these bounds (I use a ray line intersection algorithm in another part of my code)
ptInLayerCoords1 = [[bottle.layer presentationLayer] convertPoint:corner1 toLayer:self.view.layer];
ptInLayerCoords2 = [[bottle.layer presentationLayer] convertPoint:corner2 toLayer:self.view.layer];
ptInLayerCoords3 = [[bottle.layer presentationLayer] convertPoint:corner3 toLayer:self.view.layer];
ptInLayerCoords4 = [[bottle.layer presentationLayer] convertPoint:corner4 toLayer:self.view.layer];
}
I am currently rotating a UIImageView by touching, dragging, and releasing. I am rotating the object by the function:
-(void)rotateForTime:(CGFloat)time distance:(CGFloat)distance
{
CABasicAnimation* rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotationAnimation.toValue = [NSNumber numberWithFloat:distance];
rotationAnimation.removedOnCompletion = NO;
rotationAnimation.fillMode = kCAFillModeForwards;
rotationAnimation.duration = time;
rotationAnimation.repeatCount = 1.0;
rotationAnimation.timingFunction = [CAMediaTimingFunctionfunctionWithName:
kCAMediaTimingFunctionEaseOut];
rotationAnimation.delegate = self;
[bottle.layer addAnimation:rotationAnimation forKey:@"rotationAnimation"];
}
Since after a rotation, the bounds of the rectangular image change, in order to tell if the user touches inside the image or not, I keep track of the corners of the image by rotating them the same distance I rotate the image and then use vector->edge intersection detection methods to check if the user touched in the bounds of the rectangle made by the corners. I use this function to rotate the corners:
-(void) rotateImageBounds:(CGFloat) angle
{
// Translate the center and the corners of the image to the origin and their respective positions.
corner1.x = corner1.x - bottle.center.x;
corner1.y = corner1.y - bottle.center.y;
corner2.x = corner2.x - bottle.center.x;
corner2.y = corner2.y - bottle.center.y;
corner3.x = corner3.x - bottle.center.x;
corner3.y = corner3.y - bottle.center.y;
corner4.x = corner4.x - bottle.center.x;
corner4.y = corner4.y - bottle.center.y;
// Rotates the point around the origin/center.
// x = x1cos(angle) - x1sin(angle)
// y = x1sin(angle) + y1cos(angle)
CGFloat tempX;
tempX = (corner1.x*cos(angle)) - (corner1.y*sin(angle));
corner1.y = (corner1.x*sin(angle)) + (corner1.y*cos(angle));
corner1.x = tempX;
tempX = (corner2.x*cos(angle)) - (corner2.y*sin(angle));
corner2.y = (corner2.x*sin(angle)) + (corner2.y*cos(angle));
corner2.x = tempX;
tempX = (corner3.x*cos(angle)) - (corner3.y*sin(angle));
corner3.y = (corner3.x*sin(angle)) + (corner3.y*cos(angle));
corner3.x = tempX;
tempX = (corner4.x*cos(angle)) - (corner4.y*sin(angle));
corner4.y = (corner4.x*sin(angle)) + (corner4.y*cos(angle));
corner4.x = tempX;
// Translates the points back to the original center of the image.
corner1.x = corner1.x + bottle.center.x;
corner1.y = corner1.y + bottle.center.y;
corner2.x = corner2.x + bottle.center.x;
corner2.y = corner2.y + bottle.center.y;
corner3.x = corner3.x + bottle.center.x;
corner3.y = corner3.y + bottle.center.y;
corner4.x = corner4.x + bottle.center.x;
corner4.y = corner4.y + bottle.center.y;
}
This works fine after the first rotation, the corners look like the are still at the corners of the image. However, after more rotations, the image and the corners no longer line up. As far as I know, the rotation method is accurate and the math for the corners is right. I've been trying to figure out this problem for days now. Any help would be greatly appreciated.
EDIT * Problem solved:
I ended up letting the animation remove itself when it was done and just rotate the image into the right position when I was done (it was easier this way because I also need to rotate the image during mouse motion).
For the rotation function: (angle is the angle that the mouse (or touch) was currently at, gotten using atan2(dy,dx) where dy is the height between the touch and center and dx is the width. Distance is the distance it needs to rotate.
-(void)rotateForTime:(CGFloat)time distance:(CGFloat)distance
{
animating = TRUE;
CABasicAnimation* rotationAnimation;
rotationAnimation = [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
rotationAnimation.fromValue = [NSNumber numberWithFloat:angle];
rotationAnimation.toValue = [NSNumber numberWithFloat:distance + angle];
rotationAnimation.removedOnCompletion = YES;
rotationAnimation.fillMode = kCAFillModeForwards;
rotationAnimation.duration = time;
rotationAnimation.repeatCount = 1.0;
rotationAnimation.timingFunction = [CAMediaTimingFunction
functionWithName:kCAMediaTimingFunctionEaseOut];
rotationAnimation.delegate = self;
[bottle.layer addAnimation:rotationAnimation forKey:@"rotationAnimation"];
}
And then at the end of the animation:
-(void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
{
animating = FALSE;
// Rotates the image to the last spot that the animation rotated to.
[bottle.layer setValue:[NSNumber numberWithFloat:angle + distance]
forKeyPath:@"transform.rotation.z"];
// Gives the corners of the image after rotation (Thanks to mustISignUp) check to make sure
// that the touch is within these bounds (I use a ray line intersection algorithm in another part of my code)
ptInLayerCoords1 = [[bottle.layer presentationLayer] convertPoint:corner1 toLayer:self.view.layer];
ptInLayerCoords2 = [[bottle.layer presentationLayer] convertPoint:corner2 toLayer:self.view.layer];
ptInLayerCoords3 = [[bottle.layer presentationLayer] convertPoint:corner3 toLayer:self.view.layer];
ptInLayerCoords4 = [[bottle.layer presentationLayer] convertPoint:corner4 toLayer:self.view.layer];
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我对 iPhone 不熟悉,但这通常是由于累积舍入误差而发生的(如果
corner.x
/corner.y尤其 code> 是整数)。
要解决此问题,您不必每帧(或每次用户触摸屏幕或其他任何时间)再次旋转一个旋转的矩形,而是仅存储相对于矩形原始方向的角度偏移并重新计算每帧的新坐标。
I'm not familiar with the IPhone, but this usually happens due to accumulating rounding errors (which are especially apparent if
corner.x
/corner.y
are integers).To fix, rather than having a rotated rectangle that you rotate again every frame (or every time the user touches the screen, or whatever), you store only the angle-offset from the rectangle's original orientation and recalculate the new coordinates every frame.
正如 BlueRaja 所说,数学并不准确——它本质上是近似的,而且你正在积累错误。您需要每次根据图层的绝对尺寸和旋转重新计算角的位置,而不是尝试跟踪它。
幸运的是 CALayer 可以为你做到这一点,没有太多麻烦
As BlueRaja says, the maths isn't accurate - it's inherently approximate, and you are accumulating errors. You need to recalculate the position of the corners from the layer's absolute dimensions and rotation each time, not try to track it.
Fortunately CALayer can do this for you without too much hassle