Reputation: 4546
I'm having difficulties adding momentum to my spinning wheel.
I have this wheel (something like this) and I'm rotating it around it's center by using a single touch event.
No problems here, but when the touch (aka drag) ends; I want the wheel to keep it's momentum and ease out it's movement.
Anyone who can give me some pointers, it doesn't necessarily have to be in objective-c.
AS3, javascript or JAVA will also be sufficient.
* UPDATE (code for rotating the wheel) *
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
rotation = _startAngle = atan2(384 - touchPoint.y, 512 - touchPoint.x);
};
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint touchPoint = [[touches anyObject] locationInView:self.view];
rotation = atan2(384 - touchPoint.y, 512 - touchPoint.x);
rotation = fmod(rotation - _startAngle, M_PI * 2.0f);
[wheel setTransform:CGAffineTransformMakeRotation(_circleRotationOffset + rotation)];
};
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
_circleRotationOffset = fmod(_circleRotationOffset + rotation, M_PI * 2);
};
Upvotes: 4
Views: 3338
Reputation: 4546
I managed to get some nice results.
How I did it, should be tweaked more, but this is the basics:
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint touchPoint = [[touches anyObject] locationInView:self.view];
rotation = _startAngle = atan2(384 - touchPoint.y, 512 - touchPoint.x);
[_history removeAllObjects];
};
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint touchPoint = [[touches anyObject] locationInView:self.view];
[_history insertObject:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithDouble:CFAbsoluteTimeGetCurrent()], @"time", [NSValue valueWithCGPoint:touchPoint], @"point", [NSNumber numberWithFloat: _circleRotationOffset + rotation], @"rotation", nil] atIndex:0];
if ([_history count] == 3) {
[_history removeLastObject];
}
rotation = atan2(384 - touchPoint.y, 512 - touchPoint.x) - _startAngle;
[circleImage setTransform:CGAffineTransformMakeRotation(_circleRotationOffset + rotation)];
};
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint tp = [[touches anyObject] locationInView:self.view];
_circleRotationOffset += rotation;
NSDictionary *lo = [_history lastObject];
CGPoint pp = [[lo objectForKey:@"point"] CGPointValue];
double timeDif = CFAbsoluteTimeGetCurrent() - [[lo objectForKey:@"time"] doubleValue];
float lastRotation = [[lo objectForKey:@"rotation"] floatValue];
// Calculate strength
float dist = sqrtf(((pp.x - tp.x) * (pp.x - tp.x)) + ((pp.y - tp.y) * (pp.y - tp.y)));
float strength = MIN(1.0f, dist / 80.0f) * (timeDif / .025) * M_PI;
float p = _circleRotationOffset;
float dif = _circleRotationOffset - lastRotation;
BOOL inc = dif > 0;
if (dif > 3 || dif < -3) { // Some correction
inc = !inc;
}
if (inc) {
_circleRotationOffset += strength;
} else {
_circleRotationOffset -= strength;
}
[circleImage.layer removeAllAnimations];
CAKeyframeAnimation *animation = [CAKeyframeAnimation animationWithKeyPath:@"transform.rotation.z"];
animation.duration = MAX(strength / 2.5, 1.0f);
animation.cumulative = YES;
animation.repeatCount = 1;
animation.values = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:p],
[NSNumber numberWithFloat: _circleRotationOffset], nil];
animation.keyTimes = [NSArray arrayWithObjects:
[NSNumber numberWithFloat:0],
[NSNumber numberWithFloat:1.0], nil];
animation.timingFunctions = [NSArray arrayWithObjects:
[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut], nil];
animation.removedOnCompletion = YES;
animation.delegate = self;
animation.fillMode = kCAFillModeForwards;
[circleImage.layer addAnimation:animation forKey:@"rotate"];
};
Upvotes: 1
Reputation: 2249
If you are spinning the UIImageView with code like:
[UIView beginAnimations:@"rotateImage" context:nil];
[UIView setAnimationDuration:4.0];
wheelImageView.transform = CGAffineTransformMakeRotation(3.14159265*5);
[UIView commitAnimations];
You could use [UIView setAnimationCurve:UIViewAnimationCurveEaseOut];
This makes it so the animation will start off fast and start to slow down over time.
Click Here for More info on UIViewAnimationCurve
Upvotes: 1
Reputation: 100632
You want the momentum to reduce due to friction; friction is a force that's a function of velocity. So technically you've got a differential equation going on. That's not really worth investing too much thought into though, because the solution is probably more easily reached by hand waving.
So: store current angle and current angular velocity. n times a second (probably via an NSTimer
or a CADisplayLink
) add the angular velocity to the angle, then multiply the angular velocity by something to make it smaller — such as 0.995. Constants closer to 1.0 will make it take longer to slow down; if you go above 1.0 it'll obviously accelerate. This is effectively a form of Euler integration but, again, it's not worth worrying about.
It's possibly also worth putting a minimum cap on angular velocity so that if it drops below, say 0.01 radians/second then you snap it down to 0. That effectively modifies your model of friction slightly to jump from kinetic to static friction at an opportune moment, and acts as a floating point precision buffer zone.
To get initial velocity from a drag you can just work out the vector from the centre of the wheel to the start of the drag, rotate that vector by 90 degrees, do a dot product with that and the drag vector and scale according to distance from the centre.
Upvotes: 5