WebView内部页面的交互和层级问题

我们知道creator里的WebView,VideoPlayer等特殊组件有一个非常严重的问题,就是不管你怎么设置层级,这类组件始终处于最上层!其他UI组件会被遮挡。

我们打开浏览器运行,F12检测元素就可以清楚的看到他们的层级关系。
如下图:

通过上图我们可以清楚的看到,video(videoPlayer组件) 和 iframe(webView组件) 在 canvas(GameCanvas) 上层,而我们creator内置UI组件都是在GameCanvas层渲染的,所以不管我们怎么改UI层级都无效。

解决方案可以参考我之前的一篇文章: videoPlayer组件一直处于在最上层的问题

其实除了上述的办法,还有一个解决方案,就是我们可以通过自己写一个简单的html页面来中转!

大致思路:

首先我们自己用 nginx 搭建一个简单的 html 页面,在通过 webView组件加载我们自己的web页面,然后在我们自己的页面加载一些需要的video或者其他网页,一些需要显示在Video组件上层的组件我们就可以通过html标签来实现了(需要一点web知识),然后和creator JS交互就可以了。

如何与 WebView 内部页面进行交互呢?:

可以参考一下
官方文档

个人感觉官方文档讲得不够全面。

我的实现如下:

首先我们先来看下官方的:

我们可以看到,Web平台上会出现跨域的问题,所以我们需要分别对web平台和移动平台做处理,

web平台:

在Web上我们可以通过 window.postMessage 实现跨域消息传递:

cocos 发送消息到 web:

cocos JS:

this.webView._sgNode._renderCmd._iframe.contentWindow.postMessage(data, "*");
//如果因为版本原因 _sgNode 被舍弃了,可以换成以下
this.webView._impl._iframe.contentWindow.postMessage(data, "*");

这里需要注意的是:我们必须等webView加载完成之后才能发送。

html JS

//接收来自cocos的消息
  window.addEventListener('message', function (e) {
        var data = e.data;//参数
        console.log("-----------message--------------", data)
  });

web 发送消息到 cocos:

html JS

//browser 浏览器下,向cocos发送消息
parent.postMessage("------------hello!-----cocos---------", "*")

cocos JS:

if (cc.sys.isBrowser) {
    //这里是浏览器环境下, 接收web传过来的消息
    window.addEventListener('message', function (e) {
        console.log("----cocos---",e.data);
    })
}

演示如下:


以上是运行在浏览器环境下的交互


移动平台:

移动平台下就和官网的区别不大了。

首先需要初始化:

//初始化
 init() {
        // 这里是与内部页面约定的关键字,请不要使用大写字符,会导致 location 无法正确识别。
        var scheme = "testkey";
        //这里是移动端, 接收web传过来的消息
        function jsCallback(target, url) {
            // 这里的返回值是内部页面的 URL 数值,需要自行解析自己需要的数据。
            var str = url.replace(scheme + '://', ''); // str === 'a=1&b=2'
            // webview target
            console.log("jsCallback-------str-------", str);
            window.closeWebView(target, url);
        }
        this.webView.setJavascriptInterfaceScheme(scheme);
        this.webView.setOnJSCallback(jsCallback);

        //web
        window.closeWebView = this.closeWebView.bind(this);
},

cocos 发送消息到 web:

cocos JS:

let data = {
	id:123456
}
data = JSON.stringify(data); //注意这里需要把参数序列化
//调用web页面 定义的全局函数
this.webView.evaluateJS("setBackgroundColor(" + data + ")");

web 发送消息到 cocos :

html JS

 function onClick() {
  	 console.log("-------web--------onClick----->>cocos JS-------------", window.isNative)
     //android or ios
     document.location = 'testkey://a=1&b=2'
 }

demo完整代码:

cocos JS:

if (cc.sys.isBrowser) {
    //这里是浏览器环境下, 接收web传过来的消息
    window.addEventListener('message', function (e) {
        console.log("----cocos---",e.data);
        window.closeWebView(e);
    })
}

