基于threejs实现中国地图轮廓动画(1)

本文介绍了如何使用SVG路径和THREE.js库在地图投影问题中生成动态的亮光点动画,通过SVG数据处理和Three.js的几何体及着色器实现地理多边形的渲染,解决地图失真的问题。
摘要由CSDN通过智能技术生成

// coordinate 多边形数据

coordinate.forEach((rows:any) => {

const line = lineDraw(rows, outLineColor ?? ‘0xffffff’);

chinaLines.add(line);

});

});

positions = new Float32Array(linePinots.flat(1));

// 设置顶点

opacityGeometry.setAttribute(“position”, new BufferAttribute(positions, 3));

// 设置 粒子透明度为 0

opacitys = new Float32Array(positions.length).map(() => 0);

opacityGeometry.setAttribute(“aOpacity”, new BufferAttribute(opacitys, 1));

// 控制 颜色和粒子大小

const params = {

pointSize: 2.0,

pointColor: tintColor

}

const vertexShader = `

attribute float aOpacity;

uniform float uSize;

varying float vOpacity;

void main(){

gl_Position = projectionMatrixmodelViewMatrixvec4(position,1.0);

gl_PointSize = uSize;

vOpacity=aOpacity;

}

`

const fragmentShader = `

varying float vOpacity;

uniform vec3 uColor;

float invert(float n){

return 1.-n;

}

void main(){

if(vOpacity <=0.2){

discard;

}

vec2 uv=vec2(gl_PointCoord.x,invert(gl_PointCoord.y));

vec2 cUv=2.*uv-1.;

vec4 color=vec4(1./length(cUv));

color*=vOpacity;

color.rgb*=uColor;

gl_FragColor=color;

}

`

const material = new ShaderMaterial({

vertexShader: vertexShader,

fragmentShader: fragmentShader,

transparent: true, // 设置透明

uniforms: {

uSize: {

value: params.pointSize

},

uColor: {

value: new Color(params.pointColor)

}

}

})

const opacityPoints = new Points(opacityGeometry, material)

return {

chinaLines,

opacityPoints,

opacitys,

linePinots,

opacityGeometry

}

}

// let indexBol = true

/**

  • 边框 图形绘制

  • @param polygon 多边形 点数组

  • @param color 材质颜色

  • */

function lineDraw(polygon:any, color:ColorRepresentation) {

const lineGeometry = new BufferGeometry();

const pointsArray = new Array();

polygon.forEach((row:any) => {

const projectionRow = projection(row);

if (!projectionRow) {

return

}

let x = projectionRow[0]

let y = projectionRow[1]

// 创建三维点

pointsArray.push(new Vector3(x, -y, 0));

linePinots.push([x, -y, 0]);

});

// 放入多个点

lineGeometry.setFromPoints(pointsArray);

const lineMaterial = new LineBasicMaterial({

color: color,

});

return new Line(lineGeometry, lineMaterial);

}

export { countryLine };

全部代码: demo

遇到问题



geojson 版本我们需要将提供的经纬度坐标点转场成平面,各平台算法不同,投影失真情况不同,所以一些情况地图会失真无法重合。

我们刚才使用卡墨托投影转换,也会失真并且和echarts 地图轮廓对不上,所以想起其他方案。

我们利用svg路径来取点,UI提供的svg地图轮廓肯定是一致的。

SVG版本



设计思路:

  1. 加载svg 取所有的点

  2. 根绝点来创建threejs 亮光点

  3. 移动动画

核心代码:

import * as THREE from ‘three’;

import { initRender } from ‘./render’;

import { initScene } from ‘./scene’;

import { initCamera } from ‘./camera’;

// import { countryLine } from “./countryPolygon”;

import { SVGLoader } from ‘three/examples/jsm/loaders/SVGLoader.js’;

export interface OutLineConfig {

outline: boolean;

outlineColor?: THREE.ColorRepresentation;

tintColor: THREE.ColorRepresentation;

speed: number;

tintLength?: number;

tintPointSize?: number;

}

