Tutorial: Interaction and hierarchy of WebView internal pages

Interaction and hierarchy of WebView internal pages

We know that the special components such as WebView and VideoPlayer in
Cocos Creator have a disadvantage of always being at the top of the z-order. That is, always on top of other comoponents. This behavior results in UI components being blocked.

When opening the browser and running an app, press F12 to see the hierarchical relationship between all components. As shown below:

The above picture clearly shows that the videoPlayer component and the webView component are on top of the canvas component. The Cocos Creator built-in UI components are all rendered on the layer. No matter how we change the UI, this will always happen.

There is a solution. We can write a simple html page to help

General idea

First, use nginx to build a simple HTML page. Next load your own web page through the webView component. Third load some required video or other web pages on your own page. Some components that need to be displayed on the upper layer of the Video component can be implemented through HTML tags and then can interact with Cocos Creator using JavaScript.

How to interact with WebView internal pages?

First, please refer to the Webview documentation.

Cross-domain issues will occur on the Web platform. This requires developers to deal with web and mobile platforms separately.

On web platforms

On the Web, we can achieve cross-domain messaging through window.postMessage:

Cocos Creator sending messages to the web

Creator JavaScript:

this.webView._sgNode._renderCmd._iframe.contentWindow.postMessage(data, "*");
// If _sgNode is abandoned due to version, you can replace it 
// with the following
this.webView._impl._iframe.contentWindow.postMessage(data, "*");

Note: we must wait for the webView to load, before it can be sent.

HTML JavaScript

// Receive messages from cocos
  window.addEventListener('message', function (e) {
        var data = e.data; // parameter
        console.log("-----------message--------------", data)
  });

The web sends a message to Cocos Creator

HTML JavaScript

// browser, send a message to cocos
parent.postMessage("------------hello!-----cocos---------", "*")

Cocos Creator JavaScript

if (cc.sys.isBrowser) {
    // This is the browser environment, receiving messages from the web
    window.addEventListener('message', function (e) {
        console.log("----cocos---",e.data);
    })
}

View a demo running in a web browser:


Mobile platforms

First, please refer to the Webview documentation.

Second, we need to properly initialize

// initialization
 init() {
        // This is the keyword agreed with the internal page. Please do 
        // not use uppercase characters, which will cause the location 
        // to not be recognized correctly.
        var scheme = "testkey";
        // this is the mobile terminal, receiving messages from the web
        function jsCallback(target, url) {
            // The return value here is the URL value of the internal 
            // page, you need to parse the data you need.
            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 Creator sends a message to the web

Cocos Creator JavaScript

let data = {
	id:123456
}
// Note that the parameters need to be serialized here
data = JSON.stringify(data); 
// Call the global function defined by the web page
this.webView.evaluateJS("setBackgroundColor(" + data + ")");

The web sends a message to Cocos Creator

HTML JavaScript

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

Demo complete code:

Cocos Creator JavaScript:

if (cc.sys.isBrowser) {
    /// This is the browser environment, receiving messages from the 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() {
        // This is the keyword agreed with the internal page. Please do not
        // use uppercase characters, which will cause the location to not 
        // be recognized correctly.
        var scheme = "testkey";
        // This is the mobile terminal, receiving messages from the web
        function jsCallback(target, url) {
            // The return value here is the URL value of the internal 
            // page, you need to parse the data you need.
            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
    },

    // Binding button
    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)
        
        // Browser
        if (cc.sys.isBrowser) {
            console.log("-----cocos------Browser---------");
            text = "-----cocos------Browser---------";
            this.webView._sgNode._renderCmd._iframe.contentWindow.postMessage(data, "*");
            // If _sgNode is abandoned because of version, you can 
            // replace it with the following
			//this.webView._impl._iframe.contentWindow.postMessage(data, "*");
        } 
        // mobile
        else if (cc.sys.isNative) 
        { 
            console.log("-----cocos------Native---------");
            text = "-----cocos------Native---------";
            data = JSON.stringify(data);
            // setBackgroundColor is a web global function, data parameter
            this.webView.evaluateJS("setBackgroundColor(" + data + ")");
        }

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

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

    // Events
    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()">Trigger cocos to close webView</button>
</body>
<script>
    let iframeWeb = document.getElementById("iframeID");

    // ---------------browser-------need--------
    window.addEventListener('message', function (e) {
        var data = e.data;  //e.data contains all the parameters you have
        // passed, you can make your own judgment based on the parameters
        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, send a message to cocos
            parent.postMessage("------------hello!-----cocos---------", "*")
        }
    }
</script>

</html>
2 Likes

Thanks for this post. :smiley: :star_struck:
I will try when possible.