浏览器中内嵌一个浏览器

现在的ai确实越来越先进了,只是描述的了一下,就可以用单一个html完成初步的设计,而且果里果的气息,确实6,而且现在好多应用者不是原生开发的,它们的本质就是一个app内塞了个chromium浏览器,这样就可以写一次代码多平台运行了,像是我的世界教程版内嵌了一个完整的浏览器,用uni-app、react native、flutter框架等,之前是塞个浏览器,现在编译成了原生,webos代码如下:

html 复制代码
<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <title>WeOS - 浏览器中的操作系统</title>
  <style>
    * { margin: 0; padding: 0; box-sizing: border-box; }
    body {
      font-family: 'Segoe UI', sans-serif;
      height: 100vh;
      overflow: hidden;
      background: linear-gradient(135deg, #1e3c72, #2a5298);
      user-select: none;
    }

    /* 桌面 */
    #desktop {
      position: relative;
      width: 100%;
      height: calc(100% - 50px);
      background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==') repeat;
    }

    /* 任务栏 */
    #taskbar {
      position: fixed;
      bottom: 0;
      width: 100%;
      height: 50px;
      background: rgba(0, 0, 0, 0.8);
      backdrop-filter: blur(10px);
      display: flex;
      align-items: center;
      padding: 0 10px;
      color: white;
      z-index: 1000;
    }


    /* 暗黑模式标签栏 */
    #tab-list-* {
    font-family: 'Consolas', monospace;
    font-size: 13px;
    }

    /* 滚动条美化 */
    #tabs-container-*::-webkit-scrollbar {
    width: 6px;
    }
    #tabs-container-*::-webkit-scrollbar-track {
    background: #1e1e1e;
    }
    #tabs-container-*::-webkit-scrollbar-thumb {
    background: #444;
    border-radius: 3px;
    }

    #start-btn {
      width: 40px;
      height: 40px;
      background: #0078d4;
      border-radius: 8px;
      display: flex;
      align-items: center;
      justify-content: center;
      cursor: pointer;
      font-weight: bold;
    }

    /* 桌面图标 */
    .icon {
      position: absolute;
      width: 80px;
      text-align: center;
      color: white;
      cursor: pointer;
      padding: 10px;
      border-radius: 8px;
      transition: background 0.2s;
    }
    .icon:hover { background: rgba(255,255,255,0.2); }
    .icon img { width: 48px; height: 48px; }
    .icon span { display: block; margin-top: 5px; font-size: 12px; }

    /* 窗口 */
    .window {
      position: absolute;
      background: white;
      border-radius: 10px;
      box-shadow: 0 10px 30px rgba(0,0,0,0.3);
      overflow: hidden;
      resize: both;
      min-width: 300px;
      min-height: 200px;
      display: flex;
      flex-direction: column;
    }
    .window-header {
      background: #f0f0f0;
      padding: 8px 12px;
      display: flex;
      justify-content: space-between;
      align-items: center;
      cursor: move;
      font-weight: bold;
      border-bottom: 1px solid #ddd;
    }
    .window-controls button {
      width: 16px; height: 16px; margin-left: 6px;
      border: none; border-radius: 50%; cursor: pointer;
    }
    .close { background: #ff5f57; }
    .minimize { background: #ffbd2e; }
    .maximize { background: #28ca42; }

    /* 内嵌浏览器 */
    #browser-toolbar {
      display: flex;
      padding: 8px;
      background: #f8f8f8;
      border-bottom: 1px solid #ddd;
      gap: 8px;
      align-items: center;
    }
    #browser-toolbar input {
      flex: 1;
      padding: 6px 10px;
      border: 1px solid #ccc;
      border-radius: 4px;
      font-size: 14px;
    }
    #browser-toolbar button {
      padding: 6px 10px;
      background: #0078d4;
      color: white;
      border: none;
      border-radius: 4px;
      cursor: pointer;
    }
    #browser-frame {
      flex: 1;
      border: none;
      width: 100%;
      height: 100%;
    }

    /* 启动菜单 */
    #start-menu {
      position: fixed;
      bottom: 60px;
      left: 10px;
      width: 300px;
      background: rgba(255,255,255,0.95);
      backdrop-filter: blur(10px);
      border-radius: 10px;
      box-shadow: 0 10px 30px rgba(0,0,0,0.2);
      padding: 10px;
      display: none;
      z-index: 999;
    }
    .menu-item {
      padding: 12px;
      display: flex;
      align-items: center;
      gap: 12px;
      cursor: pointer;
      border-radius: 6px;
    }
    .menu-item:hover { background: #e0e0e0; }
    .menu-item img { width: 24px; height: 24px; }
  </style>
</head>
<body>

  <!-- 桌面 -->
  <div id="desktop">
    <!-- 桌面图标 -->
    <div class="icon" style="top:20px;left:20px;" onclick="openBrowser()">
      <img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI0OCIgaGVpZ2h0PSI0OCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJ3aGl0ZSI+PHBhdGggZD0iTTE5IDNINmMtMS4xIDAtMiAuOS0yIDJ2MTRjMCAxLjEuOSAyIDIgMmgxM2MxLjEgMCAyLS45IDItMlY1Yy0tLjktMS0xLjgtMi0yem0wIDE2SDZjLS4zIDAtLjUtLjItLjUtLjV2LTVjMC0uMy4yLS41LjUtLjVoMTNjLjMgMCAuNS4yLjUuNXY1Yy4uMy0uMi41LS41LjV6TTYgNmgxM3YySDZ6Ii8+PC9zdmc+" alt="浏览器"/>
      <span>浏览器</span>
    </div>
  </div>

  <!-- 任务栏 -->
  <div id="taskbar">
    <div id="start-btn" onclick="toggleStartMenu()">We</div>
  </div>

  <!-- 启动菜单 -->
  <div id="start-menu">
    <div class="menu-item" onclick="openBrowser()">
      <img src="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJibGFjayI+PHBhdGggZD0iTTE5IDNINmMtMS4xIDAtMiAuOS0yIDJ2MTRjMCAxLjEuOSAyIDIgMmgxM2MxLjEgMCAyLS45IDItMlY1Yy0tLjktMS0xLjgtMi0yem0wIDE2SDZjLS4zIDAtLjUtLjItLjUtLjV2LTVjMC0uMy4yLS41LjUtLjVoMTNjLjMgMCAuNS4yLjUuNXY1Yy4uMy0uMi41LS41LjV6TTYgNmgxM3YySDZ6Ii8+PC9zdmc+" alt=""/>
      <span>打开浏览器</span>
    </div>
  </div>

  <script>
    let windowCount = 0;
    let zIndex = 100;

// === 替换原来的 openBrowser() 函数 ===
function openBrowser() {
  windowCount++;
  const win = document.createElement('div');
  win.className = 'window';
  win.id = 'win-' + windowCount;
  win.style.width = '900px';
  win.style.height = '650px';
  win.style.left = `${100 + (windowCount % 5) * 30}px`;
  win.style.top = `${50 + (windowCount % 5) * 30}px`;
  win.style.zIndex = zIndex++;

  // 标签页数据结构
  const tabs = [
    { id: 'tab-1', title: 'Example', url: 'https://example.com' }
  ];
  let activeTabId = 'tab-1';
  let tabCounter = 1;

  win.innerHTML = `
    <div class="window-header">
      <span>浏览器预览 - WeOS</span>
      <div class="window-controls">
        <button class="minimize" onclick="minimizeWindow('${win.id}')">-</button>
        <button class="maximize" onclick="toggleMaximize('${win.id}')">□</button>
        <button class="close" onclick="closeWindow('${win.id}')">×</button>
      </div>
    </div>
    <div style="display:flex;height:calc(100% - 40px);">
      <!-- 左侧标签栏 -->
      <div id="tab-list-${win.id}" style="width:200px;background:#1e1e1e;color:#ccc;display:flex;flex-direction:column;">
        <div style="padding:8px;font-weight:bold;border-bottom:1px solid #333;display:flex;justify-content:space-between;align-items:center;">
          <span>标签页</span>
          <button onclick="addTab('${win.id}')" style="background:none;border:none;color:#0a0;cursor:pointer;font-size:18px;">+</button>
        </div>
        <div id="tabs-container-${win.id}" style="flex:1;overflow-y:auto;"></div>
      </div>
      <!-- 右侧预览区 -->
      <div style="flex:1;display:flex;flex-direction:column;background:#fff;">
        <div id="browser-toolbar-${win.id}" style="display:flex;padding:8px;background:#f0f0f0;border-bottom:1px solid #ddd;gap:8px;align-items:center;">
          <button onclick="navBack('${win.id}')">←</button>
          <button onclick="navForward('${win.id}')">→</button>
          <button onclick="reload('${win.id}')">↻</button>
          <input type="text" id="url-${win.id}" style="flex:1;padding:6px;border:1px solid #ccc;border-radius:4px;" placeholder="输入 URL 按 Enter 访问" />
          <button onclick="loadURL('${win.id}')">前往</button>
        </div>
        <iframe id="frame-${win.id}" src="https://example.com" sandbox="allow-same-origin allow-scripts allow-forms allow-modals allow-popups" style="flex:1;border:none;"></iframe>
      </div>
    </div>
  `;

  document.getElementById('desktop').appendChild(win);
  makeDraggable(win);

  // 初始化标签
  const container = win.querySelector(`#tabs-container-${win.id}`);
  function renderTabs() {
    container.innerHTML = '';
    tabs.forEach(tab => {
      const tabEl = document.createElement('div');
      tabEl.style.cssText = `
        padding:10px 12px;
        border-bottom:1px solid #333;
        display:flex;
        justify-content:space-between;
        align-items:center;
        cursor:pointer;
        background:${tab.id === activeTabId ? '#007acc' : 'transparent'};
        color:${tab.id === activeTabId ? 'white' : '#ccc'};
        transition:all 0.2s;
      `;
      tabEl.innerHTML = `
        <span style="flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">${tab.title}</span>
        <span onclick="event.stopPropagation(); closeTab('${win.id}', '${tab.id}')" style="margin-left:8px;font-size:14px;opacity:0.7;">×</span>
      `;
      tabEl.onclick = () => switchTab(win.id, tab.id);
      container.appendChild(tabEl);
    });
  }

  // 切换标签
  function switchTab(winId, tabId) {
    const tab = tabs.find(t => t.id === tabId);
    if (!tab) return;
    activeTabId = tabId;
    const iframe = document.getElementById(`frame-${winId}`);
    const urlInput = document.getElementById(`url-${winId}`);
    iframe.src = tab.url;
    urlInput.value = tab.url;
    renderTabs();
  }

  // 新建标签
  window.addTab = function(winId) {
    tabCounter++;
    const newTab = {
      id: `tab-${tabCounter}`,
      title: '新标签页',
      url: 'https://baidu.com'
    };
    tabs.push(newTab);
    switchTab(winId, newTab.id);
  };

  // 关闭标签
  window.closeTab = function(winId, tabId) {
    const idx = tabs.findIndex(t => t.id === tabId);
    if (idx === -1) return;
    tabs.splice(idx, 1);
    if (activeTabId === tabId && tabs.length > 0) {
      switchTab(winId, tabs[Math.max(0, idx - 1)].id);
    } else if (tabs.length === 0) {
      closeWindow(winId);
    } else {
      renderTabs();
    }
  };

  // 加载 URL
  window.loadURL = function(winId) {
    const input = document.getElementById(`url-${winId}`);
    let url = input.value.trim();
    if (!url) return;
    if (!/^https?:\/\//i.test(url)) url = 'https://' + url;

    const iframe = document.getElementById(`frame-${winId}`);
    iframe.src = url;

    // 更新当前标签
    const tab = tabs.find(t => t.id === activeTabId);
    if (tab) {
      tab.url = url;
      tab.title = new URL(url).hostname;
      renderTabs();
    }
    input.value = url;
  };

  // 导航
  window.navBack = (winId) => {
    const iframe = document.getElementById(`frame-${winId}`);
    if (iframe.contentWindow.history.length > 1) iframe.contentWindow.history.back();
  };
  window.navForward = (winId) => {
    const iframe = document.getElementById(`frame-${winId}`);
    iframe.contentWindow.history.forward();
  };
  window.reload = (winId) => {
    document.getElementById(`frame-${winId}`).contentWindow.location.reload();
  };

  // 监听 iframe 标题变化
  const iframe = win.querySelector(`#frame-${win.id}`);
  iframe.onload = () => {
    try {
      const title = iframe.contentDocument.title || '无标题';
      const tab = tabs.find(t => t.id === activeTabId);
      if (tab) {
        tab.title = title.substring(0, 20);
        renderTabs();
      }
    } catch (e) {}
  };

  // 键盘快捷键
  win.addEventListener('keydown', (e) => {
    if (e.ctrlKey || e.metaKey) {
      if (e.key === 't') { e.preventDefault(); addTab(win.id); }
      if (e.key === 'w') { e.preventDefault(); closeTab(win.id, activeTabId); }
    }
  });

  // 初始化
  renderTabs();
  win.focus();
}




    function loadURL(winId) {
      const input = document.getElementById('url-' + winId);
      let url = input.value.trim();
      if (!url) return;
      if (!url.startsWith('http://') && !url.startsWith('https://')) {
        url = 'https://' + url;
      }
      const iframe = document.getElementById('frame-' + winId);
      iframe.src = url;
      input.value = url;
    }

    function navBack(winId) {
      const iframe = document.getElementById('frame-' + winId);
      if (iframe.contentWindow.history.length > 1) {
        iframe.contentWindow.history.back();
      }
    }

    function navForward(winId) {
      const iframe = document.getElementById('frame-' + winId);
      iframe.contentWindow.history.forward();
    }

    function reload(winId) {
      document.getElementById('frame-' + winId).contentWindow.location.reload();
    }

    // 窗口操作
    function closeWindow(id) {
      const win = document.getElementById(id);
      if (win) win.remove();
    }

    function minimizeWindow(id) {
      const win = document.getElementById(id);
      win.style.display = win.style.display === 'none' ? 'flex' : 'none';
    }

    function toggleMaximize(id) {
      const win = document.getElementById(id);
      if (win.style.width === '100vw' && win.style.height === 'calc(100vh - 50px)') {
        win.style.width = '800px';
        win.style.height = '600px';
        win.style.left = '100px';
        win.style.top = '50px';
      } else {
        win.style.width = '100vw';
        win.style.height = 'calc(100vh - 50px)';
        win.style.left = '0';
        win.style.top = '0';
        win.style.borderRadius = '0';
      }
    }

    // 拖拽支持
    function makeDraggable(el) {
      const header = el.querySelector('.window-header');
      let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;

      header.onmousedown = (e) => {
        if (e.target.tagName === 'BUTTON') return;
        e.preventDefault();
        pos3 = e.clientX;
        pos4 = e.clientY;
        el.style.zIndex = ++zIndex;

        document.onmouseup = () => {
          document.onmouseup = null;
          document.onmousemove = null;
        };

        document.onmousemove = (e) => {
          pos1 = pos3 - e.clientX;
          pos2 = pos4 - e.clientY;
          pos3 = e.clientX;
          pos4 = e.clientY;
          el.style.top = (el.offsetTop - pos2) + "px";
          el.style.left = (el.offsetLeft - pos1) + "px";
        };
      };
    }

    // 启动菜单
    function toggleStartMenu() {
      const menu = document.getElementById('start-menu');
      menu.style.display = menu.style.display === 'block' ? 'none' : 'block';
    }

    // 点击桌面关闭菜单
    document.getElementById('desktop').onclick = (e) => {
      if (!e.target.closest('#start-btn') && !e.target.closest('#start-menu')) {
        document.getElementById('start-menu').style.display = 'none';
      }
    };
  </script>
</body>
</html>

结果如下:

用自托管虚拟浏览器n.eko,可以实现这目标。

相关推荐
Mapmost2 小时前
地图引擎性能优化:解决3DTiles加载痛点的六大核心策略
前端
San30.2 小时前
Ajax 数据请求:从 XMLHttpRequest 到现代前端数据交互的演进
前端·ajax·交互
西西西西胡萝卜鸡2 小时前
虚拟列表(Virtual List)组件实现与优化铁臂猿版(简易版)
前端·vue.js
宇余3 小时前
Unibest:新一代uni-app工程化最佳实践指南
前端·vue.js
*小雪3 小时前
uniapp写H5授权登录及分享,返回到目标页面
开发语言·javascript·uni-app
性野喜悲3 小时前
ts+uniapp小程序时间日期选择框(分开选择)
前端·javascript·vue.js
你不是我我3 小时前
【Java 开发日记】SQL 语句左连接右连接内连接如何使用,区别是什么?
java·javascript·数据库
一壶浊酒..4 小时前
请求签名(Request Signature)
javascript
P***25394 小时前
前端构建工具缓存清理,npm cache与yarn cache
前端·缓存·npm