SVG na vida real. Relatório Yandex

Olá, meu nome é Artyom, sou o chefe de um dos grupos de desenvolvimento de interfaces no Yandex. Há uma semana, no Ya. Subbotnik, contei como usamos o SVG para criar um calendário interno. Esta é uma transcrição do meu relatório, com várias histórias da implementação do widget de calendário: dimensionamento, preenchimento de padrões, máscaras, símbolos e recursos de formato.



- Muitas pessoas trabalham no Yandex, todas em cidades diferentes, em fusos horários diferentes, e você precisa entender quando seus colegas estão ocupados e quando você pode encontrar e conversar com eles. Decidimos criar um calendário que ajudará a descobrir.

Começamos, é claro, com um layout. Ele ficou assim:



Mostra eventos pares e ímpares com recheios diferentes. Eventos que se sobrepõem a outros eventos, ou seja, estão na segunda camada - outro preenchimento. Eventos que duram o dia todo são pintados o dia todo. A hora atual é exibida abaixo. Esse era o objetivo.



Começamos a escolher o que faremos. Criou vários protótipos diferentes. Começamos com a tela, mas havia muito código, redimensionando manualmente para escrever. Tínhamos a ideia de que o calendário ocupa tanto espaço quanto necessário; em layouts diferentes, ele tem diferentes formas e tamanhos. E para a tela era complicado.

Havia um protótipo bacana quando geramos toda essa imagem com gradientes lineares, mas ela foi alterada ao dimensionar e ao mudar para a retina. Portanto, no final, chegamos ao SVG. Porque Em primeiro lugar, existe um sistema de coordenadas completamente independente do documento, para que você possa posicionar completamente tudo dentro, e isso não será interrompido, independentemente de qualquer coisa. Também há trabalho normal com dimensionamento. Mesmo se você ampliar o navegador, abrir a retina ou esticar o calendário de alguma forma, ele será redimensionado como uma imagem e, em qualquer caso, parecerá normal. Tivemos um preenchimento de célula no layout, e é muito bom que o SVG tenha um preenchimento de padrão.



Para desenhar um calendário, você precisa de alguns dados. Para desenhar um no layout, você precisa saber em que data ele começa - geralmente o atual - para saber quantos dias devem ser horizontais, quantas horas devem ser exibidas na vertical e a que horas começa o dia no calendário. Precisamos, de alguma forma, receber eventos.

Como temos muitos escritórios em diferentes fusos horários, decidimos que os eventos sempre chegarão ao UTC e já os exibiremos no cliente para usuários, pois é necessário consultar o calendário do nosso fuso horário e do fuso horário da pessoa, em qual calendário você está olhando - para entender que agora é noite e é melhor não marcar uma consulta. O que está destacado em vermelho será usado mais tarde.



Vamos começar com o básico. SVG é um plano de coordenadas gigante no qual gráficos vetoriais podem ser colocados arbitrariamente. Ao mesmo tempo, a parte da área que vemos é determinada pela viewBox, e o que está por trás de suas bordas é um estouro oculto nos esteróides. Seja o que for, não será visível. Decidimos que, para simplificar os cálculos, criaremos um pixel no calendário igual a um minuto. Portanto, uma hora ocupará exatamente 60 pixels. Para simplificar ainda mais, decidimos que o dia em largura também seria de 60 pixels - para que tudo fosse quadrado, como no exército. E eles começaram a escrever.

O Viewbox é definido por quatro parâmetros. Os dois primeiros são o ponto superior esquerdo no sistema de coordenadas, do qual a viewBox é considerada, para nós é 0,0. A largura é 60 * para o número de dias e a altura é 60 * para o número de horas.

É válido inserir outros documentos SVG dentro do SVG, que terão seu próprio sistema de coordenadas. E para que os eventos do dia pudessem ser posicionados apenas ao longo do eixo vertical, decidimos que para cada dia teríamos um SVG separado e apenas os mudaríamos horizontalmente em 60 * para a posição do dia no calendário. Então todos os eventos podem ser simplesmente definidos verticalmente em Y, será muito conveniente. E dentro de cada SVG, que é um dia, colocamos um retângulo que exibirá o preenchimento do dia.

