
Lembra da última vez em que você encontrou um erro de IU que levou horas e horas para ser corrigido? Talvez esse erro ocorresse periodicamente, sem motivo aparente. Talvez tenha aparecido sob certas condições (pode depender do dispositivo, sistema operacional, navegador ou ações do usuário) ou estava escondido em algum lugar nas profundezas de uma das muitas tecnologias front-end que fazem parte do lado cliente do projeto da web?
Recentemente, tive que lembrar como as causas dos erros de interface do usuário podem ser confusas. Ou seja, estamos falando sobre como corrigir um bug interessante que afeta a saída de imagens SVG no navegador Safari. Este erro ocorreu sem nenhum sistema específico e sem razão óbvia. Quando me deparei com um problema, tentei encontrar casos semelhantes, na esperança de que as descrições de tais casos me dêem uma pista do que está acontecendo. Mas não consegui encontrar nada útil. É verdade que, apesar de todos os obstáculos diante de mim, fui capaz de lidar com esse erro.
Eu analisei o problema usando algumas das estratégias de depuração que abordarei neste artigo. Depois de me livrar do erro, lembrei-me do conselhoque Chris Coyer deu a seus leitores do Twitter alguns anos atrás. Este conselho é mais ou menos assim: "Escreva o artigo que gostaria de encontrar ao visitar um mecanismo de pesquisa." Na verdade, foi o que fiz.
Visão geral do problema
Num projeto em que estava trabalhando, em um site ao vivo, encontrei um erro, cuja manifestação gravei neste vídeo. É assim que o botão se parece em seu estado normal.

Botão no estado normal
E aqui está o mesmo botão após um problema.

Parte do botão está cortada.
Reproduzi esse erro em várias situações, fazendo com que a página fosse redesenhada. Por exemplo, ocorreu quando a janela do navegador foi redimensionada.
Para demonstrar o problema, criei um exemplo no CodePen . Vamos discutir isso com mais detalhes abaixo. Mas você mesmo pode experimentar este exemplo. Ou seja, estamos falando sobre o fato de que, se você abrir este exemplo no navegador Safari, quando a página for carregada, os botões terão a aparência esperada. Mas se você clicar em um dos dois botões maiores, o bug aparecerá com sua cabeça feia.

Por que a imagem SVG é cortada?
Cada vez que o evento ocorre
paint, as imagens SVG usadas nos botões maiores não são exibidas corretamente. Essas imagens são simplesmente cortadas. Isso pode acontecer, sem motivo aparente, no carregamento da página. Isso pode acontecer mesmo quando a janela é redimensionada. Em geral, o erro aparece em várias situações.
Uma visão geral do projeto em que ocorreu o erro
Acho que ao falar sobre o erro, seria bom revelar detalhes do projeto e das condições em que ele ocorre.
- O projeto usa React (mas o leitor deste artigo não precisa conhecer o React para entendê-lo).
- As imagens SVG são importadas para o projeto como componentes React e incorporadas em HTML usando webpack.
- . .
- CSS.
- , , HTML-
<button>. - Safari ( 13 ).
Vamos examinar o erro e considerar se podemos fazer suposições sobre o que está acontecendo. As razões para tais erros geralmente não residem em algum lugar na superfície, portanto, diante de tais erros, não se pode dizer imediatamente com confiança o que está acontecendo. Quando fazemos nossa primeira tentativa de entender um problema, não precisamos nos esforçar para identificar sua causa com 100% de precisão. Investigaremos o erro passo a passo, formulando e testando hipóteses que nos ajudarão a restringir a lista de possíveis causas do que está acontecendo.
Formulando uma hipótese
À primeira vista, o que está acontecendo parece um erro CSS. Talvez, ao passar o mouse sobre o botão, alguns estilos sejam aplicados a ele, o que quebra o layout. Talvez o atributo da
overflowimagem SVG seja o culpado . Além disso, há a sensação de que o erro ocorre sem nenhum sistema específico quando a página é redesenhada por vários motivos (um evento paintquando a janela do navegador é redimensionada, quando o ponteiro do mouse está sobre um botão, quando ele é clicado e assim por diante).
Vamos começar com a suposição mais simples e óbvia. Digamos que o erro esteja no CSS. Podemos supor que há um bug no navegador Safari que resulta em saída SVG incorreta quando estilos específicos são aplicados a elementos SVG. Por exemplo, como os estilos usados para construir layouts flexíveis.
Acabamos de formular uma hipótese. Nosso próximo passo é conduzir um teste que irá confirmar ou refutar essa hipótese. O resultado de cada teste nos dará novas informações sobre o erro e ajudará na formulação das seguintes hipóteses.
Simplificando o problema
Usaremos uma estratégia de depuração chamada "simplificando o problema". Isso nos permitirá identificar onde ocorreu o erro. Em uma palestra sobre ciência da computação na Cornell University, essa estratégia é descrita como "uma abordagem para se livrar gradualmente de código que não está relacionado a erros".
Supondo que o erro esteja no CSS, podemos eventualmente encontrar a causa do erro ou excluir o CSS da equação, o que reduzirá o número de possíveis causas do erro e reduzirá a complexidade do problema.
Vamos testar nossa hipótese. Vamos tentar confirmar. Nesse caso, se você desabilitar temporariamente todos os estilos fora do padrão da página, o erro não aparecerá mais.
Aqui está o código para incluir a folha de estilo apropriada:
import 'css/app.css';
Criei este projeto CodePen para demonstrar a saída de elementos sem CSS . No React, os gráficos SVG são importados para um projeto como um componente e , em seguida, o código correspondente é incorporado ao HTML usando o webpack.