cc.Class({
    extends: cc.Component,

    properties: {
        webView: cc.WebView,
        debugText: cc.Label
    },

    start() {
        this.setDebugText("start.....")
        this.webView.url = "web ip 地址"; // 如: "http://127.0.0.1:8190/web/"
        this.init();
    },

    init() {
        // 这里是与内部页面约定的关键字,请不要使用大写字符,会导致 location 无法正确识别。
        var scheme = "testkey";
        //这里是移动端, 接收web传过来的消息
        function jsCallback(target, url) {
            // 这里的返回值是内部页面的 URL 数值,需要自行解析自己需要的数据。
            var str = url.replace(scheme + '://', ''); // str === 'a=1&b=2'
            // webview target
            console.log("jsCallback-------str-------", str);
            window.closeWebView(target, url);
        }
        this.webView.setJavascriptInterfaceScheme(scheme);
        this.webView.setOnJSCallback(jsCallback);

        //web
        window.closeWebView = this.closeWebView.bind(this);
    },

    setDebugText(str) {
        this.debugText.string = str
    },

    //绑定按钮
    cocosToWeb() {  
        let data = {
            width: this.webView.node.width,
            height: this.webView.node.height,
            isNative: cc.sys.isNative,
            color:"#FF9800"
        }
        let text;
        console.log("------cocos------data-----------", data)
        //浏览器
        if (cc.sys.isBrowser) {
            console.log("-----cocos------Browser---------");
            text = "-----cocos------Browser---------";
            this.webView._sgNode._renderCmd._iframe.contentWindow.postMessage(data, "*");
            //如果因为版本原因 _sgNode 被舍弃了,可以换成以下
			//this.webView._impl._iframe.contentWindow.postMessage(data, "*");
        } 
        //移动端
        else if (cc.sys.isNative) 
        { 
            console.log("-----cocos------Native---------");
            text = "-----cocos------Native---------";
            data = JSON.stringify(data);
            //setBackgroundColor 是 web 全局函数, data 参数
            this.webView.evaluateJS("setBackgroundColor(" + data + ")");
        }

        this.webView.node.active = true;
        this.setDebugText(text)
    },

    //关闭WebView
    closeWebView(e, url) {
        this.webView.node.active = false;
        this.setDebugText("--------cocos-----close----webView-------" + url);
    },

    //事件
    onWebFinishLoad: function (sender, event) {
        if (event === cc.WebView.EventType.LOADED) {
            this.setDebugText("----webView---loaded---finish!!----")
            this.cocosToWeb()
        } else if (event === cc.WebView.EventType.LOADING) {
            this.setDebugText("----webView---loading----")
        } else if (event === cc.WebView.EventType.ERROR) {
            this.setDebugText("----webView---load---error----")
        }
    },

});

Html:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>cocos web</title>
    <style>
        body {
            margin: 0;
            padding: 0;
            background: rgb(94, 94, 94);
        }
        div {
            width: 100%;
            height: 50px;
            position: absolute;
            margin: auto;
            left: 0;
            right: 0;
            bottom: 0;
            text-align: center;
            font-size: 30px;
        }

        iframe{
            width: 50%;
            height: 80%;
            position: absolute;
            margin: auto;
            left: 0;
            right: 0;
            top: 0;
            text-align: center;
            font-size: 30px;
        }

        button {
            position: absolute;
            width: 200px;
            height: 30px;
            background: red;
            top: 2px;
            right: 20%;
            border: 0;
        }
    </style>
</head>

<body>
    <iframe id="iframeID" src="http://www.baidu.com" frameborder="0"></iframe>
    <div id="text"></div>
    <button type="button" onclick="onClick()">触发cocos 关闭webView</button>
</body>
<script>
    let iframeWeb = document.getElementById("iframeID");

    // ---------------browser-------need--------
    window.addEventListener('message', function (e) {
        var data = e.data;  //e.data  里面有自己所传的所有参数  可以根据参数做自己的判断
        console.log("--------------this is web message--------------", data)
        setBackgroundColor(data);
    });
    // ---------------browser---------------
    function setBackgroundColor(data) {
        console.log("-------web--------data-------" + data)
        window.isNative = data.isNative;
        document.getElementsByTagName("body")[0].style.background = data.color;
        document.getElementById("text").innerHTML = "this is cocos send msg color :" + data.color;
        document.getElementById("iframeID").innerHTML = "this is cocos send msg color :" + data.color;
        // setIframeSize(data)
    }

    function setIframeSize(data){
        if (data.isNative) { //----------mobile---------
            let cocosIframe = window.parent.document.documentElement;
            console.log("-----mobile--web-------size------" + cocosIframe.clientWidth + "---" + cocosIframe.clientHeight);
            iframeWeb.style.width = cocosIframe.clientWidth + "px";
            iframeWeb.style.height = cocosIframe.clientHeight + "px";
        }else{//----------browser---------
            console.log("----browser---web-------size------" + data.width + "---" + data.height);
            iframeWeb.style.width = data.width + "px";
            iframeWeb.style.height = data.height + "px";
        }
    }

    function onClick(param) {
        console.log("-------web--------onClick----->>cocos JS-------------", window.isNative)
        if (window.isNative) {
            //android or ios
            document.location = 'testkey://a=1&b=2'
        } else {
            //browser 浏览器下,向cocos发送消息
            parent.postMessage("------------hello!-----cocos---------", "*")
        }
    }
</script>

</html>

end!

4赞

您好,请问您这个方法在ios app上测试过吗,我用这种方法web,android 都可以 但是ios不可以

赞。。。。终于解决了跨域问题

ios的我还未测试过

Cocos Creator 如何也实现webview呢,楼主知道不,感谢~:slightly_smiling:

看了帖子有2个疑问。
1、修改层级。 initVideoZIndex 这个函数写在哪 也是在main.js么。 什么时候调用呢?
2、那个Html 写在哪呢?

大佬快翻我!