Merge branch 'dev-threejs'
| | |
| | | "axios": "0.24.0", |
| | | "clipboard": "2.0.8", |
| | | "core-js": "3.25.3", |
| | | "d3": "^7.9.0", |
| | | "echarts": "5.4.0", |
| | | "echarts-gl": "^2.0.9", |
| | | "element-ui": "2.15.14", |
| | |
| | | "quill": "1.3.7", |
| | | "screenfull": "5.0.2", |
| | | "sortablejs": "1.10.2", |
| | | "three": "^0.163.0", |
| | | "v-scale-screen": "1.0.0", |
| | | "vue": "2.6.12", |
| | | "vue-count-to": "1.0.13", |
| | |
| | | <!DOCTYPE html> |
| | | <html> |
| | | <head> |
| | | <meta charset="utf-8"> |
| | | <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> |
| | | <meta name="renderer" content="webkit"> |
| | | <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> |
| | | <link rel="icon" href="<%= BASE_URL %>favicon.ico"> |
| | | <title><%= webpackConfig.name %></title> |
| | | <!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]--> |
| | | <style> |
| | | |
| | | <head> |
| | | <meta charset="utf-8"> |
| | | <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"> |
| | | <meta name="renderer" content="webkit"> |
| | | <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"> |
| | | <link rel="icon" href="<%= BASE_URL %>favicon.ico"> |
| | | <title> |
| | | <%= webpackConfig.name %> |
| | | </title> |
| | | <!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]--> |
| | | <style> |
| | | html, |
| | | body, |
| | | #app { |
| | | height: 100%; |
| | | margin: 0px; |
| | | padding: 0px; |
| | | overflow: hidden; |
| | | |
| | | } |
| | | |
| | | .chromeframe { |
| | | margin: 0.2em 0; |
| | | background: #ccc; |
| | |
| | | -ms-transform: rotate(0deg); |
| | | transform: rotate(0deg); |
| | | } |
| | | |
| | | 100% { |
| | | -webkit-transform: rotate(360deg); |
| | | -ms-transform: rotate(360deg); |
| | |
| | | -ms-transform: rotate(0deg); |
| | | transform: rotate(0deg); |
| | | } |
| | | |
| | | 100% { |
| | | -webkit-transform: rotate(360deg); |
| | | -ms-transform: rotate(360deg); |
| | |
| | | opacity: 0.5; |
| | | } |
| | | </style> |
| | | </head> |
| | | <body> |
| | | <div id="app"> |
| | | <div id="loader-wrapper"> |
| | | <div id="loader"></div> |
| | | <div class="loader-section section-left"></div> |
| | | <div class="loader-section section-right"></div> |
| | | <div class="load_title">正在加载系统资源,请耐心等待</div> |
| | | </div> |
| | | </div> |
| | | </body> |
| | | </html> |
| | | </head> |
| | | |
| | | <body> |
| | | <div id="app"> |
| | | <div id="loader-wrapper"> |
| | | <div id="loader"></div> |
| | | <div class="loader-section section-left"></div> |
| | | <div class="loader-section section-right"></div> |
| | | <div class="load_title">正在加载系统资源,请耐心等待</div> |
| | | </div> |
| | | </div> |
| | | </body> |
| | | |
| | | </html> |
| | |
| | | |
| | | NProgress.configure({ showSpinner: false }) |
| | | |
| | | const whiteList = ['/login', '/register',] |
| | | const whiteList = ['/login', '/register','/screen'] |
| | | |
| | | router.beforeEach((to, from, next) => { |
| | | NProgress.start() |
| | |
| | | <script> |
| | | import * as echarts from 'echarts'; |
| | | import 'echarts-gl'; |
| | | import mapData from '@/assets/map/zigong.json'; |
| | | import mapData from '@/assets/map/zigong2.json'; |
| | | echarts.registerMap('zigong', mapData); |
| | | console.log(mapData); |
| | | let mapChart = null; |
New file |
| | |
| | | <template> |
| | | <div class="map-container"> |
| | | <wrapper-title :title="'区域地图'"></wrapper-title> |
| | | <div class="map-content"> |
| | | </div> |
| | | </div> |
| | | |
| | | </template> |
| | | |
| | | <script> |
| | | import WrapperTitle from '../wrapper-title/index'; |
| | | |
| | | export default { |
| | | name: 'ScreenMapCover', |
| | | components: { |
| | | WrapperTitle |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .map-container { |
| | | width: 100%; |
| | | flex: 1; |
| | | margin-bottom: 20px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | |
| | | .map-content { |
| | | flex: 1; |
| | | pointer-events: none; |
| | | |
| | | } |
| | | |
| | | } |
| | | </style> |
New file |
| | |
| | | import {MathUtils} from 'three'; |
| | | import { PerspectiveCamera, CameraHelper } from 'three'; |
| | | import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'; |
| | | |
| | | |
| | | export default class Camera { |
| | | constructor(experience) { |
| | | this.experience = experience; |
| | | this.scene = this.experience.scene; |
| | | this.canvas = this.experience.canvas; |
| | | this.sizes = this.experience.sizes; |
| | | this.setInstance(); |
| | | this.setOrbitControls(); |
| | | |
| | | } |
| | | |
| | | // 设置透视相机 |
| | | setInstance() { |
| | | this.instance = new PerspectiveCamera(45,this.sizes.width / this.sizes.height, 0.001, 90000000); |
| | | this.instance.position.set(0, 45, 45); |
| | | this.scene.add(this.instance); |
| | | // const help = new CameraHelper(this.instance); |
| | | // this.scene.add(help); |
| | | } |
| | | |
| | | setOrbitControls() { |
| | | this.controls = new OrbitControls(this.instance, this.canvas); |
| | | this.controls.target.set(0, 0, 5); |
| | | this.controls.enableDamping = true; |
| | | this.controls.minDistance = 20; |
| | | this.controls.maxDistance = 80; |
| | | this.controls.maxPolarAngle = MathUtils.degToRad(80); |
| | | // this.controls.maxPolarAngle = (-Math.PI / 2); |
| | | } |
| | | |
| | | resize() { |
| | | // 重新计算比例 |
| | | this.cameraAspect = (this.sizes.width / this.sizes.height) * this.frustrum; |
| | | this.instance.left = -this.cameraAspect / 2; |
| | | this.instance.right = this.cameraAspect / 2; |
| | | this.instance.top = this.frustrum / 2; |
| | | this.instance.bottom = -this.frustrum / 2; |
| | | this.instance.updateProjectionMatrix(); |
| | | } |
| | | |
| | | |
| | | update() { |
| | | this.controls.update(); |
| | | } |
| | | } |
New file |
| | |
| | | import { Scene, GridHelper,AxesHelper } from 'three'; |
| | | import Stats from "three/examples/jsm/libs/stats.module"; |
| | | |
| | | import World from "./world/world"; |
| | | import Camera from "./camera"; |
| | | import Renderer from "./renderer"; |
| | | |
| | | // 工具类 |
| | | import Sizes from "./utils/sizes"; |
| | | import Time from "./utils/time"; |
| | | |
| | | export default class Experience { |
| | | constructor(canvas) { |
| | | this.canvas = canvas; |
| | | this.sizes = new Sizes(); |
| | | this.time = new Time(); |
| | | this.scene = new Scene(); |
| | | this.camera = new Camera(this); |
| | | this.renderer = new Renderer(this); |
| | | this.world = new World(this); |
| | | |
| | | // const size = 200; |
| | | // const divisions = 200; |
| | | |
| | | // const gridHelper = new GridHelper(size, divisions); |
| | | // this.scene.add(gridHelper); |
| | | |
| | | this.stats = new Stats(); |
| | | document.querySelector('.map-container').appendChild(this.stats.dom); |
| | | |
| | | |
| | | |
| | | // 帧 |
| | | this.time.on('tick', () => { |
| | | this.update(); |
| | | }); |
| | | } |
| | | |
| | | update() { |
| | | this.camera.update(); |
| | | this.world.update(); |
| | | this.renderer.update(); |
| | | this.stats.update(); |
| | | } |
| | | } |
New file |
| | |
| | | import * as THREE from 'three'; |
| | | export default class Renderer { |
| | | constructor(experience) { |
| | | this.experience = experience; |
| | | this.canvas = this.experience.canvas; |
| | | this.sizes = this.experience.sizes; |
| | | this.scene = this.experience.scene; |
| | | this.camera = this.experience.camera; |
| | | |
| | | this.setInstance(); |
| | | } |
| | | setInstance() { |
| | | this.instance = new THREE.WebGLRenderer({ |
| | | canvas: this.canvas, |
| | | antialias: true, |
| | | alpha: true, |
| | | // logarithmicDepthBuffer: true |
| | | }); |
| | | // this.instance.toneMapping = THREE.CineonToneMapping; |
| | | // this.instance.toneMappingExposure = 1.75; |
| | | // this.instance.shadowMap.enabled = true; |
| | | // this.instance.shadowMap.type = THREE.PCFSoftShadowMap; |
| | | this.instance.setSize(this.sizes.width, this.sizes.height); |
| | | this.instance.setPixelRatio(this.sizes.pixelRatio); |
| | | } |
| | | resize() { |
| | | this.instance.setSize(this.sizes.width, this.sizes.height); |
| | | this.instance.setPixelRatio(this.sizes.pixelRatio); |
| | | } |
| | | update() { |
| | | this.instance.render(this.scene, this.camera.instance); |
| | | } |
| | | } |
New file |
| | |
| | | export default class EventEmitter |
| | | { |
| | | constructor() |
| | | { |
| | | this.callbacks = {} |
| | | this.callbacks.base = {} |
| | | } |
| | | |
| | | on(_names, callback) |
| | | { |
| | | // Errors |
| | | if(typeof _names === 'undefined' || _names === '') |
| | | { |
| | | console.warn('wrong names') |
| | | return false |
| | | } |
| | | |
| | | if(typeof callback === 'undefined') |
| | | { |
| | | console.warn('wrong callback') |
| | | return false |
| | | } |
| | | |
| | | // Resolve names |
| | | const names = this.resolveNames(_names) |
| | | |
| | | // Each name |
| | | names.forEach((_name) => |
| | | { |
| | | // Resolve name |
| | | const name = this.resolveName(_name) |
| | | |
| | | // Create namespace if not exist |
| | | if(!(this.callbacks[ name.namespace ] instanceof Object)) |
| | | this.callbacks[ name.namespace ] = {} |
| | | |
| | | // Create callback if not exist |
| | | if(!(this.callbacks[ name.namespace ][ name.value ] instanceof Array)) |
| | | this.callbacks[ name.namespace ][ name.value ] = [] |
| | | |
| | | // Add callback |
| | | this.callbacks[ name.namespace ][ name.value ].push(callback) |
| | | }) |
| | | |
| | | return this |
| | | } |
| | | |
| | | off(_names) |
| | | { |
| | | // Errors |
| | | if(typeof _names === 'undefined' || _names === '') |
| | | { |
| | | console.warn('wrong name') |
| | | return false |
| | | } |
| | | |
| | | // Resolve names |
| | | const names = this.resolveNames(_names) |
| | | |
| | | // Each name |
| | | names.forEach((_name) => |
| | | { |
| | | // Resolve name |
| | | const name = this.resolveName(_name) |
| | | |
| | | // Remove namespace |
| | | if(name.namespace !== 'base' && name.value === '') |
| | | { |
| | | delete this.callbacks[ name.namespace ] |
| | | } |
| | | |
| | | // Remove specific callback in namespace |
| | | else |
| | | { |
| | | // Default |
| | | if(name.namespace === 'base') |
| | | { |
| | | // Try to remove from each namespace |
| | | for(const namespace in this.callbacks) |
| | | { |
| | | if(this.callbacks[ namespace ] instanceof Object && this.callbacks[ namespace ][ name.value ] instanceof Array) |
| | | { |
| | | delete this.callbacks[ namespace ][ name.value ] |
| | | |
| | | // Remove namespace if empty |
| | | if(Object.keys(this.callbacks[ namespace ]).length === 0) |
| | | delete this.callbacks[ namespace ] |
| | | } |
| | | } |
| | | } |
| | | |
| | | // Specified namespace |
| | | else if(this.callbacks[ name.namespace ] instanceof Object && this.callbacks[ name.namespace ][ name.value ] instanceof Array) |
| | | { |
| | | delete this.callbacks[ name.namespace ][ name.value ] |
| | | |
| | | // Remove namespace if empty |
| | | if(Object.keys(this.callbacks[ name.namespace ]).length === 0) |
| | | delete this.callbacks[ name.namespace ] |
| | | } |
| | | } |
| | | }) |
| | | |
| | | return this |
| | | } |
| | | |
| | | trigger(_name, _args) |
| | | { |
| | | // Errors |
| | | if(typeof _name === 'undefined' || _name === '') |
| | | { |
| | | console.warn('wrong name') |
| | | return false |
| | | } |
| | | |
| | | let finalResult = null |
| | | let result = null |
| | | |
| | | // Default args |
| | | const args = !(_args instanceof Array) ? [] : _args |
| | | |
| | | // Resolve names (should on have one event) |
| | | let name = this.resolveNames(_name) |
| | | |
| | | // Resolve name |
| | | name = this.resolveName(name[ 0 ]) |
| | | |
| | | // Default namespace |
| | | if(name.namespace === 'base') |
| | | { |
| | | // Try to find callback in each namespace |
| | | for(const namespace in this.callbacks) |
| | | { |
| | | if(this.callbacks[ namespace ] instanceof Object && this.callbacks[ namespace ][ name.value ] instanceof Array) |
| | | { |
| | | this.callbacks[ namespace ][ name.value ].forEach(function(callback) |
| | | { |
| | | result = callback.apply(this, args) |
| | | |
| | | if(typeof finalResult === 'undefined') |
| | | { |
| | | finalResult = result |
| | | } |
| | | }) |
| | | } |
| | | } |
| | | } |
| | | |
| | | // Specified namespace |
| | | else if(this.callbacks[ name.namespace ] instanceof Object) |
| | | { |
| | | if(name.value === '') |
| | | { |
| | | console.warn('wrong name') |
| | | return this |
| | | } |
| | | |
| | | this.callbacks[ name.namespace ][ name.value ].forEach(function(callback) |
| | | { |
| | | result = callback.apply(this, args) |
| | | |
| | | if(typeof finalResult === 'undefined') |
| | | finalResult = result |
| | | }) |
| | | } |
| | | |
| | | return finalResult |
| | | } |
| | | |
| | | resolveNames(_names) |
| | | { |
| | | let names = _names |
| | | names = names.replace(/[^a-zA-Z0-9 ,/.]/g, '') |
| | | names = names.replace(/[,/]+/g, ' ') |
| | | names = names.split(' ') |
| | | |
| | | return names |
| | | } |
| | | |
| | | resolveName(name) |
| | | { |
| | | const newName = {} |
| | | const parts = name.split('.') |
| | | |
| | | newName.original = name |
| | | newName.value = parts[ 0 ] |
| | | newName.namespace = 'base' // Base namespace |
| | | |
| | | // Specified namespace |
| | | if(parts.length > 1 && parts[ 1 ] !== '') |
| | | { |
| | | newName.namespace = parts[ 1 ] |
| | | } |
| | | |
| | | return newName |
| | | } |
| | | } |
New file |
| | |
| | | /** |
| | | * 计算大小 |
| | | */ |
| | | import EventEmitter from './eventEmitter'; |
| | | export default class Sizes extends EventEmitter { |
| | | constructor() { |
| | | super(); |
| | | this.width = document.body.clientWidth; |
| | | this.height = document.body.clientHeight; |
| | | this.device = document.body.clientWidth <= 968 ? 'mobile' : 'pc'; |
| | | // 设备像素 |
| | | this.pixelRatio = Math.min(window.devicePixelRatio, 2); |
| | | |
| | | // 宽高变化 |
| | | window.addEventListener('resize', () => { |
| | | // this.width = window.innerWidth; |
| | | // this.height = window.innerHeight; |
| | | |
| | | this.width = document.body.clientWidth; |
| | | this.height = document.body.clientHeight; |
| | | this.pixelRatio = Math.min(window.devicePixelRatio, 2); |
| | | this.trigger('resize'); |
| | | |
| | | if(this.width < 968 && this.device !== 'mobile') { |
| | | this.device = 'mobile'; |
| | | this.trigger('devicechange'); |
| | | } else if(this.width >= 968 && this.device !== 'pc') { |
| | | this.device = 'pc'; |
| | | this.trigger('devicechange'); |
| | | } |
| | | }); |
| | | } |
| | | } |
New file |
| | |
| | | import EventEmitter from "./eventEmitter"; |
| | | export default class Time extends EventEmitter { |
| | | constructor() { |
| | | super(); |
| | | this.start = Date.now(); |
| | | this.current = this.start; |
| | | this.elapsed = 0; |
| | | this.delta = 16; |
| | | window.requestAnimationFrame(() => { |
| | | this.tick(); |
| | | }); |
| | | } |
| | | |
| | | tick() { |
| | | const currentTime = Date.now(); |
| | | this.delta = currentTime - this.current; |
| | | this.current = currentTime; |
| | | this.elapsed = this.current - this.start; |
| | | this.trigger('tick'); |
| | | |
| | | window.requestAnimationFrame(() => { |
| | | this.tick(); |
| | | }); |
| | | } |
| | | } |
New file |
| | |
| | | import * as THREE from 'three'; |
| | | import rotatingAperture from '@/assets/map/texture/rotatingAperture.png'; |
| | | import rotatingPoint from '@/assets/map/texture/rotating-point2.png'; |
| | | import circlePoint from '@/assets/map/texture/circle-point.png'; |
| | | import sceneBg from '@/assets/map/texture/scene-bg2.png'; |
| | | export default class Enviroment { |
| | | constructor(experience) { |
| | | this.experience = experience; |
| | | this.scene = this.experience.scene; |
| | | this.textureLoader = new THREE.TextureLoader(); |
| | | |
| | | this.setSunLight(); |
| | | this.setRotateHola(); |
| | | this.setBackground(); |
| | | this.setCirclePoint(); |
| | | } |
| | | setSunLight() { |
| | | // 平行光1 |
| | | let directionalLight1 = new THREE.DirectionalLight(0xffffff, 0.6); |
| | | directionalLight1.position.set(400, 200, 200); |
| | | // 平行光2 |
| | | let directionalLight2 = new THREE.DirectionalLight(0xffffff, 0.6); |
| | | directionalLight2.position.set(-400, -200, -300); |
| | | // 环境光 |
| | | let ambientLight = new THREE.AmbientLight(0xffffff, 0.5); |
| | | |
| | | this.scene.add(directionalLight1); |
| | | this.scene.add(directionalLight2); |
| | | this.scene.add(ambientLight); |
| | | } |
| | | |
| | | setRotateHola() { |
| | | const rotatingApertureTexture = this.textureLoader.load(rotatingAperture); |
| | | const rotatingPointTexture = this.textureLoader.load(rotatingPoint); |
| | | const meshConfig1 = { |
| | | width: 48, |
| | | height: 48, |
| | | texture: rotatingApertureTexture, |
| | | positionList: [0, 0.4, 0], |
| | | scaleList: [1, 1, 1], |
| | | rotateList: [-Math.PI / 2, 0, 0] |
| | | }; |
| | | const meshConfig2 = { |
| | | width: 40, |
| | | height: 40, |
| | | texture: rotatingPointTexture, |
| | | positionList: [0, 0.3, 0], |
| | | scaleList: [1, 1, 1], |
| | | rotateList: [-Math.PI / 2, 0, 0] |
| | | }; |
| | | this.hola1 = this.createMesh(meshConfig1); |
| | | this.hola2 = this.createMesh(meshConfig2); |
| | | this.scene.add(this.hola1); |
| | | this.scene.add(this.hola2); |
| | | } |
| | | |
| | | setBackground() { |
| | | const sceneBgTexture = this.textureLoader.load(sceneBg); |
| | | const plane = new THREE.PlaneGeometry(120, 120); |
| | | const material = new THREE.MeshPhongMaterial({ |
| | | // color: 0x061920, |
| | | color: 0xffffff, |
| | | map: sceneBgTexture, |
| | | transparent: true, |
| | | opacity: 1, |
| | | // depthTest: true |
| | | }); |
| | | this.background = new THREE.Mesh(plane, material); |
| | | this.background.rotation.set(-Math.PI / 2, 0, 0); |
| | | this.background.position.set(0, 0.1, 0); |
| | | this.scene.add(this.background); |
| | | } |
| | | |
| | | setCirclePoint() { |
| | | const circleTexture = this.textureLoader.load(circlePoint); |
| | | const plane = new THREE.PlaneGeometry(45, 45); |
| | | const material = new THREE.MeshPhongMaterial({ |
| | | color: 0x00ffff, |
| | | map: circleTexture, |
| | | transparent: true, |
| | | opacity: 1, |
| | | depthTest: false, |
| | | }); |
| | | this.circle = new THREE.Mesh(plane, material); |
| | | this.circle.rotation.set(-Math.PI / 2, 0, 0); |
| | | this.circle.position.set(0, 0.2, 0); |
| | | this.scene.add(this.circle); |
| | | } |
| | | |
| | | createMesh(config) { |
| | | let { width, height, texture, positionList, rotateList, scaleList } = config; |
| | | let plane = new THREE.PlaneGeometry(width, height); |
| | | let material = new THREE.MeshBasicMaterial({ |
| | | map: texture, |
| | | transparent: true, |
| | | opacity: 1, |
| | | depthTest: false, |
| | | }); |
| | | let mesh = new THREE.Mesh(plane, material); |
| | | mesh.position.set(...positionList); |
| | | mesh.scale.set(...scaleList); |
| | | mesh.rotation.set(...rotateList); |
| | | return mesh; |
| | | } |
| | | |
| | | update() { |
| | | if (this.hola1) { |
| | | this.hola1.rotation.z += 0.001; |
| | | } |
| | | if (this.hola2) { |
| | | this.hola2.rotation.z -= 0.001; |
| | | } |
| | | } |
| | | } |
New file |
| | |
| | | import * as THREE from 'three'; |
| | | import * as d3 from 'd3'; |
| | | import mapData from '@/assets/map/zigong2.json'; |
| | | |
| | | import textureMapImage from '@/assets/map/texture/gz-map.jpg'; |
| | | import textureMapFxImage from '@/assets/map/texture/gz-map-fx.jpg'; |
| | | // 地图深度 |
| | | const MAP_DEPTH = 0.2; |
| | | |
| | | const projection = d3.geoMercator().center([104.779307, 29.33924]).translate([0, 0, 0]); |
| | | export default class Map { |
| | | constructor(experience) { |
| | | this.experience = experience; |
| | | this.scene = this.experience.scene; |
| | | this.material = null; |
| | | this.textureLoader = new THREE.TextureLoader(); |
| | | this.setTexture(); |
| | | this.operationData(mapData); |
| | | } |
| | | setTexture() { |
| | | const textureMap = this.textureLoader.load(textureMapImage); |
| | | const textureMapFx = this.textureLoader.load(textureMapFxImage); |
| | | textureMap.wrapS = textureMapFx.wrapS = THREE.RepeatWrapping; |
| | | textureMap.wrapT = textureMapFx.wrapT = THREE.RepeatWrapping; |
| | | textureMap.flipY = textureMapFx.flipY = false; |
| | | textureMap.rotation = textureMapFx.rotation = THREE.MathUtils.degToRad(45); |
| | | const scale = 0.128; |
| | | textureMap.repeat.set(scale, scale); |
| | | textureMapFx.repeat.set(scale, scale); |
| | | this.topFaceMaterial = new THREE.MeshPhongMaterial({ |
| | | map: textureMap, |
| | | color: 0xb4eeea, |
| | | combine: THREE.MultiplyOperation, |
| | | transparent: true, |
| | | opacity: 1, |
| | | }); |
| | | this.sideMaterial = new THREE.MeshLambertMaterial({ |
| | | color: 0x123024, |
| | | transparent: true, |
| | | opacity: 0.9, |
| | | }); |
| | | } |
| | | |
| | | /** |
| | | * 解析json数据,并绘制地图多边形 |
| | | * @param {*} jsondata 地图数据 |
| | | */ |
| | | operationData(jsondata) { |
| | | |
| | | this.map = new THREE.Object3D(); |
| | | // geo信息 |
| | | const features = jsondata.features; |
| | | features.forEach((feature) => { |
| | | // 单个省份 对象 |
| | | const province = new THREE.Object3D(); |
| | | // 地址 |
| | | province.properties = feature.properties.name; |
| | | // 多个情况 |
| | | // console.log(feature.geometry.type); |
| | | if (feature.geometry.type === "MultiPolygon") { |
| | | console.log(feature.geometry.coordinates); |
| | | feature.geometry.coordinates.forEach((coordinate) => { |
| | | coordinate.forEach((rows) => { |
| | | const line = this.drawBoundary(rows); |
| | | const mesh = this.drawExtrudeMesh(rows); |
| | | province.add(line); |
| | | province.add(mesh); |
| | | }); |
| | | }); |
| | | } |
| | | |
| | | // 单个情况 |
| | | if (feature.geometry.type === "Polygon") { |
| | | feature.geometry.coordinates.forEach((coordinate) => { |
| | | const line = this.drawBoundary(coordinate); |
| | | const mesh = this.drawExtrudeMesh(coordinate); |
| | | province.add(line); |
| | | province.add(mesh); |
| | | }); |
| | | } |
| | | this.map.add(province); |
| | | }); |
| | | this.map.position.set(1, 1, -2); |
| | | this.map.scale.set(10, 10, 10); |
| | | this.map.rotation.set(THREE.MathUtils.degToRad(-90), 0, THREE.MathUtils.degToRad(20)); |
| | | this.container = new THREE.Object3D(); |
| | | this.container.add(this.map); |
| | | this.scene.add(this.container); |
| | | } |
| | | |
| | | /** |
| | | * 画区域分界线 |
| | | * @param {*} polygon 区域坐标点数组 |
| | | * @returns 区域分界线 |
| | | */ |
| | | drawBoundary(polygon) { |
| | | const points = []; |
| | | for (let i = 0; i < polygon.length; i++) { |
| | | const [x, y] = projection(polygon[i]); |
| | | points.push(new THREE.Vector3(x, -y, 0)); |
| | | } |
| | | const lineGeometry = new THREE.BufferGeometry().setFromPoints(points); |
| | | const lineMaterial = new THREE.LineBasicMaterial({ |
| | | color: 0xffffff, |
| | | linewidth: 2, |
| | | transparent: true, |
| | | depthTest: false, |
| | | }); |
| | | const line = new THREE.Line(lineGeometry, lineMaterial); |
| | | line.translateZ(MAP_DEPTH + 0.001); |
| | | return line; |
| | | } |
| | | /** |
| | | * 绘制区域多边形 |
| | | * @param {*} polygon 区域坐标点数组 |
| | | * @returns 区域多边形 |
| | | */ |
| | | drawExtrudeMesh(polygon) { |
| | | const shape = new THREE.Shape(); |
| | | for (let i = 0; i < polygon.length; i++) { |
| | | const [x, y] = projection(polygon[i]); |
| | | if (i === 0) { |
| | | shape.moveTo(x, -y); |
| | | } |
| | | shape.lineTo(x, -y); |
| | | } |
| | | const geometry = new THREE.ExtrudeGeometry(shape, { |
| | | depth: MAP_DEPTH, |
| | | bevelEnabled: false, |
| | | bevelSegments: 1, |
| | | bevelThickness: 0.1, |
| | | }); |
| | | return new THREE.Mesh(geometry, [ |
| | | this.topFaceMaterial, |
| | | this.sideMaterial, |
| | | ]); |
| | | } |
| | | } |
New file |
| | |
| | | import EventEmitter from '../utils/eventEmitter'; |
| | | import Enviroment from './enviroment'; |
| | | import Map from './map'; |
| | | |
| | | export default class World extends EventEmitter { |
| | | constructor(experience) { |
| | | super(); |
| | | this.experience = experience; |
| | | this.scene = this.experience.scene; |
| | | this.enviroment = new Enviroment(this.experience); |
| | | this.map = new Map(this.experience); |
| | | } |
| | | |
| | | update() { |
| | | this.enviroment.update(); |
| | | } |
| | | } |
New file |
| | |
| | | <template> |
| | | <div class="map-container" > |
| | | <canvas class="world" ref="worldContainer"></canvas> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import Experience from './experience/index'; |
| | | let world = null; |
| | | export default { |
| | | name: 'ScreenMapThree', |
| | | mounted() { |
| | | world = new Experience(this.$refs.worldContainer); |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .map-container { |
| | | width: 100%; |
| | | height: 100%; |
| | | position: absolute; |
| | | left: 0; |
| | | top: 0; |
| | | z-index: 0; |
| | | .world { |
| | | width: 100%; |
| | | height: 100%; |
| | | } |
| | | } |
| | | </style> |
| | |
| | | <script> |
| | | import * as echarts from 'echarts'; |
| | | import 'echarts-gl'; |
| | | import mapData from '@/assets/map/zigong1.json'; |
| | | import mapData from '@/assets/map/zigong2.json'; |
| | | import WrapperTitle from '../wrapper-title/index'; |
| | | import ScreenData from '../screen-data/index'; |
| | | |
| | |
| | | 返回 |
| | | </div> |
| | | <div class="wrapper-content"> |
| | | <screen-map-three></screen-map-three> |
| | | <div class="left-container wrapper"> |
| | | <screen-face class="enter-left" :class="{ 'animate-enter-x': isEnd }"></screen-face> |
| | | <screen-car class="enter-left animate-delay-1" :class="{ 'animate-enter-x': isEnd }"></screen-car> |
| | | <screen-video class="enter-left animate-delay-2" :class="{ 'animate-enter-x': isEnd }"></screen-video> |
| | | </div> |
| | | <div class="center-container center-wrapper"> |
| | | <screen-map></screen-map> |
| | | <screen-map-cover></screen-map-cover> |
| | | <screen-table class="enter-top" :class="{ 'animate-enter-y': isEnd }"></screen-table> |
| | | <!-- <screen-detection></screen-detection> --> |
| | | |
| | |
| | | import ScreenFace from '../screen-face/index'; |
| | | import ScreenVideo from '../screen-video/index'; |
| | | import ScreenCar from '../screen-car/index'; |
| | | import ScreenMap from '../screen-map/index'; |
| | | import ScreenMapCover from '../screen-map-cover/index'; |
| | | import ScreenTable from '../screen-table/index'; |
| | | import ScreenMapThree from '../screen-map-three/index'; |
| | | import ScreenData from '../screen-data/index'; |
| | | |
| | | export default { |
| | | name: 'ScreenWrapper', |
| | | components: { |
| | | SelectItem, |
| | | ScreenExamine, |
| | | ScreenMap, |
| | | ScreenTable, |
| | | ScreenMapCover, |
| | | ScreenMapThree, |
| | | ScreenFace, |
| | | ScreenVideo, |
| | | ScreenCar, |
| | |
| | | flex-direction: column; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .animate-enter-x { |
| | | animation: enter-x 0.4s ease forwards; |
| | | } |
| | | |
| | | .animate-enter-y { |
| | | animation: enter-y 0.4s ease forwards; |
| | | } |
| | | |
| | | .enter-left { |
| | | transform: translateX(-100px); |
| | | opacity: 0; |
| | | } |
| | | |
| | | .enter-right { |
| | | transform: translateX(100px); |
| | | opacity: 0; |
| | | } |
| | | |
| | | .enter-top { |
| | | transform: translateY(100px); |
| | | opacity: 0; |
| | | } |
| | | |
| | | .animate-delay-1 { |
| | | animation-delay: 0.1s; |
| | | } |
| | |
| | | } |
| | | |
| | | @keyframes enter-x { |
| | | 100% { |
| | | from { |
| | | opacity: 0; |
| | | } |
| | | |
| | | to { |
| | | opacity: 1; |
| | | transform: translateX(0); |
| | | } |
| | | } |
| | | |
| | | @keyframes enter-y { |
| | | 100% { |
| | | from { |
| | | opacity: 0; |
| | | } |
| | | |
| | | to { |
| | | opacity: 1; |
| | | transform: translateY(0); |
| | | } |
| | |
| | | <template> |
| | | <div class="screen-container"> |
| | | |
| | | <screen-title></screen-title> |
| | | <v-scale-screen width="1920" height="1080" :autoScale="true" :delay="0" class="screen"> |
| | | <screen-wrapper></screen-wrapper> |
| | |
| | | import ScreenTitle from './components/screen-title/index.vue'; |
| | | import ScreenWrapper from './components/screen-wrapper/index.vue'; |
| | | |
| | | |
| | | export default { |
| | | name: 'App', |
| | | components: { |