news 2026/6/26 3:44:03

给“拉票”加点WebGL:我做了个能拖拽旋转的3D星图,每一面墙都是我的代码故事(附完整源码) | 博客之星求投票

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
给“拉票”加点WebGL:我做了个能拖拽旋转的3D星图,每一面墙都是我的代码故事(附完整源码) | 博客之星求投票

文章目录

  • 前言
  • 一、网页效果预览
  • 二、功能和交互式体验
  • 三、 技术实现详解
    • 1. 技术栈
    • 2. 分级渲染策略
    • 3. Three.js 3D场景搭建
      • 3.1 粒子系统(星空背景)
      • 3.2信息墙面(数据展示)
      • 3.3 光线效果
    • 4. 数据管理与模态窗口
  • 四、完整代码
  • 五、我的拉票心声
  • 六、投票方式:
  • 七、 最后想说

前言

CSDN博客之星年度评选投票再度开启,紧张刺激的时刻如期而至。今年我荣幸入选TOP 300,首次以候选人身份参与这场技术人的年度盛会。

观察过往的拉票方式,无论是转发链接、群发文字,还是发红包、送礼物,似乎都已显得常规。作为一名开发者,我不禁思考:为什么不将技术创作本身,变成拉票的媒介呢?

于是这一次,我决定不仅用文字,更想用代码来表达——我花了一些时间,基于 Three.js 制作了一个互动式 3D 星图拉票页面。它不只是拉票工具,更是我在CSDN创作历程的可视化呈现。希望它能为你带来不一样的投票体验,也让你看见:技术,可以让表达更有趣。

如果你也喜欢这种“用代码说话”的方式,欢迎体验这个小小作品,并为我投上一票。


一、网页效果预览

如果技术人有浪漫,那大概就是把抽象的付出,变成一片可触碰的星空。


点击这里在线体验

二、功能和交互式体验


这不是一张静态图片。它是一个活着的、呼吸的微型宇宙。

  • 星空粒子背景:由5000颗(低性能设备自适应为1500颗)粒子组成的星云,持续缓缓脉动。

  • 悬浮数据墙面:8面可交互的3D墙面,清晰展示着我的关键博客数据。

  • 实时交互:鼠标拖拽旋转视角,滚轮缩放探索细节,触摸设备亦可流畅操作。

  • 响应式设计:适配PC和移动端,在华丽与流畅间取得平衡。

三、 技术实现详解

1. 技术栈

整个项目采用原生HTML+CSS+JavaScript实现,核心3D渲染使用Three.js。

2. 分级渲染策略

针对移动端和pc端做了不同渲染策略,保证移动端运行流畅。

// 设备性能检测detectLowEndDevice(){constcanvas=document.createElement('canvas');constgl=canvas.getContext('webgl2')||canvas.getContext('webgl');if(!gl)returntrue;constdebugInfo=gl.getExtension('WEBGL_debug_renderer_info');constrenderer=debugInfo?gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL):'';constisMobile=/iPhone|iPad|iPod|Android/i.test(navigator.userAgent);constisLowEndGPU=/Mali|Adreno|Mesa/i.test(renderer);constisLowMemory=navigator.deviceMemory&&navigator.deviceMemory<4;returnisMobile||isLowEndGPU||isLowMemory;}
// 根据设备性能调整渲染质量init(){constisLowEnd=this.detectLowEndDevice();this.renderer.setPixelRatio(Math.min(window.devicePixelRatio,1.2));this.createOptimizedParticles(isLowEnd);this.createLightBeams(isLowEnd);this.setupLighting(isLowEnd);}

3. Three.js 3D场景搭建

3.1 粒子系统(星空背景)

动态设置粒子数:
低端设备(检测为低性能时):1500个粒子
高端设备:5000个粒子