Visualização normal do botão
Se você abrir o projeto mencionado no Safari e clicar em um dos botões grandes, o erro ainda estará lá. Também acontece quando a página é carregada, mas ao usar CodePen, você precisa clicar no botão para acionar um erro.

O erro não desapareceu mesmo quando o CSS foi desativado (Safari 13)
Como resultado, podemos concluir que o CSS não tem nada a ver com isso. No entanto, podemos prestar atenção ao fato de que em tais condições apenas dois dos cinco botões são exibidos incorretamente. Vamos nos lembrar disso e passar para a próxima hipótese.
Isolamento de erro
Nossa próxima hipótese é que o Safari tem um bug ao renderizar imagens SVG dentro de elementos HTML
<button>. Como o problema surge quando os primeiros dois botões são exibidos, isolamos o primeiro e vemos o que acontece.
Sara Drazner nesteO material explica a importância do isolamento. Eu recomendo fortemente a leitura deste material para qualquer pessoa interessada em mais detalhes sobre ferramentas de depuração e diferentes abordagens para encontrar bugs. Aqui está uma citação desse material: “O isolamento é talvez o princípio básico mais importante de depuração. O código que funciona em nossos projetos pode ser espalhado por diferentes bibliotecas e estruturas. Muitas pessoas podem estar envolvidas no trabalho em projetos, e alguns daqueles que contribuíram para o desenvolvimento dos projetos não trabalham mais neles. Isolar o problema nos ajuda a eliminar lentamente o que não está causando o erro. Isso permite, no final, encontrar a origem do problema e focar nela. "
O isolamento de falhas é freqüentemente referido como um " caso de teste abreviado ".
Mudei o botão para uma página em branco separada (criei um teste separado para ele). Ou seja, este projeto CodePen foi criado para investigar um botão isoladamente. Embora tenhamos concluído que CSS não é a causa do problema, precisamos deixar os estilos desativados até descobrirmos a verdadeira causa do problema. Isso nos permitirá simplificar o problema tanto quanto possível.

