QML WebEngineView 抖动内容

6

我正在尝试使用 QML 和 WebEngineView 组件在 Ubuntu 14.04 桌面上制作一个简单的网页浏览器。应用程序将在具备触摸板的设备上运行,因此最好能够使 WebEngineView 中显示的内容可以滑动。

我试图以以下方式实现,但并没有成功:

...    
        WebEngineView {
            id: webView
            url: "http://google.com"
            width: parent.width
            height: winternet.height-navigationBar.height-iStatusBar.height-iBackButton.height
            anchors.top: navigationBar.bottom

            MouseArea {
                anchors.fill: parent
                drag.target: parent.data
            }

            onLinkHovered: {
                webView.url = hoveredUrl
            }
       }
...

如果您对此有任何想法或经验,请帮忙!


我猜所有鼠标/触摸板操作都由'WebEngineView'本身管理,因此这很可能是不可能的。如果您可以禁用'WebEngineView'的滚动条和鼠标交互,并将其放入'Flickable'中,则可能有意义。 - folibis
我也尝试过那种方法,但是没有找到如何将WebEngineView的宽度设置为其包含页面的宽度。 - Evgeny Slastnikov
我有同样的问题。你找到答案了吗? - feedbackloop
我在 Chromium API https://www.chromium.org/developers/design-documents/aura/gesture-recognizer 中找不到它。我是否使用了错误的文档? - user1415536
看起来,Qt 在短时间内不打算解决这个问题。创建错误报告完全没有帮助。他们只是忽略它们。这可以从绝大多数未修复的 2 级或更高优先级错误以及承诺在版本 5.6 中修复错误但实际上并未修复中真正体现出来。我想知道发布 Qt 的新版本的意义何在,如果旧版本的错误没有得到解决...... 这是一个修辞问题... - user1415536
1个回答

6

我希望能让WebEngineView也可以滑动,于是决定使用Flickable。但是,直接尝试这样做是不可取的:

...
Flickable {
    WebEngineView {...}
}
...

无法工作。

进一步调查引导我到基于Qt的嵌入式触摸设备Web浏览器。我已经在我的PC上试用过了。它似乎正好做我想要的事情,但它太复杂了,而GPL许可证使其对任何类型的使用都无用。

经过一些实验,我发现如果Flickable.contentHeightFlickable.contentWidth至少与WebEngineView显示的实际网页大小相匹配,则可以使用滑动。 Flickable的这些属性可能具有比实际页面大小更大的值。在这种情况下,您将能够超出页面内容进行滑动。如果Flickable.contentHeight和/或Flickable.contentWidth小于页面大小,则仍然可以滑动。这取决于您是否希望以这种方式进行操作。

因此,最终获取实际页面大小并将其设置为Flickable.contentHeight和Flickable.contentWidth。我在这里给你讲一个简短的故事:使用WebEngineView API无法获取所需值(或者至少我在Qt 5.7/5.8文档中没有找到任何内容)。但是我偶然发现了SO上的这个答案。使用这个答案,我成功地使一切正常工作:

...
Item {
    Layout.fillHeight: true
    Layout.fillWidth: true

    Flickable {
        id: flick
        anchors.fill: parent

        WebEngineView {
            anchors.fill: parent
            id: webView
        }
    }

    webView.onLoadingChanged: {
        if (webView.loadProgress == 100) {
            webView.runJavaScript(
                "document.documentElement.scrollHeight;",
                function (i_actualPageHeight) {
                    flick.contentHeight = Math.max (
                        i_actualPageHeight, flick.height);
                })
            webView.runJavaScript(
                "document.documentElement.scrollWidth;",
                function (i_actualPageWidth) {
                    flick.contentWidth = Math.max (
                        i_actualPageWidth, flick.width);
                })
        }
    }
}
...

上面的代码片段可能需要一些调整,但它几乎是我拥有的可用代码的复制。

UPD 1:我已经发现这不是最终的解决方案,因为由于某种原因,在加载新页面后,document.documentElement.scrollWidth 可能没有被重置,而保持与前一页相同。

UPD 2:我已经解决了上述问题,但解决方案有点丑陋:在 WebEngineView.onLoadingChanged 中将 Flickable.contentWidth 重置为 Flickable.width。将 Flickable.contentWidth 设置为 0 将导致加载后内容高度不合适地变大。

我做出的另一个调整是删除了对100%加载状态的要求。

UPD 3:更完整的 flickable WebEngiveView 版本。使用用户脚本而不是直接调用 JavaScript,因为我遇到了一些奇怪的错误,导致 WebEngineView 关闭。

// Copyright 2019 Utility Tool Kit Open Source Contributors
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License.  You may obtain a copy
// of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
// License for the specific language governing permissions and limitations under
// the License.
//
// Author: Innokentiy Alaytsev <alaitsev@gmail.com>
//
// File name: qml/Utk/Qml/FlickableWebEngineView.qml
//
// Description: The QML FlickableWebEngineView QtQuick 2 component.


import QtQuick 2.7

import QtWebEngine 1.3