//创建粒子系统createOptimizedParticles(isLowEnd){constparticleCount=isLowEnd?1500:5000;constpositions=newFloat32Array(particleCount*3);constcolors=newFloat32Array(particleCount*3);constsizes=newFloat32Array(particleCount);for(leti=0;i<particleCount;i++){consti3=i*3;constradius=Math.pow(Math.random(),0.7)*80+20;constangle=Math.random()*Math.PI*2;constheight=(Math.random()-0.5)*40;positions[i3]=radius*Math.cos(angle);positions[i3+1]=height;positions[i3+2]=radius*Math.sin(angle);colors[i3]=0.2+Math.random()*0.3;colors[i3+1]=0.4+Math.random()*0.4;colors[i3+2]=0.8+Math.random()*0.2;sizes[i]=Math.random()*4+1;}constgeometry=newTHREE.BufferGeometry();geometry.setAttribute('position',newTHREE.BufferAttribute(positions,3));geometry.setAttribute('color',newTHREE.BufferAttribute(colors,3));geometry.setAttribute('size',newTHREE.BufferAttribute(sizes,1));constmaterial=newTHREE.ShaderMaterial({uniforms:{uTime:{value:0},uPixelRatio:{value:Math.min(window.devicePixelRatio,1.2)}},vertexShader:`attribute float size; attribute vec3 color; varying vec3 vColor; uniform float uTime; uniform float uPixelRatio; void main() { vColor = color; vec3 pos = position; pos.y += sin(uTime + position.x * 0.01) * 0.3; vec4 mvPosition = modelViewMatrix * vec4(pos, 1.0); gl_PointSize = size * uPixelRatio * (400.0 / -mvPosition.z); gl_Position = projectionMatrix * mvPosition; }`,fragmentShader:`varying vec3 vColor; void main() { float dist = distance(gl_PointCoord, vec2(0.5)); if (dist > 0.5) discard; float alpha = 1.0 - smoothstep(0.0, 0.5, dist); alpha *= 0.7; gl_FragColor = vec4(vColor, alpha); }`,transparent:true,blending:THREE.AdditiveBlending,depthWrite:false});constparticles=newTHREE.Points(geometry,material);this.particles.push(particles);this.scene.add(particles);}

3.2信息墙面(数据展示)

每个墙面都是一个独立的3D平面,采用Canvas动态生成纹理:

//创建墙面createEnhancedTexture(){constcanvas=document.createElement('canvas');canvas.width=512;canvas.height=384;constctx=canvas.getContext('2d');constgradient=ctx.createLinearGradient(0,0,512,384);gradient.addColorStop(0,'rgba(0, 0, 0, 0.95)');gradient.addColorStop(0.5,'rgba(10, 5, 20, 0.85)');gradient.addColorStop(1,'rgba(0, 15, 20, 0.9)');ctx.fillStyle=gradient;ctx.fillRect(0,0,512,384);// 半透明边框ctx.strokeStyle=this.data.color+'60';ctx.lineWidth=3;ctx.shadowColor=this.data.color;ctx.shadowBlur=15;ctx.strokeRect(6,6,500,372);ctx.shadowBlur=0;ctx.font='bold 70px Arial';ctx.textAlign='center';ctx.textBaseline='middle';ctx.fillStyle=this.data.color;ctx.shadowColor=this.data.color;ctx.shadowBlur=20;ctx.fillText(this.data.icon,256,90);ctx.shadowBlur=0;ctx.font='bold 52px -apple-system, BlinkMacSystemFont, sans-serif';ctx.fillStyle='#00ffff';ctx.shadowColor='#00ffff';ctx.shadowBlur=15;ctx.fillText(this.data.value,256,200);ctx.shadowBlur=0;ctx.font='26px -apple-system, BlinkMacSystemFont, sans-serif';ctx.fillStyle='#ffffff';ctx.fillText(this.data.title,256,290);returncanvas;}

3.3 光线效果

为重要数据(如阅读量、粉丝数)添加了动态光束:

//创建光束createLightBeams(isLowEnd){if(isLowEnd){this.lightBeams=[];return;}constbeamIds=['read','fans'];this.walls.forEach((wall,index)=>{if(beamIds.includes(wall.data.id)){constgeometry=newTHREE.CylinderGeometry(0.3,3,35,8,1,true);constmaterial=newTHREE.MeshBasicMaterial({color:wall.data.color,transparent:true,opacity:0.06,side:THREE.DoubleSide,blending:THREE.AdditiveBlending});constbeam=newTHREE.Mesh(geometry,material);beam.position.copy(wall.mesh.position);beam.position.y+=18;beam.rotation.z=Math.PI;beam.userData={originalOpacity:0.06};this.lightBeams.push(beam);this.scene.add(beam);}});}

4. 数据管理与模态窗口

所有博客数据都存储在BLOG_STAR_DATA对象中,点击墙面时会显示详细信息的模态窗口:

