使用 Three.js 与 CSS3DRenderer 在 Vue3 中加载网页为 3D 模型

21 阅读3分钟

在 Web 开发中,Three.js 是一个非常强大的 3D 渲染库,而 CSS3DRenderer 则可以将 HTML 元素(如 iframe)渲染到 3D 场景中,这使得我们可以将普通网页嵌入并展示在三维空间中,赋予 Web 页面更酷的交互形式。

本文将带你一步一步在 Vue3 项目中使用 Three.js + CSS3DRenderer 实现一个可交互、可缩放旋转的 3D 网页嵌入效果。

项目介绍

我们的目标是:

  • 使用 Three.js 创建一个三维场景;
  • 使用 CSS3DRenderer 将网页(iframe)加载为 3D 对象;
  • 使用 OrbitControls 实现鼠标交互控制;
  • 所有内容封装在 Vue3 <script setup> 中运行。

核心技术栈

  • Vue 3
  • Three.js
  • three/examples/jsm/renderers/CSS3DRenderer.js
  • three/examples/jsm/controls/OrbitControls.js

代码详解

1. HTML 模板结构

<template>
  <div class="box" ref="box"></div>
</template>

我们在页面中只保留一个容器 box,所有 3D 内容都将渲染到这个容器中。

2. 初始化场景核心逻辑

下面是初始化场景的完整逻辑:

const initScene = () => new THREE.Scene();
const initCamera = (boxElement) => {
  const camera = new THREE.PerspectiveCamera(75, boxElement.clientWidth / boxElement.clientHeight, 1, 6000);
  camera.position.z = 3000;
  camera.lookAt(0, 0, 0);
  return camera;
};
const initRenderer = (boxElement) => {
  const renderer = new THREE.WebGLRenderer({ antialias: true });
  renderer.setClearColor(0xffffff); // 背景设置为白色
  renderer.setSize(boxElement.clientWidth, boxElement.clientHeight);
  boxElement.appendChild(renderer.domElement);
  return renderer;
};

3. CSS3D 渲染器初始化

CSS3DRenderer 会创建一个可以渲染 DOM 元素的 3D 渲染器。通过它我们可以将 iframe 包装成 DOM 元素再变成 CSS3DObject 加入到场景中。

const initLabelRenderer = (boxElement) => {
  const labelRenderer = new CSS3DRenderer();
  labelRenderer.setSize(window.innerWidth, window.innerHeight);
  labelRenderer.domElement.style.position = 'absolute';
  labelRenderer.domElement.style.top = '0';
  boxElement.appendChild(labelRenderer.domElement);
  return labelRenderer;
};

4. 嵌入网页模型

const initModel = (scene, w, h, url) => {
  const domEle = document.createElement('div');
  domEle.innerHTML = `
    <div style="width:${w}px; height:${h}px;">
      <iframe src="${url}" width="${w}" height="${h}" frameborder="0"></iframe>
    </div>
  `;
  const domEleObj = new CSS3DObject(domEle);
  domEleObj.position.set(0, 0, 0);
  scene.add(domEleObj);
};

你可以把任意网页以 iframe 的形式加载进来,并作为 3D 模型对象在场景中展示。


5. 渲染逻辑与交互控制

创建完所有元素后,我们通过 OrbitControls 实现摄像机的缩放/旋转/平移交互:

const controls = new OrbitControls(camera, labelRenderer.domElement);
controls.update();

然后使用渲染函数刷新场景:

const render = (renderer, scene, camera, labelRenderer) => {
  renderer.render(scene, camera);
  labelRenderer.render(scene, camera);
};

const animate = () => {
  requestAnimationFrame(animate);
  render(renderer, scene, camera, labelRenderer);
};

最后在 onMounted 中初始化所有内容:

onMounted(() => {
  if (!box.value) return;

  const scene = initScene();
  const labelRenderer = initLabelRenderer(box.value);
  const camera = initCamera(box.value);
  const renderer = initRenderer(box.value);

  const controls = new OrbitControls(camera, labelRenderer.domElement);
  controls.update();

  initModel(scene, 1920, 1080, 'https://www.baidu.com/');

  animate();
});

完整代码

<template>
  <div class="box" ref="box"></div>
</template>

