Unity-使用3D WebView for Windows 加载网页

文章目录[x]
  1. 0.1:一、前言
  2. 0.2:二、功能介绍
  3. 0.3:三、系统要求
  4. 0.4:四、问题解决

一、前言

最近有个项目需要实现一个PC客户端,客户端主要功能是加载前端网页,通过客户端按钮实现网页的切换。使客户端内容和网页内容看起来是一个整体。我这里使用的开发工具是Unity2021.3,加载网页用的插件是3D WebView for Windows and macOS (Web Browser),版本4.2。地址:https://assetstore.unity.com/packages/tools/gui/3d-webview-for-windows-and-macos-web-browser-154144 。客户端运行环境是Windows系统,最开始的时候也考虑过使用WPF进行开发,但是我很长时间没有用WPF了,就选择了使用Unity开发了。

二、功能介绍

摘自插件在asset store页面上的概述。

1.加载网页

插件可以从URL或者本地的html文件中加载网页。webview.WebView.LoadUrl(url);

2.观看视频

插件默认可以看YouTube的视频,但是其他类型的网页视频基本看不了(除了ogg、webM等)。因为很多格式是需要H.264 License,所以插件默认不支持观看。但是插件提供了试用方法,打包后也是有效的。

3.方便使用

插件提供了几种预制体,可以拖拽到场景中快速上手使用。

4.屏幕键盘

插件提供了屏幕键盘,方便在触摸屏上使用。

5.由Chromium支持

6.可以与前端js交互

7.监听浏览器事件

例如TitleChanged、UrlChanged 以及 PageLoadFailed

8.查看PDF

9.支持世界坐标系和画布

10.包括额外的 Chromium 专用 API

11.支持透明页面

三、系统要求

• Windows 8+ (x64, x86) 以及 D3D11 显卡

• macOS 10.10+ (x64) 以及 Metal 显卡

• Unity 5.6+(由于 Unity 的一个程序漏洞,Windows 不支持 2017.3 - 2018.1 版本)

四、问题解决

官网上有很多实用的文章和教程,能够解决遇到的大部分问题。官方文档https://support.vuplex.com/search 其他零碎问题:输入法、中文输入、开启上传文件、开启下载文件、文件保存位置、光标状态、页面内拖拽或拖拽滑动页面、cookie的增删改查。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using UnityEngine;
using Vuplex.WebView;
using Vuplex.WebView.Demos;
using Debug = UnityEngine.Debug;

/// <summary>
/// 解决webview输入问题 没有这个脚本,不能正常输入
/// (解决中文输入是在CanvasWebViewPrefab.cs Input.imeCompositionMode = IMECompositionMode.On;)
/// 解决在Windows平台上传文件,打开选择文件对话框
/// </summary>
public class WebViewKeyBoardInput : MonoBehaviour
{
    private CanvasWebViewPrefab canvasWebView;
    HardwareKeyboardListener _hardwareKeyboardListener;

    public System.Action<ProgressChangedEventArgs> OnLoadProgressChanged;

    async void Start()
    {
        canvasWebView = transform.GetComponent<CanvasWebViewPrefab>();
        #region 输入框输入功能
        _setUpHardwareKeyboard(canvasWebView);
        #endregion
        await canvasWebView.WaitUntilInitialized();

        #region 开启上传功能
        var standaloneWebView = canvasWebView.WebView as StandaloneWebView;
        standaloneWebView.SetNativeFileDialogEnabled(true);
        #endregion

        #region 开启下载功能
        var webViewWithDownloads = canvasWebView.WebView as IWithDownloads;
        if (webViewWithDownloads == null)
        {
            Debug.Log("This 3D WebView plugin doesn't yet support IWithDownloads: " + canvasWebView.WebView.PluginType);
            return;
        }
        webViewWithDownloads.SetDownloadsEnabled(true);
        webViewWithDownloads.DownloadProgressChanged += (sender, eventArgs) => {
            Debug.Log($@"DownloadProgressChanged:
                    Type: {eventArgs.Type},
                    Url: {eventArgs.Url},
                    Progress: {eventArgs.Progress},
                    Id: {eventArgs.Id},
                    FilePath: {eventArgs.FilePath},
                    ContentType: {eventArgs.ContentType}"
            );
            if (eventArgs.Type == ProgressChangeType.Finished)
            {
                Debug.Log("Download finished");
                // 默认下载路径是 Application.temporaryCachePath(Appdata/Local/Temp/公司名/应用名)
                string[] filePath = eventArgs.FilePath.Split('\\');
                string fileName = filePath[filePath.Length - 1];
                //如果原来路径上存在该文件 则需要删除后在进行移动
                if(File.Exists(Application.dataPath + "/../Download/" + fileName))
                {
                    File.Delete(Application.dataPath + "/../Download/" + fileName);
                }
                //移动到指定的固定位置
                File.Move(eventArgs.FilePath, Application.dataPath + "/../Download/" + fileName);
                //打开下载文件的目录
                Application.OpenURL($"File://{Application.dataPath}/../Download/");

                //只能打开绝对路径
                //string newPath = Application.dataPath + "/../Download/" + fileName;
                //WindowsTools.ExplorerFile(newPath);
            }
        };
        #endregion

        #region file选择 新版3dwebview4.2支持自定义文件对话框(也有bug,多次选择没有效果)
        //var webViewWithFileSelection = canvasWebView.WebView as IWithFileSelection;
        //if (webViewWithFileSelection != null)
        //{
        //    webViewWithFileSelection.FileSelectionRequested += (sender, eventArgs) =>
        //    {
        //        // Note: Here's where the application could use a system API or third party
        //        //       asset to show a file selection UI and then pass the selected file(s) to
        //        //       the Continue() callback.
        //        var filePaths = new string[] { OpenWindowDialog() };
        //        eventArgs.Continue(filePaths);
        //    };
        //}

        #endregion

        #region 页面加载失败回调
        canvasWebView.WebView.PageLoadFailed += WebView_PageLoadFailed;
        #endregion

        #region 加载进度 新版插件才支持
        canvasWebView.WebView.LoadProgressChanged += (sender, args) =>
        {
            if(OnLoadProgressChanged!=null)
            {
                OnLoadProgressChanged.Invoke(args);
            }
        };
        #endregion

        #region 光标状态 新版插件才支持

        #endregion
    }

