Hola, mi nombre es Artyom, soy el jefe de uno de los grupos de desarrollo de interfaces en Yandex. Hace una semana en Ya. Subbotnik Dije cómo usamos SVG para crear un calendario interno. Esta es una transcripción de mi informe, varias historias de la implementación del widget de calendario: escala, relleno de patrones, máscaras, símbolos y características de formato.

- Hay muchas personas trabajando en Yandex, todas en diferentes ciudades, en diferentes zonas horarias, y debe comprender cuándo están ocupados sus colegas y cuándo puede reunirse y hablar con ellos. Decidimos diseñar un calendario que ayudará a descubrirlo.
Comenzamos, por supuesto, con un diseño. Se veía así:

Muestra eventos pares e impares con diferentes rellenos. Los eventos que se superponen a otros eventos, es decir, están en la segunda capa, otro relleno. Los eventos que duran todo el día pintan todo el día. La hora actual se muestra a continuación. Ese era el objetivo.

Comenzamos a elegir en qué lo haremos. Hizo varios prototipos diferentes. Comenzamos con el lienzo, pero había mucho código, escalando manualmente para escribir. Tuvimos la idea de que el calendario ocupa tanto espacio como sea necesario, en diferentes diseños tiene diferentes formas y tamaños. Y para el lienzo fue complicado.
Hubo un prototipo genial cuando generamos esta imagen completa con gradientes lineales, pero se movió al escalar y al cambiar a la retina. Por lo tanto, al final, llegamos a SVG. Por qué En primer lugar, hay un sistema de coordenadas completamente independiente del documento, por lo que puede colocar completamente todo dentro, y esto no se romperá independientemente de nada. También hay un trabajo normal con el escalado. Incluso si hace zoom en el navegador, si abre la retina o estira el calendario de alguna manera, cambiará de tamaño como una imagen y, en cualquier caso, se verá normal. Tuvimos un relleno de celda en el diseño, y es muy bueno que SVG tenga un relleno de patrón.

Para dibujar un calendario, necesita algunos datos. Para dibujar el que está en el diseño, debe saber en qué fecha comienza, generalmente el actual, para saber cuántos días deben ser horizontales, cuántas horas deben mostrarse verticalmente y qué hora comienza el día en el calendario. Necesitamos de alguna manera obtener eventos.
Como tenemos muchas oficinas en diferentes zonas horarias, decidimos que los eventos siempre llegarán a UTC, y ya los mostraremos en el cliente para los usuarios, ya que es necesario mirar el calendario de nuestra zona horaria y de la zona horaria de la persona, de quién es el calendario que estás mirando, para entender que ahora es de noche y que es mejor no hacer una cita. Lo que está resaltado en rojo se usará más adelante.

Comencemos con lo básico. SVG es un plano de coordenadas gigante en el que los gráficos vectoriales se pueden colocar arbitrariamente. Al mismo tiempo, la parte del área que vemos está determinada por viewBox, y lo que está detrás de sus bordes es un desbordamiento oculto en los esteroides. Sea lo que sea, no será visible. Decidimos que, para simplificar los cálculos, haremos que un píxel en el calendario sea igual a un minuto. Por lo tanto, una hora ocupará exactamente 60 píxeles. Para hacerlo aún más simple, decidimos que el día en ancho también sería de 60 píxeles, de modo que todo fuera cuadrado, como en el ejército. Y comenzaron a componer.
Viewbox está configurado por cuatro parámetros. Los dos primeros son el punto superior izquierdo en el sistema de coordenadas, desde el cual se considera viewBox, para nosotros es 0.0. El ancho es 60 * para la cantidad de días y la altura es 60 * para la cantidad de horas.
Es válido insertar otros documentos SVG dentro del SVG, que tendrá su propio sistema de coordenadas dentro. Y para que los eventos en el día pudieran posicionarse solo a lo largo del eje vertical, decidimos que para cada día tendremos un SVG separado, y simplemente los cambiaremos horizontalmente por 60 * a la posición del día en el calendario. Entonces, todos los eventos se pueden configurar simplemente verticalmente en Y, será muy conveniente. Y dentro de cada SVG, que es un día, colocamos un rectángulo que mostrará el relleno del día.
Este rectángulo, dado que no se especifica color de relleno, heredará la propiedad de relleno de SVG. En este caso, este día está funcionando y dos días a la semana libres, por lo que están inundados de luz. Esto solo está determinado por las clases.

