Animate a property back and forth (without jumping to the end) - animation

I am using the following code to "zoom" a page in and out.
const doEffect = args => {
const screen = args.object.page.getViewById('mainScreen')
screen.animate({
scale: {
x: isSettingsShown ? 1 : .75,
y: isSettingsShown ? 1 : .75
},
duration: 2000
})
isSettingsShown = !isSettingsShown
}
There is a toggle button on the page that updates the isSettingsShown variable. However when I toggle the var before the animation has ended, it "jumps" to the end of the animation. Can I prevent this from happening? I would like to start the animation from the last position it was at.

Pausing the animation or retaining the state of animation is still an open feature request at the moment.
You may still be able to do this natively by operating on the nativeView's CALayer (iOS) / Animator (Android).

Related

macOS, how resize window across screens?

I'm trying to programmatically resize macOS windows. Similar to Rectangle.
I have the basic resizing code working, for example, move the window to the right half, and when there is only one screen it works fine, however when I try to resize with two screens (in a vertical layout) the math does not work:
public func moveRight() {
guard let frontmostWindowElement = AccessibilityElement.frontmostWindow()
else {
NSSound.beep()
return
}
let screens = screenDetector.detectScreens(using: frontmostWindowElement)
guard let usableScreens = screens else {
NSSound.beep()
print("Unable to obtain usable screens")
return
}
let screenFrame = usableScreens.currentScreen.adjustedVisibleFrame
print("Visible frame of current screen \(usableScreens.visibleFrameOfCurrentScreen)")
let halfPosition = CGPoint(x: screenFrame.origin.x + screenFrame.width / 2, y: -screenFrame.origin.y)
let halfSize = CGSize(width: screenFrame.width / 2, height: screenFrame.height)
frontmostWindowElement.set(size: halfSize)
frontmostWindowElement.set(position: halfPosition)
frontmostWindowElement.set(size: halfSize)
print("movedWindowRect \(frontmostWindowElement.rectOfElement())")
}
If my window is on the main screen then the resizing works correctly, however if it is a screen below (#3 in the diagram below) then the Y coordinate ends up in the top monitor (#2 or #1 depending on x coordinate) instead of the original one.
The output of the code:
Visible frame of current screen (679.0, -800.0, 1280.0, 775.0)
Raw Frame (679.0, -800.0, 1280.0, 800.0)
movedWindowRect (1319.0, 25.0, 640.0, 775.0)
As far as I can see the problem lies in how Screens and windows are positioned:
I'm trying to understand how should I position the window so that it remains in the correct screen (#3), but having no luck so far, there doesn't seem to be any method to get the absolute screen dimensions to place the screen in the correct origin.
Any idea how can this be solved?
I figured it out, I completely missed one of the functions used in the AccessibilityElement class:
static func normalizeCoordinatesOf(_ rect: CGRect) -> CGRect {
var normalizedRect = rect
let frameOfScreenWithMenuBar = NSScreen.screens[0].frame as CGRect
normalizedRect.origin.y = frameOfScreenWithMenuBar.height - rect.maxY
return normalizedRect
}
Basically, since everything is calculated based on the main screen then there is no other option than to take the coordinates of that one and then offset to get the real position of the screen element.

How to only play animation while a sprite is moving in Phaser 3?

I'm creating a game in Phaser 3, using the matter physics, as I've done before. A problem I've never run into until now, however, involves animations and movement. I want my animation to only play while the player is moving, and stop when he stops moving, like a player would in a real game.
I've tried all sorts of do-while loops, using "animationcomplete" and without it. I've also tried having my animation on loop (repeat: -1) and having it set to only execute once.
I DO know that, .isDown tracks the key and does the following code as long as it as pressed, versus .JustDown only executing it once. I'm not really sure which to use there either.
Code for animation
//Animations
//Running right animation
var runRightFrameNames = this.anims.generateFrameNames('sheet', {
start: 14,
end: 16,
zeroPad: 4,
prefix: 'run_right/'
})
this.anims.create({
key: 'runRight',
frames: runRightFrameNames,
frameRate: 1,
repeat: 0
})
Current code inside update() function, not the only thing I've tried
//If the right arrowkey is pressed
if (this.arrowKeys.right.isDown) {
//Move player
this.hero.setVelocityX(5);
//Play animation
this.hero.anims.play('runRight');
//When animation is done, stop hero
this.hero.on("animationcomplete", () => {
this.hero.setVelocityX(0);
});
}
You can modify your code snippet like this:
//If the right arrowkey is pressed
if (this.arrowKeys.right._justDown) {
console.log('Cursor right is pressed!');
//Move player
this.hero.setVelocityX(5);
//Play animation
this.hero.anims.play('runRight');
} else if (this.arrowKeys.right._justUp) {
console.log('Cursor right is NOT pressed!');
}
EDIT:
To fix the hero's animation change this line this.hero.anims.play('runRight'); to this.hero.anims.play('runRight', true);
You should also change the repeat value to -1 like so:
this.anims.create({
key: 'runRight', frames: runRightFrameNames, frameRate: 6, repeat: -1
});

Multiple overlapping animations on the same element in svg.js

I want to start a animation on an element while a previous animation is still active. However, calling animate() on the element queues the new animation at the end of the current animation.
For example, consider an animation where an element is being moved to a new position. Now, I also want to make it fade out when it reaches a certain position. The following queues the “once” animation at the end of the move, rather than at 80%.
rect.animate(1000).move(100, 100)
.once(0.8, function(pos, eased) {
rect.animate(200).opacity(0);
});
How do I make the element start fading out when it reaches 80% of the move? The API seems to be designed for chaining animations rather simultaneous overlapping animations.
What you are trying to do is a bit more complicated. Unforrtunately its not possible to "hack" into the current animation and add a new animation on the fly.
However what you can do is adding a new property which should be animated:
var fx = rect.animate(1000).move(100, 100)
.once(0.8, function(pos, eased) {
fx.opacity(0);
});
As you will notice that has its own problems because the oopacity immediately jumps to 80%. So this is not an approach which works for you.
Next try: Use the during method:
var morh = SVG.morph(
var fx = rect.animate(1000).move(100, 100)
.once(0.8, function(pos, eased) {
fx.during(function(pos, morphFn, easedPos) {
pos = (pos - 0.8) / 0.2
this.target().opacity(pos)
}
});
We just calculate the opacity ourselves

SVG performance drop when animating 2 or more elements, RaphaelJS

I am testing SVG performance using RaphaelJS library.
My code works, can be found here: JSFiddle
When you type in textbox "1" and press "add", a rectangle will be generated on screen and 4 animations will loop on it- moving right, down, left, up (also rotating, scaling and changing colour).
Performance seems to be ok. But add another element on stage and the performance gets knocked down to minimum in 3-4 seconds. Checked in Chrome timeline, the thing that is getting stacked up is "Animation Frame Fired - > Install Timer".
Perhaps I am doing loop incorrectly? Altough next animation starts when the previous ends, through callback function. Or is it Raphael itself? Should I try doing this with SVG and SMIL? (Raphael uses VML)
Any ideas?
------------------------------------UPDATE--------------------------------------
With RaphaelJS I did bad animation loop hooks, see answer below.
But another problem that does occur - add 1 element 10 times and you can see how animations get distorted, not finishing their full cycle, or add 10 elements 1 time and after few seconds you can see delayed animations on some of the elements.
I made SMIL version JSFiddle (no Raphael here), animations do not lag, delay, but they get syncronized. Can anyone explain why? And how to make those animations NOT sync, but unique?
I think the problem is you are recursively calling animations on a set.
So at the end of each animation, each element in the set calls an animation for the set again, so it spirals and grinds to a halt. You can get around this, by using 'this' instead of the set 'rectangles'.
//define 4 animations
var move_up = Raphael.animation({fill: "green", transform: "t0,0r360s1,1"}, 400, function(){ this.attr({"transform" : "t0,0"}); this.animate(move_right); });
var move_left = Raphael.animation({fill: "yellow", transform: "t0,100r270s0.5,0.5"}, 400, function(){ this.animate(move_up); });
var move_down = Raphael.animation({fill: "red", transform: "t100,100r180s1,1"}, 400, function(){ this.animate(move_left); });
var move_right = Raphael.animation({fill: "blue", transform: "t100,0r90s1.5,1.5"}, 400, function(){this.animate(move_down); });
jsfiddle

Animation end goes to initial values

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);

Resources