    /// <summary>
    /// 页面加载失败回调  失败后显示提示,同时让浏览器加载空页面防止出现404界面
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    private void WebView_PageLoadFailed(object sender, System.EventArgs e)
    {
        WindowsTools.MessageBox(System.IntPtr.Zero, "数据加载失败,请检查是否可以连接到服务器!", "错误", 0);
        canvasWebView.WebView.LoadUrl("about:blank");
    }

    void _setUpHardwareKeyboard(CanvasWebViewPrefab webView)
    {

        _hardwareKeyboardListener = HardwareKeyboardListener.Instantiate();
        _hardwareKeyboardListener.KeyDownReceived += (sender, eventArgs) => {
            var webViewWithKeyDown = webView.WebView as IWithKeyDownAndUp;
            if (webViewWithKeyDown != null)
            {
                webViewWithKeyDown.KeyDown(eventArgs.Value, eventArgs.Modifiers);
            }
            else
            {
                webView.WebView.SendKey(eventArgs.Value);
            }
        };
        _hardwareKeyboardListener.KeyUpReceived += (sender, eventArgs) => {
            var webViewWithKeyUp = webView.WebView as IWithKeyDownAndUp;
            webViewWithKeyUp?.KeyUp(eventArgs.Value, eventArgs.Modifiers);
        };
    }

    

    float timer = 0;
    private void Update()
    {
        //让弹窗保持在最前端 因为不知道什么时候才有弹窗,所以循环查找
        timer += Time.deltaTime;
        if(timer >= 0.5f)
        {
            SetFileDialogFor();
            timer = 0;
        }
    }

    private void SetFileDialogFor()
    {
        IntPtr hWnd =WindowsTools.FindWindow(null,"Open Files");
        if (hWnd == IntPtr.Zero)
            hWnd = WindowsTools.FindWindow(null, "Open File");
        if(hWnd != IntPtr.Zero)
            WindowsTools.SetForegroundWindow(hWnd);
    }
}

cookie操作

//保存cookie
private async void SetWebCookie(string cookieValue)
{
    if (Web.CookieManager == null)
    {
        Debug.Log("Web.CookieManager isn't supported on this platform.");
        return;
    }
    string[] urlSplit = loginUrl.Split(':');

    var success = await Web.CookieManager.SetCookie(new Cookie
    {
        Domain = urlSplit[1].Replace("//", ""),
        Path = "/",
        Name = "Admin-Token",
        Value = cookieValue,
        //到期时间7天
        ExpirationDate = (int)DateTimeOffset.Now.ToUnixTimeSeconds() + 60 * 60 * 24* 7,
        HttpOnly = false,
        Secure = false
    });
    Debug.Log($"保存cookie {success}!");
}

//获取cookie
private async void GetCookie()
{
    if (Web.CookieManager == null)
    {
        Debug.Log("Web.CookieManager isn't supported on this platform.");
        return;
    }
    var cookies = await Web.CookieManager.GetCookies("http://192.168.32.227");
    if (cookies.Length > 0)
    {
        Debug.Log("Cookie: " + cookies[0]);
    }
    else
    {
        Debug.Log("Cookie not found.");
    }
}

 

点赞
  1. 哒哒说道:

    我想知道,使用3D webview的时候能否达到这样的需求。
    我有一个可以全屏幕拖动的悬浮框层级在webview之上,点击这个悬浮框可以弹出对话框提示用户是否确认关闭webview。
    需求是这个悬浮框不会影响webview的渲染以及点击事件,对话框层级同样是在webview上面,在对话框出现时无法穿透点击webview。
    期待您的回复

    1. Alan Alan说道:

      应该是能实现的,当你使用CanvasWebViewPrefab作为渲染网页的载体,它就相当于UGUI中的挂Image的物体。

  2. 小熊说道:

    你好 我用这个插件单独运行时没问题,但是放进项目中就会卡,延迟也很大,请问应怎么解决呢

    1. Alan Alan说道:

      提供的信息太少了,没办法判断是插件的原因,还是你项目中其它功能的原因。

发表回复

昵称和uid可以选填一个,填邮箱必填(留言回复后将会发邮件给你)
tips:输入uid可以快速获得你的昵称和头像(已失效)