用Three.js搞一个3D词云

简介: 2D词云经常用,是时候升级了,用一下3D词云!用Three.js搞一个3D词云!快快快!点进来瞅瞅!

2D词云经常用,是时候升级了,用一下3D词云!

1.球坐标

除了常见的笛卡尔坐标系,极坐标系,还有一种坐标系,球坐标。通过以坐标原点为参考点,由方位角、仰角和距离构成一个三维坐标。

在three.js数学库里有个球坐标https://threejs.org/docs/#api/zh/math/Spherical

Spherical( radius : Float, phi : Float, theta : Float )

  • radius:半径,或者说从该点到原点的(欧几里得距离,即直线距离)。默认值为1.0。范围[0,无穷)
  • phi:与y轴(向上)的极角(以弧度为单位)。 默认值为 0。范围[0,PI]
  • theta:绕y轴(向上)的赤道角(方位角)(以弧度为单位)。 默认值为 0。范围[0,2*PI]

极角(phi)位于正 y 轴和负 y 轴上,与其的夹角。赤道角(方位角)(theta)从正 z 开始,环绕一圈。

2.线性插值函数

从开始值到结束值,映射到[0,1]的区间,通过[0,1]范围的值可以得到一个开始值到结束值之间的线性映射的值。 我们通常手动这样写

js

原始值val范围min,max新值value范围newMin,newMaxvalue=newMin+ ((val-min)/(max-min))*(newMax-newMin)

在three.js数学工具里https://threejs.org/docs/?q=Math#api/zh/math/MathUtils.lerp

lerp(x:Float,y:Float,t:Float):Float

  • x:开始值。
  • y:结束值。
  • t:闭合区间[0,1]中的插值因子。

返回基于给定间隔从两个已知点线性插值的值-t=0将返回x,t=1将返回y。

使用

js

value=lerp(newMin,newMax,(val-min)/(max-min))

3.画文本

  • canvas文本

js