Hay un espacio en blanco. Ahora necesita agregar la cuadrícula. Como queríamos cambiar el tamaño del calendario, y las líneas de la cuadrícula siempre deberían ser de un solo píxel, utilizamos el atributo vector-effect = non-scaling-stroke. Esto lleva al hecho de que no importa cómo cambiemos el tamaño o el zoom, siempre habrá una cuadrícula de un solo píxel. Es suficiente agregar la cantidad correcta de líneas horizontales y verticales, y habrá una grilla de este tipo.

Hemos descubierto la base, pasemos a los eventos de todo el día. Esto es algo muy complicado. Notó que hay eventos en los calendarios y hay una marca de verificación "todo el día". Estos eventos son diferentes en el sentido de que pasan todo el día, sin importar la zona horaria que esté viendo. Por lo tanto, si en algún lugar al comienzo de las zonas horarias en Alaska el evento comienza temprano en la mañana, entonces en algún lugar dentro de las 48 horas en el extremo opuesto del globo continuará. Suena complicado, pero es más fácil de implementar: solo compare la fecha con la fecha del día mostrado. Si golpea, significa un evento en ese día. Si dos eventos para todo el día caen en el día, entonces se muestra el que comenzó más tarde. Así que llena los eventos de espectáculos para todo el día.

Con otros eventos es un poco más difícil. Hay, digamos, una reunión. Está lleno de azul, todo es simple. Sin embargo, si dos reuniones se llevan a cabo en una fila, de acuerdo con nuestro diseño, las llenamos con diferentes colores, son pares e impares.
Si una reunión se cruza con otra, se encuentra más arriba, debe mostrarla de alguna manera. Si hay intersecciones con las reuniones, se vierten completamente por separado, en celdas. Y para hacernos aún más divertidos, no solo tenemos reuniones, sino también ausencias, conferencias y mucho más. No quería codificar todo esto en el diseño, así que decidimos averiguar cómo es más o menos entre navegadores y conveniente para configurar en CSS.

Ahora habrá el ejemplo más difícil de todo el informe, sea paciente y esté atento, habrá tres pasos, luego será más fácil.
Comencemos en orden. SVG tiene una etiqueta <defs>, le permite declarar elementos dentro de ella que no se muestran, pero puede usarlos por referencia, refiriéndose a ellos. Lo primero que haremos es declarar <defs> y crear un patrón en él. <patrón> es una etiqueta que le permite declarar un patrón que puede usar para llenar un elemento particular con un patrón particular.
Necesitamos hacer células en este patrón. Tenemos 60 por 60 píxeles, las celdas deben ser de 6 por 6, por lo que declaramos un patrón de 12 por 12 y dibujamos un <path> dentro de él, como en el diagrama de la izquierda. Tiene un atributo d, que indica exactamente cómo se mueve la línea. Comienza desde el punto 0,0, y luego las coordenadas muestran a las flechas cómo se dibuja. Si lo llenamos de blanco, obtenemos el siguiente patrón: lo que no está inundado de blanco, está inundado de negro.

Vaya al siguiente paso, ahora declare la máscara. <mask> es un elemento en SVG que le permite agregar un canal alfa a otros elementos. Lo que se dibuja en negro en la máscara, en ese elemento al que se aplica la máscara, es invisible, transparente. Lo que está pintado en blanco es opaco. Lo que es gris es translúcido. Tenemos un patrón en blanco y negro, agregaremos un rectángulo dentro de la máscara y lo rellenaremos con este patrón. Ahora tenemos una máscara.
El siguiente paso es <symbol>. Esta es una etiqueta en SVG, que le permite declarar gráficos reutilizables. Muy a menudo, los símbolos se usan, por ejemplo, para iconos. Y aquí declaramos un símbolo, dentro del cual ponemos dos rectángulos. Uno no se llena con nada para que herede la propiedad de relleno del SVG principal, y el otro se llenará con currentColor y le aplicará una máscara. Ahora tendremos dos rectángulos: uno con agujeros y relleno con currentColor, y el otro sin agujeros y relleno con relleno. Y están acostados uno encima del otro. Si establecemos estos colores de la misma manera, tendremos un relleno sólido. Y si es diferente - células. Todo esto fue. Ahora puede usar CSS y, a través de clases, establecer un relleno arbitrario de dos colores para todos los eventos.

