Cocos Creator webview 嵌入

本次在工作中遇到了 cocos 的web嵌套问题,官网给的文档感觉不太明确,一点一点的验证完成了探索。写这篇博客,就是想分享给大家,不用走那么多探索道路,下面把主要的代码贴出来,以供参考。

需求:
一个项目中将活动等不含游戏逻辑的数据,单独拆分为单独的项目,主包(ios,android,h5)用webview进行加载。这里子包也是用creator做的,打包成了h5。

1、主包的webview

import { ipc } from "./WebInteractive";

const { ccclass, property } = cc._decorator;
/**
 * 设置web背景色为透明
 * IOS WebViewImpl-ios.mm 文件
 * 
 * setupWebView 函数  -> if (!self.uiWebView) 后面
 *        添加
 *          [self.uiWebView setOpaque: NO];
 *          [self.uiWebView setBackgroundColor:[UIColor clearColor] ];
 * 
 * ANDROD  Cocos2dxWebView.java 文件
 * Cocos2dxWebView 类 -> 带两个参数的构造函数 
 *        添加
 *          this.setBackgroundColor(0);
 * 
 * H5 端 参见下方 initIframe函数
 */

/** 当前webid */
let totalId = -1;

/** 加载监听 */
export interface UIWebViewListener {
    /** 加载成功 */
    onScuccess?: () => void;
    /** 加载失败 */
    onFail?: () => void;
    /** 加载中 */
    onloading?: () => void;
}

@ccclass
export default class UIWebView extends cc.Component {

    web: cc.WebView = null;

    /** 链接路径 */
    private _url_: string = "";
    /** 当前iframe窗口 */
    private contentWindow: any = null;
    /** web 加载完成回调 */
    private listener: UIWebViewListener = null;
    /** 拦截标识 (要用小写哦) */
    private scheme = "testscheme";
    /** 当前web的标记 */
    webId: number | string = -1;

    onLoad() {
        let webNode = new cc.Node();
        webNode.name = "WebView";
        // 添加widget
        let widget = webNode.addComponent(cc.Widget) as cc.Widget;
        widget.isAlignTop = true;
        widget.isAlignLeft = true;
        widget.isAlignRight = true;
        widget.isAlignBottom = true;
        widget.top = 0;
        widget.bottom = 0;
        widget.left = 0;
        widget.right = 0;

        // 添加web
        this.web = webNode.addComponent(cc.WebView) as cc.WebView;
        this.node.addChild(webNode);

        this.addListener();
        this.webId = (totalId += 1);
        if (this._url_) this.initWeb();
    }

    /**
     * 打开页面
     * @param url 
     * @param listener 监听器
     */
    open(url: string, listener?: UIWebViewListener) {
        this._url_ = url;
        this.listener = listener;
        if (this.web) this.initWeb();
    }

    /**
     * 初始当前web
     * model 显示类型
     * webId 当前页面id
     * origin 来源,嵌入的类型 1安卓, 2ios, 3 H5
     */
    private initWeb() {
        this.initIframe();
        let status = this._url_.lastIndexOf("?") === -1;
        this.web.url = `${this._url_}${status ? "?" : "&"}webId=${this.webId}&origin=${this.getOrigin()}`;
        console.log("当前url:", this.web.url);
    }

    /**
     * 来源,嵌入的类型
     * @returns  1安卓, 2ios, 3 H5
     */
    private getOrigin(): 1 | 2 | 3 {
      // 这里是自定义的判断,根据自己项目修改
        if (IS_ANDROID_APP) return 1;
        if (IS_IOS_APP) return 2;
        return 3;
    }

    /**
     * 设置iframe样式
     */
    private initIframe() {
        // 浏览器web,需要处理iframe样式
        if (cc.sys.isNative || !this.web || !this.web['_impl'] || !this.web['_impl']['_iframe']) return;

        let iframe: Element = this.web['_impl']['_iframe'];
        iframe.setAttribute("allowTransparency", "true");
        iframe.setAttribute("frameBorder", "0");
        iframe['style']['background'] = "transparent";
        this.contentWindow = iframe['contentWindow'];
    }

    /**
     * 添加监听
     */
    private addListener() {
        this.web.node.on("loading", this.onWebLoading, this);
        this.web.node.on("loaded", this.onWebLoaded, this);
        this.web.node.on("error", this.onWebLoadedError, this);
        this.initAPP();
    }

    /**
     * 加载失败
     * @param evt 
     */
    private onWebLoadedError(evt: cc.WebView) {
        console.log("onWebLoadedError");
        if (this.listener && this.listener.onFail) this.listener.onFail();
        this.closePage();
    }

    /**
     * 加载完成
     * @param evt 
     */
    private onWebLoaded(evt: cc.WebView) {
        console.log("onWebLoaded", evt);
        if (this.listener && this.listener.onScuccess) this.listener.onScuccess();
    }

    /**
     * 加载中
     * @param evt 
     */
    private onWebLoading(evt: cc.WebView) {
        console.log("onWebLoading");
        if (this.listener && this.listener.onloading) this.listener.onloading();
    }

    private initAPP() {
        if (!cc.sys.isNative) return;
        console.log("注册:", this.scheme);
        this.web.setJavascriptInterfaceScheme(this.scheme);
        this.web.setOnJSCallback((target: cc.WebView, url: string) => this.onJSCallback(target, url));
    }

