Prólogo
La energía eólica es un tipo de energía limpia en desarrollo, que es inagotable e inagotable. Por supuesto, la construcción de parques eólicos primero debe considerar las condiciones meteorológicas y las condiciones sociales naturales. En los últimos años, la energía eólica marina y terrestre de China se ha desarrollado rápidamente. El agua de mar y la tierra proporcionan una buena garantía geológica para nuestra generación de energía eólica. Son estos sitios los que proporcionan energía inagotable para nuestra energía eólica. Ahora estamos trabajando duro para explorar estas áreas.
Este artículo implementa el proceso general del parque eólico. Permite a todos ver un sistema completo de vista previa de energía eólica.
Cabe señalar que este proyecto es el uso de Hightopo de TH para Web productos para construir.
Dirección de vista previa: hightopo.com/demo/wind-p ...
Proceso general
El siguiente es un diagrama de flujo de todo el proyecto. Podemos ingresar a la página de distribución del sitio y a la página de control centralizado desde la página de inicio.
La página de distribución del sitio incluye dos escenas 3D diferentes, a saber, el aeropuerto de viento de tierra y el aeropuerto de viento de mar. Al hacer clic en dos aeropuertos eólicos en 3D, finalmente entrará en la escena de las turbinas eólicas en 3D.
Efecto de vista previa
Inicio
1. Efecto del mapa mundial
2. Efecto del mapa de China
2. Efecto del mapa de la ciudad
Página del centro de control central (sin efecto de animación):
Página de distribución del sitio (sin efecto de animación) :
Aeropuerto Landwind:
Aeropuerto Seawind:
Implementación de código
Podemos ver que la tierra en la página de inicio tiene tres estados de perspectiva: mapa mundial, mapa de China y mapa de la ciudad. Haga clic en cada cámara de estado para ir a la posición correspondiente. Antes de eso, tenemos que almacenar previamente el centro y el ojo correspondientes .
Será mejor que creemos un nuevo archivo data.js , específicamente utilizado para proporcionar datos.
El pseudocódigo relevante es el siguiente:
// 记录位置
var cameraLocations = {
earth: {
eye: [-73, 448, 2225],
center: [0, 0, 0]
},
china: {
eye: [-91, 476, 916],
center: [0, 0, 0]
},
tsankiang: {
eye: [35, 241, 593],
center: [0, 0, 0]
}
}
Ok, después de tener los datos. Deberíamos escuchar el evento a continuación. Podemos hacer clic en el botón o en el área resaltada (solo se puede hacer clic en el botón en el mapa mundial) para ingresar a la perspectiva del mapa de China.
Podemos obtener estos dos nodos primero, y luego hacer lo mismo con sus eventos de clic. Sin embargo, creo que este método puede optimizarse y reemplazarse por una forma de pensar.
Primero podemos filtrar los eventos. Creamos dos matrices, una contiene eventos que se pueden ejecutar como click y onEnter , y otra contiene todos los nodos que pueden desencadenar eventos. Esto puede ayudarnos a mantener y aclarar la estructura.
En la figura a continuación, podemos ver que si el nodo actual no tiene permisos de evento o el evento actual en sí no tiene permisos, se filtrará. Si ambos pueden regresar correctamente, se ejecuta el evento correspondiente.
El pseudocódigo relevante es el siguiente:
// 权限事件
this.eventMap = {
clickData: true,
onEnter: true,
onLeave: true
}
// 权限节点
this.nodeMap = {
outline: true,
outline2: true,
earth: true,
bubbles: true,
circle: true
}
/**
* 监听事件
*/
initMonitor() {
var gv = this.gv
var self = this
var evntFlow = function (e) {
var event = e.kind
var tag = e.data && e.data.getTag()
// 检查当前事件或者节点是否能够被执行
if (!self.eventMap[event] && !self.nodeMap[tag]) return false
self.nodeEvent(event, tag)
}
gv.mi(eventFlow)
}
Mientras el nodo que estamos ejecutando actualmente cumpla con los requisitos, pasaremos el evento (evento ejecutado actualmente) y la etiqueta (etiqueta de nodo) a la función de ejecución nodeEvent para ejecutar **. ** Esto no desperdiciará recursos para lidiar con esos eventos o nodos no válidos.
¡Echemos un vistazo a cómo manejar nodeEvent !
El pseudocódigo relevante es el siguiente:
/**
* 气泡事件
* @param { string } event 当前事件
* @param { string } propertyName 当前节点标签
*/
bubblesEvent(event, propertyName) {
var dm = this.dm
var account = dm.getDataByTag('account')
var currentNode = dm.getDataByTag(propertyName)
var self = this
var clickData = function() {
// 执行清除动作
self.clearAction()
}
var onEnter = function() {
// do something
}
var onLeave = function() {
// do something
}
var allEvent = { clickData, onEnter, onLeave }
allEvent[event] && allEvent[event]()
}
Como puede ver, podemos usar la cadena propertyName (etiqueta de nodo) para formar un nombre de método. Por ejemplo, la etiqueta del nodo actual es burbujas. Después de este [`$ $ properName} Evento`] , el método es este ['bubblesEvent'] . Por supuesto, este método se define de antemano.
En el método de nodo específico, creamos la función de evento correspondiente. De acuerdo con el evento pasado para determinar si hay un método correspondiente. Ejecutar si hay, de lo contrario devolver falso . Las ventajas de esto son: desacoplamiento, estructura simple y posicionamiento rápido de problemas.
Sin embargo, si lo pensamos cuidadosamente, cuando hacemos clic en el mapa mundial y el mapa de China, ¡las funciones son casi las mismas! Si podemos fusionarlos, ¡será mucho más conveniente! ! Modifiquemos el código.
El pseudocódigo relevante es el siguiente:
/**
* 执行节点事件
*/
nodeEvent(event, propertyName) {
// 过滤是否有可以合并的事件
var filterEvents = function(propertyName) {
var isCombine = false
var self = this
if (['earth', 'china'].includes(propertyName)) {
self.changeCameraLocaltions(event, propertyName)
isCombine = true
}
return !isCombine
}
var eventFun = this[`${propertyName}Event`]
// 执行对应的节点事件
filterEvents(propertyName)
&&
eventFun
&&
eventFun(event, propertyName)
}
Determinamos de antemano si el evento actual se puede combinar, si devuelve ** falso **, ya no ejecutamos el siguiente código y luego ejecutamos nuestra propia función.
En este momento, podemos pasar la etiqueta nodo correspondiente, desde data.js los cameraLocations adoptadas para la variable correspondiente centro, ojo.
El pseudocódigo relevante es el siguiente:
/**
* 移动镜头动画
* @param { object } config 坐标对象
*/
moveCameraAnim(gv, config) {
var eye = config.eye var center = config.center // 如果动画已经存在,进行清空
if(globalAnim.moveCameraAnim) {
globalAnim.moveCameraAnim.stop() globalAnim.moveCameraAnim = null
}
var animConfig = {
duration: 2e3
}
globalAnim.moveCameraAnim = gv.moveCamera(eye, center, animConfig)
}
// 需要改变相机位置
changeCameraLocaltions(event, properName) {
var config = cameraLocations[properName]
// 移动相机
this.moveCameraAnim(this.gv, config)
}
La animación de cámara en movimiento utiliza el método moveCamera de gv , que acepta 3 parámetros, ojo (cámara) , ** centro (objetivo) y animConfig (configuración de animación). ** Luego ponemos la parte posterior animación actual a la globalAnim de moveCameraAnim atribuye a ayudarnos a limpiar.
Luego, es hora de cambiar de página, lo que requiere mucho cuidado. Porque una vez que un determinado atributo no se borra, causará problemas tales como pérdidas de memoria y el rendimiento será cada vez más lento. ¡Hará que la página se congele!
Por lo tanto, necesitamos una función ** clearAction específicamente para borrar el modelo de datos. ** Deberíamos poner todos los objetos de animación en un objeto o matriz. Esto es conveniente para limpiar al cambiar de página.
El pseudocódigo relevante es el siguiente:
/**
* 清除动作
*/
clearAction(index) {
var { dm, gv } = this
var { g3d, d3d } = window
allListener.mi3d && g3d.umi(allListener.mi3d)
allListener.mi2d && gv.umi(allListener.mi2d)
dm.removeScheduleTask(this.schedule)
dm && dm.clear()
d3d && d3d.clear()
window.d3d = null
window.dm = null
for (var i in globalAnim) {
globalAnim[i] && globalAnim[i].pause()
globalAnim[i] = null
}
// 清除对应的 3D 图纸
ht.Default.removeHTML(g3d)
gv.addToDOM()
ht.Default.xhrLoad(`displays/HT-project_2019/风电/${index}.json`, function (text) {
let json = ht.Default.parse(text)
gv.deserialize(json, function(json, dm2, gv2, datas) {
if (json.title) document.title = json.title
if (json.a['json.background']) {
let bgJSON = json.a['json.background']
if (bgJSON.indexOf('scenes') === 0) {
var bgG3d
if (g3d) {
bgG3d = g3d
} else {
bgG3d = new ht.graph3d.Graph3dView()
}
var bgG3dStyle = bgG3d.getView()
bgG3dStyle.className = index === 1 ? '' : index === 3 ? 'land' : 'offshore'
bgG3d.deserialize(bgJSON, function(json, dm3, gv3, datas) {
init3d(dm3, gv3)
})
bgG3d.addToDOM()
gv.addToDOM(bgG3dStyle)
}
gv.handleScroll = function () {}
}
init2d(dm2, gv2)
})
})
}
Primero, necesitamos eliminar dm (modelo de datos) y gv (dibujo) . También tenga en cuenta: mi (visualización) , Horario (tareas programadas) debe dm.clear () antes de la opción Quitar . Todas las animaciones realizan una operación stop () y luego establecen su valor en nulo . Cabe señalar aquí que después de ejecutar stop , la función de devolución de llamada finishFunc se llamará una vez .
Cuando nuestro dibujo 2D contiene un fondo 3D, necesitamos determinar si ya existe una instancia 3D, y si existe, no necesitamos crearla nuevamente. Si está interesado, puede obtener información sobre el problema de pérdida de memoria de la aplicación webGL.
Al ingresar dos escenas 3D, necesitamos una animación de apertura, como el gif al principio. Por lo tanto, debemos guardar el centro y el ojo de las dos animaciones de apertura en las ubicaciones de la cámara que hemos definido .
// 记录位置
var cameraLocations = {
earth: {
eye: [-73, 448, 2225],
center: [0, 0, 0]
},
china: {
eye: [-91, 476, 916],
center: [0, 0, 0]
},
tsankiang: {
eye: [35, 241, 593],
center: [0, 0, 0]
},
offshoreStart: {
eye: [-849, 15390, -482],
center: [0, 0, 0]
},
landStart: {
eye: [61, 27169, 55],
center: [0, 0, 0]
},
offshoreEnd: {
eye: [-3912, 241, 834],
center: [0, 0, 0]
},
landEnd: {
eye: [4096, 4122, -5798],
center: [1261, 2680, -2181]
}
}
offshoreStart, offshoreEnd, landStart y landEnd representan las posiciones inicial y final de las plantas de energía en alta mar y en tierra **. ** **
Necesitamos determinar si la carga actual es una planta de energía en alta mar o una planta de energía en tierra. Podemos agregar className al cargar los dibujos correspondientes .
Hemos definido el parámetro de índice en la función clearAction . Si hace clic en la planta de energía terrestre, se pasa el número 3. Si es una planta de energía en alta mar, es el número 4.
Por ejemplo, si necesito cargar una granja de tierra, entonces puedo agregar className al juzgar g3d.className = index === 3? 'Land': 'offshore' .
Luego haga un juicio de inicialización en init.
El pseudocódigo relevante es el siguiente:
init() {
var className = g3d.getView().className
// 执行单独的事件
this.selfAnimStart(className)
this.initData()
// 监听事件
this.monitor()
}
Obtenemos el ** className correspondiente, ** pasamos el tipo correspondiente y ejecutamos el evento de inicialización correspondiente, y la animación de la cámara se realiza mediante la función moveCameraAnim que hemos definido .
El pseudocódigo relevante es el siguiente:
/**
* 不同风电场的开场动画
*/
selfAnimStart(type) {
var gv = this.gv
var { eye, center } = cameraLocations[`${type}End`]
var config = {
duration: 3000,
eye,
center,
}
this.moveCameraAnim(gv, config)
}
Resumen
Este proyecto nos dio una mejor comprensión de la energía eólica. Ya sea por las ventajas regionales del parque eólico, o por la estructura y el principio de funcionamiento de la turbina eólica.
Después de terminar este proyecto, he ganado mucho crecimiento y conocimiento. Una buena manera para el rápido crecimiento de la tecnología es elegir constantemente los detalles. El proyecto es una obra de arte, que debe pulirse continuamente hasta que sea satisfactorio. Cada punto sutil afectará el rendimiento detrás. Por lo tanto, debemos hacer cualquier cosa con el espíritu de un artesano.
Por supuesto, también espero que algunos socios tengan el coraje de explorar el campo de Internet industrial. Podemos lograr mucho más que eso. Esto requiere nuestra imaginación para agregar demostraciones más divertidas y prácticas a este campo. Y también puede aprender mucho conocimiento en la industria.