当前位置: 首页 > 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.gsyq.cn/news/1609054.html

相关文章:

  • 电池回收真的还能闭环吗? - 蓝色星球
  • 如何使用DevStore?3分钟完成OpenEuler开发工具一键部署
  • 告别命令行恐惧:用WinSCP和FileZilla在Windows上轻松管理远程服务器文件
  • GoldHEN Cheats Manager:如何在PS4上实现专业级游戏修改
  • CVE-2026-7261实战教程:PHP SoapServer释放后重用漏洞检测、利用与完整修复配置清单
  • 从模型到部署:OpenVINO™量化实战,解锁YOLOv8的千帧性能
  • STM32CubeIDE 1.19.0版本 创建工程
  • AI率爆表怎么办?10款降AIGC工具实测(含免费降ai率工具)真实避坑指南
  • 保姆级教程:在Ubuntu 20.04上用YOLOv5s训练自己的人脸检测模型(附数据集)
  • 现在爆火的VibeCoding是什么?和AICoding有什么区别
  • Windows系统文件ActiveSyncProvider.dll丢失找不到问题解决
  • 告别卡顿!用noVNC+Node.js在Windows上搭建流畅的Web版远程桌面(保姆级避坑指南)
  • 干货合集:2026年真正好用的专业AI论文工具
  • 窑炉温度测不准?我见过最离谱的错误,是工程师把红外枪当成了“万能方案“
  • 华为AC+AP组网实战:手把手教你配置隧道转发,搞定办公与访客Wi-Fi隔离
  • 孤能子视角:观察符
  • TEL TTLD30-11 5880-000029-V2印刷电路板
  • Python MQTT实战:从paho-mqtt基础连接到高级回调与QoS策略的完整指南
  • Windows系统文件advapi32res.dll丢失找不到问题解决
  • CCRC-CSERE网络安全应急响应工程师认证信息整理
  • OpenAI王炸发布GPT-5.6!Sol、Terra、Luna三神降临,“Ultra模式”开启多智能体内卷时代!
  • Fast-GitHub:国内开发者告别GitHub龟速下载的终极解决方案
  • ComfyUI-KJNodes:模块化节点系统的架构设计与技术实现
  • 抖音内容批量下载工具:从手动保存到自动化管理的解决方案
  • 从零到一:手把手教你为SPSS配置R环境并安装高级PSM插件
  • 2001-2024年企业绿色媒体覆盖率绿色新闻数据
  • 3个简单步骤掌握Cellpose:让细胞分割从复杂变轻松
  • uni-app Vue3 集成uQRCode实现微信支付二维码动态生成与弹窗交互
  • 跨越数据鸿沟:领域自适应(Domain Adaptation)核心思想与实践路径
  • Citizens2:Minecraft服务器NPC插件终极指南