Esse retângulo, como nenhuma cor de preenchimento é especificada, herdará a propriedade de preenchimento do SVG. Nesse caso, este dia está funcionando e dois dias por semana de folga, para que sejam inundados de luz. Isso é determinado apenas pelas classes.



Há um espaço em branco. Agora você precisa adicionar a grade. Como queríamos redimensionar o calendário e as linhas de grade sempre deveriam ter um pixel, usamos o atributo vector-effect = non-scaling-stroke. Isso leva ao fato de que, independentemente de como redimensionamos ou aumentamos o zoom, sempre haverá uma grade de pixel único. Basta adicionar a quantidade certa de linhas horizontais e verticais, e haverá uma grade desse tipo.



Nós descobrimos a base, vamos para os eventos do dia todo. Isso é uma coisa tão complicada. Você notou que há eventos nos calendários e há uma marca de seleção "o dia todo". Esses eventos são diferentes, pois acontecem o dia todo, independentemente do fuso horário em que você está olhando. Portanto, se em algum lugar no início dos fusos horários no Alasca o evento começar de manhã cedo, em algum momento de 48 horas no extremo oposto do globo ainda continuará. Parece complicado, mas é mais fácil de implementar: basta comparar a data com a data do dia exibido. Se ocorrer, significa um evento nesse dia. Se dois eventos para o dia inteiro caírem no dia, será exibido o que mais tarde começou. Então, o preenchimento mostra os eventos do dia inteiro.



Com outros eventos, é um pouco mais difícil. Há, digamos, uma reunião. Está cheio de azul, tudo é simples. No entanto, se duas reuniões forem realizadas seguidas, de acordo com nosso layout, as preencheremos com cores diferentes, elas serão pares e ímpares.

Se uma reunião se cruza com outra, fica mais alta, é necessário exibi-la de alguma forma. Se houver interseções com reuniões, elas serão derramadas completamente separadamente, nas células. E para nos tornar ainda mais divertidos, temos não apenas reuniões, mas também ausências, conferências e muito mais. Como eu não queria codificar tudo isso no layout, decidimos descobrir como é mais ou menos cross-browser e conveniente de configurar em CSS.



Agora, haverá o exemplo mais difícil de todo o relatório, seja paciente e observe, haverá três etapas e será mais fácil.

Vamos começar em ordem. O SVG possui uma tag <defs>, que permite declarar elementos dentro dele que não são exibidos, mas você pode usá-los por referência, consultando-os. A primeira coisa que faremos é declarar <defs> e criar um padrão nele. <padrão> é uma tag que permite declarar um padrão que você pode usar para preencher um elemento específico com um padrão específico.

Precisamos criar células nesse padrão. Como temos 60 por 60 pixels, as células devem ter 6 por 6, declaramos um padrão de 12 por 12 e desenhamos um <caminho> dentro dele, como no diagrama à esquerda. Possui um atributo d, que indica exatamente como a linha se move. Começa a partir do ponto 0,0 e, em seguida, as coordenadas mostram as setas como são desenhadas. Se o preenchermos com branco, obtemos o seguinte padrão: o que não é inundado de branco, é inundado de preto.



Vá para o próximo passo, agora declare a máscara. <mask> é um elemento do SVG que permite adicionar um canal alfa a outros elementos. O que é desenhado em preto na máscara, naquele elemento ao qual a máscara é aplicada, é invisível, transparente. O que é pintado de branco é opaco. O que é cinza é translúcido. Temos um padrão preto e branco, e adicionaremos um retângulo dentro da máscara e o preencheremos com esse padrão. Agora nós temos uma máscara.

O próximo passo é <símbolo>. Essa é uma tag no SVG, que permite declarar gráficos reutilizáveis. Na maioria das vezes, os símbolos são usados, por exemplo, para ícones. E aqui declaramos um símbolo, dentro do qual colocamos dois retângulos. Um não preenche nada, de modo que herda a propriedade fill do SVG pai, e o outro preenche com currentColor e aplica uma máscara a ele. Agora teremos dois retângulos: um com furos e preenchido com currentColor e o outro sem furos e preenchido com preenchimento. E eles estão deitados um em cima do outro. Se definirmos essas cores da mesma forma, teremos um preenchimento sólido. E se diferente - células. Isso tudo foi. Agora você pode simplesmente usar CSS e, através das classes, definir um preenchimento arbitrário de duas cores para todos os eventos.



