Cuando estaba buscando inspiración en echarts hace dos días, vi muchos ejemplos similares de mapas, posicionamiento de mapas, etc., pero parecía que no había ningún mapa del metro, así que pasé un tiempo jugando con esta demostración interactiva del mapa del metro. Los puntos en la línea del metro se descargaron aleatoriamente de Internet. Este artículo registra algunos de mis logros (después de todo, todavía soy un novato) y la implementación del código, espero que pueda ayudar a algunos amigos. Por supuesto, si tienes alguna opinión, puedes decírmelo directamente. Sólo comunicándonos juntos podremos avanzar.
representaciones
http://www.hightopo.com/demo/subway/index.html
El mapa tiene demasiado contenido. Si desea mostrarlo todo, las fuentes aparecerán un poco pequeñas, pero no importa, puede acercar o alejar según sea necesario. Las fuentes y el contenido dibujado no se distorsionarán. Después de todo, todos están dibujados con vectores ~.
Generación de interfazEl div subyacente se genera a través del componente ht.graph.GraphView. Luego, puede usar los buenos métodos proporcionados por HT para Web y simplemente llamar al pincel de lienzo para dibujar de manera informal. Primero veamos cómo generar el div subyacente:
var dm = new ht.DataModel();//Contenedor de datos var gv = new ht.graph.GraphView(dm);//Componente de topología gv.addToDOM();//Agregue el componente del gráfico de topología al cuerpo
La función addToDOM se declara de la siguiente manera:
addToDOM = function(){ var self = this, view = self.getView(), style = view.style; document.body.appendChild(view); agrega el div subyacente del componente al cuerpo style.left = '0 ';//El componente predeterminado está en una posición absoluta, así que establezca la posición style.right = '0' style.top = '0' style.bottom = '0'; () { self.iv(); }, false); //Evento de cambio de ventana}
Ahora puedo hacer garabatos en este div ~ Primero obtengo los puntos en el mapa del metro descargado y los coloco en metro.js. Este archivo js es todo el contenido descargado, no hice nada. Otros cambios son principalmente para agregar estos. apunta a la matriz según la línea, como por ejemplo:
mark_Point13 = [];//La matriz de líneas contiene las coordenadas iniciales y finales de la línea y el nombre de la línea t_Point13 = [];//La matriz de estaciones contiene las coordenadas de la estación de transferencia en la línea y el nombre de la estación n_Point13 = [ ];//La matriz de estaciones pequeñas contiene las coordenadas de las estaciones pequeñas en la línea y los nombres de las estaciones pequeñas mark_Point13.push({ nombre: 'Línea 13', valor: [113.4973,23.1095]}); mark_Point13.push({ nombre: 'Línea 13', valor: [113.4155,23.1080]}); t_Point13.push({ nombre: 'Yu Zhu', valor: [113.41548,23.10547]} ); n_Point13.push({nombre: 'Yufengwei', valor: [113.41548,23.10004]});
A continuación, para dibujar las líneas del metro, declaré una matriz lineNum para contener los números de todas las líneas del metro en js, y una matriz de colores para contener los colores de todas las líneas del metro. El índice de estos colores es el mismo que el del metro. línea en líneaNum. Los índices numerados se corresponden uno a uno:
var númerolínea = ['1', '2', '3', '30', '4', '5', '6', '7', '8', '9', '13', '14 ', '32', '18', '21', '22', '60', '68'];var color = ['#f1cd44', '#0060a1', '#ed9b4f', '#ed9b4f', '#007e3a', '#cb0447', '#7a1a57', '#18472c', '#008193', '#83c39e', '#8a8c29', '#82352b', '#82352b', '#09a1e0', '#8a8c29', '#82352b', '#b6d300', '#09a1e0'];
Luego recorra lineNum, pase los elementos y colores en lineNum a la función createLine y dibuje las líneas del metro y la combinación de colores de acuerdo con estos dos parámetros. Después de todo, el método de nomenclatura en el archivo js también es el nombre de la línea Add. el número correspondiente, por lo que solo necesitamos combinar la cadena con este número para obtener la matriz correspondiente en js:
let lineName = 'Line' + num; let line = window[lineName]; La definición de createLine también es muy simple. Mi código ha establecido muchos estilos, por lo que parece un poco excesivo. Para crear una tubería ht.Polyline, podemos agregar puntos específicos a esta variable a través de la función polyline.addPoint() y establecer el método de conexión de los puntos a través de setSegments. function createLine(num, color) {//Dibujar una línea de mapa var polyline = new ht.Polyline();//Polygon pipeline polyline.setTag(num);//Establecer la etiqueta de etiqueta de nodo como la única etiqueta if(num = == '68') polyline.setToolTip('AP M');//Establecer información de solicitud else if(num === '60') polyline.setToolTip('G F'); polyline.setToolTip('Line' + num); if(color) { polyline.s({//s es la abreviatura de setStyle, establece el estilo 'shape.border.width': 0.4,//Establece el ancho del borde de el polígono 'shape .border.color': color,//Establece el color del borde del polígono 'select.width': 0.2,//Establece el ancho del borde del nodo seleccionado'select.color': color//Establece el color del borde del nodo seleccionado}); let lineName = 'Line' + let line = window[lineName]; for(let i = 0; i < line.length; i++) { for(let j = 0; j < line[i].coords.length; j++) { polyline.addPoint({x: línea[i].coords[j][0]*300, y: -line[i].coords[j][1]*300}); if(num === '68'){//línea APM (Hay dos, pero los puntos están en la misma matriz) if(i === 0 && j === 0) { polyline.setSegments([1] } else if(i === 1 &&); j === 0) { polyline.getSegments().push(1); else { polyline.getSegments().push(2); En la capa inferior, el punto se establece en la parte superior de la capa superior. dm.add(polyline);// Agregue la tubería al contenedor de datos para su almacenamiento; de lo contrario, la tubería está en un estado libre y no se mostrará en el mapa de topología return polyline;}
Hay varias situaciones para agregar puntos en la línea del metro en el código anterior. Esto se debe a que Line68 tiene un punto de salto al configurar la línea en js, por lo que debemos saltar a él. Para la declaración específica de Line68. matriz, consulte metro.js.
Una cosa a tener en cuenta aquí es que si usa la función addPoint y no establece segmentos, los puntos agregados se conectarán con líneas rectas de forma predeterminada. La definición de segmentos es la siguiente:
1: moveTo, ocupa 1 punto de información, que representa el punto de inicio de una nueva ruta
2: lineTo, ocupa información de 1 punto, que representa la conexión desde el último punto hasta este punto
3: quadraticCurveTo, ocupa información de 2 puntos, el primer punto se usa como punto de control de la curva y el segundo punto se usa como punto final de la curva.
4: bezierCurveTo, ocupa información de 3 puntos, el primer y segundo punto se utilizan como puntos de control de la curva y el tercer punto se utiliza como punto final de la curva.
5: closePath, no ocupa información del punto, representa el final de este dibujo de ruta y se cierra al punto de inicio de la ruta
Entonces, si queremos realizar un comportamiento de salto, simplemente establezca los segmentos en 1.
Finalmente, se dibujan estos puntos en la línea del metro. Esta parte también está separada en metro.js. Los nombres comienzan con mark_Point, t_Point y n_Point. Expliqué estas matrices en la parte de visualización de js anterior. arriba para ver mirar.
Agregamos nodos ht.Node en estos puntos. Cuando los nodos se agregan al contenedor de datos dm, se mostrarán en el mapa de topología. Por supuesto, la premisa es que el contenedor de datos establecido por el componente del mapa de topología gv es este dm. . Debido al espacio limitado, solo mostraré la parte del código para agregar puntos en la línea del metro:
var tName = 't_Point' + num;var tP = window[tName];//Estación grande if(tP) {//Algunas líneas no tienen estaciones de transferencia for(let i = 0; i < tP.length; i++) { let nodo = crearNodo(tP[i].nombre, tP[i].valor, color[index]);//Agregar nodo node.s({//Establecer el estilo del nodo 'label.scale': 0.05,//Escalado del texto para evitar restricciones del navegador El problema del tamaño mínimo de fuente'label.font': ' negrita 12px arial, sans-serif'//Establece la fuente del texto }); node.setSize(0.6, 0.6);//Establece el tamaño del nodo. Dado que el desplazamiento entre cada punto en js es demasiado pequeño, tuve que configurar el nodo más pequeño node.setImage('images/rotating arrow.json');//Establecer la imagen del nodo node.a('alarmColor1 ', ' RGB(150, 150, 150)'); // atributo attr, puede configurar cualquier cosa aquí. alarmColor1 es el atributo vinculado en el json de la imagen establecida anteriormente. Para obtener más información, consulte el Manual de vectores web de HT (http://www.hightopo). com/guide/guide/core/vector/ht-vector-guide.html#ref_binding) node.a('alarmColor2', 'rgb(150, 150, 150)');// Igual que el anterior node.a('tpNode', true);// Esta configuración de propiedad solo se usa para distinguir sitios de transferencia y sitios pequeños, que se usarán más adelante}}
Se han agregado todas las líneas y estaciones de metro. ¡pero! Es posible que no pueda ver los gráficos que dibujó porque son demasiado pequeños. En este momento, puede configurar la función fitContent en el componente de topología GraphView. Por cierto, también configuramos todo en el gráfico de topología para que sea inamovible.
gv.fitContent(false, 0.00001);//Tamaño adaptable, el parámetro 1 es si se anima, el parámetro 2 es el valor de relleno de gv y el borde gv.setMovableFunc(function(){ return false;//Establezca el nodo en gv en ser inmueble });
Ahora se puede mostrar el mapa de la ruta del metro ~ Echemos un vistazo a la interacción.
interacciónEl primero es el evento de movimiento del mouse. Cuando el mouse se desliza sobre una línea específica, la línea se volverá más gruesa. Si pasa el cursor por un momento, también puede ver el número de esta línea cuando el mouse se mueve a un sitio de transferencia. sitio pequeño, el ícono correspondiente al sitio se hará más grande y cuando el color cambie, la fuente también se hará más grande. Cuando mueva el mouse, el ícono volverá a su color original y la fuente se hará más pequeña. La diferencia es que cuando el mouse se mueve hacia la estación de transferencia, la estación de transferencia girará.
Para el evento de deslizamiento del mouse, realizo directamente el evento mousemove basado en el div subyacente de gv, paso los parámetros del evento a través de la función getDataAt encapsulada por ht, obtengo el nodo correspondiente bajo el evento y luego puedo operar el nodo a voluntad. :
gv.getView().addEventListener('mousemove', function(e) { var data = gv.getDataAt(e);// Pase el punto de coordenadas lógicas o el parámetro de evento de evento interactivo y devuelva la primitiva bajo el punto actual if( nombre) { originNode(nombre);//Mantenga el nodo en su tamaño original en todo momento} if (instancia de datos de ht.Polyline) {//Determine el tipo de nodo de evento dm.sm().ss(data);//Seleccione el nombre de la tubería = ''; clearInterval(interval); else if (datastanceof ht.Node) { if(data. getTag( ) !== name && data.a('tpNode')) {// Si no es el mismo nodo y el objeto del evento mousemove es del tipo ht.Node, establezca el intervalo de rotación del nodo = setInterval(function() { data.setRotation(data.getRotation() - Math.PI/16); //Rotar según su propia rotación}, 100 } if(data.a('npNode')) {/ /Si el mouse se mueve a un sitio pequeño, detenga la animación clearInterval(interval); } expandNode(data, name);//// Función de nodo de zoom personalizada, relativamente fácil, ya no me quedo con el código, puedes ir a http://hightopo.com/ para ver dm.sm().ss(data); //Establece el nombre del nodo seleccionado = data.getTag();//Como variable de almacenamiento del nodo anterior, puedes obtener el nodo a través de este valor} else {//En cualquier otro caso, no se selecciona nada y la animación continúa el sitio de transferencia se borra dm.sm( ).ss(null name =); ''; borrarIntervalo(intervalo); }});
Cuando el mouse pasa sobre una línea de metro, se muestra la información de la línea específica. Lo hago configurando la información sobre herramientas (nota: el interruptor de información sobre herramientas de gv debe estar activado):
gv.enableToolTip();//Active el interruptor de información sobre herramientas if(num === '68') polyline.setToolTip('AP M');//Establezca la información del mensaje else if(num === '60') polilínea.setToolTip('G F'); si no, polilínea.setToolTip('Línea' + num);
Luego uso el formulario en la esquina inferior derecha para hacer clic en una línea específica del formulario, o hago doble clic en cualquier sitio o línea en el mapa topológico. El mapa topológico se adaptará a la parte correspondiente y la parte en la que se hizo doble clic. se mostrará en el centro del mapa de topología.
Parece que todavía no he explicado la parte de declaración del formulario. . . Es decir, crear un componente de formulario a través de la nueva clase ht.widget.FomePane, obtener el div subyacente del componente de formulario a través de form.getView(), colocar este div en la esquina inferior derecha del cuerpo y luego agregar una fila a el formulario a través de la función addRow Elementos del formulario, puede agregar cualquier número de elementos en esta fila, a través de addRow El segundo parámetro de la función (una matriz) establece el ancho del elemento de formulario agregado y el tercer parámetro establece la altura de la fila:
function createForm() {//Crea el formulario en la esquina inferior derecha var form = new ht.widget.FormPane(); form.setWidth(200);//Establece el ancho del formulario form.setHeight(416);// Establezca la altura del formulario let view = form.getView(); document.body.appendChild(view);//Agregue el formulario al cuerpo view.style.zIndex = 1000; view.style.bottom = '10px'; // Casi todos los componentes ht establecen rutas absolutas view.style.right = '10px'; forEach(function(nameString) { form.addRow([//Agregar una fila al formulario{//El primer botón de elemento de formulario en esta fila: {//Agregar el ícono del botón al formulario: 'images/Line'+nameString.value+'.json',//Establecer el fondo del ícono del botón: '',//Establecer el color del borde del fondo del botón: '',//Establecer el color del borde del botón en el que se puede hacer clic: falso // Configurar el botón para que no se pueda hacer clic} }, {// El botón del segundo elemento del formulario: {etiqueta: nameString.name, labelFont: 'bold 14px arial, sans-serif', labelColor: '#fff', fondo: '', borderColor: '', onClicked: function() {//Evento de devolución de llamada al hacer clic en el botón gv.sm().ss(dm.getDataByTag(nameString.value) );//Establece la línea correspondiente al botón presionado seleccionado gv.fitData(gv.sm().ld(), true, 5);//Muestra la línea de metro seleccionada en el centro del mapa topológico} } } ], [0.1, 0.2], 23);//El segundo parámetro es establecer el ancho de la matriz en el primer parámetro, menos mayor que 1 es la relación, mayor que 1 es el ancho real. El tercer parámetro es la altura de la fila});}.
Haga clic en el sitio para mostrar la marca roja, haga doble clic en el nodo para colocarlo de forma adaptativa en el centro del mapa de topología y haga doble clic en el espacio en blanco para ocultar la marca roja. El contenido se controla mediante el monitoreo de eventos del componente de topología. gv. Es muy claro y fácil de entender. El código es el siguiente.
var node = createRedLight();//Crea un nuevo nodo, que se muestra como un estilo de luz roja gv.mi(function(e) {//Monitoreo de eventos en el componente de topología en ht if(e.kind === 'clickData ' && (e.data.a('tpNode') || e.data.a('npNode'))) {//e.kind obtiene el tipo de evento actual, e.data obtiene el nodo bajo el evento actual node.s('2d.visible', true);//Configura el nodo para que sea visible node.setPosition(e. data.getPosition() .x, e.data.getPosition().y);//Establece las coordenadas del nodo en la posición del nodo bajo el evento actual} else if(e.kind === 'doubleClickData') {//Haga doble clic en el nodo gv.fitData(e.data, false, 10);//Adapte el nodo debajo del evento al centro del mapa de topología. El parámetro 1 es el nodo adaptativo, el parámetro 2 es si se anima. y el parámetro 3 es Relleno de gv y borde } else if(e.kind === 'doubleClickBackground') {//Haga doble clic en el espacio en blanco node.s('2d.visible', false);//Establezca el nodo nodo será invisible Ver HT para Web Manual de estilo (http://www.hightopo.com/guide/guide/core/theme/ht-theme-guide.html#ref_style) }});
Tenga en cuenta que s (estilo) y a (attr) se definen así. s son algunos atributos de estilo predefinidos por ht, y a es un atributo personalizado por nuestros usuarios. El resultado generalmente se llama llamando a esta cadena. ser una constante o una función, que es muy flexible.
Finalmente, se crea una pequeña parte. Cuando se selecciona un sitio, se mostrará un ícono de respiración rojo encima del sitio para indicar el sitio seleccionado actualmente.
La parte de respiración se completa usando la función setAnimation de ht. Antes de usar esta función, primero debe activar el interruptor de animación del contenedor de datos y luego configurar la animación:
dm.enableAnimation();//Activar la función de cambio de animación del contenedor de datos createRedLight() { var node = new ht.Node(); node.setImage('images/RedLight.json');//Establecer la imagen nodo del nodo .setSize(1, 1);//Establece el tamaño del nodo node.setLayer('firstTop');//Establece el nodo que se mostrará en la capa superior de gv node.s('2d.visible', false);//El el nodo es invisible node.s( 'select.width', 0);// El borde cuando se selecciona el nodo es 0 y invisible node.s('2d.selectable', false);//Establezca este atributo, el nodo no se puede seleccionar node.setAnimation({//Para obtener detalles sobre cómo configurar la animación, consulte el Manual de animación HT para Web (http://www.hightopo.com/guide/guide /plugin/animation/ht-animation-guide.html) expandWidth: { propiedad: ancho,// Establezca esta propiedad y el tipo de acceso no está configurado, el valor predeterminado es establecer y obtener la propiedad a través de setWidth/getWidth El ancho aquí y. Se utilizan las alturas siguientes. Todos se obtienen por el tamaño establecido previamente desde: 0.5, //Valor del atributo al comienzo de la animación a: 1,//Valor del atributo al final de la animación siguiente: colapsoWidth//Tipo de cadena, especifica qué ejecutar después la animación actual se completa La siguiente animación puede fusionar varias animaciones}, colapsoWidth: { propiedad: ancho, desde: 1, hasta: 0,5, siguiente: expandWidth }, expandHeight: { propiedad: altura, desde: 0,5, hasta: 1, siguiente: colapsoHeight }, colapsoHeight: { propiedad: altura, desde: 1, hasta: 0.5, siguiente: expandHeight }, inicio: [expandWidth, expandHeight]// Matriz, utilizada para especificar una o más animaciones que se iniciarán}) ; dm.add(nodo); devolver nodo;}
¡Todo el código ha terminado!
ResumirMe tomó dos días completar esta demostración y siempre me sentí un poco reacio a hacerlo. Sin embargo, a veces no podía entenderlo y tomó mucho tiempo, pero en general gané mucho. Pensar que siempre que pase, solo use getPoints (). push para agregar puntos al polígono. Después de pedir ayuda a un maestro, descubrí que este método no solo es un desvío, sino que también causa varios problemas, por ejemplo, antes de obtener puntos. , ya debes tener puntos en el polígono. Es posible, pero en muchos casos, los puntos inicializados no son fáciles de configurar y el código será muy engorroso. Los puntos se agregan directamente a la variable poligonal mediante el método addPoint y los puntos se conectan mediante líneas rectas de forma predeterminada. No es necesario establecer segmentos, qué función tan bonita.
Además, debido a que el tamaño de zoom predeterminado de ht es 20 y el espacio de mi demostración es muy pequeño, la visualización del mapa de la línea del metro también es muy pequeña cuando se amplía al máximo, por lo que cambié el atributo zoomMax predeterminado de ht en htconfig. , cambiar Este valor debe estar antes de todas las llamadas a ht, porque los valores establecidos en htconfig no se pueden cambiar más adelante.
Lo anterior es el mapa interactivo de la línea de metro basado en HTML5 Canvas que le presenta el editor. Espero que le resulte útil. Si tiene alguna pregunta, déjeme un mensaje y el editor le responderá a tiempo. ¡También me gustaría agradecer a todos por su apoyo al sitio web de artes marciales VeVb!