【问题标题】:Implementing Fluent Design's reveal highlight effect in JavaFX在 JavaFX 中实现 Fluent Design 的高亮显示效果
【发布时间】:2021-06-25 10:43:04
【问题描述】:

我想实现 JavaFX 的显示高亮效果,可以在 Windows 10 的各个部分看到,尤其是设置和计算器应用程序。

效果似乎由两部分组成,边框突出显示 (seen here) 和背景突出显示 (seen here虽然由于压缩,个人看起来更好)。 p>

我的第一直觉是看看这是否可以在某种像素着色器中完成,但在谷歌搜索之后,JavaFX 似乎确实为类似的东西提供了公共 API?

是否可以在不借助画布并手动绘制整个 UI 的情况下创建这种效果?

【问题讨论】:

    标签: java javafx effects fluent-design


    【解决方案1】:

    首先我想说我不知道​​ Windows 是如何实现这种风格的。但我的一个想法是有多个层:

    1. 黑色背景。

    2. 随着鼠标移动,带有从白色到透明的径向渐变的圆圈。

    3. 一个黑色背景的区域和一个在选项节点所在的地方都有孔的形状。

    4. 具有分层背景的选项节点。

      • 当鼠标没有悬停时:

        1. 没有插图的透明背景。
        2. 带有轻微嵌入的黑色背景。
      • 当鼠标悬停时:

        1. 低不透明度白色背景,无插图。
        2. 带有轻微嵌入的黑色背景。
        3. 以鼠标为中心的白色到透明径向渐变背景。

    不幸的是,这意味着很多样式都必须在代码中完成,尽管我更愿意将大部分样式放在 CSS 中。这是我快速模拟的概念验证。它功能不全,但显示您想要的外观是可能的。

    OptionsPane.java

    import javafx.beans.InvalidationListener;
    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    import javafx.geometry.Bounds;
    import javafx.geometry.HPos;
    import javafx.geometry.Insets;
    import javafx.geometry.VPos;
    import javafx.scene.Node;
    import javafx.scene.control.Label;
    import javafx.scene.input.MouseEvent;
    import javafx.scene.layout.Background;
    import javafx.scene.layout.BackgroundFill;
    import javafx.scene.layout.HBox;
    import javafx.scene.layout.Priority;
    import javafx.scene.layout.Region;
    import javafx.scene.layout.TilePane;
    import javafx.scene.layout.VBox;
    import javafx.scene.paint.Color;
    import javafx.scene.paint.RadialGradient;
    import javafx.scene.paint.Stop;
    import javafx.scene.shape.Circle;
    import javafx.scene.shape.Rectangle;
    import javafx.scene.shape.Shape;
    import javafx.scene.text.Font;
    
    public class OptionsPane extends Region {
    
      public static class Option {
    
        private final String title;
        private final String subtitle;
        private final Node graphic;
    
        public Option(String title, String subtitle, Node graphic) {
          this.title = title;
          this.subtitle = subtitle;
          this.graphic = graphic;
        }
    
        public String getTitle() {
          return title;
        }
    
        public String getSubtitle() {
          return subtitle;
        }
    
        public Node getGraphic() {
          return graphic;
        }
      }
    
      private final ObservableList<Option> options = FXCollections.observableArrayList();
    
      private final TilePane topTiles = new TilePane();
      private final Region midCover = new Region();
      private final Circle underGlow = new Circle();
    
      public OptionsPane() {
        setBackground(new Background(new BackgroundFill(Color.BLACK, null, null)));
    
        underGlow.setManaged(false);
        underGlow.setRadius(75);
        underGlow.visibleProperty().bind(hoverProperty());
        underGlow.setFill(
            new RadialGradient(
                0, 0,
                0.5, 0.5,
                1.0,
                true,
                null,
                new Stop(0.0, Color.WHITE),
                new Stop(0.35, Color.TRANSPARENT)));
        addEventFilter(
            MouseEvent.MOUSE_MOVED,
            e -> {
              underGlow.setCenterX(e.getX());
              underGlow.setCenterY(e.getY());
            });
    
        midCover.setBackground(new Background(new BackgroundFill(Color.BLACK, null, null)));
    
        topTiles.setMinSize(0, 0);
        topTiles.setVgap(20);
        topTiles.setHgap(20);
        topTiles.setPadding(new Insets(20));
        topTiles.setPrefTileWidth(250);
        topTiles.setPrefTileHeight(100);
        topTiles.setPrefColumns(3);
        options.addListener(
            (InvalidationListener)
                obs -> {
                  topTiles.getChildren().clear();
                  options.forEach(opt -> topTiles.getChildren().add(createOptionRegion(opt)));
                });
    
        getChildren().addAll(underGlow, midCover, topTiles);
      }
    
      public final ObservableList<Option> getOptions() {
        return options;
      }
    
      @Override
      protected void layoutChildren() {
        double x = getInsets().getLeft();
        double y = getInsets().getTop();
        double w = getWidth() - getInsets().getRight() - x;
        double h = getHeight() - getInsets().getBottom() - y;
    
        layoutInArea(midCover, x, y, w, h, -1, HPos.CENTER, VPos.CENTER);
        layoutInArea(topTiles, x, y, w, h, -1, HPos.CENTER, VPos.CENTER);
    
        Shape coverShape = new Rectangle(x, y, w, h);
        for (Node optionNode : topTiles.getChildren()) {
          Bounds b = optionNode.getBoundsInParent();
          Rectangle rect = new Rectangle(b.getMinX(), b.getMinY(), b.getWidth(), b.getHeight());
          coverShape = Shape.subtract(coverShape, rect);
        }
        midCover.setShape(coverShape);
      }
    
      private Region createOptionRegion(Option option) {
        Label titleLabel = new Label(option.getTitle());
        titleLabel.setTextFill(Color.WHITE);
        titleLabel.setFont(Font.font("System", 13));
    
        Label subtitleLabel = new Label(option.getSubtitle());
        subtitleLabel.setTextFill(Color.DARKGRAY);
        subtitleLabel.setFont(Font.font("System", 10));
    
        VBox textBox = new VBox(5, titleLabel, subtitleLabel);
        HBox.setHgrow(textBox, Priority.ALWAYS);
    
        HBox container = new HBox(10, textBox);
        container.setPadding(new Insets(10));
        if (option.getGraphic() != null) {
          container.getChildren().add(0, option.getGraphic());
        }
    
        setNonHoverBackground(container);
        container
            .hoverProperty()
            .addListener(
                (obs, ov, nv) -> {
                  if (!nv) {
                    setNonHoverBackground(container);
                  }
                });
    
        container.setOnMouseMoved(e -> setHoverBackground(container, e.getX(), e.getY()));
    
        return container;
      }
    
      private void setNonHoverBackground(Region region) {
        BackgroundFill fill1 = new BackgroundFill(Color.TRANSPARENT, null, null);
        BackgroundFill fill2 = new BackgroundFill(Color.BLACK, null, new Insets(2));
        region.setBackground(new Background(fill1, fill2));
      }
    
      private void setHoverBackground(Region region, double x, double y) {
        RadialGradient gradient =
            new RadialGradient(
                0, 0,
                x, y,
                400,
                false,
                null,
                new Stop(0.0, new Color(1, 1, 1, 0.2)),
                new Stop(0.35, Color.TRANSPARENT));
    
        BackgroundFill fill1 = new BackgroundFill(new Color(1, 1, 1, 0.3), null, null);
        BackgroundFill fill2 = new BackgroundFill(Color.BLACK, null, new Insets(2));
        BackgroundFill fill3 = new BackgroundFill(gradient, null, null);
        region.setBackground(new Background(fill1, fill2, fill3));
      }
    }
    

    Main.java

    import java.util.ArrayList;
    import java.util.List;
    import javafx.application.Application;
    import javafx.scene.Scene;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.Rectangle;
    import javafx.stage.Stage;
    
    public class Main extends Application {
    
      @Override
      public void start(Stage primaryStage) {
        OptionsPane pane = new OptionsPane();
    
        List<OptionsPane.Option> options = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
          Rectangle graphic = new Rectangle(20, 20, Color.DARKGRAY);
          options.add(
              new OptionsPane.Option("Option Title #" + (i + 1), "Description #" + (i + 1), graphic));
        }
        pane.getOptions().addAll(options);
    
        primaryStage.setScene(new Scene(pane));
        primaryStage.show();
      }
    }
    

    这就是它的样子:

    这并不完全相同,但您可以自己试验并根据需要进行更改。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-04-26
      • 1970-01-01
      • 2012-06-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-06-08
      相关资源
      最近更新 更多