Estuve en un viaje de negocios hace unos días y vi el panel de monitoreo aéreo en el avión. Además de reproducir series de televisión y anuncios, de vez en cuando también cambiaba a un sistema de monitoreo para la navegación de la aeronave. El sistema se sentía un poco tosco, así que por capricho hice una versión mejorada del sistema de monitoreo usando HT para Web. La demostración es bastante buena, así que me gustaría compartirla con ustedes para que podamos aprender unos de otros.
manifestación
Proceso de implementaciónEfecto caminar entre las nubes
Para lograr el efecto de un avión volando a través de las nubes, el primer problema que encontré fue la superposición del vuelo del avión, que comúnmente se conoce como efecto de perspectiva. Aquí utilicé el canal de nubes y el fondo de nubes para fluir a diferentes velocidades. para crear un efecto de perspectiva voladora.
Presenté las nubes en forma de texturas, pero solo las texturas bloquearían el cielo y el avión, lo que afectaría en gran medida la apariencia del avión volando, así que activé la transparencia y la opacidad de los elementos gráficos correspondientes, y establezca diferentes niveles de transparencia para el fondo de la nube y el canal de la nube. No solo agrega una sensación de capas, sino que también da a las personas la ilusión de nubes flotando frente a sus ojos.
El canal de nube utiliza el tipo ht.Polyline. La escala del canal aumenta la proporción del eje Y, dando al canal de nube un espacio vertical más grande. Configurar la copia inversa permite que la textura se muestre dentro del canal de nube, como. si el avión está en el aire viajando a través del mar de nubes; el fondo de la nube adopta el tipo ht.Node y solo se muestra una superficie como fondo de la nube.
El efecto general del flujo de nubes se logra mediante el desplazamiento, y el desplazamiento de textura de la primitiva correspondiente o de la superficie primitiva correspondiente se cambia para lograr el efecto de un avión que viaja a través de las nubes. El código es el siguiente:
var i = 1, p = 0;setInterval(() => { i -= 0.1; p += 0.005; nubes.s('shape3d.uv.offset', [i, 0]); cloudBackground.s(' all.uv.offset', [p, 0]);}, 100);
Efecto lifting
Aunque logra el efecto de un avión volando entre las nubes, si el avión solo vuela en línea recta, también reducirá la sensación real de volar. Creo que los amigos que han volado en un avión deben haber encontrado turbulencias causadas por el flujo de aire, y con frecuencia. Siento la turbulencia causada por el vuelo del avión. El ascenso y descenso en el camino se debe en realidad a que la ruta del avión no siempre está fija a una cierta altitud, a veces sube y otras baja, así que utilicé el complemento de extensión de animación HT ht-animation.js
-in para lograr el efecto de baches del avión. El código es el siguiente:
dm.enableAnimation(20);plane.setAnimation({ back1: { de: 0, a: 160, aceleración: 'Cubic.easeInOut', duración: 8000, siguiente: up1, onUpdate: función (valor) { valor = parseInt( valor); var p3 = this.p3(); this.p3(valor, p3[1], p3[2]); }, //...Omitir inicio similar: [back1]});Restricciones del ángulo de visión del sector esférico
Después de que se perfeccionó el efecto de vuelo, me encontré con un problema más difícil, porque aunque el avión en realidad estaba volando a través del mar de nubes, solo volaba en el canal, y el fondo era en realidad solo una textura plana, así que cuando el la perspectiva alcanzó un cierto Cuando se alcanza este nivel, habrá una fuerte sensación de disonancia e irrealidad, y se necesita un límite del ángulo de visión para realizar el ajuste del ángulo de visión justo dentro de un cierto rango.
Las restricciones del ángulo de visión generalmente limitan el ojo y el centro de g3d. Los amigos que no saben mucho al respecto pueden leer el manual de 3D en el sitio web oficial de Hightopo, que no entrará en detalles aquí; rango de ángulo, decidí fijar la posición del centro, el código es el siguiente:
g3d.addPropertyChangeListener(e => { // Punto central fijo if (e.property === 'center') { e.newValue[0] = center[0]; e.newValue[1] = center[1]; e.newValue[2] = centro[2]; }}
Luego limite el ojo a un cierto rango y listo. Sin embargo, aquí no es tan simple. Al principio, limité el ojo a un espacio cúbico, pero el efecto de interacción no fue ideal considerando eso en la interacción predeterminada de g3d. , el mouse Al arrastrar y desplazarse para cambiar la perspectiva, el ojo en realidad se mueve sobre una superficie esférica con el centro como centro, así que decidí excavar un espacio restringido para el ojo de la bola, que es un sector esférico. Para aquellos que no entienden bien, pueden consultar esta imagen:
El límite del ángulo de visión esférico en forma de abanico requiere un total de tres parámetros, a saber, el eje de referencia central, el ángulo entre el eje central y el borde exterior y el radio limitado de la bola. El eje de referencia central se puede determinar en función de. Línea de extensión que conecta el ojo inicial y el centro, y se ubica el radio limitado de la bola. Se divide en límite máximo y límite mínimo. El código es el siguiente:
function limitEye(g3d, eye, center, options) { var limitMaxL = options.limitMaxL, limitMinL = options.limitMinL, limitA = options.limitA; g3d.addPropertyChangeListener(e => { // Punto central fijo if (e.property = == 'centro') { e.newValue[0] = centro[0]; e.newValue[1] = centro[1]; e.newValue[2] = center[2]; } // Limita el ángulo de visión if (e.property === 'eye') { var newEyeV = new ht.Math.Vector3(e.newValue), centerV = new ht.Math.Vector3(centro), refEyeV = nuevo ht.Math.Vector3(ojo), refVector = refEyeV.clone().sub(centroV), nuevoVector = newEyeV.clone().sub(centerV); if (centerV.distanceTo(newEyeV) > limitMaxL) { newVector.setLength(limitMaxL); e.newValue[0] = newVector.x; nuevoVector.y; e.nuevoValue[2] = nuevoVector.z } if (centerV.distanceTo(newEyeV) < limitMinL) { nuevoVector.setLength(limitMinL); e.nuevoValue[0] = nuevoVector.x; e.nuevoValue[1] = nuevoVector.y; refVector) > límiteA) { var oldLength = newVector.length(), oldAngle = newVector.angleTo(refVector), refLength = oldLength * Math.cos(oldAngle), vertVector, realVector, realEye; refVector.setLength(refLength); newEyeV = newVector.clone().add(centerV); refVector.clone().add(centerV); vertVector = newEyeV.clone().sub(refEyeV); vertLength = refLength * Math.tan(limitA); vertVector.setLength(vertLength); .add(centerV); // Evita que el ángulo de movimiento sea mayor a 180 grados e invierte la perspectiva if (oldAngle > Math.PI / 2) { realEye.negate(); } e.newValue[0] = realEye.x; e.newValue[1] = realEye.y; )}sistema de seguimiento de aeronaves
Por supuesto, como sistema de monitoreo, es natural agregar un pequeño mapa en la esquina inferior derecha y proporcionar tres modos: enfocarse en la aeronave, enfocarse en la trayectoria de vuelo y enfocarse en el mapa, y controlar el efecto de flujo. de la trayectoria de vuelo de acuerdo con la dirección de vuelo de la aeronave. Entre ellos, centrarse en la aeronave seguirá el movimiento de la aeronave y realizará fitData para que la aeronave esté siempre en el centro del minimapa.
var fitFlowP = función (e) { if (e.property === 'posición' && e.data === plano) { mapGV.fitData(plano, falso }};buttonP.s({ 'interactivo': verdadero, 'onClick': función (evento, datos, vista, punto, ancho, alto) { map.a('fitDataTag', 'plane2D'); mapDM.md(fitFlowP); }});buttonL.s({ 'interactive': true, 'onClick': función (evento, datos, vista, punto, ancho, alto) { mapDM.umd(fitFlowP); map. a('fitDataTag', 'flyLine'); mapGV.fitData(flyLine, false }});// ... omitido
Se agregaron indicaciones para mover el mouse a la posición correspondiente de la aeronave para nombrarla, hacer doble clic para mostrar el panel de información de la posición correspondiente de la aeronave y enfocar la perspectiva en el panel, hacer clic en cualquier parte de la aeronave para volver a la modo de vuelo del avión y otros efectos.
Agregar un panel de monitoreo a la izquierda reemplaza el doble clic mencionado anteriormente en la posición correspondiente, que se enfoca directamente en el panel de información en la posición correspondiente. El botón aquí permite la interacción y agrega la lógica de interacción correspondiente. El código es el siguiente:
button_JC.s({ 'interactive': true, 'onClick': función (evento, datos, vista, punto, ancho, alto) { event.preventDefault(); let g3d = G.g3d, g3dDM = G.g3d.dm (); g3d.fireInteractorEvent({ tipo: 'doubleClickData', datos: g3dDM.getDataByTag(data.getTag()) }) }});//... omitidoEfecto de renderizado del cielo
Al ser un sistema de monitoreo, se debe monitorear las 24 horas del día sin distinción. Esto implica un problema. Me resulta imposible volar sobre el cielo azul en medio de la noche. Esto carece de autenticidad, por lo que debe haber un. El proceso del cielo de claro a oscuro y luego de oscuro a claro, tentativamente configuré este proceso en los dos períodos de tiempo de 06:00-06:30 y 19:00-19:30.
El cielo usa una forma esférica de shape3d: 'esfera' para envolver toda la escena, y luego usa reverse.flip para copiar hacia atrás y mezclar tinte. Después de eso, el cielo se puede renderizar en el color que quiero. Luz y sombra del cielo según el tiempo, solo necesito cambiar el valor del tinte.
Sin embargo, debido a las diferentes condiciones de iluminación entre el día y la noche, la intensidad de la luz reflejada por las nubes también es diferente, lo que conduce a la diferencia entre las nubes durante el día y la noche. Por lo tanto, también es necesario ajustar la transparencia de la opacidad de la nube. texturas de fondo de canal y nube, que son más transparentes por la noche. El código es el siguiente:
if ((hora > 6 && hora < 19) || (hora == 6 && minutos >= 30)) { timePane && timePane.a({ 'mañana.visible': falso, 'día.visible': verdadero, ' anochecer.visible': falso, 'noche.visible': falso, 'día.opacidad': 1 }) skyBox.s({ shape3d.blend: 'rgb(127, 200, 240)', }) cloudBackground.s({ back.opacity: 0.7, }) cloud.s({ shape3d.opacity: 0.7, })} else if ((hora < 6 || hora > 19) || (hora == 19 && minutos >= 30)) {//...omitido} else if (hora == 6 && minutos < 15 ) {//...omitido} else if (hora == 6 && minutos >= 15 && minutos < 30) {//...omitido} else if (hora == 19 && minutos < 15) { //...Omitido} else if (hora == 19 && minutos >= 15 && minutos < 30) {//...Omitido}
Aquí también agregué soporte para el ícono de estado de tiempo en la esquina superior derecha del panel de tiempo y agregué un efecto de aparición y desaparición gradual cuando se cambia el ícono. Al mismo tiempo, agregué un clic para cambiar. a la siguiente posición del icono de estado de tiempo para el icono de estado del panel de tiempo.
Para demostrar el efecto, agregué un botón para duplicar el tiempo. La siguiente imagen muestra los cambios a 500 veces el flujo de tiempo:
ResumirA través de esta demostración, descubrí que hay muchos detalles en la vida que las personas no han notado y que existe la posibilidad de visualización de datos. En esta era de big data, vale la pena explorar más posibilidades. La visualización de detalles a su alrededor no sólo puede aprovechar mejor el potencial de HT para la Web, sino también fortalecer la calidad general de uno como programador.