I run an animation on my buttons where I change i.e. Opacity. When the animation is complete all the button's Opacity goes back to initial values. The code:
CABasicAnimation animation = CABasicAnimation.FromKeyPath("opacity");
animation.To = NSNumber.FromFloat( 0.1f );
animation.Duration = animationDuration;
animation.TimingFunction = CAMediaTimingFunction.FromName (CAMediaTimingFunction.EaseOut);
How can I set the animation to stay in the To value?
I don't see the error in the code, but there is another way to do that instead of CoreAnimation, which is a bit verbose.
For UIKit elements it is usually easier to use the UIView.Animate method. So , your sample would be:
UIView.Animate (
animationDuration,
0,
UIViewAnimationOptions.CurveEaseOut,
delegate {yourButton.Alpha = 0.1f; },
null);
Related
I want to create an animation with CALayers.
I have a parent layer with multiple sublayers and I would like to zoom in and scroll.
First I trying to zoom on the parent layer, as follows:
let transformAnimation = CABasicAnimation(keyPath: "bounds.size.width")
transformAnimation.duration = 2.3
transformAnimation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transformAnimation.toValue = 650*2
transformAnimation.beginTime = CACurrentMediaTime() + 4
transformAnimation.autoreverses = false
transformAnimation.isRemovedOnCompletion = false
transformAnimation.fillMode = kCAFillModeForwards
parentLayer.add(transformAnimation, forKey: "transformAnimation")
//
let transformAnimation2 = CABasicAnimation(keyPath: "bounds.size.height")
transformAnimation2.duration = 2.3
transformAnimation2.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transformAnimation2.toValue = 650*2 //CGAffineTransform.identity
transformAnimation2.beginTime = CACurrentMediaTime() + 4
transformAnimation2.autoreverses = false
transformAnimation2.isRemovedOnCompletion = false
transformAnimation2.fillMode = kCAFillModeForwards
parentLayer.add(transformAnimation2, forKey: "transformAnimation2")
When the animation is applied, the sublayers are left in wrong position and size. Should I also apply the animation to them?
How can I do this?
Thanks!
I'm guessing that you're updating width and height of one layer and you're wondering whether the bounds of sublayers will also change. No, you'll probably have to animate those separately, too. But rather than initiating separate animations, you can them with a CAAnimationGroup.
If these were views, you could define constraints that coordinate the resizing of subviews more gracefully. But with layers, you're going to have to do this yourself. (There might be reasons why you're doing it the way you are, but it's not clear from the question.)
I just need to know which is the best way to change frame's position with Animation class and libgdx:
I make an Animation object with many TextureRegions and I found this solution for update the frame's position in the render method:
batch.begin();
if (currentFrame.equals(tex1))
batch.draw(currentFrame, w, h,120,160);
else if (currentFrame.equals(tex2))
batch.draw(currentFrame, w+5, h+10,120,160);
batch.end();
This draw each frame of animation in different position, in sequence;
is this a good solution or exist something better?
Thanks.
After you have initialized your animation object, you just get the current frame using the getKeyFrame method and draw it with the batch:
TextureRegion currentFrame = animation.getKeyFrame(Gdx.graphics.getDeltaTime(), true);
batch.draw(currentFrame, 0, 0);
I have a sub-layer keyframe animation defined to animate an image along a BSpline, grouped together with a rotate animation. With the sub-layer.speed set to 0, I can drag the image back and forth along the curve by adjusting the animationsGroup.timeOffset value based on the distance dragged.
What I want to do is, after a certain threshold (say %15) is to set the animation speed to 1 so the animation completes by itself, but it's not that simple. The animation either completes immediately and resets everything back to the start position, or the animiation reaches the end of the path, loops round to zero, and continues animating until it's back to the point where the animation kicks in.
What I want is:
Tstart -> drag -> T0.15 -> animation -> Tend
But what I'm getting is
Tstart -> drag -> T0.15 -> animation -> Tend -> Tstart -> T0.15
I've already looked into use of timeOffset and time-warps, and fiddled with the parameters but to no avail.
You can that outcome by wrapping your animation group in a new animation group whose duration is 0.85 times the animation groups duration. Here is a bit of code that does just that:
- (void) setTimeOffset:(double)value
{
if (value < 0.15) {
// This is what you are already doing.
_animGroup.timeOffset = value;
[_someSublayer addAnimation:_animGroup forKey:#"animGroup"];
} else if (_animGroup.speed == 0.0) {
// You must set the final position before animating otherwise the
// layer will come back to it's original position.
_someSublayer.position = _theEndPointOfYourPath;
// Now set the speed of the animation group to 1 so that it animates.
// Be sure to leave the timeOffset alone so that is starts where you want it.
_animGroup.speed = 1.0;
// Create a new animation group around it whose duration is the remaining time
// of the animation group and add that to the layer instead. It will prevent
// animGroup from wrapping.
CAAnimationGroup *outerGroup = [[CAAnimationGroup alloc] init];
outerGroup.animations = #[_animGroup];
outerGroup.duration = _animGroup.duration - _animGroup.timeOffset;
[_someSublayer addAnimation:outerGroup forKey:#"animGroup"];
}
}
What is the standard method for smooth camera movement within SceneKit (OpenGL)?
Manually changing x,y isn't smooth enough, yet using CoreAnimation creates "pulsing" movement. The docs on SceneKit seem to be very limited so any examples would be appreciated, I'm currently doing this:
- (void)keyDown:(NSEvent *)theEvent {
int key = [theEvent keyCode];
int x = cameraNode.position.x;
int y = cameraNode.position.y;
int z = cameraNode.position.z;
int speed = 4;
if (key==123) {//left
x-=speed;
} else if (key==124) {//right
x+=speed;
} else if (key==125) {//down
y-=speed;
} else if (key==126) {//up
y+=speed;
}
//move the camera
[SCNTransaction begin];
[SCNTransaction setAnimationDuration: 1.0];
// Change properties
cameraNode.position = SCNVector3Make(x, y, z);
[SCNTransaction commit];
}
To minimise the pulsing movements (due to the key repeat) you can use an "easeOut" timingFunction:
//move the camera
[SCNTransaction begin];
[SCNTransaction setAnimationDuration: 1.0];
[SCNTransaction setAnimationTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut]];
// Change properties
cameraNode.position = SCNVector3Make(x, y, z);
[SCNTransaction commit];
That said, the best thing to do here is probably to manage a target position (a vector3) yourself and update the position of the camera at every frame to go to this target smoothly.
I've been experimenting with this. The best I've found so far is to record the state of the input keys in internal state, modified by keyDown: and keyUp:, and run an NSTimer to apply them. The timer uses the actual, measured time delta between firings to determine how far to move the camera. That way irregular timings don't have too much effect (and I can call my method to update the camera position at any time without worrying about changing its movement speed).
It takes some work to make this behave correctly, though. keyDown: and keyUp: have some obnoxious behaviours when it comes to game input. For example, repeating keys. Also, they may fire even after your view loses focus or your app goes to the background, if keys are held down across the transition. Etc. Not insurmountable, but annoying.
What I haven't yet done is add acceleration and deceleration, which I think will aid the perception of it being smooth. Otherwise it feels pretty good.
I move camera using this code:
let lerpX = (heroNode.position.x - followCamera.position.x) * 0.05
let lerpZ = (heroNode.position.z - followCamera.position.z) * 0.05
followCamera.position.x += lerpX
followCamera.position.z += lerpZ
CAAnimation does not provide a mechanism for assigning callback functions other than the standard "animationDidStart:"/"animationDidStop:" methods.
I have a custom UIControl that utilizes 2 CALayers that overlap. The purpose of this control is similar to an old fashioned sonar. The top layer's contents contains an image that gets rotated constantly (call this layer "wand"). Beneath that layer is a "spriteControl" layer that renders blips as the wand passes over them.
The objects that the blips represent are pre-fetched and organized into invisible CAShapeLayers by the spriteControl. I am using a CABasicAnimation to rotate the wand 10 degrees at a time, then utilizing the "animationDidStop:" method to invoke a method on the spriteControl that takes the current rotation value of the wand layer (a.k.a. heading) and animates the alpha setting from 1.0 to 0.0 for simulating the blip in and fade out effect. Finally, the process is started over again indefinitely.
While this approach of using the CAAnimation callbacks ensures that the timing of the wand reaching a "ping" position (i.e. 10deg, 20deg, 270deg, etc) always coincide with the lighting of the blips in the other layer, there is this issue of stopping, recalculating, and starting the animation every 10 degrees.
I could spawn an NSTimer to fire a method that queries the angle of the wand's presentation layer to get the heading value. However, this makes it more difficult to keep the wand and the blip highlighting in sync, and/or cause some to get skipped altogether. This approach is discussed a bit here: How can I callback as a CABasicAnimation is animating?
So my question is whether or not there is anything I can do to improve the performance of the wand layer rotation without reimplementing the control using OpenGL ES. (I realize that this would be easily solved in an OpenGL environment, however, to use it here would require extensive redesign that simply isn't worth it.) While the performance issue is minor, I can't shake the feeling that there is something simple and obvious that I could do that would allow the wand to animate indefinitely without pausing to perform expensive rotation calculations in between.
Here is some code:
- (void)rotateWandByIncrement
{
if (wandShouldStop)
return;
CGFloat newRotationDegree = (wandRotationDegree + WAND_INCREMENT_DEGREES);
if (newRotationDegree >= 360)
newRotationDegree = 0;
CATransform3D rotationTransform = CATransform3DMakeRotation(DEGREES_TO_RADIANS(newRotationDegree), 0, 0, 1);
CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:#"transform"];
animation.toValue = [NSValue valueWithCATransform3D:rotationTransform];
animation.duration = WAND_INCREMENT_DURATION;
animation.fillMode = kCAFillModeForwards;
animation.removedOnCompletion = FALSE;
animation.delegate = self;
[wandLayer addAnimation:animation forKey:#"transform"];
}
- (void)animationDidStart:(CAAnimation *)theAnimation
{
if (wandShouldStop)
return;
NSInteger prevWandRotationDegree = wandRotationDegree - WAND_INCREMENT_DEGREES;
if (prevWandRotationDegree < 0)
prevWandRotationDegree += 360;
// Pulse the spriteControl
[[self spriteControl] pulseRayAtHeading:prevWandRotationDegree];
}
- (void)animationDidStop:(CAAnimation *)theAnimation finished:(BOOL)flag
{
// update the rotation var
wandRotationDegree += WAND_INCREMENT_DEGREES;
if (wandRotationDegree >= 360)
wandRotationDegree = 0;
// This applies the rotation value to the model layer so that
// subsequent animations start where the previous one left off
CATransform3D rotationTransform = CATransform3DMakeRotation(DEGREES_TO_RADIANS(wandRotationDegree), 0, 0, 1);
[CATransaction begin];
[CATransaction setDisableActions:TRUE];
[wandLayer setTransform:rotationTransform];
[CATransaction commit];
//[wandLayer removeAnimationForKey:#"transform"];
[self rotateWandByIncrement];
}
Let's say it takes 10 seconds for the radar to make one complete rotation.
To get the wand to rotate indefinitely, attach a CABasicAnimation to it with its Duration property set to 10 and its RepeatCount property set to 1e100f.
The blips can each be animated using their own instance CAKeyframeAnimation. I won't write the details, but for each blip, you specify an array of opacity values (I assume opacity is how you're fading out the blips) and an array of time percentages (see Apple's documentation).