软件教程 2025年08月6日
0 收藏 0 点赞 214 浏览 5322 个字
摘要 :

文章目录 一、借助CSS3DRenderer和CSS3DObject实现融合 二、代码实现过程 (一)封装相关代码 (二)初始化场景和渲染器 (三)创建echarts图表 (四)实现效果:创建……




  • 一、借助CSS3DRenderer和CSS3DObject实现融合
  • 二、代码实现过程
    • (一)封装相关代码
    • (二)初始化场景和渲染器
    • (三)创建echarts图表
    • (四)实现效果:创建饼图

    如果你开发过可视化大屏项目,比如三维数字孪生、智慧城市看板等项目中,经常会碰到一个棘手的问题:怎么在three.js构建的三维场景里,动态展示echarts图表呢?今天,就来和大家详细讲讲,如何利用three.js和ECharts技术的融合,通过自定义拖拽的方式,在three.js三维场景中加载不同的echarts图表组件

    一、借助CSS3DRenderer和CSS3DObject实现融合

    three.js提供了一个很实用的API——CSS3DRenderer,它能把DOM元素渲染到3D场景里。不过,使用的时候有些地方得注意:

    • CSS3DRenderer渲染的内容,没办法像3D模型材质那样进行导入导出操作。
    • 它只支持基础的3D变换,像位移、旋转、缩放这些,像复杂光照、阴影、自定义材质、粒子系统这些高级效果就实现不了。
    • 原生支持DOM元素,也就是说,可以直接把HTML、CSS元素,像div、svg,还有ECharts画布当作3D对象来渲染。

    二、代码实现过程

    (一)封装相关代码

    为了让代码结构更清晰,我们把渲染和创建echarts模块的代码,用class类函数封装成css3DRendererModules 。具体代码如下:

    export default class css3DRendererModules {
      css3DRenderer: CSS3DRenderer | null;
      css3DControls: OrbitControls | null;
      raycaster = new THREE.Raycaster();
      mouse = new THREE.Vector2();
      scene: THREE.Scene | null;
      camera: THREE.PerspectiveCamera | null;
      renderer: THREE.WebGLRenderer | null;
      container: HTMLElement | null;
      constructor() {
        this.css3DRenderer = null;
        this.css3DControls = null;
        this.raycaster = new THREE.Raycaster();
        this.mouse = new THREE.Vector2();
        this.scene = null;
        this.camera = null
        this.renderer = null
        this.container = document.querySelector(\'#echarts\');
      }
    }
    

    在这段代码里,定义了一些属性,用于存放渲染器、控制器、场景、相机等对象,constructor构造函数里对这些属性进行了初始化,并获取了页面上id为echarts的DOM元素。

    (二)初始化场景和渲染器

    接下来,在init方法里创建场景、相机、渲染器,并且让CSS3DRenderer渲染器和WebGLRenderer渲染器的位置重叠,同时给相关元素添加pointerEvents属性,避免影响WebGLRenderer渲染器中场景的交互功能。代码如下:

    init() {
      // 创建场景
      this.scene = new THREE.Scene();
      const rgbeLoader = new RGBELoader();
      const texture = await rgbeLoader.loadAsync(\'hdr/view-hdr-11.hdr\');
      texture.mapping = THREE.EquirectangularReflectionMapping;
      // 创建相机
      const { offsetWidth, offsetHeight } = this.container;
      const aspectRatio = offsetWidth / offsetHeight;
      this.camera = new THREE.PerspectiveCamera(45, aspectRatio, 1, 20000);
      this.camera.position.set(0, 2, 6);
      this.camera.name = \'Camera\';
      this.camera.updateProjectionMatrix();
      // 创建渲染器
      this.renderer = new THREE.WebGLRenderer({
        antialias: true, // 开启硬件抗锯齿
        alpha: true,
        preserveDrawingBuffer: true,
        powerPreference: \'high-performance\', // 优先使用高性能GPU
      });
      this.renderer.setClearColor(0xcccccc);
      this.renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2)); // 限制最大像素比为2
      // 创建css3d渲染器
      this.css3DRenderer = new CSS3DRenderer();
      this.css3DRenderer.setSize(offsetWidth, offsetHeight);
      this.css3DRenderer.domElement.style.position = \'absolute\';
      this.css3DRenderer.domElement.style.pointerEvents = \'none\';
      this.css3DRenderer.domElement.style.top = \'0\';
      this.css3DRenderer.domElement.style.zIndex = \'0\';
      this.css3DControls = new OrbitControls(
        this.camera,
        this.css3DRenderer.domElement
      );
    }
    

    这段代码依次完成了场景、相机、WebGLRenderer渲染器和CSS3DRenderer渲染器的创建,还设置了相机的位置、渲染器的一些属性,并且初始化了用于控制相机视角的OrbitControls

    (三)创建echarts图表

    下面的createEcharts方法,用来动态创建DOM元素内容,通过Raycaster射线检测和THREE.Vector2()方法获取鼠标在三维场景中的相对位置,根据传入的echarts图表参数信息设置图表数据和类型,再把元素节点转换为three.js可渲染的内容,添加到场景中。代码如下:

    /**
     * 创建echarts
     * @param options - 选项
     */
    createEcharts(options: unknown) {
      const { modelData, clientY, clientX } = options as EchartsType;
        
      if (!this?.container || !this?.camera || !this.scene?.children)
        return;
    
      const rect = this?.container.getBoundingClientRect();
      this.mouse.x = ((clientX - rect.left) / rect.width) * 2 - 1;
      this.mouse.y = -((clientY - rect.top) / rect.height) * 2 + 1;
      this.raycaster.setFromCamera(this.mouse, this?.camera);
    
      const intersects = this.raycaster
      .intersectObjects(this.scene?.children, true)
      .slice(0, 1);
    
      if (intersects.length == 0) return;
    
      const element = document.createElement(\'div\');
    
      const tagsMode = createApp({
        mounted() {
          const chartDom = this.$refs.chart as HTMLElement;
          const myChart = echarts.init(chartDom);
          myChart.setOption(modelData?.options);
        },
        render() {
          return (
            <div
              ref=\"chart\"
              id=\"echarts\"
              style={{
                width: `${modelData.width}px`,
                height: `${modelData.height}px`,
                backgroundColor: \'rgba(0,0,0,0.6)\',
                borderRadius: \'4px\',
                pointerEvents: \'auto\',
              }}
            ></div>
          );
        },
      });
    
      const vNode = tagsMode.mount(document.createElement(\'div\'));
      element.appendChild(vNode.$el);
      const cssObject = new CSS3DObject(element);
      cssObject.position.set(0, 1.5, 0);
      cssObject.scale.set(0.004, 0.004, 0.004);
      const boxGeometry = new THREE.BoxGeometry(3, 0.5, 0.5);
      const boxMaterial = new THREE.MeshBasicMaterial({
        color: 0x00ff00,
        wireframe: true,
        visible: false,
      });
      const helperBox = new THREE.Mesh(boxGeometry, boxMaterial);
      helperBox.add(cssObject);
      helperBox.userData = {
        isTransformControls: true,
        ...options,
      };
      const { x, y, z } = intersects[0].point;
      helperBox.position.set(x, y, z);
      helperBox.name = modelData.name;
      this.scene?.add(helperBox);
    }
    

    这里使用了jsx语法,要是想正常使用,得安装@vitejs/plugin-vue-jsx插件,并且在vite.config.ts里添加jsx相关配置vueJsx() 。具体配置代码如下:

    import { defineConfig, loadEnv } from \'vite\';
    import vue from \'@vitejs/plugin-vue\';
    import vueJsx from \'@vitejs/plugin-vue-jsx\';
    export default defineConfig((mode) => {
      return {
        plugins: [vue(),  vueJsx()],
        resolve: {
          alias: {
            \'@\': resolve(__dirname, \'src\'),
            vue: \'vue/dist/vue.esm-bundler.js\',
          },
        },
      };
    });
    

    (四)实现效果:创建饼图

    下面这段代码展示了如何创建一个饼图:

    const css3DRendererModules = new css3DRendererModules();
    css3DRendererModules.init()
    const config =  {
      options: {
        title: {
          text: \'今日访客\',
          left: \'center\',
          textStyle: {
            color: \'#fff\',
          },
        },
        tooltip: {
          trigger: \'item\',
        },
        legend: {
          orient: \'vertical\',
          left: \'left\',
          textStyle: {
            color: \'#fff\',
          },
        },
        series: [
          {
            name: \'今日访客\',
            type: \'pie\',
            radius: \'50%\',
            data: [
              { value: 1048, name: \'北京\' },
              { value: 735, name: \'上海\' },
              { value: 580, name: \'广州\' },
              { value: 484, name: \'深圳\' },
              { value: 300, name: \'成都\' },
            ],
            emphasis: {
              itemStyle: {
                shadowBlur: 10,
                shadowOffsetX: 0,
                shadowColor: \'rgba(0, 0, 0, 0.5)\',
                color: \'#fff\',
              },
            },
          },
        ],
      },
      height: 500,
      width: 850,
      type: \'pie\',
      name: \'饼图\',
    }
      
    css3DRendererModules.createEcharts(config);
    

    如何在three.js三维场景里添加echarts图表组件通过上述步骤,一个在three.js三维场景中动态创建echarts图表的功能就实现啦。

微信扫一扫

支付宝扫一扫

版权: 转载请注明出处:https://www.zuozi.net/6988.html

管理员

相关推荐
2025-08-06

文章目录 一、Promise基础回顾 二、Promise 与 axios 结合使用场景及方法 (一)直接返回 axios …

269
2025-08-06

文章目录 一、模块初始化时的内部机制 二、常见导出写法的差异分析 (一)写法一:module.exports…

107
2025-08-06

文章目录 一、ResizeObserver详解 (一)ResizeObserver是什么 (二)ResizeObserver的基本用法 …

683
2025-08-06

文章目录 一、前期准备工作 (一)下载相关文件 (二)安装必要工具 二、处理扣子空间生成的文件…

338
2025-08-06

文章目录 一、官方文档 二、自动解包的数据类型 ref对象:无需.value即可访问 reactive对象:保持…

371
2025-08-06

文章目录 一、Hooks的工作原理 二、在if语句中使用Hook会出什么岔子? 三、React官方的Hook使用规…

843
发表评论
暂无评论

还没有评论呢,快来抢沙发~

助力内容变现

将您的收入提升到一个新的水平

点击联系客服

在线时间:08:00-23:00

客服QQ

122325244

客服电话

400-888-8888

客服邮箱

122325244@qq.com

扫描二维码

关注微信客服号