当前位置: 首页 > news >正文

Three.js 3D饼图教程

3D饼图 ·3D Pie· ▶ 在线运行案例

  • 案例合集:三维可视化功能案例(threehub.cn)
  • 开源仓库github地址:https://github.com/z2586300277/three-cesium-examples
  • 400个案例代码:网盘链接

你将学到什么

  • OrbitControls 相机轨道交互
  • requestAnimationFrame渲染循环与resize自适应

效果说明

本案例演示3D饼图效果:基于 WebGL 实现「3D饼图」可视化效果,附完整可运行源码;核心用到 OrbitControls。建议先打开文首在线案例查看动态画面,再对照下方源码逐步理解。

核心概念

  • OrbitControls轨道旋转缩放;开enableDamping时每帧需controls.update()

实现步骤

  • 搭建 Scene / Camera / Renderer 与 OrbitControls
  • rAF 循环中 update 并 render
  • 代码要点

    import * as THREE from "three";

    import { OrbitControls } from "three/addons/controls/OrbitControls.js"; import { FontLoader } from 'three/examples/jsm/loaders/FontLoader.js' import { TextGeometry } from 'three/examples/jsm/geometries/TextGeometry.js'

    // 创建渲染器 var renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true, logarithmicDepthBuffer: true }); // 设置canvas画布大小为窗口 renderer.setSize(window.innerWidth, window.innerHeight); renderer.setPixelRatio(window.devicePixelRatio * 2); // 设置像素比 document.body.appendChild(renderer.domElement); // canvas画布插入dom树

    // 创建场景 var scene = new THREE.Scene();

    // 辅助线 var axisHelper = new THREE.AxesHelper(500); scene.add(axisHelper);

    // 添加点光源 let light1 = new THREE.PointLight("#fff", 3, 0, 0); light1.position.set(0, 1160, 2160); scene.add(light1);

    //环境光 let ambient = new THREE.AmbientLight("#fff", 0.6); scene.add(ambient);

    // 创建相机 var camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.1, 4000); camera.position.set(70, 230, 230); // 设置相机位置

    // 创建控制器 let controls = new OrbitControls(camera, renderer.domElement);

    // 渲染 !(function render() { controls.update(); // Update controls renderer.render(scene, camera); requestAnimationFrame(render); })();

    // 字体加载器 const fontUrl = FILE_HOST + 'files/json/font.json' new FontLoader().load(fontUrl, function (font) { const group = new THREE.Group(); group.rotateX(-(Math.PI / 180) * 90); scene.add(group);

    const outR = 100; // 外半径 const innerR = 60; // 内半径 const startAngle = 45; // 起始位置

    const h1 = 100; // 高度 const color1 = 0xe20f9f; // 颜色 let angleLength1 = 160; // 长度

    const h2 = 70; // 高度 const color2 = 0xffa500; // 颜色

    // 创建图块 function createPieBlock(outR, innerR, h, startAngle, angleLength, color, rateText) { // 形状 const shape = new THREE.Shape(); shape.absarc(0, 0, outR, (Math.PI / 180)startAngle, (Math.PI / 180)(startAngle + angleLength)); shape.lineTo(shape.currentPoint.x(innerR / outR), shape.currentPoint.y(innerR / outR)); shape.absarc(0, 0, innerR, (Math.PI / 180)(startAngle + angleLength), (Math.PI / 180)startAngle, true);

    // 冲压几何体配置 const extrudeSettings = { curveSegments: 100, steps: 2, depth: h, bevelEnabled: true, bevelThickness: 1, bevelSize: 0, bevelOffset: 0, bevelSegments: 1, };

    // 创建几何体、材质、物体 const geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings); const material = new THREE.MeshLambertMaterial({ color: color, //side: THREE.DoubleSide, /* opacity: 0.7, transparent: true, depthWrite: false, */ }); const mesh = new THREE.Mesh(geometry, material); group.add(mesh);

    // 创建文字 const textGeometry = new TextGeometry(rateText, { font: font, size: 18, depth: 5.5, }); const textMaterial = new THREE.MeshPhongMaterial({ color: color }); const text = new THREE.Mesh(textGeometry, textMaterial);

    // 旋转 text.rotateX((Math.PI / 180) * 90); //text.rotateY((Math.PI / 180) * (startAngle + angleLength / 2 - 90)); text.updateMatrix(); // 更新矩阵

    // 包围盒 textGeometry.computeBoundingBox(); const { max, min } = textGeometry.boundingBox; // 包围盒中心 const textCenter = new THREE.Vector3((max.x - min.x) / 2, (max.y - min.y) / 2, (max.z - min.z) / 2); textCenter.applyMatrix4(text.matrix.clone()); // 目标位置 const targetPostion = new THREE.Vector3( Math.cos((Math.PI / 180)(startAngle + angleLength / 2))(innerR + (outR - innerR) / 2), Math.sin((Math.PI / 180)(startAngle + angleLength / 2))(innerR + (outR - innerR) / 2), h + 30 ); // 移动 text.position.add(targetPostion.sub(textCenter)); mesh.add(text); }

    !(function h() { if (angleLength1 >= 230) return (controls.autoRotate = true); setTimeout(() => { h(); }, 50); const arr = [...group.children]; arr.forEach((obj) => { group.remove(obj); });

    angleLength1 += 1;

    // 创建图表 createPieBlock(outR, innerR, h1, startAngle, angleLength1, color1,${Math.floor((angleLength1 / 360) * 100)}%); createPieBlock(outR, innerR, h2, startAngle + angleLength1, 360 - angleLength1, color2,${100 - Math.floor((angleLength1 / 360) * 100)}%); })(); });

    完整源码:GitHub

    小结

    • 本文提供3D饼图完整 Three.js 源码与在线 Demo,建议先运行案例再改 uniform/参数做二次实验
    • 更多 Three.js 实战案例见 three-cesium-examples 合集 与 GitHub 开源仓库
http://www.jsqmd.com/news/1097090/

相关文章:

  • 电池回收真的还能闭环吗? - 蓝色星球
  • 小孔洞,大未来
  • 如何使用DevStore?3分钟完成OpenEuler开发工具一键部署
  • GPT-SoVITS声音克隆实战:如何用5秒音频让AI学会你的语气和语速?
  • Ark布局
  • 告别Anaconda臃肿!8G内存老电脑也能流畅跑Python,手把手教你安装Miniconda3-py37_4.9.2
  • 技术专利的申请策略与知识产权保护
  • PCIe 6.0实战笔记:Shared Flow Control里的Optimized FC到底怎么用?
  • 告别命令行恐惧:用WinSCP和FileZilla在Windows上轻松管理远程服务器文件
  • 终极解决方案:3分钟在Windows系统轻松安装安卓APK应用
  • GoldHEN Cheats Manager:如何在PS4上实现专业级游戏修改
  • CVE-2026-7261实战教程:PHP SoapServer释放后重用漏洞检测、利用与完整修复配置清单
  • 批量推理(Batch Inference)的吞吐量极限:调度策略与显存管理深度剖析
  • 从模型到部署:OpenVINO™量化实战,解锁YOLOv8的千帧性能
  • AI建站工具怎么选?一份详细的选型标准与对比指南
  • 保姆级教程:用Sylvain Calinon的PbDlib库,5分钟搞定机器人模仿学习Demo
  • STM32CubeIDE 1.19.0版本 创建工程
  • [智能体-603]:OpenClaw与飞书互通是什么意思?分别从功能和技术两个角度阐述,通俗易通
  • 别再只配团体名了!中兴5960X交换机SNMPv3安全配置实战(含Trap告警)
  • AI率爆表怎么办?10款降AIGC工具实测(含免费降ai率工具)真实避坑指南
  • 保姆级教程:在Ubuntu 20.04上用YOLOv5s训练自己的人脸检测模型(附数据集)
  • PIC 单片机不同串口间不同波特率的转换及应用电路
  • Sunny网络中间件:从抓包到二次开发,构建跨平台网络分析利器
  • PD 芯片:转接器边充边传的技术,手机快充并进行数据传输
  • 现在爆火的VibeCoding是什么?和AICoding有什么区别
  • Python异步编程asyncio完全指南:从原理到实战,彻底掌握高并发
  • Windows系统文件ActiveSyncProvider.dll丢失找不到问题解决
  • 【架构实战】分布式事务最终一致性:从理论到工程实践
  • FanControl终极指南:Windows风扇控制软件完全配置与优化教程
  • 生命周期长的集合