showDetailModal(data){// 动态填充数据document.getElementById('detailIcon').textContent=data.icon;document.getElementById('detailTitle').textContent=data.title;document.getElementById('detailValue').textContent=data.value;document.getElementById('detailDescription').innerHTML=data.detail;document.getElementById('detailThanks').textContent=data.thanks;// 显示模态窗口modal.style.display='block';}


四、完整代码

git clone https://gitcode.com/sd1sd2/3d_Vote.git

五、我的拉票心声


做这个页面不仅仅是为了拉票,更是想展示技术人的浪漫——我们用代码构建世界,用算法描绘星空。

在CSDN的5年多创作历程中,我写了130+篇原创文章,收获了2.2万+粉丝的支持,总阅读量超过39万次。每篇文章都凝聚着我的心血,都源于实战的打磨、经验总结、反复的调试与无私的分享。您的一票,便是对我这些年坚持创作最好的认可。这次评选,对我来说不只是荣誉。我想证明一件事:坚持技术分享是有价值的,用心写的内容会被看见。

六、投票方式:

  • 点击这个按钮前往投票
  • 每天都可以投票(根据你的码龄、博客等级决定投票数,可以把所有票投给同一个用户)
  • 2025年12月21日之后注册用户、未发布过原创文章,无权投票。
  • 投票截止:2026年1月23日 23:59:59

如果你愿意多帮一点

  • 把星空页面分享给你的技术群

  • 把文章分享给你觉得可能感兴趣的朋友

  • 如果你也是入围的博主,我们也可以互投


七、 最后想说

感谢你花费时间,阅读至此,甚至体验了那个小小的星空。技术之路,犹如星空探索,浩瀚无垠又充满惊喜。感谢每一位在技术路上同行的伙伴,感谢CSDN这个让我们相遇的平台。

技术人的浪漫,就是把抽象的逻辑变成可视的美感。 我把我的技术足迹变成了星空,现在,我想邀请你成为这片星空里的一颗光点。

你的每一票,都会让这个星空更亮一点。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/26 2:30:23

ssm686科研项目评审系统

目录科研项目评审系统摘要开发技术源码文档获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;科研项目评审系统摘要 科研项目评审系统旨在通过信息化手段优化科研项目的申报、评审和管理流程&#xff0c;提升评审效率与公平性。该系统采用SSM&…

作者头像 李华
网站建设 2026/6/24 22:15:54

ssm688校园安全管理系统

目录SSM688校园安全管理系统摘要开发技术源码文档获取/同行可拿货,招校园代理 &#xff1a;文章底部获取博主联系方式&#xff01;SSM688校园安全管理系统摘要 校园安全管理系统基于SSM&#xff08;SpringSpringMVCMyBatis&#xff09;框架开发&#xff0c;旨在通过信息化手段…

作者头像 李华
网站建设 2026/6/10 11:10:01

亲测好用10个AI论文网站,专科生毕业论文必备!

亲测好用10个AI论文网站&#xff0c;专科生毕业论文必备&#xff01; AI 工具如何让论文写作更高效 对于专科生来说&#xff0c;撰写毕业论文往往是一项既复杂又耗时的任务。从选题、查资料到撰写初稿、修改润色&#xff0c;每一个环节都需要投入大量时间和精力。而随着 AI 技…

作者头像 李华
网站建设 2026/6/13 16:03:19

Doris与StarRocks对比:新一代OLAP引擎

Doris与StarRocks对比:新一代OLAP引擎 关键词:OLAP引擎、Doris、StarRocks、对比分析、分布式架构、查询优化、数据建模 摘要:本文深入对比分析Apache Doris与StarRocks两款新一代OLAP引擎,从技术架构、核心特性、查询优化、数据建模、生态集成等维度展开详细探讨。通过剖析…

作者头像 李华
网站建设 2026/6/20 21:33:01

linux内核 - 进程管理和调度(基于6.19内核)

一&#xff1a;概述所有现代操作系统都能够同时运行多个进程——至少在用户看来是这样。如果系统只有一个处理器&#xff0c;那么在任意时刻实际上只能运行一个程序。在多处理器系统中&#xff0c;真正能够并行运行的进程数量取决于物理 CPU 的数量。内核和处理器通过以极快的速…

作者头像 李华
网站建设 2026/6/26 2:39:20

驱动开发系列76 - Mesa NIR 通用中间语言表示

一:概述 NIR 是Mesa 的通用中间表示,适用于所有图形API和硬件后端。本文介绍下NIR的设计架构。包括NIR核心数据结构,NIR优化过程,NIR降级和验证。 二:架构概览 NIR 作为Mesa中所有着色器的汇聚点,提供了一种通用表示,使得不同前端语言(GLSL、SPIRV、TGSI)和后端(LLV…

作者头像 李华