    /**
     * 原生里接收到子包数据
     * @param target 
     * @param url 
     */
    private onJSCallback(target: cc.WebView, url: string) {
        console.log("setOnJSCallback.url:", url);
        let str = url.replace(`${this.scheme}://?msg=`, '');
        let param = JSON.parse(decodeURIComponent(str));
        console.log("param : ", JSON.stringify(param));
        ipc.handleIpcMessage(param);
    }
    /**
     * 传递数据
     * @param data 
     * @returns 
     */
    sendToWebMessage(data: { args?: any, type: string }) {
        let info = JSON.stringify({ webId: this.webId, ...data });
        console.log("send to web :", JSON.stringify(info));
        if (this.contentWindow) {
            console.log("h5 send to child");
            this.contentWindow.postMessage(info, "*");
        }
        else if (cc.sys.isNative) {
            console.log("app send to child");
            this.web.evaluateJS(`nativeInteractive(${info})`);
        }
    }

    closePage() {
        this.node.destroy();
    }
}

2、主包的接收逻辑

import UIWebView from "./UIWebView";

/**
 * 当主包是web时会使用
 * @param event 
 */
function receiveMessage(event) {

    console.log("receiveMessage", event);
    let data: { type: string; webId: string | number; args?: any } = event.data;
    ipc.handleIpcMessage(data);
}

window.addEventListener("message", receiveMessage, false);


/** 处理交互数据 */
export namespace ipc {

    /**
     * 处理web子包交互数据
     * @param data 
     */
    export function handleIpcMessage(data: { type: string; webId: string | number; args?: any }) {

        // @ts-ignore
        let components: UIWebView[] = cc.director.getScene().getComponentsInChildren(UIWebView);

        switch (data.type) {
            case "close": // 关闭页面
                {
                    let coms = components.filter(v => v.webId == data.webId);
                    coms.forEach(v => v.closePage());
                }
                break;

            case "webLoadSoucess": // webview加载成功
                {
                  
                }
                break;
        }

    }
}

3、子包的接收与发送

/*
 * 分包与主包交互使用 WebInteractive
 */

/**
 * 获取地址中的参数
 * @param paraName 
 * @returns 
 */
export function getQueryString(paraName: string) {
    let url = document.location.toString();
    let arrObj: string[] = url.split("?");

    if (arrObj.length > 1) {
        let arrPara = arrObj[1].split("&");
        let arr;

        for (let i = 0; i < arrPara.length; i++) {
            arr = arrPara[i].split("=");

            if (arr != null && arr[0] == paraName) {
                let res = arr[1];
                try {
                    res = decodeURIComponent(res);
                } catch (error) { }
                return res;
            }
        }
        return "";
    } else {
        return "";
    }
}


/**
 * 接收H5主包传递的数据
 * @param event 
 */
function receiveMessage(event) {

    console.log("child receiveMessage", event.data);
    let data: { type: string; webId: string | number; args?: any } = event.data;
    ipc.handleIpcMessage(data);
}

window.addEventListener("message", receiveMessage, false);

/**
 * 接收APP主包传递的数据
 * @param
 */
function nativeInteractive(data: { type: string; webId: string | number; args?: any }) {
    console.log("child nativeInteractive", JSON.stringify(data || {}));
    ipc.handleIpcMessage(data);
}

window['nativeInteractive'] = nativeInteractive;




/** 发送向主包的消息 */
export namespace ipc {
    /** 正常成功加载 */
    export function sendLoadSuccess() {
        sendMessageToApp({ type: "webLoadSoucess" });
    }

    /**
     * 当前页面关闭
    */
    export function closeWeb() {
        sendMessageToApp({ type: "close" });
    }
    /**
     * 发送消息到主包
     * @param data 
     */
    export function sendMessageToApp(data: { webId?: string | number; type: string; args?: any }) {

        if (!data.webId) data.webId = getQueryString("webId");
        // origin 来源,嵌入的类型 1安卓, 2ios, 3 H5
        let origin = getQueryString("origin");
        if (origin == 1 || origin == 2) {
            //创建消息序列化字符串
            let msg = "testscheme://?msg=" + encodeURIComponent(JSON.stringify(data));
            console.log("======msg=======", msg);
            let iframe = document.createElement("iframe");
            iframe.setAttribute("tag", "xlby");
            iframe.setAttribute("src", msg);
            iframe.setAttribute("style", "display:none;");
            iframe.setAttribute("height", "0px");
            iframe.setAttribute("width", "0px");
            iframe.setAttribute("frameborder", "0");
            document.body.appendChild(iframe);
            iframe.parentNode && iframe.parentNode.removeChild(iframe);
            iframe = null;

        } else {
            // "*" 标识任何窗口都能接受到,如严谨模式,建议使用主包固定ip或域名 例如 "http://172.16.111.7:7457"
            parent && parent.postMessage(data, "*");
        }
    }

    /**
     * 处理交互逻辑
     * @param data 
     */
    export function handleIpcMessage(data: { type: string; webId: string | number; args?: any }) {
        // TODO 待处理
    }

}

4、子包启动场景中根据路径获取意图

const { ccclass, property } = cc._decorator;
//去掉场景背景
cc.macro.ENABLE_TRANSPARENT_CANVAS = true;

@ccclass
export default class Hall extends cc.Component {

    onLoad() {
    	// 通知加载成功了,这是因为h5中监听不到子包内容的加载情况 ,主包中可以做加载超时的情况处理
        ipc.sendLoadSuccess();
    	 this.init();
    }

    /**
     * 页面加载
     */
    private init() {
        let model = getQueryString("model");
        console.log("----model---------", model);
        switch (model) {
            case "gNotice":
             	// TODO 意图实现
                break
        }
    }
}

以上就是关键代码。

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值