Agora você precisa determinar exatamente quais eventos devem ser incluídos no calendário em um determinado dia. Temos um fuso horário de +3, no qual todos sentamos, tem uma escala de 9 a 20 horas. Há também uma pessoa sentada em Orenburg condicional, com um fuso horário de +5, sua escala é compensada em duas horas em relação a nós. Faremos uma projeção no UTC e veremos que no UTC esse intervalo de cima para baixo precisa ser exibido na parte inferior, para que o usuário possa, alternando entre os fusos horários, ver os eventos que se enquadram no calendário dele e no calendário da pessoa que ele está olhando. .

Lembre-se desses números, que estão deslocados, porque é mais fácil posicionar os eventos que chegam no UTC no mesmo UTC. Para fazer isso, pegamos a tag <g>, que indica um grupo no SVG, e posicionamos todos os eventos lá absolutamente pelo UTC, e alteramos <g> pelo número de pixels necessários para exibir um ou outro fuso horário.



Resumindo este estudo, obtemos que temos um símbolo ao qual nos referimos, existe um tipo de evento, nível, paridade, há -120 minutos desde o início do dia no UTC e uma duração de 30 minutos. Ao adicionar todos os eventos, obtemos uma imagem dessas.



O horário atual também é feito de forma simples: será uma linha com o mesmo efeito de traçado sem escala, de modo que seja sempre de pixel único. É assim que é exibido.

O tempo não pára e é necessário que a seta se mova. A maneira mais legal de criarmos é a animação. Decidimos fazer uma animação que deslocaria a seta pelo número de minutos em um dia e a faria em um dia. E para que ele não se mova constantemente lentamente, ou seja, marcando uma vez por minuto, usamos os passos (). E assim que adicionamos, o tempo começou a mudar. Ao mesmo tempo, de fato, como a animação não garante que ela se mova constantemente, ela fica para trás ou outra coisa. Mas temos eventos no calendário atualizados de tempos em tempos, e em algum lugar a cada dois a três minutos ou quando o usuário sai da guia e retorna, todo o calendário é redesenhado e o horário é atualizado. Portanto, a animação é visível apenas quando você está sentado e assistindo atentamente, independentemente de estar marcando ou não.



Há um problema. Aqui, ampliei um calendário para que ele se parecesse mais com o de produção. Ficou claro que as células não são mais quadradas. Isso ocorre porque as proporções não são preservadas e, se esticarmos ou alterarmos a proporção fisicamente, ela será alterada como na figura. Para evitar isso, você precisa escrever um pouco de JS. Há a viewBox de proporção, que estava em nosso SVG original, e a proporção real, usada em nosso layout. Se você encontrar a razão dessas proporções e depois colocá-la na transformação do padrão, as células se tornarão quadradas. E também esse coeficiente, que chegamos aqui, pode ser usado se quisermos entender onde o usuário clicou. Como temos um minuto no SVG original igual a um pixel, e pelas coordenadas do clique multiplicadas por esse coeficiente, podemos entender a que horas o usuário ficou.



Resta adicionar HTML para que haja letras e números no topo. Obtenha um calendário.



Portanto, isso ocorre na produção através dos olhos de um usuário que fica no fuso horário +5. Abaixo está uma chave de alternância que meu colega pressiona e o calendário se move nos fusos horários. Depois, ele clica no evento e vê que, no sábado, no fuso horário +5, ou seja, agora, meu relatório está ativado.



Mais alguns exemplos. Aqui está o calendário do desenvolvedor, ele tem stand-ups, várias reuniões regulares e é isso. Aqui está o calendário do gerente. E aqui está o designer.

Use CSS, use SVG. Obrigada

Source: https://habr.com/ru/post/pt461571/


All Articles