如何使 WebView 的背景透明?

How do I make the background of a WebView transparent?

我在 IntelliJ IDEA 中有一个 Maven 项目,使用 JDK 15 和 JavaFX 15。

我试过了:

  1. 为 WebEngine 设置样式
  2. 为 WebView 设置混合模式
  3. 试过网页

没有成功。如何让WebView的背景透明?

我的模块-info.java:

module project {
    requires javafx.controls;
    requires javafx.web;
    requires javafx.graphics;
    requires uk.co.caprica.vlcj;
    requires uk.co.caprica.vlcj.javafx;

    exports project;
}

我的 pom.xml 依赖项:

<dependencies>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>15</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-web</artifactId>
            <version>15</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-graphics</artifactId>
            <version>15</version>
        </dependency>
        <dependency>
            <groupId>uk.co.caprica</groupId>
            <artifactId>vlcj</artifactId>
            <version>4.7.0</version>
        </dependency>
        <dependency>
            <groupId>uk.co.caprica</groupId>
            <artifactId>vlcj-javafx</artifactId>
            <version>1.0.2</version>
        </dependency>
</dependencies>

我创建了一个新项目并尝试实现答案中的示例。发生了。不幸的是,单击按钮后背景颜色不会变得透明。您知道示例为什么不起作用吗?

Main.java:

import javafx.application.Application;
import javafx.stage.Stage;

import com.sun.webkit.dom.HTMLDocumentImpl;
import java.lang.reflect.Field;
import javafx.geometry.Insets;
import javafx.scene.control.Button;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.web.WebView;

public class Main extends Application {

    @Override
    public void start(final Stage stage) {
        initStage(stage);
    }

    private void initStage(Stage stage){
        WebView wv = new WebView();
        wv.setStyle("-fx-background-color: transparent;");

        wv.getEngine().load("https://w3schools.com");
        VBox vb = new VBox(wv);
        vb.setBackground(new Background(new BackgroundFill(Color.ORANGERED, CornerRadii.EMPTY, Insets.EMPTY)));

        Button b = new Button("remove page background");

        b.setOnAction((event) -> {
            HTMLDocumentImpl cast = (HTMLDocumentImpl) wv.getEngine().getDocument();

            try {
                Field f = wv.getEngine().getClass().getDeclaredField("page");
                f.setAccessible(true);
                com.sun.webkit.WebPage page = (com.sun.webkit.WebPage) f.get(wv.getEngine());
                page.setBackgroundColor((new java.awt.Color(1, 1, 1, 1)).getRGB());
                f.setAccessible(false);
            } catch (Exception e) {
            }

        });
        vb.getChildren().add(b);

        stage.setScene(new Scene(vb));
        stage.show();
    }

    public static void main(String[] args) {
        launch();
    }
}

虚拟机选项:

--module-path ${PATH_TO_FX} --add-modules javafx.controls,javafx.web --add-exports javafx.web/com.sun.webkit.dom=ALL-UNNAMED

WebView 在网页中呈现 DOM CSS。如果你想要一个透明的背景,你也必须修改那里的CSS。

编辑...以下工作正常,但打开了一大堆蠕虫。

    @Override
    public void start(Stage primaryStage) throws Exception {
    WebView wv = new WebView();
    wv.setStyle("-fx-background-color: transparent;");

    wv.getEngine().load("https://w3schools.com");
    VBox vb = new VBox(wv);
    vb.setBackground(new Background(new BackgroundFill(Color.ORANGERED, CornerRadii.EMPTY, Insets.EMPTY)));

    Button b = new Button("remove page background");

    b.setOnAction((event) -> {
        HTMLDocumentImpl cast = HTMLDocumentImpl.class.cast(wv.getEngine().getDocument());
        Node item = cast.getElementsByTagName("html").item(0);

        try {
            // Use reflection to retrieve the WebEngine's private 'page' field. 
            Field f = wv.getEngine().getClass().getDeclaredField("page");
            f.setAccessible(true);
            com.sun.webkit.WebPage page = (com.sun.webkit.WebPage) f.get(wv.getEngine());
            page.setBackgroundColor((new java.awt.Color(0, 0, 0, 0)).getRGB());
            f.setAccessible(false);
        } catch (Exception e) {
        }

    });
    vb.getChildren().add(b);

    primaryStage.setScene(new Scene(vb));
    primaryStage.show();
}

已添加导入列表

import com.sun.webkit.dom.HTMLDocumentImpl;
import java.lang.reflect.Field;
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import org.w3c.dom.Node;

您可能需要添加以下内容:

--add-exports=javafx.web/com.sun.webkit.dom=ALL-UNNAMED \

project.properties 文件的 run.jvm args 参数中。

注意...这是一种 hack,不被 oracle 鼓励。

编辑 (2)

同样,这是一种破解,充其量是不完整的。

此快照是在删除背景后立即向下滚动后拍摄的。

从技术上讲,我认为发生的事情是,每当 DOM 渲染引擎重绘帧时,都希望按层次绘制所有内容,并且永远不会期望背景是透明的。因此它不会“清除”东西,它只是重复地在自身之上绘制前景。

此外,还有 1 件有趣的事情正在发生。

如果您单击 + 重新加载页面,或者如果您单击“接受 cookie”,页面背景将被删除....我很确定这会触发 dom 更新。

很明显,WebView 和 WebPage 组件是两个不同领域的一部分。 WebView 是 DOM 操作...WebPage 是 DOM 在处理渲染的特定阶段使用的东西。

如果我对为什么 WebPage 中的功能被“隐藏”发表意见,我可能会暂停这个帐户......但根据个人经验,每当事情难以掌握时,例如 WebPage ,在 openJFx 中,这通常意味着您将浪费数月的时间来尝试为将来很可能会更改的内容找到解决方案,从而使您的更改过时。

我的建议是要么放弃这个问题,要么彻底改变你的心态。

我认为你应该做的基本上是在像窗格这样的东西中模拟渲染。

所以...新舞台...PaneX 作为场景,解析 web 视图中的任何内容,并使用 css 和诸如此类的东西将节点放入 PaneX 中,这些节点在外观和感觉上与在网络视图中。 ...正如您想象的那样...这将是一个麻烦事...

或者,您可能想考虑获取 openjfx 代码库并使用修改后的源代码,以便通过添加额外代码让 WebPage 有效地清除自身...但这取决于您。

最后,您必须考虑到这样做的原因可能与性能问题有关。

举例来说,您不是只清除网页中的背景,而是删除页面中 CSS 中的任何和所有背景,您将不得不经常这样做。这意味着性能问题,因为它本质上意味着在页面加载时进行完整的 dom 树解析。

经过多次尝试,我终于达到了想要的效果。

JDK 15,JavaFX 15.

我试图从这里实现一个示例:

而且背景是透明的。然后我想通了为什么。这是由于我通过大量试验和错误添加的 VM 选项。

如果没有这些 VM 选项:

,该示例将无法运行
--add-exports
javafx.web / com.sun.javafx.webkit = ALL-UNNAMED
--add-exports
javafx.web / com.sun.webkit = ALL-UNNAMED
--add-opens
javafx.web / javafx.scene.web = ALL-UNNAMED

答案中的示例(我选择的最佳示例)很可能也适用于这些选项。