前言

  • 之前在part2中说的添加自定义主题配色已经开发完成了,除此之外我还添加了一些的3d特效。

前期文章

成果链接

具体效果

最后出来的效果还是蛮炫的。





添加与修改

  1. 设置中添加主题定制,预设3种主题。
  2. 主题定制支持取色器取色。
  3. 添加3d球体的环形动态特效。

主题预设

由于项目一开始就使用的dataV,并且有计划的提取出了颜色值,所以定制主题便不是很麻烦,直接提取改变dataV的color属性即可,其他部分直接使用vue的动态样式绑定。

设置配置

先在设置组件SetDrawer中预先设置好需要使用的配置信息,包括预设的主题,然后使用vue3的onBeforeMount在挂载组件之前将包含颜色的配置信息用sessionStorage.setItem("config", JSON.stringify(setData.value))先存储到浏览器本地,这样可以防止刷新页面时配置信息丢失重置。

//系统配置
function sysConfig() {
setData.value.sysVer = PK.version//获取系统版本号
process.env.NODE_ENV == "development" ?
setData.value.dataType = dataTypeList[0] ://开发环境使用离线数据
setData.value.dataType = dataTypeList[1];//生产环境使用在线数据
let ss = sessionStorage.getItem("config");//获取缓存配置
//缓存中有配置取出配置,无则使用初始配置
if (ss) {
let cuVer = setData.value.sysVer,//当前版本号
ssVer = JSON.parse(ss).sysVer,//缓存版本号
isUpDate = null;//是否更新缓存
//当前版本号与缓存版本号若不等清除缓存使用当前配置,否则使用缓存配置
(cuVer !== ssVer) ?
(isUpDate = true) :
isUpDate = false;
isUpDate ?
(
(sessionStorage.removeItem("config")),//清除缓存
(sessionStorage.setItem("config", JSON.stringify(setData.value)))//设置缓存配置
) :
(setData.value = JSON.parse(ss));
} else {
sessionStorage.setItem("config", JSON.stringify(setData.value));//设置缓存配置
}
};

值得注意的是,需要在存入配置信息前判断缓存中是否已经存在配置信息,若有,则直接使用缓存配置,若没有则存入预先在代码中写好配置。

加载配置