Uma página com apenas um botão
Se você abrir este projeto no Safari, descobrimos que não podemos mais causar um erro. Mesmo depois de clicar no botão, a imagem não muda. Mas isso não pode ser considerado uma solução aceitável para o problema. No entanto, o código para este projeto CodePen nos dá uma excelente base para a criação de um exemplo reproduzível mínimo.
Exemplo reproduzível mínimo
A principal diferença entre os dois projetos CodePen anteriores é a combinação de botões. O exame de todas as combinações de botões possíveis permite concluir que o problema só ocorre quando ocorre um evento
paintpara uma imagem SVG maior, ao lado da qual uma imagem SVG menor está localizada na página.
Saber disso nos permitiu criar um exemplo reproduzível mínimo que nos permitiu reproduzir o erro e ainda nos livrar de quaisquer elementos desnecessários. Graças a um exemplo reproduzível mínimo, podemos estudar o problema mais profundamente e destacar exatamente a parte do código que o causa.
Aqui está o projeto CodePen em questão.

Exemplo reproduzível mínimo
Se você abrir este projeto no Safari e clicar no botão, novamente encontramos um problema. Isso nos permitirá formular uma hipótese de que os dois elementos SVG de alguma forma entram em conflito um com o outro. Se você sobrepor a segunda imagem SVG sobre a primeira, verifica-se que o tamanho do que resta do fundo da primeira imagem corresponde exatamente ao tamanho da imagem menor.

Um desenho em que a segunda imagem é colocada em cima da primeira, distorcida como resultado de um erro
Dividir para reinar
Conseguimos reproduzir o bug usando o número mínimo de elementos representados por um par de imagens SVG. Agora vamos restringir ainda mais a área do problema, restringindo-a à parte específica do código SVG que é a origem do problema. Se entendermos o código SVG em termos gerais e ainda quisermos encontrar a origem do problema, podemos usar uma estratégia de pesquisa em árvore binária usando uma abordagem de dividir para conquistar. Aqui está outro trecho da palestrada Cornell University Computer Science: “Por exemplo, você pode começar examinando uma grande parte do código e colocar o código de validação no meio da parte que está examinando. Se não ocorrer um erro aqui, significa que sua fonte está na segunda metade do código. Caso contrário, sua fonte está na primeira metade do código. "
Ao examinar o código SVG, você pode tentar remover um elemento
<filter>da descrição da primeira imagem (e também <defs>, já que não há nada neste bloco de qualquer maneira). Vamos nos interessar pelos tipos de tarefas que o elemento resolve <filter>. Uma ótima explicação para isso pode ser encontrada aqui . Ou seja, estamos falando sobre o seguinte: “Para aplicar filtros a imagens SVG, existe um elemento especial chamado<filter>... Essencialmente, lembra elementos projetados para trabalhar com gradientes lineares, máscaras, modelos e outros efeitos gráficos. O elemento <filter>nunca é exibido sozinho. Ele é usado apenas como algo que pode ser referenciado por meio de um atributo filterno código SVG ou uma função url()em CSS. "
Em nossa imagem SVG, um filtro é usado para adicionar uma pequena sombra interna na parte inferior da imagem. Depois de retirar o filtro do código da primeira imagem, esperamos que esta sombra desapareça. Se depois disso o problema persistir, então podemos concluir que algo está errado com o outro código para descrever o elemento SVG.Eu
criei outro projeto CodePen para demonstrar os resultados deste teste.

