优化更多界面的动画 (#4790)

This commit is contained in:
Glavo
2025-11-15 21:22:18 +08:00
committed by GitHub
parent 44869da20e
commit 75c4fb6550
8 changed files with 83 additions and 63 deletions

View File

@@ -49,6 +49,7 @@ import org.jackhuang.hmcl.task.Task;
import org.jackhuang.hmcl.task.TaskExecutor;
import org.jackhuang.hmcl.ui.account.AccountListPage;
import org.jackhuang.hmcl.ui.animation.AnimationUtils;
import org.jackhuang.hmcl.ui.animation.ContainerAnimations;
import org.jackhuang.hmcl.ui.animation.Motion;
import org.jackhuang.hmcl.ui.construct.*;
import org.jackhuang.hmcl.ui.construct.MessageDialogPane.MessageType;
@@ -91,7 +92,7 @@ public final class Controllers {
private static Scene scene;
private static Stage stage;
private static Lazy<VersionPage> versionPage = new Lazy<>(VersionPage::new);
private static VersionPage versionPage;
private static Lazy<GameListPage> gameListPage = new Lazy<>(() -> {
GameListPage gameListPage = new GameListPage();
gameListPage.selectedProfileProperty().bindBidirectional(Profiles.selectedProfileProperty());
@@ -128,7 +129,18 @@ public final class Controllers {
// FXThread
public static VersionPage getVersionPage() {
return versionPage.get();
if (versionPage == null) {
versionPage = new VersionPage();
}
return versionPage;
}
@FXThread
public static void prepareVersionPage() {
if (versionPage == null) {
LOG.info("Prepare the version page");
versionPage = FXUtils.prepareNode(new VersionPage());
}
}
// FXThread
@@ -521,7 +533,11 @@ public final class Controllers {
}
public static void navigate(Node node) {
decorator.navigate(node);
decorator.navigate(node, ContainerAnimations.NAVIGATION, Motion.SHORT4, Motion.EASE);
}
public static void navigateForward(Node node) {
decorator.navigate(node, ContainerAnimations.FORWARD, Motion.SHORT4, Motion.EASE);
}
public static void showToast(String content) {

View File

@@ -17,13 +17,11 @@
*/
package org.jackhuang.hmcl.ui.animation;
import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.animation.*;
import javafx.scene.Node;
import javafx.scene.layout.Pane;
import javafx.util.Duration;
import org.jackhuang.hmcl.ui.decorator.DecoratorAnimatedPage;
public enum ContainerAnimations implements TransitionPane.AnimationProducer {
NONE {
@@ -199,6 +197,56 @@ public enum ContainerAnimations implements TransitionPane.AnimationProducer {
);
}
},
NAVIGATION {
@Override
public void init(TransitionPane container, Node previousNode, Node nextNode) {
}
@Override
public Animation animate(Pane container, Node previousNode, Node nextNode, Duration duration, Interpolator interpolator) {
Timeline timeline = new Timeline();
if (previousNode instanceof TransitionPane.EmptyPane) {
return timeline;
}
Duration halfDuration = duration.divide(2);
timeline.getKeyFrames().add(new KeyFrame(Duration.ZERO,
new KeyValue(previousNode.opacityProperty(), 1, interpolator)));
timeline.getKeyFrames().add(new KeyFrame(halfDuration,
new KeyValue(previousNode.opacityProperty(), 0, interpolator)));
if (previousNode instanceof DecoratorAnimatedPage prevPage) {
Node left = prevPage.getLeft();
Node center = prevPage.getCenter();
timeline.getKeyFrames().add(new KeyFrame(Duration.ZERO,
new KeyValue(left.translateXProperty(), 0, interpolator),
new KeyValue(center.translateXProperty(), 0, interpolator)));
timeline.getKeyFrames().add(new KeyFrame(halfDuration,
new KeyValue(left.translateXProperty(), -30, interpolator),
new KeyValue(center.translateXProperty(), 30, interpolator)));
}
timeline.getKeyFrames().add(new KeyFrame(halfDuration,
new KeyValue(nextNode.opacityProperty(), 0, interpolator)));
timeline.getKeyFrames().add(new KeyFrame(duration,
new KeyValue(nextNode.opacityProperty(), 1, interpolator)));
if (nextNode instanceof DecoratorAnimatedPage nextPage) {
Node left = nextPage.getLeft();
Node center = nextPage.getCenter();
timeline.getKeyFrames().add(new KeyFrame(halfDuration,
new KeyValue(left.translateXProperty(), -30, interpolator),
new KeyValue(center.translateXProperty(), 30, interpolator)));
timeline.getKeyFrames().add(new KeyFrame(duration,
new KeyValue(left.translateXProperty(), 0, interpolator),
new KeyValue(center.translateXProperty(), 0, interpolator)));
}
return timeline;
}
}
;
protected static void reset(Node node) {

View File

@@ -51,6 +51,7 @@ import org.jackhuang.hmcl.ui.Controllers;
import org.jackhuang.hmcl.ui.FXUtils;
import org.jackhuang.hmcl.ui.account.AddAuthlibInjectorServerPane;
import org.jackhuang.hmcl.ui.animation.*;
import org.jackhuang.hmcl.ui.animation.TransitionPane.AnimationProducer;
import org.jackhuang.hmcl.ui.construct.DialogAware;
import org.jackhuang.hmcl.ui.construct.DialogCloseEvent;
import org.jackhuang.hmcl.ui.construct.Navigator;
@@ -364,57 +365,8 @@ public class DecoratorController {
// ==== Navigation ====
private static final TransitionPane.AnimationProducer ANIMATION = (Pane container,
Node previousNode, Node nextNode,
Duration duration,
Interpolator interpolator) -> {
Timeline timeline = new Timeline();
if (previousNode instanceof TransitionPane.EmptyPane) {
return timeline;
}
Duration halfDuration = duration.divide(2);
List<KeyFrame> keyFrames = new ArrayList<>();
keyFrames.add(new KeyFrame(Duration.ZERO,
new KeyValue(previousNode.opacityProperty(), 1, interpolator)));
keyFrames.add(new KeyFrame(halfDuration,
new KeyValue(previousNode.opacityProperty(), 0, interpolator)));
if (previousNode instanceof DecoratorAnimatedPage prevPage) {
Node left = prevPage.getLeft();
Node center = prevPage.getCenter();
keyFrames.add(new KeyFrame(Duration.ZERO,
new KeyValue(left.translateXProperty(), 0, interpolator),
new KeyValue(center.translateXProperty(), 0, interpolator)));
keyFrames.add(new KeyFrame(halfDuration,
new KeyValue(left.translateXProperty(), -30, interpolator),
new KeyValue(center.translateXProperty(), 30, interpolator)));
}
keyFrames.add(new KeyFrame(halfDuration,
new KeyValue(nextNode.opacityProperty(), 0, interpolator)));
keyFrames.add(new KeyFrame(duration,
new KeyValue(nextNode.opacityProperty(), 1, interpolator)));
if (nextNode instanceof DecoratorAnimatedPage nextPage) {
Node left = nextPage.getLeft();
Node center = nextPage.getCenter();
keyFrames.add(new KeyFrame(halfDuration,
new KeyValue(left.translateXProperty(), -30, interpolator),
new KeyValue(center.translateXProperty(), 30, interpolator)));
keyFrames.add(new KeyFrame(duration,
new KeyValue(left.translateXProperty(), 0, interpolator),
new KeyValue(center.translateXProperty(), 0, interpolator)));
}
timeline.getKeyFrames().setAll(keyFrames);
return timeline;
};
public void navigate(Node node) {
navigator.navigate(node, ANIMATION);
public void navigate(Node node, AnimationProducer animationProducer, Duration duration, Interpolator interpolator) {
navigator.navigate(node, animationProducer, duration, interpolator);
}
private void close() {
@@ -586,7 +538,8 @@ public class DecoratorController {
public void startWizard(WizardProvider wizardProvider, String category) {
FXUtils.checkFxUserThread();
navigator.navigate(new DecoratorWizardDisplayer(wizardProvider, category), ContainerAnimations.FADE);
navigator.navigate(new DecoratorWizardDisplayer(wizardProvider, category),
ContainerAnimations.FORWARD, Motion.SHORT4, Motion.EASE);
}
// ==== Authlib Injector DnD ====

View File

@@ -123,7 +123,7 @@ public final class JavaManagementPage extends ListPageBase<JavaManagementPage.Ja
}
void onShowRestoreJavaPage() {
Controllers.navigate(new JavaRestorePage(ConfigHolder.globalConfig().getDisabledJava()));
Controllers.navigateForward(new JavaRestorePage(ConfigHolder.globalConfig().getDisabledJava()));
}
private void onAddJavaBinary(Path file) {

View File

@@ -161,6 +161,9 @@ public class RootPage extends DecoratorAnimatedPage implements DecoratorPage {
String currentId = getSkinnable().getMainPage().getCurrentGame();
return Lang.indexWhere(list, instance -> instance.getId().equals(currentId));
}, it -> getSkinnable().getMainPage().getProfile().setSelectedVersion(it.getId()));
if (AnimationUtils.isAnimationEnabled()) {
FXUtils.prepareOnMouseEnter(gameListItem, Controllers::prepareVersionPage);
}
// third item in left sidebar
AdvancedListItem gameItem = new AdvancedListItem();

View File

@@ -244,7 +244,7 @@ public final class ModListPage extends ListPageBase<ModListPageSkin.ModInfoObjec
} else if (result.isEmpty()) {
Controllers.dialog(i18n("mods.check_updates.empty"));
} else {
Controllers.navigate(new ModUpdatesPage(modManager, result));
Controllers.navigateForward(new ModUpdatesPage(modManager, result));
}
})
.withStagesHint(Collections.singletonList("mods.check_updates")),

View File

@@ -461,7 +461,7 @@ public final class VersionSettingsPage extends StackPane implements DecoratorPag
if (advancedVersionSettingPage == null)
advancedVersionSettingPage = new AdvancedVersionSettingPage(profile, versionId, lastVersionSetting);
Controllers.navigate(advancedVersionSettingPage);
Controllers.navigateForward(advancedVersionSettingPage);
}
});
showAdvancedSettingPane.setRight(button);

View File

@@ -91,6 +91,6 @@ public final class WorldListItem extends Control {
}
public void showManagePage() {
Controllers.navigate(new WorldManagePage(world, backupsDir));
Controllers.navigateForward(new WorldManagePage(world, backupsDir));
}
}