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>