用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

相关文章
|
4月前
|
JavaScript
js实现图片3D轮播效果(收藏)
js实现图片3D轮播效果(收藏)
27 0
|
8月前
|
JavaScript 前端开发 索引
用Three.js搞个炫酷3D地球
地球人怎么可以不会画地球!从canvas画地球贴图开始,用Three.js手把手教你实现一个炫酷的3D地球!
用Three.js搞个炫酷3D地球
|
11月前
|
JavaScript
基于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 定位引擎
251 0
原生 js 实现类 3d 地图大屏展示自动高亮轮播、显示悬浮提示 tootip 的方案:svg + popper.js 定位引擎
|
9月前
|
JavaScript 前端开发 CDN
JavaScript 实现 3D 模型
JavaScript 实现 3D 模型
|
5月前
|
人工智能 JavaScript Linux
基于Three.js的3D自动纹理化开发包
DreamTexture.js 基于 Three.js 和稳定扩散(stable diffusion) AI 模型开发,用于实现 3D 模型的自动纹理化。
56 0
|
9月前
|
JavaScript 前端开发
探索3D魔力:与Three.js共舞的五大库和工具
探索3D魔力:与Three.js共舞的五大库和工具
95 0
|
9月前
|
JavaScript 前端开发 C++
用Three.js搞个3D金字塔
来来来,进来就看用Three.js搞个简单的3D金字塔!祝福大家都能成为金字塔顶端的大佬!嘛哩嘛哩哄!祈祷成功!
|
9月前
|
JSON 数据格式
用Three.js搞个炫酷3D字体
三角形飞啊飞~飞啊飞~飞到一起,成了彩色字体!点击进来就看如何用three.js实现炫酷3D字体!!
用Three.js搞个炫酷3D字体