赞
踩
书接上回,我们在上文中的基础之上添加飞线、扩散波来表示具体业务 - 资金的流向,再添加一些大屏元素让整个页面看起来更加成型。
此工具主要分为三个功能点
import { util } from './util.js'; drawFlyLine(scene, mapUrl, data) { let mapData = util.decode(mapUrl); this.dataKeys = {}; mapData.features.forEach((d) => { const { name, cp } = d.properties; this.dataKeys[name] = [...cp]; }); let lineGroup = new THREE.Group(); let flyLineGroup = new THREE.Group(); data.forEach((d) => { // 处理起始位置坐标转换 let sSiteName = d.source.name; let tSiteName = d.target.name; sSiteName = sSiteName.replace(RegExp("站", "g"), "市"); tSiteName = tSiteName.replace(RegExp("站", "g"), "市"); const slnglat = this.dataKeys[sSiteName]; const tlnglat = this.dataKeys[tSiteName]; const [x1, y1, z1] = this.lnglatToMector(slnglat); const [x2, y2, z2] = this.lnglatToMector(tlnglat); const curve = new THREE.QuadraticBezierCurve3( new THREE.Vector3(x1, y1, z1 + 0.28), new THREE.Vector3((x1 + x2) / 2, (y1 + y2) / 2, 3.5), new THREE.Vector3(x2, y2, z2 + 0.28) ); let line = this.createLine(curve); let color = new THREE.Vector3( 0.5999758518718452, 0.7798940272761521, 0.6181903838257632 ); // 创建骨架线和飞线 let flyLine = this.createFlyLine( curve, { speed: 0.4, color: color, number: 1, //同时跑动的流光数量 length: 0.3, //流光线条长度 size: 3, //粗细 }, 5000 ); lineGroup.add(line); flyLineGroup.add(flyLine); }); scene.add(lineGroup); scene.add(flyLineGroup); return { lineGroup, flyLineGroup, }; }
// 创建骨架线 createLine (curve) { const points = curve.getPoints(100); let geometry = new LineGeometry(); let positions = [] let colors = []; let color = new THREE.Color(); /** * HSL中使用渐变 * h — hue value between 0.0 and 1.0 * s — 饱和度 between 0.0 and 1.0 * l — 亮度 between 0.0 and 1.0 */ for ( let j = 0; j < points.length; j ++ ) { // color.setHSL( .31666+j*0.005,0.7, 0.7); //绿色 color.setHSL( .81666 + j, 0.88, 0.715 + j * 0.0025 ); //粉色 colors.push( color.r, color.g, color.b ); positions.push( points[ j ].x, points[ j ].y, points[ j ].z + 0.01 ); } geometry.setPositions( positions ); geometry.setColors( colors ); let matLine = new LineMaterial( { linewidth: 0.0006, vertexColors: true, dashed: false } ); let line = new Line2( geometry, matLine ) return line; }
import { Line2 } from "three/examples/jsm/lines/Line2"; import { LineGeometry } from "three/examples/jsm/lines/LineGeometry"; import { LineMaterial } from "three/examples/jsm/lines/LineMaterial"; // 顶点着色器 let vertexShader = ` varying vec2 vUv; attribute float percent; uniform float u_time; uniform float number; uniform float speed; uniform float length; varying float opacity; uniform float size; void main() { vUv = uv; vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 ); float l = clamp(1.0-length,0.0,1.0);//空白部分长度 gl_PointSize = clamp(fract(percent*number + l - u_time*number*speed)-l ,0.0,1.) * size * (1./length); opacity = gl_PointSize/size; gl_Position = projectionMatrix * mvPosition; }`; // 分片着色器 let fragmentShader = ` #ifdef GL_ES precision mediump float; #endif varying float opacity; uniform vec3 color; void main(){ if(opacity <=0.2){ discard; } gl_FragColor = vec4(color,1.0); }`; let commonUniforms = { u_time: { value: 0.0 }, }; /** * @param curve {THREE.Curve} 路径, * @param matSetting {Object} 材质配置项 * @param pointsNumber {Number} 点的个数 越多越细致 * */ createFlyLine (curve, matSetting, pointsNumber) { var points = curve.getPoints( pointsNumber ); var geometry = new THREE.BufferGeometry().setFromPoints( points ); let length = points.length; var percents = new Float32Array(length); for (let i = 0; i < points.length; i+=1){ percents[i] = (i/length); } geometry.setAttribute('percent', new THREE.BufferAttribute(percents,1)); let lineMaterial = this.initLineMaterial(matSetting); var flyLine = new THREE.Points( geometry, lineMaterial ); return flyLine } // 首先要写出一个使用fragmentshader生成的material并赋在点上 initLineMaterial(setting){ let number = setting ? (Number(setting.number) || 1.0) : 1.0; let speed = setting ? (Number(setting.speed) || 1.0) : 1.0; let length = setting ? (Number(setting.length) || 0.5) : 0.5; let size = setting ?(Number(setting.size) || 3.0) : 3.0; let color = setting ? setting.color || new THREE.Vector3(0,1,1) : new THREE.Vector3(0,1,1); let singleUniforms = { u_time: commonUniforms.u_time, number: {type: 'f', value:number}, speed: {type:'f',value:speed}, length: {type: 'f', value: length}, size: {type: 'f', value: size}, color: {type: 'v3', value: color} }; let lineMaterial = new THREE.ShaderMaterial({ uniforms: singleUniforms, vertexShader: vertexShader, fragmentShader: fragmentShader, transparent: true, //blending:THREE.AdditiveBlending, }); return lineMaterial; }
let posArr = [ { 'x': - 1.7049594735603837, 'y': 3.208354470512221, 'z': - 3.4350509144786985 }, { 'x': - 2.1965610576118175, 'y': 2.1955955192304506, 'z': - 3.9184792759587768 }, { 'x': - 2.2290975556080355, 'y': 2.6054406912933263, 'z': - 3.639066211507457 }, { 'x': 0.5738958419746141, 'y': - 0.44114968930852216, 'z': 4.9473255920938985 }, { 'x': - 0.9326350073394328, 'y': 2.8399222968004114, 'z': - 4.00812091773949 }, { 'x': 3.469198597393574, 'y': 1.2295167303380952, 'z': - 3.3842206934036057 }, { 'x': - 2.4019084876611916, 'y': - 2.190220428765315, 'z': 3.7991801866087123 }, { 'x': - 2.49363689878109, 'y': - 4.099696049856375, 'z': 1.4050862307450966 }, { 'x': - 2.3729307780326305, 'y': 2.840227787960863, 'z': 3.3618901878497454 }, { 'x': - 2.0636200279017873, 'y': 0.7444294629976027, 'z': - 4.493027615657812 }, { 'x': 0.47725894517680106, 'y': 2.4327372143508037, 'z': - 4.34212085796347 }, { 'x': - 2.4777001955161246, 'y': - 1.2092952460724242, 'z': 4.171163716394502 }, { 'x': - 0.03915748918627658, 'y': - 0.008362945319338826, 'z': 4.999839672648135 }, { 'x': 1.5223738738260317, 'y': - 1.032865814102439, 'z': - 4.649254348640267 }, { 'x': - 0.26640112020426315, 'y': - 4.314854187280748, 'z': 2.5121830716848077 }, { 'x': - 4.031470206741836, 'y': - 2.606648761952297, 'z': - 1.3973654511134501 }, { 'x': 0.8544382232162094, 'y': 1.5274953155132989, 'z': 4.683662390031124 }, { 'x': 3.0409624989238546, 'y': 1.76433738825175, 'z': - 3.555230043268055 }, { 'x': - 4.721251023266457, 'y': 1.2354922989397954, 'z': - 1.0878177947459262 }, { 'x': 2.1518961827021106, 'y': 3.891904027152385, 'z': - 2.285262755638206 }, { 'x': 0.8501960736517479, 'y': - 2.851729208821255, 'z': - 4.018060123480341 }, { 'x': 2.5631840141785176, 'y': 4.263234820997851, 'z': - 0.5048926326370041 }, { 'x': - 0.4580143454812531, 'y': - 2.6523265200067385, 'z': 4.213714144386437 } ]; // 绘制飞线 let targetFlyLineDatas = { 'x': - 1.7049594735603837, 'y': 3.208354470512221, 'z': - 3.4350509144786985 }; flyLineModel = flyLineUtil.drawFlyLine(posArr, targetFlyLineDatas); earthGroup.add(flyLineModel.lineGroup) earthGroup.add(flyLineModel.flyLineGroup)
// 飞线model let flyLineModel = null; let commonUniformsValue = 0.0; render() { // 飞线动画 this.flyLineAnimation(); // 地球自转动画 this.earthRotationAnimation(); scene.traverse(this.darkenNonBloomed) // 隐藏不需要辉光的物体 bloomComposer.render() scene.traverse(this.restoreMaterial) // 还原 // 更新性能插件 stats.update(); TWEEN.update(); renderer.render(scene, camera); requestAnimationFrame(this.render); // 呼吸灯效果要放到最后渲染,要不然没效果 if (composer) { composer.render(); } } // 飞线动画 flyLineAnimation() { commonUniformsValue -= 0.02; if (flyLineModel) { flyLineModel.flyLineGroup.children.forEach(item => { item.material.uniforms.u_time.value = commonUniformsValue }) } }
此工具主要分为五个功能点
drawLightBar(scene, mapUrl, datas) { let mapData = util.decode(mapUrl); this.dataKeys = {}; mapData.features.forEach(d => { const { name, cp } = d.properties; this.dataKeys[name] = [...cp]; }); this.colors = ['#fff', '#ff0']; this.colorIndex = 0; this.textures = [new THREE.TextureLoader().load(img1), new THREE.TextureLoader().load(img2), new THREE.TextureLoader().load(img3)]; this.pointsLength = 20; const sixMeshgroup = new THREE.Group(); const sixLineGroup = new THREE.Group(); const waveGroup = new THREE.Group(); const lightBarGroup = new THREE.Group(); const lightCurtainGroup = new THREE.Group(); const labelGroup = new THREE.Group(); datas.forEach((d, i) => { let siteName = d.name siteName = siteName.replace(RegExp('站', 'g'), '市'); const lnglat = this.dataKeys[siteName]; const [x, y, z] = this.lnglatToMector(lnglat); let color = '#fff'; if (d.main) { color = '#ff0' } // 绘制六边体 sixMeshgroup.add(this.drawSixMesh(x, y, z, color)); // 绘制6边线 sixLineGroup.add(this.drawSixLineLoop(x, y, z, color)); // 绘制扩散波 waveGroup.add(this.drawWaveMesh(x, y, z, color)); // 绘制柱子 const [plane1, plane2] = this.drawPlane(x, y, z, d.value, color); lightBarGroup.add(plane2); lightBarGroup.add(plane1); // 绘制柱子光幕 lightCurtainGroup.add(this.drawPlanelightCurtain(x, y, z, d.value, color)); // 绘制站名 labelGroup.add(this.drawLabel(x, y, 2.3, d, color)); }); /** * 这里有个坑:不知道为啥,如果把mosh或line放到lightCurtainGroup前面加载到scene里面,透明度会被遮挡 * 所以,这里是按照光柱-光柱光罩-底部元素-标题来添加到scene中 */ scene.add(lightBarGroup); scene.add(lightCurtainGroup); scene.add(sixMeshgroup); scene.add(sixLineGroup); scene.add(waveGroup); scene.add(labelGroup); return { lightBarGroup: lightBarGroup, lightCurtainGroup: lightCurtainGroup, sixMeshgroup: sixMeshgroup, sixLineGroup: sixLineGroup, waveGroup: waveGroup, labelGroup: labelGroup } }
drawSixMesh(x, y, z, color) { let radius = 0.06; let segments = 6; if (color !== '#fff') { radius = 0.1 } const geometry = new THREE.CircleGeometry(radius, segments); const material = new THREE.MeshBasicMaterial({ color: color }); const mesh = new THREE.Mesh(geometry, material); mesh.position.set(x, y, z + 0.28); return mesh; } drawSixLineLoop(x, y, z, color) { // 绘制六边型 let innerRadius = 0.12; let outerRadius = 0.15; let thetaSegments = 36; if (color !== '#fff') { innerRadius = 0.15 outerRadius = 0.2 } const geometry = new THREE.RingGeometry(innerRadius, outerRadius, thetaSegments); const material = new THREE.MeshBasicMaterial({ color: color, transparent: true }); // geometry.vertices.shift(); const line = new THREE.Mesh(geometry, material); line.position.set(x, y, z + 0.28); return line; }
drawWaveMesh(x, y, z, color) { let material = new THREE.MeshBasicMaterial( { color: color === '#fff' ? '#22ffcc' : '#ff0', map: this.textures[2], transparent: true, //使用背景透明的png贴图,注意开启透明计算 opacity: 1.0, // side: THREE.DoubleSide, //双面可见 depthWrite: false, //禁止写入深度缓冲区数据 } ); const geometry = new THREE.PlaneGeometry(1, 1); let mesh = new THREE.Mesh( geometry, material ); let size = 5 * 0.06;//矩形平面Mesh的尺寸 if (color !== '#fff') { size = 5 * 0.09 } mesh.size = size;//自顶一个属性,表示mesh静态大小 mesh.scale.set( size, size, size );//设置mesh大小 mesh._s = 0.5;//自定义属性._s表示mesh在原始大小基础上放大倍数 光圈在原来mesh.size基础上1~2倍之间变化 mesh.position.set(x, y, z + 0.28); mesh.layers.enable(1); return mesh; }
let lightBarModel = null; let posArr = [ { 'x': - 1.7049594735603837, 'y': 3.208354470512221, 'z': - 3.4350509144786985 }, { 'x': - 2.1965610576118175, 'y': 2.1955955192304506, 'z': - 3.9184792759587768 }, { 'x': - 2.2290975556080355, 'y': 2.6054406912933263, 'z': - 3.639066211507457 }, { 'x': 0.5738958419746141, 'y': - 0.44114968930852216, 'z': 4.9473255920938985 }, { 'x': - 0.9326350073394328, 'y': 2.8399222968004114, 'z': - 4.00812091773949 }, { 'x': 3.469198597393574, 'y': 1.2295167303380952, 'z': - 3.3842206934036057 }, { 'x': - 2.4019084876611916, 'y': - 2.190220428765315, 'z': 3.7991801866087123 }, { 'x': - 2.49363689878109, 'y': - 4.099696049856375, 'z': 1.4050862307450966 }, { 'x': - 2.3729307780326305, 'y': 2.840227787960863, 'z': 3.3618901878497454 }, { 'x': - 2.0636200279017873, 'y': 0.7444294629976027, 'z': - 4.493027615657812 }, { 'x': 0.47725894517680106, 'y': 2.4327372143508037, 'z': - 4.34212085796347 }, { 'x': - 2.4777001955161246, 'y': - 1.2092952460724242, 'z': 4.171163716394502 }, { 'x': - 0.03915748918627658, 'y': - 0.008362945319338826, 'z': 4.999839672648135 }, { 'x': 1.5223738738260317, 'y': - 1.032865814102439, 'z': - 4.649254348640267 }, { 'x': - 0.26640112020426315, 'y': - 4.314854187280748, 'z': 2.5121830716848077 }, { 'x': - 4.031470206741836, 'y': - 2.606648761952297, 'z': - 1.3973654511134501 }, { 'x': 0.8544382232162094, 'y': 1.5274953155132989, 'z': 4.683662390031124 }, { 'x': 3.0409624989238546, 'y': 1.76433738825175, 'z': - 3.555230043268055 }, { 'x': - 4.721251023266457, 'y': 1.2354922989397954, 'z': - 1.0878177947459262 }, { 'x': 2.1518961827021106, 'y': 3.891904027152385, 'z': - 2.285262755638206 }, { 'x': 0.8501960736517479, 'y': - 2.851729208821255, 'z': - 4.018060123480341 }, { 'x': 2.5631840141785176, 'y': 4.263234820997851, 'z': - 0.5048926326370041 }, { 'x': - 0.4580143454812531, 'y': - 2.6523265200067385, 'z': 4.213714144386437 } ]; // 绘制标点 lightBarModel = lightBarUtil.drawLightBar(posArr) earthGroup.add(lightBarModel.sixMeshgroup) earthGroup.add(lightBarModel.sixLineGroup) earthGroup.add(lightBarModel.waveGroup)
render() { // 光柱扩散波动画 this.lightBarAnimation(); // 飞线动画 this.flyLineAnimation(); // 地球自转动画 this.earthRotationAnimation(); scene.traverse(this.darkenNonBloomed) // 隐藏不需要辉光的物体 bloomComposer.render() scene.traverse(this.restoreMaterial) // 还原 // 更新性能插件 // stats.update(); TWEEN.update(); renderer.render(scene, camera); requestAnimationFrame(this.render); // 呼吸灯效果要放到最后渲染,要不然没效果 if (composer) { composer.render(); } } // 光柱扩散波动画 lightBarAnimation() { if (lightBarModel) { lightBarModel.waveGroup.children.forEach(mesh => { mesh._s += 0.008; mesh.scale.set( mesh.size * mesh._s, mesh.size * mesh._s, mesh.size * mesh._s ); if (mesh._s <= 1.5) { //mesh._s=1,透明度=0 mesh._s=1.5,透明度=1 mesh.material.opacity = ( mesh._s - 1 ) * 2; } else if (mesh._s > 1.5 && mesh._s <= 2) { //mesh._s=1.5,透明度=1 mesh._s=2,透明度=0 mesh.material.opacity = 1 - ( mesh._s - 1.5 ) * 2; } else { mesh._s = 1.0; } } ); } }
这一篇文章非常简单,但是也非常出效果
阅读前面几篇文章,都可以看到有顶部、左侧、右侧的面板,主要我不会录gif图片,大屏元素面板还有一些动态加载的效果。
Vue 提供了 transition 的封装组件,可以给任何元素和组件添加进入/离开过渡;在进入/离开的过渡中,会有 6 个 class 切换:v-enter 、v-enter-active 、v-enter-to 、v-leave 、v-leave-active 、v-leave-to。
我们正好使用这个特性来达到面板进入的效果
<div class="page"> <transition enter-active-class="animated fadeInDown" leave-active-class="animated fadeOutUp" appear > <navigation /> </transition> <transition enter-active-class="animated fadeInLeft" leave-active-class="animated fadeOutLeft" appear > <div class="left"> <yyzsr /> <lrze /> <jlr /> <sfzj /> <yysrbl /> </div> </transition> <div class="content" > <div class="c-left"> <div class="c-l-1"> <div class="label"> 勘探与生产 </div> <div class="value"> 8,287.6 </div> </div> <div class="c-l-2"> <div class="label"> 炼油与化工 </div> <div class="value"> 5,342.2 </div> </div> <div class="c-l-3"> <div class="label"> 销售 </div> <div class="value"> 11,846.5 </div> </div> <div class="c-l-4"> <div class="label"> 天然气 </div> <div class="value"> 4,214.9 </div> </div> <div class="c-l-5"> <div class="label"> 中油国际 </div> <div class="value"> 11,377.4 </div> </div> <div class="c-l-6"> <div class="label"> 世界贸易 </div> <div class="value"> 23,887.8 </div> </div> </div> <div class="c-right"> <div class="c-r-1"> <div class="label"> 亚太合作区 </div> <div class="value"> 8,287.6 </div> </div> <div class="c-r-2"> <div class="label"> 中亚-俄罗斯合作区 </div> <div class="value"> 14,738.9 </div> </div> <div class="c-r-3"> <div class="label"> 中东合作区 </div> <div class="value"> 34,784.6 </div> </div> <div class="c-r-4"> <div class="label"> 美洲合作区 </div> <div class="value"> 5,979.8 </div> </div> <div class="c-r-5"> <div class="label"> 非洲合作区 </div> <div class="value"> 44,468.6 </div> </div> <div class="c-r-6"> <div class="label"> 特殊地区 </div> <div class="value"> 2,986.6 </div> </div> </div> </div> <transition enter-active-class="animated fadeInRight" leave-active-class="animated fadeOutRight" appear > <div class="right"> <yqcl /> <gnyyjgl /> <gncpyxsl /> <gntrqxsl /> <yftrqd /> </div> </transition> </div>
PS:项目源码及3d模型会在第一篇文章给出下载地址,或者添加wx:z13964122832备注“全球图源码”
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。