Consequências da remoção do elemento <filter>
O problema, como você pode ver facilmente, não foi a lugar nenhum. E a sombra interna continua a ser exibida mesmo após a remoção do código do filtro. Mas agora, entre outras coisas, o problema aparece em todos os navegadores. Isso nos permite concluir que o erro está em algum lugar no restante do código de descrição do botão. Se você remover o resto
idde<g filter="url(#filter0_ii)">, em seguida, a sombra desaparece. O que está acontecendo aqui?
Vamos dar uma outra olhada na definição acima de um elemento
<filter>e observar as seguintes palavras: “Um elemento<filter>nuncaéexibido sozinho. É usado apenas como algo que pode ser referenciado por meio de um atributofilterem SVG. " (Destaquei um fragmento do texto.)
Assim, sabendo disso, podemos concluir que a declaração do filtro da segunda imagem SVG se aplica à primeira imagem SVG, o que leva ao erro.
Bug fix
Agora sabemos que nosso problema está relacionado ao elemento
<filter>. Também sabemos que ambas as imagens SVG possuem este elemento, já que o filtro é usado para criar uma sombra interna circular. Vamos comparar o código de duas imagens SVG e pensar se podemos explicar o erro e corrigi-lo.
Simplifiquei o código de ambas as imagens para que você possa ver claramente o que está acontecendo nelas.
Aqui está o código para a primeira imagem SVG:
<svg width="46" height="46" viewBox="0 0 46 46">
<g filter="url(#filter0_ii)">
<!-- ... -->
</g>
<!-- ... -->
<defs>
<filter id="filter0_ii" x="0" y="0" width="46" height="46">
<!-- ... -->
</filter>
</defs>
</svg>
Aqui está o código para a segunda imagem:
<svg width="28" height="28" viewBox="0 0 28 28">
<g filter="url(#filter0_ii)">
<!-- ... -->
</g>
<!-- ... -->
<defs>
<filter id="filter0_ii" x="0" y="0" width="28" height="28">
<!-- ... -->
</filter>
</defs>
</svg>
Analisando esses dois fragmentos, você pode perceber que
id=filter0_iio mesmo identificador é utilizado na construção . O Safari aplica a última definição de filtro analisada pelo navegador aos elementos (neste caso, o filtro da segunda imagem). Isso leva ao fato de que a primeira imagem é cortada. Seu tamanho original é 48px, e após a aplicação do filtro, é recortado um pedaço do tamanho dele 26px. A propriedade idDOM deve ter um valor exclusivo. Se houver vários idênticos na página id, o navegador não consegue descobrir qual deles precisa usar. E uma vez que a propriedade é filtersubstituída quando cada evento ocorrepaint, então, dependendo de qual das definições estará pronta primeiro (algo como uma condição de corrida ocorre aqui), o erro aparece ou não.
Vamos tentar atribuir valores únicos
idno código de cada uma das imagens e ver os resultados. Aqui está o projeto CodePen correspondente.

A atribuição de ids únicos resolveu o problema
Se você agora abrir o projeto no Safari e clicar no botão, pode ter certeza de que resolvemos o problema atribuindo
idfiltrosexclusivosusados em imagens SVG. Se você pensar no fato de que o projeto tinha valores não únicos para um atributo comoid, isso levará à conclusão de que o problema deveria ter aparecido em todos os navegadores, não apenas no Safari. Mas, por alguma razão, outros navegadores (incluindo Chrome e Firefox) parecem ter lidado com essa situação incomum sem erros. Pode ser uma coincidência, no entanto.
Resultado
Foi outra aventura! Começamos sabendo apenas que havia algum tipo de erro no projeto, que às vezes aparece e depois não aparece, mas no final entendemos perfeitamente os motivos do que estava acontecendo e resolvemos o problema. Depurar o código da interface do usuário e descobrir por que os gráficos estão distorcidos pode ser complicado se você não entender o que está acontecendo. Felizmente para nós, existem estratégias de depuração que podem nos ajudar a encontrar a causa raiz até dos erros mais confusos.
Em primeiro lugar, simplificamos o problema formulando hipóteses que nos permitiram remover do projeto componentes não relacionados ao erro (estilos, marcações, eventos dinâmicos, etc.). Depois disso, isolamos a marcação e encontramos um exemplo reproduzível mínimo. Isso nos permitiu focar em um pequeno trecho de código. E finalmente identificamos o problema usando uma estratégia de dividir e conquistar para nos livrarmos do erro.
Obrigado a todos que reservaram seu tempo para ler este artigo. Mas, antes de encerrarmos a conversa, deixe-me falar sobre outra estratégia de depuração mencionada nas palestras.Cornell University. A questão é que no processo de trabalho você precisa fazer pausas, descansar e liberar a cabeça de todos os pensamentos: “Se demorar muito para depurar, o programador se cansa. Pode acontecer que ele, estando em tal estado, trabalhe em vão. Em uma situação como essa, vale a pena fazer uma pausa e jogar tudo fora da sua cabeça. E depois de um tempo, você deve tentar olhar para o problema de um ponto de vista diferente. "
Como você corrige erros incompreensíveis?