Ahora debe determinar exactamente qué eventos deben incluirse en el calendario en un día determinado. Tenemos una zona horaria de +3, en la que todos nos sentamos, tiene una escala de 9 a 20 horas. También hay una persona que se sienta en Orenburg condicional, tiene una zona horaria de +5, su escala se compensa con dos horas en relación con nosotros. Haremos una proyección en UTC, y veremos que en UTC este intervalo de arriba a abajo debe mostrarse en la parte inferior para que el usuario pueda, cambiando entre zonas horarias, ver los eventos que caen en su calendario y el calendario de la persona que está mirando. .
Recuerde estos números, que están en desplazamiento, porque es más fácil colocar los eventos que llegan en UTC en el mismo UTC. Para hacer esto, tomamos la etiqueta <g>, que denota un grupo en SVG, y colocamos todos los eventos allí absolutamente por UTC, y cambiaremos <g> por la cantidad de píxeles que necesitamos para mostrar una u otra zona horaria.

Resumiendo este estudio, obtenemos que tenemos un símbolo al que nos referimos, hay un tipo de evento, nivel, paridad, hay -120 minutos desde el comienzo del día en UTC y una duración de 30 minutos. Al agregar todos los eventos, obtenemos esa imagen.

El tiempo actual también se realiza de manera simple, será una línea con el mismo efecto de trazo sin escala, de modo que siempre sea de un solo píxel. Así es como se muestra.
El tiempo no se detiene y es necesario que la flecha se mueva. La forma más genial que se nos ocurrió es la animación. Decidimos que haríamos una animación que desplazaría la flecha por la cantidad de minutos en un día y lo haría en un día. Y para que no se mueva constantemente lentamente, es decir, marcando una vez por minuto, utilizamos los pasos (). Y tan pronto como lo agregamos, el tiempo comenzó a moverse. Al mismo tiempo, de hecho, dado que la animación no garantiza que se moverá constantemente, se queda atrás u otra cosa. Pero nuestros eventos en el calendario se actualizan de vez en cuando, y en algún lugar cada dos o tres minutos o cuando el usuario ha dejado la pestaña y regresado, se vuelve a dibujar todo el calendario y se actualiza la hora. Por lo tanto, la animación es visible solo cuando estás sentado y observando atentamente si está marcando o no.

Hay un problema Aquí hice un calendario más ancho para que se pareciera más al que está en producción. Quedó claro que las celdas ya no son cuadradas. Esto se debe a que las proporciones no se conservan, y si estiramos o cambiamos la relación de aspecto físicamente, entonces cambia como en la imagen. Para evitar esto, necesitas escribir un pequeño JS. Existe la relación de aspecto viewBox, que estaba en nuestro SVG original, y la relación de aspecto real, que se usa en nuestro diseño. Si encuentra la proporción de estas proporciones y luego la coloca en la transformación del patrón, las celdas se volverán cuadradas. Y también este coeficiente, que obtuvimos aquí, se puede usar si queremos entender dónde hizo clic el usuario. Como tenemos un minuto en el SVG original igual a un píxel, por las coordenadas del clic multiplicado por este coeficiente, podemos entender a qué hora tiene el usuario.

Queda por agregar HTML para que haya letras y números en la parte superior. Obtén un calendario.


Así que esto se ve en producción a través de los ojos de un usuario que se sienta en la zona horaria +5. A continuación se muestra un interruptor de palanca que mi colega presiona, y el calendario se mueve en zonas horarias. Luego hace clic en el evento y ve que el sábado en la zona horaria +5, es decir, en este momento, mi informe está activado.

Unos cuantos ejemplos más. Aquí está el calendario del desarrollador, él tiene stand-ups, varias reuniones regulares y eso es todo. Aquí está el calendario del gerente. Y aquí está el diseñador.
Usa CSS, usa SVG. Gracias