/** *canvas文本 * @param {String} text 文本字符串 * @param {Number} fontSize 字体大小 * @param {String} color 颜色 * @returns */exportfunctiongetCanvasText(text, fontSize, color, bg) {  const canvas = document.createElement('canvas');  const ctx = canvas.getContext('2d');  ctx.font = fontSize + 'px Arial';  ctx.fillStyle = color;  let padding = 5;  //测量文本大小,并设置canvas宽高预留padding  canvas.width = ctx.measureText(text + '').width + padding * 2;  canvas.height = fontSize * 1.2 + padding * 2;  ctx.clearRect(0, 0, canvas.width, canvas.height);  ctx.fillStyle = bg;  ctx.rect(0, 0, canvas.width, canvas.height);  ctx.fill();  ctx.font = fontSize + 'px Arial';  ctx.fillStyle = color;  ctx.fillText(text, padding, fontSize + padding * 0.5);  return canvas;}
  • 文本网格

js

/*** * 文本网格 * @param {String} text 文本字符串 * @param {Number} fontSize 字体大小 * @param {String} color 颜色 */exportfunctiongetTextMesh(THREE, text, fontSize, color) {    const canvas = getCanvasText(text, fontSize * 10, color, 'rgba(0,0,0,0)');  const map = newTHREE.CanvasTexture(canvas);  map.wrapS = THREE.RepeatWrapping;  map.wrapT = THREE.RepeatWrapping;  const material = newTHREE.MeshBasicMaterial({    map: map,    transparent: true,    side: THREE.DoubleSide  });  const geometry = newTHREE.PlaneGeometry(canvas.width * 0.1, canvas.height * 0.1);  const mesh = newTHREE.Mesh(geometry, material);  return { material, geometry, canvas, mesh };}

注意:canvas贴图一定要放大倍数,否则会近看模糊, 为了保持大小,创建二维平面板时可以对应缩小比例

4.开搞

  • 计算文本坐标,将球面上的坐标点分成that.data.length

js

const vector = newTHREE.Vector3();const phi = Math.acos(THREE.MathUtils.lerp(-1, 1, idx / (that.data.length - 1)));const theta = Math.sqrt(that.data.length * Math.PI) * phi;vector.setFromSphericalCoords(that.radius, phi, theta);

因为反余弦函数的值域范围刚好是[0,PI],定义域范围是[-1,1],那么我们可以通过lerp(-1,1,t)的线性插值函数得到对应的极角,这里使用的是数据索引idx

setFromSphericalCoords通过球坐标转成三维坐标

js

//文本大小线性插值,根据数据值大小做映射let s = THREE.MathUtils.lerp(                that.minFontSize,                that.maxFontSize,                (item.value - min) / size              );              let { mesh, geometry } = getTextMesh(THREE, text, s, that.color);              mesh.name = 'text' + idx;                            mesh.position.set(vector.x, vector.y, vector.z);                           textGroup.add(mesh);

  • 将文本坐标设置成球坐标转换后的笛卡尔三维坐标,我们会发现文本全部垂直,虽然在一个球体对应的坐标上,但是并没有形成一个友好的球体。我们需要的效果是让字体沿着球面摆放

js

 geometry.lookAt(vector);geometry.translate(vector.x, vector.y, vector.z);

将二维平面几何看向坐标点,然后按着坐标点进行移动,即可得到一个文本球体

  • 可以增加个雾来增加层次感

js

this.scene.fog = newTHREE.FogExp2(newTHREE.Color('#000000'), 0.003);

  • 3D词云在数据多的情况下很好看,但是数据少的时候就显得很空,这时候可以加个球框

js

  const g = newTHREE.IcosahedronGeometry(that.radius * 2, 2);            const m = newTHREE.MeshBasicMaterial({              color: that.color,              transparent: true,              opacity: 0.2,              wireframe: true            });            const mm = newTHREE.Mesh(g, m);            this.objGroup.add(mm);

  • 加个自动旋转

js

   animateAction() {          if (this.objGroup) {            if (this.objGroup.rotation.y >= Math.PI * 2) {              this.objGroup.rotation.y = 0;            } else {              this.objGroup.rotation.y += 0.001;            }          }        }

  • 加个随机颜色

js

   const color = `rgb(${Math.random() * 255},${Math.random() * 255},${                Math.random() * 255              })`;                            let { mesh, geometry } = getTextMesh(THREE, text, s, color);

GitHub

https://github.com/xiaolidan00/my-three

相关文章
|
6月前
|
JavaScript
js实现图片3D轮播效果(收藏)
js实现图片3D轮播效果(收藏)
74 0
|
6月前
three.js的3D模型渲染主要构成
three.js的3D模型渲染主要构成
105 0
|
JavaScript 前端开发 索引
用Three.js搞个炫酷3D地球
地球人怎么可以不会画地球!从canvas画地球贴图开始,用Three.js手把手教你实现一个炫酷的3D地球!
用Three.js搞个炫酷3D地球
基于three.js的牛逼轰轰的3D编辑器nunuStudio!
这是一款基于Three.js的3D编辑器,我之前一直喊错,叫人家"牛牛",因为我觉得它真的好牛,其实人家正确拼音喊“努努”! 可以发布web的运行包,直接可以网页端二次开发,真的不要太方便了!
基于three.js的牛逼轰轰的3D编辑器nunuStudio!
|
JavaScript 定位技术
原生 js 实现类 3d 地图大屏展示自动高亮轮播、显示悬浮提示 tootip 的方案:svg + popper.js 定位引擎
原生 js 实现类 3d 地图大屏展示自动高亮轮播、显示悬浮提示 tootip 的方案:svg + popper.js 定位引擎
334 0
原生 js 实现类 3d 地图大屏展示自动高亮轮播、显示悬浮提示 tootip 的方案:svg + popper.js 定位引擎
|
3月前
|
编解码 缓存 算法
Three.js如何降低3D模型的大小以便更快加载
为加快600MB的3D模型在Three.js中的加载速度,可采用多种压缩方法:1) 减少顶点数,使用简化工具或LOD技术;2) 压缩纹理,降低分辨率或转为KTX2等格式;3) 采用高效文件格式如glTF 2.0及draco压缩;4) 合并材质减少数量;5) 利用Three.js内置优化如BufferGeometry;6) 按需分批加载模型;7) Web Workers后台处理;8) 多模型合并减少绘制;9) 使用Texture Atlas及专业优化工具。示例代码展示了使用GLTFLoader加载优化后的模型。
436 12
|
3月前
|
存储 JavaScript 前端开发
小白实战!用JS实现一个3D翻书效果,附上代码
小白实战!用JS实现一个3D翻书效果,附上代码
|
3月前
|
存储 JavaScript 前端开发
使用JS创造一个3D粒子化星空,十分酷炫,大家快进来看看吧
使用JS创造一个3D粒子化星空,十分酷炫,大家快进来看看吧
|
6月前
|
JavaScript 开发工具 git
Three.js第1篇,Three.js新手教学,如何在项目中使用Three.js(three.js使用流程详细,three.js的使用方式,three.js创建3d物体)
Three.js封装了WebGL的底层细节,是一款运行在浏览器中的 3D 引擎,可以用它创建各种三维场景,包括了摄影机、光影、材质等各种对象,目前在Git上已经拥有90k+的star。
162 0
Three.js第1篇,Three.js新手教学,如何在项目中使用Three.js(three.js使用流程详细,three.js的使用方式,three.js创建3d物体)
|
JavaScript 前端开发 CDN
JavaScript 实现 3D 模型
JavaScript 实现 3D 模型
143 0