Item {
    property alias flickable: flickable;
    property alias webView: webView;

    property bool userDragImgEnabled: true;
    property bool userSelectEnabled: true;


    readonly property string kDisableUserDragCssId:
    "utk_qml_flickable_web_engine_view_disable_user_drag_css";

    readonly property string kDisableUserSelectCssId:
    "utk_qml_flickable_web_engine_view_disable_user_select_css";

    readonly property string kDisableUserDragCss:
    "{                                  \\                    \
        -webkit-user-drag: none;        \\                    \
        -khtml-user-drag: none;         \\                    \
        -moz-user-drag: none;           \\                    \
        -ms-user-drag: none;            \\                    \
        user-drag: none;                \\                    \
    }";

    readonly property string kDisableUserSelectCss:
    "{                                  \\                    \
        -webkit-touch-callout: none;    \\                    \
        -webkit-user-select: none;      \\                    \
        -khtml-user-select: none;       \\                    \
        -moz-user-select: none;         \\                    \
        -ms-user-select: none;          \\                    \
        user-select: none;              \\                    \
    }";


    WebEngineScript {
        id: disableUserDragScript;
        name: kDisableUserDragCssId;
        injectionPoint: WebEngineScript.DocumentReady;
        sourceCode: applyCssJavaScript ("img", kDisableUserDragCss, kDisableUserDragCssId);
        worldId: WebEngineScript.MainWorld;
    }

    WebEngineScript {
        id: disableUserSelectScript;
        name: kDisableUserSelectCssId;
        injectionPoint: WebEngineScript.DocumentReady;
        sourceCode: applyCssJavaScript ("body", kDisableUserSelectCss, kDisableUserSelectCssId);
        worldId: WebEngineScript.MainWorld;
    }


    Flickable {
        id: flickable;
        anchors.fill : parent;

        clip: true;

        WebEngineView {
            id: webView;

            anchors.fill : parent;

            scale: 1;

            onLoadingChanged: {
                if (loadRequest.status !== WebEngineView.LoadSucceededStatus) {
                    return;
                }

                flickable.contentHeight = 0;
                flickable.contentWidth = flickable.width;

                runJavaScript (
                    "document.documentElement.scrollHeight;",
                    function (actualPageHeight) {
                        flickable.contentHeight = Math.max (
                            actualPageHeight, flickable.height);
                    });

                runJavaScript (
                    "document.documentElement.scrollWidth;",
                    function (actualPageWidth) {
                        flickable.contentWidth = Math.max (
                            actualPageWidth, flickable.width);
                    });
            }
        }
    }


    onUserDragImgEnabledChanged: {
        if (userDragImgEnabled &&
            (webView.loadRequest.status === WebEngineView.LoadSucceededStatus)) {
            runJavaScript (revertCssJavaScript (kDisableUserDragCssId));
        }
        else {
            webView.userScripts = currentUserScripts ();
        }
    }


    onUserSelectEnabledChanged: {
        if (userSelectEnabled &&
            (webView.loadRequest.status === WebEngineView.LoadSucceededStatus)) {
            runJavaScript (revertCssJavaScript (kDisableUserSelectCssId));
        }
        else {
            webView.userScripts = currentUserScripts ();
        }
    }


    function currentUserScripts () {
        var userScriptsToSkip = [
            disableUserDragScript.name,
            disableUserSelectScript.name
        ];

        var updatedUserScripts = [];

        for (var i in webView.userScripts) {
            var script = webView.userScripts[ i ];

            if (-1 == userScriptsToSkip.indexOf (script.name)) {
                updatedUserScripts.push (script);
            }
        }

        if (!userDragImgEnabled) {
            updatedUserScripts.push (disableUserDragScript);
        }

        if (!userSelectEnabled) {
            updatedUserScripts.push (disableUserSelectScript);
        }

        return updatedUserScripts;
    }


    function applyCssJavaScript (selector, css, cssId) {
        var applyCssJavaScript =
            "(function () {                                               \
                cssElement = document.createElement ('style');            \
                                                                          \
                head = document.head ||                                   \
                    document.getElementsByTagName ('head')[ 0 ];          \
                                                                          \
                head.appendChild (cssElement);                            \
                                                                          \
                cssElement.type = 'text/css';                             \
                cssElement.id = '%1';                                     \
                                                                          \
                if (cssElement.styleSheet)                                \
                {                                                         \
                    cssElement.styleSheet.cssText = '%2 %3';              \
                }                                                         \
                else                                                      \
                {                                                         \
                    cssElement.appendChild (                              \
                        document.createTextNode ('%2 %3'));               \
                }                                                         \
            })();";

        return applyCssJavaScript
            .arg (cssId)
            .arg (selector)
            .arg (css);
    }


    function revertCssJavaScript (cssId) {
        var revertCssJavaScript =
            "(function () {                                               \
                 var element = document.getElementById('%1');             \
                                                                          \
                 if (element) {                                           \
                     element.outerHTML = '';                              \
                                                                          \
                     delete element;                                      \
                 }                                                        \
            })()";

        return revertCssJavaScript.arg (cssId);
    }
}


这对我非常有帮助,感谢分享。我现在正在研究如何将其放在选项卡组件内部。 - mozcelikors
@mozcelikors 请尝试使用 UPD 3 版本。它可能也适用于您。只需将其放入“FlickableWebEngineView.qml”文件中,并从需要它的位置进行导入即可。 - Innokentiy Alaytsev
根据我的经验,之前的版本效果更好,我只是添加了以下内容:onUrlChanged: { flick.contentHeight = 0; flick.contentWidth = 0; }使用用户脚本方法似乎无法在某些网站上进行滑动操作,但这可能是由于我的qtwebengine版本问题。 - mozcelikors

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接