class MapOutline {

private parentDom: HTMLElement;

private width: number;

private height: number;

private renderer: THREE.WebGLRenderer;

private scene: THREE.Scene;

private camera: THREE.PerspectiveCamera;

private opacitys: Float32Array | null = null;

private linePinots: any[] = [];

private opacityGeometry: THREE.BufferGeometry | null = null;

private currentPos = 0;

public constructor(

containerId: string,

public config: OutLineConfig = { outline: false, speed: 3, tintColor: ‘#008792’ },

) {

this.parentDom = document.getElementById(containerId)!;

this.width = this.parentDom.offsetWidth;

this.height = this.parentDom.offsetHeight;

this.renderer = initRender(this.width, this.height);

this.parentDom?.appendChild(this.renderer.domElement);

this.scene = initScene();

this.camera = initCamera(this.width, this.height);

}

public render = () => {

const loader = new SVGLoader();

loader.load(‘./chinaLine.svg’, (data) => {

const paths = data.paths;

const group = new THREE.Group();

group.scale.multiplyScalar(0.34);

group.position.x = -117;

group.position.y = 90;

group.scale.y *= -1;

let allPoints: any[] = [];

let pointsMesh: THREE.Points | null = null;

// eslint-disable-next-line @typescript-eslint/prefer-for-of

for (let i = 0; i < paths.length; i++) {

const path = paths[i];

const strokeColor = path.userData?.style.stroke;

const material = new THREE.MeshBasicMaterial({

color: ‘red’,

opacity: path.userData?.style.strokeOpacity,

transparent: path.userData?.style.strokeOpacity < 1,

side: THREE.DoubleSide,

depthWrite: false,

});

for (let j = 0, jl = path.subPaths.length; j < jl; j++) {

const subPath = path.subPaths[j];

let subPoints = subPath.getPoints();

allPoints = allPoints.concat(subPoints);

const geometry = SVGLoader.pointsToStroke(subPath.getPoints(), path.userData?.style);

if (geometry) {

const mesh = new THREE.Mesh(geometry, material);

group.add(mesh);

}

}

allPoints = allPoints.map((item) => {

item.z = 0;

return item;

});

for (const point of allPoints) {

this.linePinots.push(point.x * 0.34 - 117, -point.y * 0.34 + 90, point.z);

}

this.opacityGeometry = new THREE.BufferGeometry();

this.opacitys = new Float32Array(this.linePinots.length).map(() => 0);

this.opacityGeometry.setAttribute(‘position’, new THREE.Float32BufferAttribute(this.linePinots, 3));

this.opacityGeometry.setAttribute(‘aOpacity’, new THREE.BufferAttribute(this.opacitys, 1));

// 控制 颜色和粒子大小

const params = {

pointSize: 5.0,

pointColor: ‘DarkOrange’,

};

const vertexShader = `

attribute float aOpacity;

uniform float uSize;

varying float vOpacity;

void main(){

gl_Position = projectionMatrixmodelViewMatrixvec4(position,1.0);

gl_PointSize = uSize;

vOpacity=aOpacity;

}

`;

const fragmentShader = `
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数前端工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Web前端开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上前端开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:前端)

核心竞争力,怎么才能提高呢?

成年人想要改变生活,逆转状态?那就开始学习吧~

万事开头难,但是程序员这一条路坚持几年后发展空间还是非常大的,一切重在坚持。

为了帮助大家更好更高效的准备面试,特别整理了《前端工程师面试手册》电子稿文件。

前端面试题汇总

JavaScript

性能

linux

前端资料汇总

完整版PDF资料免费分享,只需你点赞支持,动动手指点击此处就可领取了

前端工程师岗位缺口一直很大,符合岗位要求的人越来越少,所以学习前端的小伙伴要注意了,一定要把技能学到扎实,做有含金量的项目,这样在找工作的时候无论遇到什么情况,问题都不会大。

性能

linux

前端资料汇总

完整版PDF资料免费分享,只需你点赞支持,动动手指点击此处就可领取了

前端工程师岗位缺口一直很大,符合岗位要求的人越来越少,所以学习前端的小伙伴要注意了,一定要把技能学到扎实,做有含金量的项目,这样在找工作的时候无论遇到什么情况,问题都不会大。

  • 7
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值