<script setup>
import { ref, onMounted } from 'vue';
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'; // 引入 OrbitControls
import { CSS3DRenderer, CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer.js';

const box = ref(null);

/**
 * 初始化场景
 */
const initScene = () => {
  const scene = new THREE.Scene();
  return scene;
};

/**
 * 初始化相机
 * @param {*} boxElement 盒子元素
 */
const initCamera = boxElement => {
  const camera = new THREE.PerspectiveCamera(
    75,
    boxElement.clientWidth / boxElement.clientHeight,
    1,
    6000
  );
  camera.lookAt(0, 0, 0);
  camera.position.z = 3000;
  // camera.position.x = 20;
  // camera.position.y = 20;
  return camera;
};

/**
 * 初始化渲染器
 * @param {*} boxElement 盒子元素
 */
const initRenderer = boxElement => {
  const renderer = new THREE.WebGLRenderer({ antialias: true }); // 设置抗锯齿
  renderer.setClearColor(0xffffff); // 将背景颜色设置为白色
  renderer.setSize(boxElement.clientWidth, boxElement.clientHeight);
  boxElement.appendChild(renderer.domElement);
  return renderer;
};

/**
 * 构建CSS 3D渲染器
 * @param {*} boxElement 盒子元素
 */
const initLabelRenderer = boxElement => {
  const labelRenderer = new CSS3DRenderer();
  labelRenderer.setSize(window.innerWidth, window.innerHeight);
  labelRenderer.domElement.style.position = 'absolute';
  labelRenderer.domElement.style.top = 0;
  boxElement.appendChild(labelRenderer.domElement);
  return labelRenderer;
};

/**
 * 初始化模型
 * @param {*} s 场景 必传
 * @param {*} w 宽
 * @param {*} h 高
 * @param {*} url iframe链接地址
 */
const initModel = (s, w, h, url) => {
  const domEle = document.createElement('div');
  const html = `
  <div style="width:${w}px; height:${h}px;">
    <iframe src="${url}" width="${w}" height="${h}" frameborder=0></iframe>
  </div>
  `;
  domEle.innerHTML = html;
  const domEleObj = new CSS3DObject(domEle);
  domEleObj.position.set(0, 0, 0);
  s.add(domEleObj);
};

// 渲染场景于css3D渲染器
const render = (renderer, scene, camera, labelRenderer) => {
  renderer.render(scene, camera);
  labelRenderer.render(scene, camera);
};

onMounted(() => {
  // 确保 box 元素存在
  if (!box.value) return;

  // 初始化场景
  const scene = initScene();

  // 初始化CSS 3D渲染器
  const labelRenderer = initLabelRenderer(box.value);

  // 初始化相机
  const camera = initCamera(box.value);

  // 初始化渲染器
  const renderer = initRenderer(box.value);

  // 创建 OrbitControls 控制器
  const controls = new OrbitControls(camera, labelRenderer.domElement);
  controls.update(); // 更新控制器以确保有效初始状态

  // 初始化模型
  initModel(scene, 1920, 1080, 'https://www.baidu.com/');

  // 定义动画函数
  const animate = () => {
    requestAnimationFrame(animate); // 请求下一帧动画
    render(renderer, scene, camera, labelRenderer); // 渲染场景
  };

  animate(); // 开始动画循环
});
</script>

<style scoped>
.box {
  width: 100vw;
  height: 100vh;
}

.domBox {
  background-color: aqua;
}
</style>

最终效果

你可以通过鼠标拖动、缩放、旋转视角,从三维空间中观察你加载的网页内容。这种方式非常适用于以下场景:

  • 可视化多个网页窗口或大屏展示;
  • 将网页内容融入 3D 虚拟世界;
  • 创建沉浸式交互体验。

小结

通过结合 Vue3 与 Three.js 的 CSS3D 渲染器,我们实现了一个非常炫酷的 3D 网页展示方式。你可以进一步扩展此项目,支持拖拽网页、动态加载内容、添加多个 iframe 组合成 3D 空间 UI 等。


拓展方向

  • 动态切换 iframe 页面;
  • 支持多 iframe 多视角布局;
  • 支持鼠标拖动模型位置;
  • 与 WebXR 或 WebGPU 结合实现更高级别的互动。