精华内容
下载资源
问答
  • 三级联动世界城市英文版
  • 主要介绍了js显示世界时间的方法,可现实世界各大城市的当前时间,涉及JavaScript针对时间的获取与运算操作技巧,需要的朋友可以参考下
  • js世界时钟,比较好看,比较全 有16个时区的,有重点城市
  • sim.jsJavascript进行城市模拟
  • three.js实现世界地图城市迁徙图

    千次阅读 2019-09-04 23:08:49
    three.js实现世界地图城市迁徙图 初始化场景、相机、渲染器,设置相机位置。 // 初始化场景 var scene = new THREE . Scene ( ) ; // 初始化相机,第一个参数为摄像机视锥体垂直视野角度,第二个参数...

    概况如下:

    1、THREE.CylinderGeometryTHREE.SphereGeometry绘制地图上的标记;
    2、THREE.CanvasTexture用于加载canvas绘制的字体;
    3、THREE.ShapeMeshLine用于实现平面地图;
    4、THREE.ExtrudeGeometry用于将绘制的平面地图沿Z轴拉伸,出现3d效果;
    5、THREE.CubicBezierCurve3用于绘制轨迹曲线;

    效果图如下:在这里插入图片描述
    预览地址:three.js实现世界地图城市迁徙图

    初始化场景、相机、渲染器,设置相机位置。

    // 初始化场景
    var scene = new THREE.Scene();
    // 初始化相机,第一个参数为摄像机视锥体垂直视野角度,第二个参数为摄像机视锥体长宽比,
    // 第三个参数为摄像机视锥体近端面,第四个参数为摄像机视锥体远端面
    var camera = new THREE.PerspectiveCamera(20, dom.clientWidth / dom.clientHeight, 1, 100000);
    // 设置相机位置,对应参数分别表示x,y,z位置
    camera.position.set(0, 0, 400);
    var renderer = new THREE.WebGLRenderer({
    	  alpha: true,
    	  antialias: true
    });
    

    设置场景窗口尺寸,并且初始化控制器,窗口尺寸默认与浏览器窗口尺寸保持一致,最后将渲染器加载到dom中。

    // 设置窗口尺寸,第一个参数为宽度,第二个参数为高度
    renderer.setSize(dom.clientWidth, dom.clientHeight);
    // 初始化控制器
    var orbitcontrols = new THREE.OrbitControls(camera,renderer.domElement);
    // 将渲染器加载到dom中
    dom.appendChild(renderer.domElement);
    

    绘制平面地图方法,通过THREE.Shape来实现。

    // 绘制地图函数
    var drawShape = function (pos) {
    	var shape = new THREE.Shape();
    	// 计算平均每格占比
    	var average = getAverage();
    	shape.moveTo(pos[0][0], pos[0][1]);
    	pos.forEach(function (item) {
    		shape.lineTo(item[0], item[1]);
    	})
    	return shape;
    }
    

    ExturdeGeometry配置参数。

    // ExturdeGeometry配置参数
    var options = {
    	depth: zHeight, // 定义图形拉伸的深度,默认100
    	steps: 0, // 拉伸面方向分为多少级,默认为1
    	bevelEnabled: true, // 表示是否有斜角,默认为true
    	bevelThickness: 0, // 斜角的深度,默认为6
    	bevelSize: 0, // 表示斜角的高度,高度会叠加到正常高度
    	bebelSegments: 0, // 斜角的分段数,分段数越高越平滑,默认为1
    	curveSegments: 0 // 拉伸体沿深度方向分为多少段,默认为1
    }
    

    将平面地图拉伸,模拟出现3d效果,通过THREE.ExtrudeGeometry来实现。

    // 将shape转换为ExtrudeGeometry
    var transition3d = function (shapeObj, identify) {
    	var geometry = new THREE.ExtrudeGeometry(shapeObj, options);
        var material1 = new THREE.MeshBasicMaterial({
        	color: faceColor
        });
        var material2 = new THREE.MeshBasicMaterial({
        	color: sideColor
        });
        // 绘制地图
        shapeGeometryObj['shapeGeometry' + identify] = new THREE.Mesh(geometry, [material1, material2]);
        // 将地图加入场景
        scene.add(shapeGeometryObj['shapeGeometry' + identify])
    }
    

    绘制世界地图参数方法

    // 计算绘制地图参数函数
    var drawShapeOptionFun = function () {
    	// 绘制世界地图
    	worldGeometry.features.forEach(function (worldItem, worldItemIndex) {
    		var length = worldItem.geometry.coordinates.length;
    		var multipleBool = length > 1 ? true : false;
    		worldItem.geometry.coordinates.forEach(function (worldChildItem, worldChildItemIndex) {
    			if (multipleBool) {
    				// 值界可以使用的经纬度信息
    				if (worldChildItem.length && worldChildItem[0].length == 2) {
    					transition3d(drawShape(worldChildItem), '' + worldItemIndex + worldChildItemIndex);
    				}
    				// 需要转换才可以使用的经纬度信息
    				if (worldChildItem.length && worldChildItem[0].length > 2) {
    					worldChildItem.forEach(function (countryItem, countryItenIndex) {
    						transition3d(drawShape(countryItem), '' + worldItemIndex + worldChildItemIndex + countryItenIndex);
    					})
    				}
    			} else {
    				var countryPos = null;
    				if (worldChildItem.length > 1) {
    					countryPos = worldChildItem;
    				} else {
    					countryPos = worldChildItem[0];
    				}
    				if (countryPos) {
    					transition3d(drawShape(countryPos), '' + worldItemIndex + worldChildItemIndex);
    				}
    			}
    		})
    	})
    }
    

    通过canvas实现说明文字方法。

    // canvas实现文字函数
    var getCanvasFont = function (w, h, textValue, fontColor) {
    	var canvas = document.createElement('canvas');
    	canvas.width = w;
    	canvas.height = h;
    	var ctx = canvas.getContext('2d');
    	ctx.fillStyle = textBackground;
    	ctx.fillRect(0, 0, w, h);
    	ctx.font = h + "px '微软雅黑'";
    	ctx.textAlign = 'center';
    	ctx.textBaseline = 'middle';
    	ctx.fillStyle = fontColor;
    	ctx.fillText(textValue, w / 2, h / 2);
    	$('body').append(canvas)
    	return canvas;
    }
    

    绘制城市标记方法。

    /** 绘制标记函数
     * pos表示经纬度信息
     * textValue表示标记内容
     * fontColor表示标记字体颜色
     * fontSize表示字体大小
    **/
    var drawMarkingFont = function (option, markingIndex) {
    	var average = getAverage();
    	var cityX = option.pos[0];
    	var cityY = option.pos[1];
    	var markingGroup = new THREE.Group();
    	// 圆锥体
    	var cylinder = new THREE.Mesh(
    		new THREE.CylinderGeometry(circularRadio, 0, circularHeight, 50, 50, false),
    		new THREE.MeshBasicMaterial({
    			color: markingColor
    		})
    	)
    	// 球体
    	var ball = new THREE.Mesh(
    		new THREE.SphereGeometry(circularRadio, 30, 30),
    		new THREE.MeshBasicMaterial({
    			color: markingColor
    		})
    	)
    	ball.position.set(cityX, cityY, circularHeight + zHeight);
    	cylinder.position.set(cityX, cityY, circularHeight / 2 + zHeight);
    	cylinder.rotation.x = 1.5;
    	// 添加文字说明
    	var textLength = option.textValue.split('').length;
    	var texture = new THREE.CanvasTexture(getCanvasFont(textLength * option.fontSize * average, option.fontSize * average, option.textValue, option.fontColor));
    	var fontMesh = new THREE.Sprite(
    		new THREE.SpriteMaterial({
    			map: texture
    		})
    	)
    	fontMesh.scale.x = option.fontSize / average * textLength;
    	fontMesh.scale.y = option.fontSize / average;
    	// 定义提示文字显示位置
    	fontMesh.position.set(cityX, cityY, circularHeight + circularRadio / 2 + zHeight / 2 + option.fontSize / average + 0.5);
    	markingGroup.add(ball);
    	markingGroup.add(cylinder);
    	markingGroup.add(fontMesh);
    	markingObj['markingGroup' + markingIndex] = markingGroup;
    	scene.add(markingGroup);
    }
    

    城市迁徙线条绘制。

    // 绘制迁徙线条函数
    var drawMetapLine = function (v0, v3) {
    	var v1 = {};
    	v1.x = (v0.x + v3.x) / 2;
    	v1.y = (v0.y + v3.y) / 2;
    	v1.z = 6;
    	// 绘制贝塞尔曲线
    	var curve = new THREE.CubicBezierCurve3(v0, v1, v1, v3);
    	var geometry = new THREE.Geometry();
    	geometry.vertices = curve.getPoints(100);
    	var line = new MeshLine();
    	line.setGeometry(geometry);
    	var material = new MeshLineMaterial({
    		color: meshLineColor,
    		lineWidth: lineWidth
    	})
    	return {
    		curve: curve,
    		lineMesh: new THREE.Mesh(line.geometry, material)
    	}
    }
    

    绘制迁徙图方法。

    // 绘制迁徙图
    var drawMetap = function () {
    	var average = getAverage();
    	var beijing = {x: 116.4551, y: 40.2539, z: zHeight};
    	var lundun = {x: 0.5, y: 51.3, z: zHeight};
    	// 经纬度信息
    	var metapArray = [];
    	// 组装线条连接经纬度信息
    	markingPos.marking.forEach(function (markingItem) {
        	metapArray.push({
        		x: markingItem.pos[0],
        		y: markingItem.pos[1],
        		z: zHeight
        	})
        })
    	// 线条集合
    	var animateDots = [];
    	// 存放线条对象集合
    	var groupLines = new THREE.Group();
    	// 绘制迁徙线条
    	metapArray.forEach(function (metapItem, metapIndex) {
    		if (metapIndex > 0) {
    			var line = drawMetapLine(metapArray[0], metapItem);
    	    	groupLines.add(line.lineMesh);
    	    	animateDots.push(line.curve.getPoints(metapNum));
    		}
    	})
    	// 添加迁徙线条到场景中
    	scene.add(groupLines);
    	// 添加线上滑动的物质
    	var aGroup = new THREE.Group();
    	for (var i = 0; i < animateDots.length; i ++) {
    		for (var j = 0; j < markingNum; j ++) {
    			var aGeo = new THREE.SphereGeometry(dotWidth, 10, 10);
    			var aMater = new THREE.MeshPhongMaterial({
    				color: markingColor,
    				transparent: true,
    				opacity: 1 - j * 1 / markingNum
    			})
    			var aMesh = new THREE.Mesh(aGeo, aMater);
    			aGroup.add(aMesh);
    		}
    	}
    	var vIndex = 0;
    	// 表示第一次循环运行
    	var firstBool = true;
    	function animationLine() {
    		aGroup.children.forEach(function (elem, index) {
    			var _index = parseInt(index / markingNum);
    			// 保证当前数组与迁徙轨迹匹配
    			var index2 = index - _index * markingNum;
    			var _vIndex = 0;
    			if (firstBool) {
    				_vIndex = vIndex - index2 % markingNum >= 0 ? vIndex - index2 % markingNum : 0;
    			} else {
    				_vIndex = vIndex - index2 % markingNum >= 0 ? vIndex - index2 % markingNum : 150 + vIndex - index2;
    			}
    			var v = animateDots[_index][_vIndex];
    			elem.position.set(v.x, v.y, v.z);
    		})
    		vIndex ++;
    		if (vIndex > metapNum) {
    			vIndex = 0;
    		}
    		if (vIndex == 150 && firstBool) {
    			firstBool = false;
    		}
    		requestAnimationFrame(animationLine);
    	}
    	scene.add(aGroup);
    	animationLine();
    }
    

    世界地图城市迁徙通过position值来实现位置的确认,动画使用requestAnimationFrame来实现。

    // 执行函数
    var render = function () {
    	scene.rotation.x = -0.8;
        renderer.render(scene, camera);
        orbitcontrols.update();
        requestAnimationFrame(render);
    }
    
    展开全文
  • 美食街 一个完整JavaScript应用程序,可用于查找世界任何城市的餐厅并获得当地专家的评论。
  • world_city世界城市

    2018-08-08 11:27:00
    世界上最完整的城市 世界上最完整的城市 世界上最完整的城市
  • [removed][removed] [removed][removed] [removed][removed] </head>... jQuery世界城市三级联动下拉选择代码是一款jQuery Select世界城市选择器特效,如果只有二级,那么会有一个空值。
  • 全国城市js数组

    热门讨论 2007-07-06 13:07:13
    全国城市省份 js 下拉列表框.
  • js全国城市数组下载

    2020-04-10 11:47:09
    js全国城市数组下载
  • Javascript省份城市js代码)

    万次阅读 2012-05-06 10:49:29
    //js 代码 function changecity(){  index = form1.Province.options.selectedIndex-1;  form1.City.length = City[index].length;  for(var i = 0;i  {  var text = City[index][i].split("-");  form...

    //js 代码
    function changecity(){
     index = form1.Province.options.selectedIndex-1;
     form1.City.length = City[index].length;
     for(var i = 0;i<City[index].length;i++)
      {
       var text = City[index][i].split("-");
      form1.City.options[i].text = text[0];
      form1.City.options[i].value =text[1];
      }
    }

    var City = new Array(  
       new Array(
          //"江苏省(全部地区)-江苏省(全部地区)",
          "江阴市-江阴市",
          "无锡市-无锡市",
          "宜兴市-宜兴市",
          "常州市-常州市",
          "南京市-南京市",
          "溧水县-溧水县",
          "高淳县-高淳县",
          "苏州市-苏州市",
          "常熟市-常熟市",
          "张家港市-张家港市",
          "昆山市-昆山市",
          "吴江市-吴江市",
          "太仓市-太仓市",
          "溧阳市-溧阳市",
          "金坛市-金坛市",
          "南通市-南通市",
          "海安县-海安县",
          "如东县-如东县",
          "启东市-启东市",
          "如皋市-如皋市",
          "通州市-通州市",
          "海门市-海门市",
          "镇江市-镇江市",
          "丹阳市-丹阳市",
          "扬中市-扬中市",
          "句容市-句容市",
          "徐州市-徐州市",
          "丰县-丰县",
          "沛县-沛县",
          "铜山县-铜山县",
          "睢宁县-睢宁县",
          "新沂市-新沂市",
          "邳州市-邳州市",
          "连云港市-连云港市",
          "赣榆县-赣榆县",
          "东海县-东海县",
          "灌云县-灌云县",
          "淮安市-淮安市",
          "涟水县-涟水县",
          "洪泽县-洪泽县",
          "盱眙县-盱眙县",
          "金湖县-金湖县",
          "盐城市-盐城市",
          "响水县-响水县",
          "滨海县-滨海县",
          "阜宁县-阜宁县",
          "射阳县-射阳县",
          "建湖县-建湖县",
          "盐都县-盐都县",
          "东台县-东台县",
          "大丰市-大丰市",
          "扬州市-扬州市",
          "宝应县-宝应县",
          "仪征县-仪征县",
          "高邮市-高邮市",
          "江都市-江都市",
          "宿迁市-宿迁市",
          "宿豫县-宿豫县",
          "沭阳县-沭阳县",
          "泗阳县-泗阳县",
          "泗洪县-泗洪县",
          "泰州市-泰州市",
          "兴化市-兴化市",
          "靖江市-靖江市",
          "泰兴市-泰兴市",
          "姜堰市-姜堰市"),
        new Array("北京-北京"),
        new Array("天津-天津"),
        new Array("上海-上海"),
        new Array("重庆-重庆"),
        new Array(
          //"广东省 (全部地区)-广东省 (全部地区)",
          "广州-广州",
          "深圳-深圳",
          "东莞-东莞",
          "中山-中山",
          "佛山-佛山",
          "珠海-珠海",
          "惠州-惠州",
          "汕头-汕头",
          "湛江-湛江",
          "潮州-潮州",
          "潮阳-潮阳",
          "江门-江门",
          "汕尾-汕尾",
          "茂名-茂名",
          "阳江-阳江",
          "肇庆-肇庆",
          "梅州-梅州",
          "云浮-云浮",
          "清远-清远",
          "韶关-韶关",
          "河源-河源",
          "揭阳-揭阳",
          "其他-其他"),
       
        new Array(
          //"浙江省(全部地区)-浙江省(全部地区)",
          "杭州-杭州",
          "宁波-宁波",
          "温州-温州",
          "金华-金华",
          "义乌-义乌",
          "绍兴-绍兴",
          "湖州-湖州",
          "余姚-余姚",
          "临海-临海",
          "萧山-萧山",
          "临安-临安",
          "诸暨-诸暨",
          "嵊泗-嵊泗",
          "兰溪-兰溪",
          "嘉兴-嘉兴",
          "海宁-海宁",
          "桐乡-桐乡",
          "衢州-衢州",
          "上虞-上虞",
          "嵊州-嵊州",
          "慈溪-慈溪",
          "丽水-丽水",
          "瓯海-瓯海",
          "黄岩-黄岩",
          "椒江-椒江",
          "其他-其他"),
        new Array(
          //"福建省(全部地区)-福建省(全部地区)",
          "福州-福州",
          "厦门-厦门",
          "泉州-泉州",
          "漳州-漳州",
          "石狮-石狮",
          "三明-三明",
          "南平-南平",
          "莆田-莆田",
          "永安-永安",
          "邵武-邵武",
          "龙岩-龙岩",
          "其他-其他"),
        new Array(
          //"湖南省(全部地区)-湖南省(全部地区)",
          "长沙 -长沙",
          "湘潭-湘潭",
          "岳阳 -岳阳",
          "株洲-株洲",
          "张家界-张家界",
          "衡阳-衡阳",
          "郴州-郴州",
          "湘乡-湘乡",
          "汨罗-汨罗",
          "常德-常德",
          "津市-津市",
          "吉首-吉首",
          "娄底-娄底",
          "涟源-涟源",
          "冷水江-冷水江",
          "怀化-怀化",
          "洪江-洪江",
          "益阳 -益阳",
          "耒阳-耒阳",
          "邵阳-邵阳",
          "永州-永州",
          "冷水滩-冷水滩",
          "其他-其他"),
        new Array(
          //"湖北省(全部地区)-湖北省(全部地区)",
          "武汉-武汉",
          "宜昌-宜昌",
          "孝感-孝感",
          "荆州-荆州",
          "襄樊-襄樊",
          "老河口-老河口",
          "枝城-枝城",
          "枣阳-枣阳",
          "荆门-荆门",
          "宜城-宜城",
          "黄石-黄石",
          "鄂州-鄂州",
          "黄冈-黄冈",
          "咸宁-咸宁",
          "武穴-武穴",
          "蒲昕-蒲昕",
          "麻城-麻城",
          "十堰-十堰",
          "恩施-恩施",
          "丹江口-丹江口",
          "利川-利川",
          "天门-天门",
          "汉川-汉川",
          "洪湖-洪湖",
          "应城-应城",
          "潜江-潜江",
          "安陆-安陆",
          "仙桃-仙桃",
          "随州-随州",
          "石首-石首",
          "其他-其他"),
        new Array(
          //"山东省(全部地区)-山东省(全部地区)",
          "济南-济南",
          "青岛-青岛",
          "烟台-烟台",
          "淄博-淄博",
          "潍坊 -潍坊",
          "临沂-临沂",
          "莱芜-莱芜",
          "济宁-济宁",
          "荷泽-荷泽",
          "日照-日照",
          "藤州-藤州",
          "聊城-聊城",
          "德州-德州",
          "滨州-滨州",
          "临清-临清",
          "青州 -青州",
          "威海-威海",
          "泰安-泰安",
          "新泰-新泰",
          "曲阜-曲阜",
          "东营-东营",
          "诸城-诸城",
          "枣庄-枣庄",
          "其他-其他"),
        new Array(
          //"辽宁省(全部地区)-辽宁省(全部地区)",
          "沈阳-沈阳",
          "铁岭-铁岭",
          "抚顺-抚顺",
          "大连-大连",
          "本溪-本溪",
          "营口-营口",
          "锦州-锦州",
          "兴城-兴城",
          "北票-北票",
          "盘锦-盘锦",
          "辽阳-辽阳",
          "铁法 -铁法",
          "鞍山-鞍山",
          "瓦房店-瓦房店",
          "丹东-丹东",
          "锦西-锦西",
          "朝阳-朝阳",
          "阜新-阜新",
          "海城-海城",
          "其他-其他"),
        new Array(
          //"吉林省 (全部地区)-吉林省 (全部地区)",
          "长春-长春",
          "吉林-吉林",
          "通化-通化",
          "扶余-扶余",
          "桦甸-桦甸",
          "延吉-延吉",
          "图门-图门",
          "龙井-龙井",
          "敦化-敦化",
          "集安-集安",
          "浑江-浑江",
          "梅河口-梅河口",
          "四平-四平",
          "公主岭-公主岭",
          "辽源-辽源",
          "白城-白城",
          "洮南-洮南",
          "九台-九台",
          "其他-其他"),
        new Array(
          //"云南省(全部地区)-云南省(全部地区)",
          "昆明-昆明",
          "曲靖-曲靖",
          "大理-大理",
          "玉溪-玉溪",
          "丽江-丽江",
          "楚雄-楚雄",
          "开远-开远",
          "迪庆-迪庆",
          "东川-东川",
          "照通-照通",
          "个旧-个旧",
          "保山-保山",
          "文山-文山"),
        new Array(
          //"四川省(全部地区)-四川省(全部地区)",
          "成都-成都",
          "宜宾-宜宾",
          "泸州-泸州",
          "内江-内江",
          "攀枝花-攀枝花",
          "西昌-西昌",
          "德阳-德阳",
          "雅安-雅安",
          "遂宁-遂宁",
          "南充-南充",
          "绵阳-绵阳",
          "广元 -广元",
          "马尔康-马尔康",
          "达县-达县",
          "华鉴-华鉴",
          "自贡-自贡",
          "乐山-乐山",
          "其他-其他"),
        new Array(
          //"安徽省(全部地区)-安徽省(全部地区)",
          "合肥-合肥",
          "芜湖-芜湖",
          "马鞍山-马鞍山",
          "蚌埠-蚌埠",
          "铜陵-铜陵",
          "淮北-淮北",
          "淮南-淮南",
          "亳州-亳州",
          "巢湖-巢湖",
          "黄山-黄山",
          "歙县-歙县",
          "宿州-宿州",
          "阜阳-阜阳",
          "六安-六安",
          "滁州-滁州",
          "宣州-宣州",
          "黄山-黄山",
          "安庆-安庆",
          "其他-其他"),
        new Array(
          //"江西省(全部地区)-江西省(全部地区)",
          "南昌-南昌",
          "德镇-德镇",
          "九江-九江",
          "鹰潭-鹰潭",
          "宜春-宜春",
          "新余-新余",
          "萍乡-萍乡",
          "赣州-赣州",
          "吉安-吉安",
          "井冈山-井冈山",
          "抚州-抚州",
          "临川-临川",
          "上饶-上饶",
          "其他-其他"),
        new Array(
          //"黑龙江省(全部地区)-黑龙江省(全部地区)",
          "哈尔滨-哈尔滨",
          "佳木斯-佳木斯",
          "牡丹江-牡丹江",
          "大庆-大庆",
          "齐齐哈尔-齐齐哈尔",
          "阿城-阿城",
          "肇东-肇东",
          "绥化-绥化",
          "伊春-伊春",
          "鹤岗-鹤岗",
          "七台河-七台河",
          "双鸭-双鸭",
          "同江-同江",
          "绥汾河-绥汾河",
          "鸡西-鸡西",
          "北安-北安",
          "黑河-黑河",
          "五大连池-五大连池"),
        new Array(
          //"河北省(全部地区)-河北省(全部地区)",
          "石家庄-石家庄",
          "邯郸-邯郸",
          "保定-保定",
          "张家口-张家口",
          "秦皇岛-秦皇岛",
          "辛集-辛集",
          "邢台-邢台",
          "泊头-泊头",
          "唐山-唐山",
          "北戴河-北戴河",
          "廊坊-廊坊",
          "定州-定州",
          "南宫-南宫",
          "衡水-衡水",
          "沙河-沙河",
          "沧州-沧州",
          "任丘-任丘",
          "承德-承德",
          "涿州-涿州",
          "其他-其他"),
        new Array(
          //"陕西省(全部地区)-陕西省(全部地区)",
          "西安-西安",
          "咸阳-咸阳",
          "宝鸡-宝鸡",
          "铜川-铜川",
          "渭南-渭南",
          "延安-延安",
          "汉中 -汉中",
          "韩城-韩城",
          "其他-其他"),
        new Array(
          //"海南省(全部地区)-海南省(全部地区)",
          "海口-海口",
          "三亚-三亚",
          "琼海-琼海",
          "通什-通什",
          "其他-其他"),
        new Array(
          //"河南省(全部地区)-河南省(全部地区)",
          "郑州-郑州",
          "洛阳-洛阳",
          "开封-开封",
          "鹤壁-鹤壁",
          "焦作-焦作",
          "许昌-许昌",
          "驻马店-驻马店",
          "周口-周口",
          "新乡-新乡",
          "安阳-安阳",
          "濮阳-濮阳",
          "漯河-漯河",
          "信阳-信阳",
          "平顶山-平顶山",
          "三门峡-三门峡",
          "南阳-南阳",
          "商丘-商丘",
          "义马 -义马",
          "其他-其他"),
        new Array(
          //"山西省(全部地区)-山西省(全部地区)",
          "太原-太原",
          "大同-大同",
          "忻州-忻州",
          "临汾-临汾",
          "运城-运城",
          "长治-长治",
          "榆次-榆次",
          "侯马-侯马",
          "阳泉-阳泉",
          "晋城-晋城",
          "其他-其他"),
        new Array(
          //"内蒙古自治区(全部地区)-内蒙古自治区(全部地区)",
          "呼和浩特-呼和浩特",
          "赤峰-赤峰",
          "包头-包头",
          "二连浩特-二连浩特",
          "临河-临河",
          "东胜-东胜",
          "洲里-洲里",
          "乌兰浩特-乌兰浩特",
          "霍林郭勒-霍林郭勒",
          "集宁-集宁",
          "乌海-乌海",
          "海拉尔-海拉尔",
          "牙克石-牙克石",
          "锡林浩特-锡林浩特",
          "通辽-通辽",
          "扎兰屯-扎兰屯",
          "其他-其他"),
        new Array(
          //"广西壮族自治区(全部地区)-广西壮族自治区(全部地区)",
          "南宁-南宁",
          "桂林 -桂林",
          "北海-北海",
          "柳州-柳州",
          "玉林-玉林",
          "百色-百色",
          "河池-河池",
          "凭祥-凭祥",
          "钦州-钦州",
          "梧州-梧州",
          "合山-合山",
          "其他-其他"),
        new Array(
          //"贵州省(全部地区)-贵州省(全部地区)",
          "贵阳-贵阳",
          "遵义-遵义",
          "铜仁-铜仁",
          "都匀-都匀",
          "兴义 -兴义",
          "赤水-赤水",
          "六盘水-六盘水",
          "凯里-凯里",
          "安顺-安顺",
          "其他-其他"),
        new Array(
          //"宁夏回族自治区(全部地区)-宁夏回族自治区(全部地区)",
          "银川-银川",
          "青铜峡-青铜峡",
          "吴忠-吴忠",
          "石嘴山-石嘴山",
          "其他-其他"),
        new Array(
          //"青海省(全部地区)-青海省(全部地区)",
          "西宁-西宁",
          "格尔木-格尔木",
          "共和-共和",
          "德令哈-德令哈",
          "其他-其他"),
        new Array(
          //"疆维吾尔自治区(全部地区)-疆维吾尔自治区(全部地区)",
          "乌鲁木齐-乌鲁木齐",
          "石河子-石河子",
          "拉玛依-拉玛依",
          "博乐-博乐",
          "塔城-塔城",
          "阿勒泰-阿勒泰",
          "哈密-哈密",
          "阿克苏-阿克苏",
          "阿图什-阿图什",
          "昌吉-昌吉",
          "奎屯-奎屯",
          "伊宁-伊宁",
          "吐鲁番-吐鲁番",
          "库尔勒-库尔勒",
          "喀什-喀什",
          "和田-和田",
          "其他-其他"),
        new Array(
          //"西藏自治区(全部地区)-西藏自治区(全部地区)",
          "拉萨-拉萨",
          "日喀则-日喀则",
          "其他-其他"),
        new Array(
          //"甘肃省(全部地区)-甘肃省(全部地区)",
          "兰州-兰州",
          "酒泉-酒泉",
          "临夏-临夏",
          "张掖-张掖",
          "峪关-峪关",
          "金昌-金昌",
          "平凉-平凉",
          "白银-白银",
          "武威-武威",
          "玉门-玉门",
          "天水-天水",
          "西峰-西峰",
          "其他-其他"),
        new Array(
          //"台湾省(全部地区)-台湾省(全部地区)",
          "台北-台北",
          "台中-台中",
          "基隆-基隆",
          "台南-台南",
          "其他-其他"),
        new Array("香港特别行政区-香港特别行政区"),
        new Array("澳门特别行政区-澳门特别行政区"),
        new Array("国外-国外")
        );

    展开全文
  • 天气提示 获取世界各地城市的本地时间和天气(OpenWeather API,Vue.js) 实时渲染链接: 截屏:
  • 在此应用程序中,我们创建了一个天气应用程序,该应用程序使用户知道世界上任何城镇的天气。 要构建此应用程序,我们使用了: Javascript ES6 Webpack DOM HTML / CSS OpenWeatherMap API Giphy API 短绒棉...
  • lammah-angular.js世界上任何城市中找到最佳的饮食,购物或游览地点。
  • ThreeJs做智慧城市项目后记

    万次阅读 多人点赞 2019-11-01 18:01:13
    只是会用一点ThreeJs,对于WebGl的原理并没了解过,这并不影响我们利用ThreeJs去做出一个非常炫酷的项目。 开始 新世界的大门打开啦! 写在前面 不要因为不了解就被这种3D展示的项目给吓到 其实实现起来...

    demo展示效果

    随着时间的推移技术的进步,前端越来越杂了,但是也越来越精彩了。只是会用一点ThreeJs,对于WebGl的原理并没了解过,这并不影响我们利用ThreeJs去做出一个非常炫酷的项目。

    开始

    新世界的大门打开啦!

    写在前面

    1. 不要因为不了解就被这种3D展示的项目给吓到 其实实现起来很简单 很简单 很简单
    2. 城市模型一份 最好是gltf模型,obj模型也没问题,我会介绍如何转化与压缩 PS:为什么只有这俩,因为我写这个项目只用到了这俩,处理的经验也是针对这俩的,我项目中所用的模型是公司所有暂不能提供。
    3. 有一定ThreeJs的基础 俗话说得好 万丈高楼平地起嘛 如果没有这方面基础的同学也不要急 推荐一本书《THREE.JS开发指南》,有基础也有提高 很棒
    4. 本文所示代码大部分只是思路 我也是第一次上手用ThreeJs处理模型并应用到项目中,可能有少许不足之处,还望各路大神指正教导
    5. 项目进行一半的时候,因为没经验,我发现让建模看着地图建模的思路是不对的,应该让他们利用geoJson或者有详细的测绘图数据作为地理数据,去建模,建造出来的更精确,而且可以利用地理坐标和世界坐标去关联(猜想),利于项目开发,毕竟第一次,这个锅我背了
    6. Threejs的文档是不全的,很多控制器loader后期处理都没有文档,要自己多看看Threejsexamples,很多效果都可以基于Demo去实现
    7. 单页面应用一定要清除ThreeJs 的创建的对象,避免内存泄露,能disposedispose,多个children的要遍历remove掉 而且里面的 materialgeometry也要删掉,最近刚知道一个取消占用的妙招,WEBGL_lose_context
    8. 后期处理对显卡有一定要求
    9. 最好一次渲染,不要多次渲染
    10. 最好不要把和数据无关的挂在vue的data上,会造成不必要的性能浪费,因为vue会深度遍历给每个对象加getter和setter,不过在这个项目里,我没发现啥差别。

    HTML部分

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <title>Threejs-city-model-show</title>
        <meta charset="utf-8" />
        <meta
          name="viewport"
          content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
        />
        <style>
          body {
            color: #fff;
            margin: 0px;
            overflow: hidden;
          }
        </style>
      </head>
    
      <body>
        <script src="../build/three.min.js"></script>
      </body>
    </html> 
    

    创建场景

    首先,我们要祭出ThreeJs的最重要的几大组件——scene(场景)camera(相机)renderer(渲染器)light(灯光),以及渲染的目标——container(就是DOM结构),老生常谈,不多说

    打个比方,scene就是舞台,camera就是拍摄舞台的摄像机,它能决定观众看到什么,而一个舞台没有灯光的话它就是黑乎乎的,所以light就是舞台上的各种灯光,所以舞台上表演什么,就是舞台中有什么,所以要加入到scene中 scene.add(“演员们(模型)”)

    var camera, scene, renderer;
    var container;
    var ambientLight, pointLight;
    
    // 初始化
    init()
    // 循环渲染每一帧  一帧一帧的 就是你打游戏时的FPS
    animate()
    
    function init(){
    	// 初始化相机 
    	// 这里使用的是透视相机来模拟人眼看到的效果 近大远小
    	camera = new THREE.PerspectiveCamera(
          45,
          window.innerWidth / window.innerHeight,
          1,
          2000
        );
        camera.position.z = 70;
        camera.position.x = 50;
        camera.position.y = 10;
    	
    	// 初始化场景
    	scene = new THREE.Scene();
    
    	// 初始化灯光
    	// 环境光 能保持整体都是亮点
    	ambientLight = new THREE.AmbientLight(0x404040)
    	// 点光源 就像灯泡一样的效果  白色灯光 亮度0.6
    	pointLight = new THREE.PointLight(0xffffff, 0.6);
    
    	// 将灯光加入到场景中
    	scene.add(ambientLight)
    	// 将灯光加到摄像机中 点光源跟随摄像机移动
    	// 为什么这样做  因为这样可以让后期处理时的辉光效果更漂亮 
    	camera.add(pointLight);
    
    	// 我们将摄像机加入到场景中
        scene.add(camera);
    
    	// 初始化渲染器
    	renderer = new THREE.WebGLRenderer({
    	  // 开启抗锯齿
          antialias: true,
          // 开启背景透明
          alpha: true
        });
        // 把自动清除颜色缓存关闭 这个如果不关闭 后期处理这块会不能有效显示
        // 书上的描述是 如果不这样做,每次调用效果组合器的render()函数时,之前渲染的场景会被清理掉。通过这种方法,我们只会在render循环开始时,把所有东西清理一遍。
        renderer.autoClear = false;
        // 背景透明 配合 alpha
        renderer.setClearColor(0xffffff, 0);
        renderer.setPixelRatio(window.devicePixelRatio);
        renderer.setSize(window.innerWidth, window.innerHeight);
        // 伽马值启动 更像人眼观察的场景
        renderer.gammaInput = true;
        renderer.gammaOutput = true;
    	
    	// 渲染到DOM中去
    	container = document.createElement("div");
        container.appendChild(renderer.domElement);
        document.body.appendChild(container);
    }
    // 这样一来,基础场景创建就完成了,接下来我们来让它循环渲染起来
    
    function animate() {
       // 这个方法低版本浏览器兼容不好 可以从github上找些兼容库 如果要兼容低版本浏览器
       requestAnimationFrame(animate);
       // 渲染我们的场景  摄像机啪啪啪的拍和录
       // 由于把renderer autoClear  关闭了 所以我们要在渲染函数中手动清除
       renderer.clear();
       renderer.render(scene, camera);
     }
    // ok 基础部分完成 接下来我们来加载模型
    

    加载城市模型

    限于经验和技术等各种外力因素影响,项目最开始时编写demo使用的是Obj模型Mtl贴图文件(不太确定贴图文件的叫法是否准确),使用起来也很简单(ThreeJs仓库里的webgl_loader_obj_mtl.html拿来改下就行了)

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <title>Threejs-city-model-show</title>
        <meta charset="utf-8" />
        <meta
          name="viewport"
          content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
        />
        <style>
          body {
            color: #fff;
            margin: 0px;
            overflow: hidden;
          }
        </style>
      </head>
    
      <body>
        <script src="../build/three.min.js"></script>
        <!-- 引入我们可爱的加载器 -->
        <script src="js/loaders/MTLLoader.js"></script>
        <script src="js/loaders/OBJLoader.js"></script>
        <script>
    	  /* 省略创建场景部分的代码 */
    
    	  // 加载的过程 
    	  var onProgress = function(xhr) {
              if (xhr.lengthComputable) {
                var percentComplete = (xhr.loaded / xhr.total) * 100;
                console.log(Math.round(percentComplete, 2) + "% downloaded");
              }
            };
    
          var onError = function() {
     		// 载入出错时候
    	  };
    
    	  // 加载Mtl贴图文件
          new THREE.MTLLoader()
            // 贴图文件的路径 
            .setPath("models/obj/male02/")
            .load("male02_dds.mtl", function(materials) {
              // 看代码意思是预加载
              materials.preload();
    
    		  // 加载OBJ模型
              new THREE.OBJLoader()
                // 设置OBJ模型的材质贴图
                .setMaterials(materials)
                .setPath("models/obj/male02/")
                .load(
                  "male02.obj",
                  function(object) {
                    object.position.y = -95;
                    scene.add(object);
                  },
                  onProgress,
                  onError
                );
            });
    	</script>
      </body>
    </html> 
    

    这一步一般会出现的问题有如下

    1. 模型加载后,不显示也不报错?
      检查场景是否正常渲染了,如果正常渲染模型的位置在哪里,摄像机在哪里,摄像机是否对着模型,灯光是否配置,模型是否太大或者太小了,超出了摄像机的摄影范围……
    2. 模型可以正常加载,但是贴图不显示?
      首先检查network是否报404错误,如果报错,一般都是mtl贴图文件(看起来像是雪碧图那种)没给你,或者路径配置的不是相对路径,如果贴图没错误,模型是黑色的,在mtl文件中可以更改kakd的三个值(对应rgb),或者打印出模型属性,在material.color中更改点色值或别的属性。黑色的时候,看不到贴图。一般这样一通操作之后,就能看到了模型了
    3. 模型文件太大了,浏览器在渲染的时候进程被完全卡死!要等待几十秒之久!天呐!
      这个问题看起来比较棘手,其实很好解决。ThreeJs官方推荐gltf格式的模型在浏览器中渲染,因为它是为浏览器而生的,性能好,体积小。我们项目中使用的模型文件,一开始是ObjMtl的,达到25MB大小,在vue项目中渲染会阻塞浏览器46s,原生html+js的项目中好些,几秒时间就行了,我怀疑是我写法的问题,但是我测试仅仅是加载模型渲染到场景,并没有多余操作和数据绑定,还是一样,阻塞进程,一度导致我怀疑人生???黑人问号脸。那么如何将Obj模型转换为gltf模型,还能再优化吗?进入下一章节!对了对了,Obj模型也是可以压缩的,而且ObjLoader2加载会快一点

    Obj模型转Gltf模型并压缩Gltf模型,性能爆炸提升!

    真的很牛逼 模型加贴图从 25mb 减小到了1.8mb 上效果图

    1.这是不加贴图和mtlobj文件 已经达到了22.5MB在这里插入图片描述

    1. 这是objgltf之后的文件,贴图转成了base64包含在了gltf文件中,可通过配置项提取出文件,稍后介绍
      在这里插入图片描述

    2. 这是经过gltf压缩处理之后的贴图+模型的文件大小在这里插入图片描述

    obj2gltf —— Obj模型转Gltf

    obj2gltf-github

    1. 用法
    // 全局安装后
                 obj文件所在目录                             输出目录 
    obj2gltf  -i ./examples/models/obj/hanchuan/city.obj -o ./gltf/city.gltf --unlit --separate
    
    1. 介绍下为什么要加这两个参数
      --unlit的作用是可以保留环境贴图的效果,环境贴图后面再介绍
      --separate是将贴图文件提取出来,提出来浏览器可以缓存,如果你需要继续压缩gltf文件,这里不加这个参数也行,因为压缩的时候也能提出来

    gltf-pipeline

    gltf-pipeline-github

    1. 用法
    gltf-pipeline -i  ../../../gltf/city.gltf  -o  ../../../examples/models/obj/hanchuan/city_small1.gltf -d --separate
    
    1. 介绍下参数
      -d--draco.compressMeshes的缩写,使用draco算法压缩模型
      --separate就是将贴图文件提取出来,不提可以不加

    这样,我们就完成了gltf模型的转化和压缩,性能暴增!秒开!
    在我们最终的模型中,obj模型297Mb,转gltf之后还有150Mb左右,最终经过压缩,还有7.3Mb!

    Gltf模型的加载

    抛弃了ObjMtl之后,我们的加载器也要做一下改变

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <title>Threejs-city-model-show</title>
        <meta charset="utf-8" />
        <meta
          name="viewport"
          content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
        />
        <style>
          body {
            color: #fff;
            margin: 0px;
            overflow: hidden;
          }
        </style>
      </head>
    
      <body>
        <script src="../build/three.min.js"></script>
        <!-- 引入我们可爱的加载器 -->
        <script src="js/loaders/GLTFLoader.js"></script>
        <script src="js/loaders/DRACOLoader.js"></script>
        <script>
    	  /* 省略创建场景部分的代码 */
    
    	  // 加载的过程 
    	  var onProgress = function(xhr) {
              if (xhr.lengthComputable) {
                var percentComplete = (xhr.loaded / xhr.total) * 100;
                console.log(Math.round(percentComplete, 2) + "% downloaded");
              }
            };
    
          var onError = function() {
     		// 载入出错时候
    	  };
    
          var loader = new THREE.GLTFLoader();
          // 这个是Threejs解析draco压缩之后的解析器 
          // 它从这里读取解析器JS
          THREE.DRACOLoader.setDecoderPath("js/libs/draco/gltf/");
          // 将Draco解析器和GltfLoader绑定在一起
          loader.setDRACOLoader(new THREE.DRACOLoader());
          loader.load(
            "models/obj/hanchuan/city_small1.gltf",
            function(gltf) {
             // gltf.scene 拿到这个可以处理模型
             scene.add(gltf.scene)
            },
            onProgress,
            onError
          );
    	</script>
      </body>
    </html> 
    

    这时候的场景,应该是这样的,很丑吧哈哈哈,没关系没关系,我们可以为它美容,不过在此之前,我们先来试着转动这个模型,看看性能怎么样。
    在这里插入图片描述

    OrbitControls——轨道控制器

    var controls
    
    function init(){
    	// 省略创建场景部分
    	controls = new THREE.OrbitControls(camera, renderer.domElement);
    }
    

    它的常用参数在源码中可以找到,也可以百度/goggle一下中文翻译的,不做太多介绍,这是其中一段源码。

    // Set to false to disable this control
    	this.enabled = true;
    
    	// "target" sets the location of focus, where the object orbits around
    	this.target = new THREE.Vector3();
    
    	// How far you can dolly in and out ( PerspectiveCamera only )
    	this.minDistance = 0;
    	this.maxDistance = Infinity;
    
    	// How far you can zoom in and out ( OrthographicCamera only )
    	this.minZoom = 0;
    	this.maxZoom = Infinity;
    
    	// How far you can orbit vertically, upper and lower limits.
    	// Range is 0 to Math.PI radians.
    	this.minPolarAngle = 0; // radians
    	this.maxPolarAngle = Math.PI; // radians
    
    	// How far you can orbit horizontally, upper and lower limits.
    	// If set, must be a sub-interval of the interval [ - Math.PI, Math.PI ].
    	this.minAzimuthAngle = - Infinity; // radians
    	this.maxAzimuthAngle = Infinity; // radians
    
    	// Set to true to enable damping (inertia)
    	// If damping is enabled, you must call controls.update() in your animation loop
    	this.enableDamping = false;
    	this.dampingFactor = 0.25;
    
    	// This option actually enables dollying in and out; left as "zoom" for backwards compatibility.
    	// Set to false to disable zooming
    	this.enableZoom = true;
    	this.zoomSpeed = 1.0;
    
    	// Set to false to disable rotating
    	this.enableRotate = true;
    	this.rotateSpeed = 1.0;
    
    	// Set to false to disable panning
    	this.enablePan = true;
    	this.panSpeed = 1.0;
    	this.screenSpacePanning = false; // if true, pan in screen-space
    	this.keyPanSpeed = 7.0;	// pixels moved per arrow key push
    
    	// Set to true to automatically rotate around the target
    	// If auto-rotate is enabled, you must call controls.update() in your animation loop
    	this.autoRotate = false;
    	this.autoRotateSpeed = 2.0; // 30 seconds per round when fps is 60
    
    	// Set to false to disable use of the keys
    	this.enableKeys = true;
    
    	// The four arrow keys
    	this.keys = { LEFT: 37, UP: 38, RIGHT: 39, BOTTOM: 40 };
    
    	// Mouse buttons
    	this.mouseButtons = { LEFT: THREE.MOUSE.LEFT, MIDDLE: THREE.MOUSE.MIDDLE, RIGHT: THREE.MOUSE.RIGHT };
    
    	// for reset
    	this.target0 = this.target.clone();
    	this.position0 = this.object.position.clone();
    	this.zoom0 = this.object.zoom;
    
    	//
    	// public methods
    	//
    
    	this.getPolarAngle = function () {
    
    • 初始化这个控制器之后,就可以操作模型旋转放大缩小了。它的原理就是控制摄像机和模型的距离,同理也可以控制模型与摄像机的距离去实现移动放大缩小等功能,可以自己尝试一下。一个比较有趣的操作是在function animate(){}中,设置camera.lookAt=scene.position效果也很不错。
    • ThreeJs中内置了很多有趣的控制器,用法和效果都可以从ThreeJsexamples中找到,记得看看。

    Stats

    玩过LOL,大型单机游戏的同学都知道,如果帧率不好,画面看起来就会卡顿,影响体验,这也为什么用requestAnimationFrame去作为渲染调用的原因之一,它的性能比函数递归setInterval实现渲染调用好很多。那么我们如何去检测我们的场景渲染的性能怎么样呢?就可以使用Stats

    // <script src="js/libs/stats.min.js"></script> 不要忘了引入进来
    var stats;
    
    function init(){
    	// 省略创建场景部分
    	stats = new Stats();
    	container.appendChild(stats.dom);
    }
    
    function animatie(){
    	stats.update();
    	// 省略renderer
    }
    
    • 初始化之后在页面左上角会看到,这个原理还没研究过,有机会翻翻源码看看。
    • 在这里插入图片描述
    • 如果实在vue/react等单页面环境中,可以通过process.env.NODE_ENV控制开发环境再显示这个。
    • 这样一来,我们在开发调试的时候,就能很直观的看出效果了。

    给scene添加自定义背景

    若不为空,在渲染场景的时候将设置背景,且背景总是首先被渲染的。 可以设置一个用于的“clear”的Color(颜色)、一个覆盖canvas的Texture(纹理),或是一个CubeTexture。默认值为null。

    • 实验结果是,TextureLoaderCubeTextureSphereGeometry都可以作为背景图,简单介绍下这三者。
    1. TextureLoader 一张图,背景看起来是静止不动的
    2. CubeTexture 立方纹理 图片是分割成6块 相当于摄像机和模型在一个正方体盒子中 背景随着摄像机转动而转动
    3. SphereGeometry 一张图 全景图原理 相当于摄像机和模型在一个圆球盒子中 背景随着摄像机转动而转动
    4. 不太理解可以百度下threejs全景图原理,不做过多叙述
    function init(){
    	// 省略其余代码
    	// ....
    	// 添加一张静止的背景图
    	scene.background = new THREE.TextureLoader().load("你的背景图")
    	// ....
    }
    
    1. 之后效果大概是这样的,我们的世界里有了天空,其实这里用CubeTexture或者SphereGeometry效果更好
    2. 在这里插入图片描述

    设置模型环境贴图和材质颜色

    细心的同学会发现,河流和楼上会有星星点点的光,这是怎么实现的呢?答案就是环境贴图

    环境贴图
    简单的讲,环境贴图就像把物体的表面化作一面镜子,可以反射出你为它赋予的图片。

    如何设置环境贴图呢?回到我们加载模型的部分。核心就是创建立方纹理然后设置某个模型的materialenvMap为这个立方纹理。 环境贴图的使用限制受纹理影响,有一部分纹理加不上环境贴图。

    <!DOCTYPE html>
    <html lang="en">
      <head>
        <title>Threejs-city-model-show</title>
        <meta charset="utf-8" />
        <meta
          name="viewport"
          content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
        />
        <style>
          body {
            color: #fff;
            margin: 0px;
            overflow: hidden;
          }
        </style>
      </head>
    
      <body>
        <script src="../build/three.min.js"></script>
        <!-- 引入我们可爱的加载器 -->
        <script src="js/loaders/GLTFLoader.js"></script>
        <script src="js/loaders/DRACOLoader.js"></script>
        <script>
    	  /* 省略创建场景部分的代码 */
    
    	 // 创建一个立方纹理
    	 var envMap = new THREE.CubeTextureLoader()
                .setPath("textures/")
                .load(new Array(6).fill("start.jpg"));
    
          var loader = new THREE.GLTFLoader();
          // 这个是Threejs解析draco压缩之后的解析器 
          // 它从这里读取解析器JS
          THREE.DRACOLoader.setDecoderPath("js/libs/draco/gltf/");
          // 将Draco解析器和GltfLoader绑定在一起
          loader.setDRACOLoader(new THREE.DRACOLoader());
          loader.load(
            "models/obj/hanchuan/city_small1.gltf",
            function(gltf) {
             // gltf.scene 拿到这个可以处理模型
             gltf.scene.traverse(function(child) {
                if (child.isMesh) {
                  /* 这些都是DEMO  具体看你模型调整 下节介绍通过鼠标点击确定模型所属对象 然后去调试模型 */
    			  // 这些名称都可以通过打印看出 console.log(child)
    
    			  // 比如我想给这些加上环境贴图 就可以这样写
                  /hai|city|liubianxing/i.test(child.name) &&
                    (child.material.envMap = envMap);
                  
                  if (/city/i.test(child.name)) {
                    // 更改模型颜色
                    child.material.color = new THREE.Color(6, 6, 5);
                    // 更改模型环境贴图影响  0-1
                    child.material.reflectivity = 0.9;
                  }
                  
    			  // 更改模型位置
                  /lumian|hai/i.test(child.name) && (child.position.y = 0.5);
                  
                  // ...
                }
              });
              
              scene.add(gltf.scene)
            },
            onProgress,
            onError
          );
          
    	</script>
      </body>
    </html> 
    

    Raycaster 光线投射

    光线投射用于进行鼠标拾取(在三维空间中计算出鼠标移过了什么物体)。

    • 打印出所有的child不好定位是哪块模型,有没有更快的方法?
    • 您好,有的。
    • 通过 THREE.Raycaster 实现模型选中与信息显示,点击打印出当前点击的模型,在它的属性中修改颜色,位置等,可以直接更新效果,调试更方便
    • 到此,经过我们的美化之后,效果就是这样了。还缺了点什么,道路咋不发光啊,看着没光效,不炫酷!
    • 在这里插入图片描述

    利用EffectComposer(效果组合器)进行后期处理

    这一块的基础建议好好看看《THREE.JS开发指南》这本书。如果需要多个pass,要学会使用MaskPassclearPass。这一块因为不熟悉,我在添加效果的时候花费了很大量的时间,尤其是Threejs内置的pass效果没有文档,甚至你都不知道内置了多少种效果…《THREE.JS开发指南》这本书介绍的比较全面,用法也很详细。

    利用EffectComposer进行后期处理——辉光(bloompass)

    如何设置后期处理?

    1. 创建一个EffectComposer对象,然后在该对象上添加后期处理通道。
    2. 配置该对象,使它可以渲染我们的场景,并用额外的后期处理步骤
    3. render循环中,使用EffectComposer渲染场景、应用通道,并输出结果

    几个引用介绍

    • EffectComposer效果组合器,每个通道会按照其加入EffectComposer的顺序执行。
    • RenderPass该通道在指定的场景和相机的基础上渲染出一个新的场景。一般在第一个加入到Composer中,它会渲染场景,但是不会将渲染结果输出到屏幕上。
    • ShaderPass使用该通道可以传入一个自定义的着色器,用来生成高级的、自定义的后期处理通道
    • BloomPass该通道会使明亮区域渗入较暗的区域,模拟相机照到过多亮光的情形
    • CopyShader它不会添加任何特殊效果,只是将最后一个通道的结果复制到屏幕上,BloomPass无法直接添加到屏幕上,需要借助这个Shader,其实使用bloompass.renderToScreen = true是可以添加的,但是后续再加处理效果会无效,所以一定要借用这个Shader
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <title>Threejs-city-model-show</title>
        <meta charset="utf-8" />
        <meta
          name="viewport"
          content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
        />
        <style>
          body {
            color: #fff;
            margin: 0px;
            overflow: hidden;
          }
        </style>
      </head>
    
      <body>
        <!-- 省略其他引入的 -->
        <!-- 引入Effect -->
        <script src="js/postprocessing/EffectComposer.js"></script>
        <!-- 引入Effect配套的render -->
        <script src="js/postprocessing/RenderPass.js"></script>
        <script src="js/postprocessing/ShaderPass.js"></script>
        <!-- 引入各种需要的shader -->
        <script src="js/shaders/CopyShader.js"></script>
        <script src="js/shaders/LuminosityHighPassShader.js"></script>
        <script src="js/postprocessing/UnrealBloomPass.js"></script>
        <script>
          var clock;
    	  /* 省略创建场景部分的代码 */
    	  // 初始化renderPass
    	  var renderScene = new THREE.RenderPass(scene, camera);
    	
    	  // 初始化bloomPass 
    	  var bloomPass = new THREE.UnrealBloomPass(
    	    // 没研究过这些参数的意义 会提上日程
            new THREE.Vector2(window.innerWidth, window.innerHeight),
            1.5,
            0.4,
            0.85
          );
          // 一些参数 可以调整看效果
          bloomPass.threshold = 0.36;
          bloomPass.strength = 0.6;
          bloomPass.radius = 0;
    
    	  // effectCopy
          var effectCopy = new THREE.ShaderPass(THREE.CopyShader);
          // 让effectCopy渲染到屏幕上 没这句不会再屏幕上渲染
          effectCopy.renderToScreen = true;
    	  
    	  // 初始化 composer
    	  var composer = new THREE.EffectComposer(renderer);
    	  // 模版缓冲(stencil buffer) https://blog.csdn.net/silangquan/article/details/46608915
          composer.renderTarget1.stencilBuffer = true;
          composer.renderTarget2.stencilBuffer = true;
          composer.setSize(window.innerWidth, window.innerHeight);
          composer.addPass(renderScene);
    	  composer.addPass(bloomPass);
          composer.addPass(effectCopy);
    
    	  // 修改animate
    	  function animate() {
            requestAnimationFrame(animate);
            var delt = clock.getDelta();
            stats.update();
            renderer.clear();
            // 删除renderer使用composerrender去渲染
            // renderer.render(scene, camera);
            
    		// 没理解透这个delt的作用 ???
            composer.render(delt);
          }
    	</script>
      </body>
    </html> 
    

    在这里插入图片描述这样 辉光效果就出来了。还不够还不够,让我们加上FocusShaper,让它看起来像聚焦在中心一样(突出中心)。

    1. 颜色越亮,发光效果越强
    2. 辉光受环境贴图影响
    3. 模型可以通过map贴图来更改亮度,比如暗色的贴图,它反光就会很软

    为场景添加聚焦效果——FocusShader

    我们要引入FocusShader

    • FocusShader是一个简单的着色器,其结果是中央区域渲染的比较锐利,单周围比较模糊。
    • 在这里插入图片描述
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <title>Threejs-city-model-show</title>
        <meta charset="utf-8" />
        <meta
          name="viewport"
          content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0"
        />
        <style>
          body {
            color: #fff;
            margin: 0px;
            overflow: hidden;
          }
        </style>
      </head>
    
      <body>
        <!-- 省略其他引入的 -->
        <!-- 引入Effect -->
        <script src="js/postprocessing/EffectComposer.js"></script>
        <!-- 引入Effect配套的render -->
        <script src="js/postprocessing/RenderPass.js"></script>
        <script src="js/postprocessing/ShaderPass.js"></script>
        <!-- 引入各种需要的shader -->
        <script src="js/shaders/CopyShader.js"></script>
        <script src="js/shaders/LuminosityHighPassShader.js"></script>
        <script src="js/postprocessing/UnrealBloomPass.js"></script>
        <!-- focusShader 相对于bloompass新加的 -->
        <script src="js/shaders/FocusShader.js"></script>
        <script>
          var clock;
    	  /* 省略创建场景部分的代码 */
    	
    	 // 创建focusShader 相对于bloompass新加的
    	 var focusShader = new THREE.ShaderPass(THREE.FocusShader);
         focusShader.uniforms["screenWidth"].value = window.innerWidth;
         focusShader.uniforms["screenHeight"].value = window.innerHeight;
         focusShader.uniforms["sampleDistance"].value = 1.07;
    
    	  // 初始化renderPass
    	  var renderScene = new THREE.RenderPass(scene, camera);
    	
    	  // 初始化bloomPass 
    	  var bloomPass = new THREE.UnrealBloomPass(
    	    // 没研究过这些参数的意义 会提上日程
            new THREE.Vector2(window.innerWidth, window.innerHeight),
            1.5,
            0.4,
            0.85
          );
          // 一些参数 可以调整看效果
          bloomPass.threshold = 0.36;
          bloomPass.strength = 0.6;
          bloomPass.radius = 0;
    
    	  // effectCopy
          var effectCopy = new THREE.ShaderPass(THREE.CopyShader);
          // 让effectCopy渲染到屏幕上 没这句不会再屏幕上渲染
          effectCopy.renderToScreen = true;
    	  
    	  // 初始化 composer
    	  var composer = new THREE.EffectComposer(renderer);
    	  // 模版缓冲(stencil buffer) https://blog.csdn.net/silangquan/article/details/46608915
          composer.renderTarget1.stencilBuffer = true;
          composer.renderTarget2.stencilBuffer = true;
          composer.setSize(window.innerWidth, window.innerHeight);
          composer.addPass(renderScene);
    	  composer.addPass(bloomPass);
    	  // 相对于bloompass新加的
    	  composer.addPass(focusShader);
          composer.addPass(effectCopy);
    
    	  // 修改animate
    	  function animate() {
            requestAnimationFrame(animate);
            var delt = clock.getDelta();
            stats.update();
            renderer.clear();
            // 删除renderer使用composerrender去渲染
            // renderer.render(scene, camera);
            
    		// 没理解透这个delt的作用 ???
            composer.render(delt);
          }
    	</script>
      </body>
    </html> 
    

    模型的渲染和后期处理就到此就全部结束了。

    Sprite精灵的应用

    精灵是一个总是面朝着摄像机的平面,通常含有使用一个半透明的纹理。

    在这里插入图片描述

     var textured = new THREE.TextureLoader().load("textures/warning.png");
     var spriteMaterial = new THREE.SpriteMaterial({
       // color: 0xffffff,
       map: textured
     });
     var sprite = new THREE.Sprite(spriteMaterial);
     sprite.position.set(
       25.729931791092394,
       10.179400757773436,
       36.07142388020101
     );
     // console.log(sprite);
     sprite.scale.x = 10;
     sprite.scale.y = 5;
    
     scene.add(sprite);
    

    这张图火灾预警的图其实就是一张透明的png图片,精灵可以用canvas贴图,你可以自己编写canvas渲染在指定点上,也可以使用CSS3DRenderer去实现。

    Group

    通常的情况下Threejs里的模型是要分组的。在处理交互起来,有分组会更加清晰明了,就像模块拆分一样。

    var group = new THREE.Group();
    

    区域、路线、移动等功能实现逻辑

    1. 不规则区域可以用ShapeGeometry创建,使用可以设置透明的material比较好。material设置transparent:true可以支持透明
    2. 移动就是更改模型位置,很简单model.position.set(x,y,z)
    3. 画线,linelineLoopCubicBezierCurve3Threejs提供的画线方法
    4. 路线循环流动效果可以创建一个管道,然后增加一个路径一样的贴图,设置wrap为重复,在animate中不断更改texture.offset即可

    VUE/React等单页面注意点

    由于单页面中,Threejs创建的任何材质,模型,贴图……只要含有dispose方法的,你在页面组件即将销毁的周期中,都要调用下dispose方法清除,不然可能内存泄漏。刚学会一个妙招,利用WEBGL_lose_context这个API 可以让当前的webgl环境失效,达到取消占用的目的。

    beforeDestory(){
    	this.bloomPass.dispose();
        this.envMap.dispose();
        this.skymap.dispose();
        this.dracoLoader.dispose();
        this.spriteMaterial.dispose();
        this.sphereGeometry.dispose();
        this.meshBasicMaterial.dispose();
        this.scene.dispose();
        this.controls.dispose();
    	
    	/*
    	const data = this.$data;
        for (let i in data) {
          if (data.hasOwnProperty(i)) {
            if (data[i] && typeof data[i].dispose == "function") {
              data[i].dispose();
            }
          }
        }
    	*/
    	// this.renderer.domElement 就是你的threejs的canvas Dom
    	let gl = this.renderer.domElement.getContext("webgl");
    
        gl && gl.getExtension("WEBGL_lose_context").loseContext();
    }
    

    模型发光还带线的效果怎么做?

    在这里插入图片描述

    
    var lineMaterial = new THREE.LineBasicMaterial({
      // 线的颜色
      color: "blue",
      transparent: true,
      opacity: 0.8,
      depthFunc: THREE.AlwaysDepth
    });
    模型.add(
      new THREE.LineSegments(模型geometry, lineMaterial)
    );
    // 之后把模型设置下透明度就成了
    
    

    坐标转换 经纬度转墨卡托

    • 先把经纬度转墨卡托坐标 然后由于墨卡托坐标比较大,找到地图模型的中心点,墨卡托转Threejs的坐标时,减去这个中心点,之后就能画出一样的点或区域,之后再将z轴(y)取反
    • x+对应东,z+对应南
    • z算出来还得取个反
    • 根据坐标系适当调整
    function lonlatToMercator(lon, lat, height) {
            var z = height ? height : 0;
            var x = (lon / 180.0) * 20037508.3427892;
            var y = (Math.PI / 180.0) * lat;
            var tmp = Math.PI / 4.0 + y / 2.0;
            y = (20037508.3427892 * Math.log(Math.tan(tmp))) / Math.PI;
            return { x: x, y: y, z: z };
          }
    
    // 找到地图的中心对应的经纬度坐标
    var center = lonlatToMercator(113.82909, 30.6549, 1);
    
    function lonlatToThree(lon, lat, height) {
      var z = height ? height : 0;
      var x = (lon / 180.0) * 20037508.3427892;
      var y = (Math.PI / 180.0) * lat;
      var tmp = Math.PI / 4.0 + y / 2.0;
      y = (20037508.3427892 * Math.log(Math.tan(tmp))) / Math.PI;
      var result = {
        x: x - center.x,
        y: y - center.y,
        z: z - center.z
      };
      // x 越大越远
      // 因为比地图大了 可以让地图整体放大或缩小 然后偏移到大概位置
      return [result.x / 100 + 17, -result.y / 100 + 33];
      // [-result.x / 100 - 14, -result.y / 100 - 35];
    }
    console.log(lonlatToThree(113.84411, 30.65231));
    

    antialias开启后,渲染还有锯齿怎么办?

    使用SSAAFXAASMAA等抗锯齿后处理。任选其一即可。

    initFxaaPass() {
    	let fxaaPass = new ShaderPass(FXAAShader);
    	const pixelRatio = this.renderer.getPixelRatio();
    	fxaaPass.material.uniforms["resolution"].value.x =
    	  1 / (this.width * pixelRatio);
    	fxaaPass.material.uniforms["resolution"].value.y =
    	  1 / (this.height * pixelRatio);
    	fxaaPass.renderToScreen = true;
    	this.fxaaPass= fxaaPass;
    },
    
    initSmaaShader() {
    	const pixelRatio = this.renderer.getPixelRatio();
    	this.smaaPass = new SMAAPass(
    	  this.width * pixelRatio,
    	  this.height * pixelRatio
    	);
    	this.smaaShader.renderToScreen = true;
    },
    
    initSsaaShader() {
    	this.ssaaRenderPass = new SSAARenderPass(this.scene, this.camera);
    	this.ssaaRenderPass.unbiased = false;
    	this.ssaaRenderPass.sampleLevel = 2;
    },
    

    利用EffectComposer应用某个效果

    initEffectComposer() {
    	const composer = new EffectComposer(this.renderer);
    	composer.setSize(this.width, this.height);
    	composer.addPass(this.renderScene);
    	composer.addPass(this.ssaaRenderPass);
    	composer.addPass(this.bloomPass);
    	composer.addPass(this.focusShader);
    	composer.addPass(this.effectCopy);
    	
    	this.composer = composer;
    },
    

    光柱效果如何实现

    在这里插入图片描述

    1. 准备一张渐变灰色png图片, 类似如下图
      在这里插入图片描述我在这 ↑
    2. 代码部分
    import * as THREE from "three";
    
    const scaleSpeed = 0.01;
    
    export default {
      data(){
        return {
          // ...  
        }
      },
      created(){
        this.loadRangeMap()
      },
      beforeDestory(){
          // ...
      },
      methods: {
        initRingAnimate() {
          Array.isArray(this.gatewayGroup.children) &&
            this.gatewayGroup.children.forEach(v => {
              Array.isArray(v.children) &&
                v.children.forEach(item => {
                  if (item.userData.type === "ring") {
                    item.rotation.z = item.rotation.z + scaleSpeed;
                  }
                });
            });
        },
        loadRangeMap() {
          this.rangeMap = this.textureLoader.load(require("../images/range.png"));
        },
        initOctahedronBufferGeometry() {
          this.octahedronBufferGeometry = new THREE.OctahedronBufferGeometry();
        },
        initCylinderBufferGeometry() {
          this.cylinderBufferGeometry = new THREE.CylinderBufferGeometry(
            2,
            2,
            14,
            12,
            1,
            true
          );
        },
        initOctahedron(color) {
          let geometry = this.octahedronBufferGeometry;
          let material = new THREE.MeshBasicMaterial({
            color,
            transparent: true,
            opacity: 0.3
          });
          let lineMaterial = new THREE.LineBasicMaterial({
            color,
            depthFunc: THREE.AlwaysDepth
          });
          let octahedron = new THREE.Mesh(geometry, material);
          let line = new THREE.LineSegments(geometry, lineMaterial);
          octahedron.add(line);
          octahedron.position.z = -8;
          return octahedron;
        },
        initRing(color) {
          let geometry = this.cylinderBufferGeometry;
          let material = new THREE.MeshBasicMaterial({
            color,
            map: this.rangeMap,
            side: THREE.DoubleSide,
            transparent: true,
            depthWrite: false
          });
          let cylinder = new THREE.Mesh(geometry, material);
          cylinder.rotation.x = (Math.PI / 180) * -90;
          cylinder.position.z = -2;
          return cylinder;
        },
        initGateway(data = { color: "#54C41D",x: 0, z: 0 }) {
          let group = new THREE.Group();
          let octahedron = this.initOctahedron(data.color);
          let ring = this.initRing(data.color);
          group.add(ring);
          group.add(octahedron);
          group.rotation.x = (Math.PI / 180) * 90;
          group.position.y = 0.2;
          group.position.x = data.x;
          group.position.z = data.z;
          this.gatewayGroup.add(group);
        }
      }
    };
    
    

    删除子对象时,用forEach等高阶循环删不干净?

    • 因为group.children是个数组,每次删除的时候,数组都会变动,比如长度是5,你删了第一个,下次循环你要删除第二个,但是数组长度变了,第二次删除的时候其实删的是第三个了。
    • 解决方案1 children.map(v=>{group.remove(children[0])}) 一直删除第一个
    • 解决方案2 for(let i = 0, l = children.length; i < l; i++){ group.remove(children[i]) } 将数组长度存储下来,就不会变啦!

    我们项目的最终效果

    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • JavaScript写的城市选择特效,包括拼音检索,弹出层等特效。
  • 代码片段: var areaObj = []; function initLocation(e){ var a = 0; for (var m in e) { areaObj[a] = e[m]; var b = 0; for (var n in e[m]) { areaObj[a][b ] = e[m][n]; } ...}
  • js世界城市下拉三级联动

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 9,196
精华内容 3,678
关键字:

世界城市js