在页面加载瞬间就需要获取颜色值的组件中使用onMounted与JSON.parse(sessionStorage.getItem("config") )获取到上一步在缓存中存下的配置信息,从配置信息中获取到颜色值,最后在利用该值将dom渲染出来,dataV的部分支持使用:color="['#fff', '#aaa']"来动态改变颜色,其中数组中第一个值为主色,第二个为副色; 非dataV的部分直接使用vue动态绑定样式的语法:style=“{color:#fff}”来修改配色。

切换配置

切换主题时直接在设置组件中改变当前使用的配色值然后刷新页面即可,因为我在切换3d球体颜色时偷了一个懒,正常流程是获取球体mesh,改变材质的color值,然后更新的。我直接刷新了浏览器(又不是不能用(*  ̄︿ ̄)),重建了场景、相机、球体等。

预设主题

我预设了三对主色、副色的颜色值。为了看起来更和谐,建议使用同一颜色的深色与浅色来搭配。



v-for即可渲染出切换按钮。

取色器

原生input实现

我也是在无意中发现input的type居然是支持color的。可以直接原生实现取色器,这样就可以用颜色吸管获取屏幕中的任何颜色了:

 <input id="colorInp" style="height: 0px;opacity: 0;width: 0px;margin: 0px;padding: 0px;" type="color" />

绑定事件

要使用自己的按钮点击打开取色器,可以直接将input的高宽赋为0,不透明度赋为0,将其隐藏后,再使用按钮绑定事件打开取色器即可:

<el-button class="main-color" :color="setData.sysColor[0]" @click="changeColor(0)">主色</el-button>
function changeColor(type: Number) {
(document.getElementById("colorInp") as any).click();//手动点击取色器
colorType = type;//改变颜色类型
};

环形特效



项目中一共有七种环形效果:

//创建环
function createRings() {
createEquatorSolidRing(earthSize + 20);//创建赤道实线环
createEquatorFlyline(earthSize + 30);//创建赤道飞线环
createEquatorDottedLineRing(earthSize + 35);//创建赤道虚线环
createSpikes(earthSize + 40);//创建赤道尖刺
createUpDownRing(earthSize - 50, earthSize - 40);//创建南北极环
createExpandRing();//创建爆炸环
createSphereGlow();//创建球体光晕
};

接下来我们详细分析一下。

赤道实线环



即为赤道上最靠近球体内层的一层环。

  1. 该环使用RingGeometry几何体实现,参数分别为:内半径、外半径、分段数。
  2. 刚创建出来的环形是平行于屏幕的,需要改变环mesh的rotation属性,绕x轴旋转90度即可。
//创建赤道实线环
function createEquatorSolidRing(r: any) {
//创建里层的环
let ringGeometry = new THREE.RingGeometry(r - 2, r + 2, 100);
let ringMaterial = new THREE.MeshBasicMaterial({
color: dvColor.value[0],
opacity: .3,
side: THREE.DoubleSide,
fog: true,
depthWrite: false,
blending: THREE.AdditiveBlending,
});
let ringMesh = new THREE.Mesh(ringGeometry, ringMaterial);
ringMesh.rotation.x = 90 * Math.PI / 180;
earthGroup.add(ringMesh);
};

赤道飞线环



即为赤道第二靠近球体的环。

  1. 仔细观察会发现,它其实并不是一个环,而是一条弧线再在环绕球体运动。
  2. 实现该弧线的时候发现,webGL的线条由于渲染器的限制,并不能设置宽度,但可以通过three.meshline(这是一个three插件,three不自带,需要npm i three.meshline安装)中的MeshLineMaterial来实现宽度的设置,修改MeshLineMaterial中的lineWidth属性值即可。
  3. dashArray为弧线段数量的倒数(0.5即为2条,0.3即为3条),dashRatio为线段的不可见部分与可见部分的比例。
  4. 几何体使用了BufferGeometry,设置setFromPoint,提取THREE.Path绘制出的arc的数据,改变几何体顶点属性即可。
  5. arc参数依次为:弧线中心x与y值、弧线半径、起始角、终止角、是否顺时针方向创建弧线(默认false)。
  6. 最后创建完成后同样需要rotation.x改变角度。
//创建赤道飞线
function createEquatorFlyline(r: any) {
const geometry = new THREE.BufferGeometry();
const path = new THREE.Path();
path.arc(0, 0, r, 0, Math.PI * 2);
const points = path.getPoints(100);//切割段数
geometry.setFromPoints(points);
const line = new MeshLine();
// 设置几何体
line.setGeometry(geometry)
const material = new MeshLineMaterial({
color: dvColor.value[0],
lineWidth: 1, // 线条的宽度
dashArray: .5, // 该数值倒数为线段数量
dashRatio: .5, // 不可见与可见比例
transparent: true, // 设置透明度
})
flylineMesh = new THREE.Mesh(line.geometry, material);
flylineMesh.rotation.x = 90 * Math.PI / 180;
earthGroup.add(flylineMesh);
};

赤道虚线环



即为赤道第三靠近球体的白色虚线环。

  1. 该环是由50个小白点组成,几何体使用了BufferGeometry,材质使用了PointsMaterial,组使用了Points。
  2. 几何体中需要使用Math.cos与sin改变点单位向量的xyz值,然后将位置列表positions使用Float32BufferAttribute(值得注意的是Float32Attribute已被删除弃用)设置position属性至ringPointGeometry中。
  3. 材质中记得设置transparent: false,与size尺寸。
  4. 最后将几何体与材质添加到点的组中,将点组添加到球体组中即可。
//创建赤道虚线环
function createEquatorDottedLineRing(r: any) {
const positions = [];
let ringPointGeometry = new THREE.BufferGeometry(); //环形点几何体
let pointNum = 50;//点的数量
let ringPointAngle = (2 * Math.PI) / pointNum; //环形点角度
for (let o = 0; o < 500; o++) {
let n = new THREE.Vector3(); //点的向量
n.x = r * Math.cos(ringPointAngle * o); //计算点的角度
n.y = 0;
n.z = r * Math.sin(ringPointAngle * o);
positions.push(n.x, n.y, n.z);
}
ringPointGeometry.setAttribute(
"position",
new THREE.Float32BufferAttribute(positions, 3)
);//设置位置属性
let ringPointMaterial = new THREE.PointsMaterial({
//环形点材质
size: 3,
// color: dvColor.value[0],
transparent: false,
blending: THREE.AdditiveBlending,
side: THREE.DoubleSide,
depthWrite: false,
});
dotLineRingMesh = new THREE.Points(
ringPointGeometry,
ringPointMaterial
);
dotLineRingMesh.name = "赤道虚线";
earthGroup.add(dotLineRingMesh);
};

赤道尖刺环



即为赤道环中类似钟表刻度的环形。

  1. 该环使用LineBasicMaterial材质、BufferGeometry几何体、LineSegments组。
  2. 材质中由于webGL限制不能使用linewidth,始终宽度为1。
  3. 几何体顶点的处理类似创建虚线环,循环改变每个尖刺的xyz向量即可。
  4. 若要改变指定尖刺的长度只需使用multiplyScalar向量与标量相乘。
  5. 最后将材质与几何体添加到组中即可。
//创建赤道尖刺
function createSpikes(spikeRadius: any) {
let spikesVerticesArray = [];
let spikesObject = new THREE.Group(); //创建尖刺的组
spikesObject.name = "赤道尖刺";
earthGroup.add(spikesObject); //将尖刺组添加到旋转组中
//创建尖刺
let spikeNum = 400;//尖刺数量
let o = (2 * Math.PI) / spikeNum;
for (let s = 0; s < spikeNum; s++) {
let r = new THREE.Vector3();
r.x = spikeRadius * Math.cos(o * s);
r.y = 0;
r.z = spikeRadius * Math.sin(o * s);
r.normalize();//归一化,将该向量转化为向量单位
r.multiplyScalar(spikeRadius);
let i = r.clone(); //克隆r至i
(s % 10 == 1) ? i.multiplyScalar(1.1) : i.multiplyScalar(1.05);//每10个计算一次向量与标量相乘
spikesVerticesArray.push(r); //将向量存入尖刺顶点列表
spikesVerticesArray.push(i);
}
let n = new Float32Array(3 * spikesVerticesArray.length); //创建顶点数组
for (let s = 0; s < spikesVerticesArray.length; s++) {
n[3 * s] = spikesVerticesArray[s].x;//给顶点数组设置坐标
n[3 * s + 1] = spikesVerticesArray[s].y;
n[3 * s + 2] = spikesVerticesArray[s].z;
}
//尖刺材质
let spikesMaterial = new THREE.LineBasicMaterial({
// linewidth: 1,//webgl渲染器限制,不能设置宽度,始终为1(three.meshline插件可解决)
// color: "#fff",
color: dvColor.value[0],
transparent: true,
opacity: .5
});
let spikesBufferGeometry = new THREE.BufferGeometry(); //创建尖刺几何体
spikesBufferGeometry.setAttribute(
"position",
new THREE.BufferAttribute(n, 3)
); //添加位置属性
let spikesMesh = new THREE.LineSegments(
spikesBufferGeometry,
spikesMaterial
);
spikesObject.add(spikesMesh); //将网格放进组
};

爆炸环



即为赤道最外面一层环,它会不断的放大渐变,形成类似爆炸冲击波一样的效果。具体原理是这样的。

  1. 先直接用MeshBasicMaterial材质中的map加载一张透明环形贴图,记得设置transparent、side、depthWrite、blending属性。
  2. 再使用PlaneGeometry几何体添加一个平面矩形,其具体参数为:矩形宽,矩形高、宽分段数、高分段数。
  3. 然后将几何体与材质添加到组中,完成后的平面也是平行于屏幕的,记得设置rotation.x的值,使之垂直于屏幕。
  4. 最后要让环产生动画需要结合gsap(这是最健全的web动画库之一,生成动画十分方便)的fromTo方法。fromTo中第一个参数为产生动画的对象,第二个参数为动画开始状态,第三个参数为动画结束状态(其中包含动画时长duration)。
  5. 这时你会发现自己的动画只会动一次,其实只需将createExpandRingAnimation添加到requestAnimationFrame动画请求帧使用的方法中使其一直render渲染即可。
//创建渐变环
function createExpandRing() {
let ringMaterial = new THREE.MeshBasicMaterial({
map: new THREE.TextureLoader().load(ringImg),
color: new THREE.Color(dvColor.value[0]),//颜色
transparent: true,
opacity: 1,
side: THREE.DoubleSide,
fog: true,
depthWrite: false,
blending: THREE.AdditiveBlending,
});
let ringGeometry = new THREE.PlaneGeometry(earthSize * 2, earthSize * 2, 10, 10);
expandRingMesh = new THREE.Mesh(ringGeometry, ringMaterial);
expandRingMesh.name = "放大环";
expandRingMesh.rotation.x = 90 * Math.PI / 180;
earthGroup.add(expandRingMesh);
}; //创建渐变环动画
function createExpandRingAnimation() {
gsap.isTweening(expandRingMesh.scale) ||//环动画
(gsap.fromTo(
expandRingMesh.scale,//缩放渐变
{ x: 1, y: 1, },
{ x: 2.7, y: 2.7, duration: 1.5 }
),
gsap.fromTo(
expandRingMesh.material,//材质的透明度渐变
{ opacity: 1, },
{ opacity: 0, duration: 1.5 }
))
};

南北极环





如图,即为球体上下的双层环形。其生成方法与赤道实线环一样,不同之处是需要改变position.y值,使之移动到球体南北极。

//创建上下环
function createUpDownRing(r1: any, r2: any) {
let ringsObject = new THREE.Group(); //创建环的组
ringsObject.name = "南北极环";
earthGroup.add(ringsObject); //将环添加到场景中
//创建内环
let a = new THREE.RingGeometry(r1, r1 - 2, 100); //圆环几何体(内半径,外半径,分段数)
let ringsOuterMaterial = new THREE.MeshBasicMaterial({
color: dvColor.value[0],
transparent: true,
opacity: .3,
side: THREE.DoubleSide,
fog: true,
depthWrite: false,
blending: THREE.AdditiveBlending,
});
let o = new THREE.Mesh(a, ringsOuterMaterial);
o.rotation.x = 90 * Math.PI / 180; //设置旋转
let r = o.clone(); //克隆外环网格o至r
o.position.y = 95; //设置位置
r.position.y = -95;
ringsObject.add(o);
ringsObject.add(r);
//创建外环
let t = new THREE.RingGeometry(r2, r2 - 2, 100);
let ringsInnerMaterial = new THREE.MeshBasicMaterial({
color: dvColor.value[0],
transparent: true,
opacity: .3,
side: THREE.DoubleSide,
fog: true,
depthWrite: false,
blending: THREE.AdditiveBlending,
});
let i = new THREE.Mesh(t, ringsInnerMaterial);
i.rotation.x = 90 * Math.PI / 180;
let n = i.clone();
i.position.y = 100;
n.position.y = -100;
ringsObject.add(i);
ringsObject.add(n);
};

球体光晕



即为球体外部的一层光晕。

  1. 因为光晕是需要一直平行于屏幕的,所以我们这里直接采用SpriteMaterial材质,将其添加到Sprite中,Sprite精灵的特性就是可以一直正对相机。
  2. 生成材质时需要添加光晕的透明贴图,同时设置属性:blending、depthWrite、transparent、side。
//创建球体发光环
function createSphereGlow() {
//SpriteMaterial材质始终朝向平面
let glowMaterial = new THREE.SpriteMaterial({
map: new THREE.TextureLoader().load(earthGlowImg),
color: new THREE.Color(dvColor.value[0]),//颜色
transparent: true,
opacity: 1,
side: THREE.DoubleSide,
fog: true,
depthWrite: false,
blending: THREE.AdditiveBlending,
})
let glowSprite = new THREE.Sprite(glowMaterial);
glowSprite.scale.set(earthSize * 3.2, earthSize * 3.2, 1); //点大小
earthGroup.add(glowSprite);
};

结语

成都的12月份好冷啊ヽ(≧□≦)ノ,手指头开始造反不听使唤了,项目到这里差不多该是告一段落了,本项目仅作为我学习webgl与可视化结合使用的一个demo,项目是完全开源了的,有想使用的可以直接在我的gitee上clone,链接在本文开头(不要忘记star啊大哥们!)。

疫情可视化part3的更多相关文章

  1. 【python疫情可视化】用pyecharts开发全国疫情动态地图,效果酷炫!

    一.效果演示 我用python开发了一个动态疫情地图,首先看下效果: 如图所示,地图根据实时数据通过时间线轮播的方式,动态展示数据的变化.随着时间的推移,疫情确诊数量的增多,地图各个省份颜色逐渐加深, ...

  2. vue3+three.js实现疫情可视化

    前言 自成都九月份以来疫情原因被封了一两周,居家着实无聊,每天都是盯着微信公众号发布的疫情数据看,那种页面,就我一个前端仔来说,看着是真的丑啊!(⊙_⊙)?既然丑,那就自己动手开整!项目是2022.9 ...

  3. “知疫”疫情防控可视化平台——NABCD分析

    "知疫"疫情防控可视化平台 项目 内容 这个作业属于那个课程 2021春季学期软件工程(罗杰.任健) 这个作业的要求在哪里 初次邂逅,需求分析 1 NABCD分析 NEED 目前多 ...

  4. 数据可视化之powerBI技巧(二十三)Power BI可视化技巧,使用DAX自定义时间轴

    ​按照自然日历来展现疫情数据时,是这样的效果, 由于各个国家的疫情爆发时间不一致,按自然日期坐标轴很难比较各个国家的蔓延速度. 如果各个国家都从蔓延日开始统计,展示之后每日的确诊人数,就是同样的时间轴 ...

  5. GitHub 热点速览 Vol.12:不可思议的浏览器 browser-2020 周涨 star 超 3 千

    作者:HelloGitHub-小鱼干 摘要:本周的 GitHub Trending 像极最近的天气,温暖如春突然来个急降温.新晋 GitHub 项目重启屈指可数的模式,好在老项目们表现甚好.比如一周就 ...

  6. 个人作业——软件工程实践总结&个人技术博客

    一. 回望 (1)对比开篇博客你对课程目标和期待,"希望通过实践锻炼,增强软件工程专业的能力和就业竞争力",对比目前的所学所练所得,在哪些方面达到了你的期待和目标,哪些方面还存在哪 ...

  7. 爬取疫情数据,以django+pyecharts实现数据可视化web网页

    在家呆着也是呆着,不如做点什么消磨时间呗~ 试试用django+pyecharts实现疫情数据可视化web页面 这里要爬疫情数据 来自丁香园.搜狗及百度的疫情实时动态展示页 先看看劳动成果: 导航栏: ...

  8. Python爬取全球疫情数据,实现可视化显示地图数据(附代码)

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 武汉地区,目前已经实现住院患者清零了,国内疫情已经稳定,然而中国以外新冠确 ...

  9. Python数据可视化实战:实时更新海外疫情数据,实现数据可视化

    前言 我国的疫情已经得到了科学的控制,开始了全面的复工复产,但是国外的疫情却“停不下来”.国外现在可谓就是处于水深火热当中啊,病毒极强的传染性,导致了许多的人都“中招”了,我国已经全面复工复产了,人大 ...

  10. 数据可视化之 图表篇(一)Power BI可视化,几张图表认识疫情现状

    ​近期国际疫情愈演愈烈,在这个特殊的时期,一方面仍要照顾好自己.不要为疫情防治添乱,另一方面,也可以利用疫情数据提升自己的数据分析和可视化技能. 下面是我制作的几个可视化图表,分别注释了每个可视化用到 ...

随机推荐

  1. vscode调试thinkhphp

    第一步先安装xdebug扩展,我用宝塔环境,所以一键安装 第二步.在vscode中安装插件 我的php.ini是这样的 xdebug.remote_enable = 1 xdebug.remote_a ...

  2. Form表单数据

    官方文档地址:https://fastapi.tiangolo.com/zh/tutorial/request-forms/ 接收的不是 JSON,而是表单字段时,要使用 Form 要使用表单,需预先 ...

  3. 初试 Ceph 存储之块设备、文件系统、对象存储

    转载自:https://cloud.tencent.com/developer/article/1010543 1.Ceph 存储介绍 Ceph 是一个开源的分布式存储系统,包括对象存储.块设备.文件 ...

  4. [题解] Codeforces 468 E Permanent 折半,DP,图论

    题目 建立一个二分图,左右各n个点,在左边的第x个点和右边的第y个点之间连一条权值为\(a_{x,y}\)的边.根据"积和式"的定义,我们是要在矩阵中选择n个位置,满足任意两个位置 ...

  5. GCN的原理及其代码实现

    图数据的特征性质 图像数据是一种特殊的图数据,图像数据是标准的2D网格结构图数据.图像数据的CNN卷积神经网络算法不能直接用在图数据上,原因是图数据具有以下特殊性. 节点分布不均匀:图像数据及网格数据 ...

  6. 换工作?试试远程工作「GitHub 热点速览 v.22.40」

    近日,潜在某个技术交流群的我发现即将毕业的小伙伴在焦虑实习.校招,刚好本周 GitHub 热榜有个远程工作项目.不妨大家换个思路,"走"出去也许有更多的机会.当然,除了全球的远程工 ...

  7. 周末IT入门锦鲤

    周末总结 第一小节 typora软件 是目前最火的文本编辑器 下载安装 路径尽量不要安装C盘,安装其他盘路径尽量简单方便后续查找使用. 文件路径 路径:计算机上一个文件资源的坐标,C:\XX文件\a. ...

  8. javaweb 导出文件名乱码的问题解决方案

    fileName = new String(fileName.getBytes("ISO8859-1"), "UTF-8"); 或者 String finalF ...

  9. 知识图谱顶会论文(ACL-2022) PKGC:预训练模型是否有利于KGC?可靠的评估和合理的方法

    PKGC:预训练模型是否有利于KGC?可靠的评估和合理的方法 论文地址:Do Pre-trained Models Benefit Knowledge Graph Completion? A Reli ...

  10. js 获取开始时间和结束时间相隔小时及分钟(时间戳操作)

    js 获取开始时间和结束时间相隔小时及分钟(时间戳操作) 场景描述:获取开始时间和结束时间相隔小时及分钟 实例: TimeOnConfirm(curDate) { if(this.pickernum ...