2D词云经常用,是时候升级了,用一下3D词云!
除了常见的笛卡尔坐标系,极坐标系,还有一种坐标系,球坐标。通过以坐标原点为参考点,由方位角、仰角和距离构成一个三维坐标。
在three.js数学库里有个球坐标threejs.org/docs/#api/z…
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 开始,环绕一圈。
从开始值到结束值,映射到[0,1]
的区间,通过[0,1]
范围的值可以得到一个开始值到结束值之间的线性映射的值。 我们通常手动这样写
原始值val范围min,max
新值value范围newMin,newMax
value=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。
使用
value=lerp(newMin,newMax,(val-min)/(max-min))
/**
*canvas文本
* @param {String} text 文本字符串
* @param {Number} fontSize 字体大小
* @param {String} color 颜色
* @returns
*/
export function getCanvasText(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;
}
/***
* 文本网格
* @param {String} text 文本字符串
* @param {Number} fontSize 字体大小
* @param {String} color 颜色
*/
export function getTextMesh(THREE, text, fontSize, color) {
const canvas = getCanvasText(text, fontSize * 10, color, 'rgba(0,0,0,0)');
const map = new THREE.CanvasTexture(canvas);
map.wrapS = THREE.RepeatWrapping;
map.wrapT = THREE.RepeatWrapping;
//透明贴图
const canvasAlpha = getCanvasText(text, fontSize * 10, '#FFFFFF', 'rgba(0,0,0,0)');
const mapAlpha = new THREE.CanvasTexture(canvasAlpha);
map.wrapS = THREE.RepeatWrapping;
map.wrapT = THREE.RepeatWrapping;
const material = new THREE.MeshBasicMaterial({
map: map,
transparent: true,
side: THREE.DoubleSide
//设置透明贴图避免字体重叠
alphaTest: 0.5,
alphaMap: mapAlpha
});
const geometry = new THREE.PlaneGeometry(canvas.width * 0.1, canvas.height * 0.1);
const mesh = new THREE.Mesh(geometry, material);
return { material, geometry, canvas, mesh };
}
注意:canvas贴图一定要放大倍数,否则会近看模糊, 为了保持大小,创建二维平面板时可以对应缩小比例
canvas二维平面的贴图文本可能会出现深度冲突,导致文本之间遮挡,可以使用添加透明贴图和透明测试值,让文本背景颜色去掉,呈现正确的重叠顺序
不推荐使用TextGeometry,因为要涵盖全部字体的typeface很大,且一旦文本多的时候,面数也多就很卡
that.data.length
个const vector = new THREE.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通过球坐标转成三维坐标
//文本大小线性插值,根据数据值大小做映射
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);
geometry.lookAt(vector);
geometry.translate(vector.x, vector.y, vector.z);
将二维平面几何看向坐标点,然后按着坐标点进行移动,即可得到一个文本球体
this.scene.fog = new THREE.FogExp2(new THREE.Color('#000000'), 0.003);
const g = new THREE.IcosahedronGeometry(that.radius * 2, 2);
const m = new THREE.MeshBasicMaterial({
color: that.color,
transparent: true,
opacity: 0.2,
wireframe: true
});
const mm = new THREE.Mesh(g, m);
this.objGroup.add(mm);
animateAction() {
if (this.objGroup) {
if (this.objGroup.rotation.y >= Math.PI * 2) {
this.objGroup.rotation.y = 0;
} else {
this.objGroup.rotation.y += 0.001;
}
}
}
const color = `rgb(${Math.random() * 255},${Math.random() * 255},${
Math.random() * 255
})`;
let { mesh, geometry } = getTextMesh(THREE, text, s, color);
https://github.com/xiaolidan00/my-three
阅读量:768
点赞量:0
收藏量:0