Tutorial de texto SVG: anotação de texto na web
Publicados: 2022-03-11Com HTML5 e CSS3, os navegadores da Web adquiriram várias tecnologias incríveis: gráficos 3D, soquetes, threads e muito mais. Com eles, os aplicativos da Web podem acessar alguns dos recursos mais sofisticados dos computadores e sistemas operacionais nos quais são usados. O navegador da Web oferece um ecossistema robusto e versátil para o desenvolvimento de aplicativos, o que é evidente pelo recente aumento de vários aplicativos da Web poderosos sem os quais não podemos viver. No entanto, algo que ainda está faltando é a beleza da anotação e decoração de texto HTML. O que é decoração de texto? Sublinhados ondulados, destaques ásperos e riscados ondulados são algumas das coisas que os navegadores da Web não oferecem suporte nativo. Isso pode parecer mais elaborado do que útil, mas a capacidade dos desenvolvedores JavaScript de produzir esses estilos pode ser útil em aspectos como recursos de e-learning e leitores de e-books baseados na web. Além disso, isso pode contribuir para o aprimoramento da experiência do usuário em aplicativos da Web que giram em torno de princípios naturais de design. No mínimo, construir essa ferramenta é divertido e fornece informações sobre as muitas peculiaridades de um navegador da web.
Os desenvolvedores encontraram muitas soluções alternativas para a limitação do navegador da web. Muitas dessas soluções envolvem o uso de CSS de maneiras menos intuitivas, pois algumas usam imagens nos pseudo elementos “::after”. Isso funciona, mas manter muitas imagens para cada par de estilo-cor geralmente se mostra difícil. Este artigo analisa a anatomia de uma biblioteca JavaScript que tenta resolver esse problema de forma elegante.
A biblioteca é de código aberto e está disponível no GitHub: Text Annotator
Visão geral
Durante o desenvolvimento desta biblioteca, foi dada atenção especial para garantir a compatibilidade com os navegadores mais populares (incluindo o IE 9+). No entanto, ao contrário de como a maioria resolve esse problema, a biblioteca não depende de truques CSS especificamente obscuros; ou pior, símbolos Unicode especiais. Em vez disso, ele usa SVG para obter decorações de texto muito melhores e mais limpas.
Fundamentalmente, a biblioteca implementa uma “classe” Anotador que pode ser usada para criar automaticamente elementos DIV, posicioná-los sob os textos a serem anotados e preencher seus fundos com imagens SVG. Múltiplos DIVs podem ser combinados para personalizar ainda mais as decorações. A abordagem é compatível com vários navegadores, oferece flexibilidade sobre o posicionamento de elementos decorativos e permite uma extensão mais fácil com modelos personalizados.
A biblioteca foi desenvolvida usando o Google Closure Tools porque é modular e cross-browser, o que ajuda a produzir código JavaScript compacto e rápido sem qualquer dependência adicional.
Arquitetura
A biblioteca foi projetada como uma coleção de “classes” JavaScript e expõe todas as funcionalidades necessárias ao usuário através do Anotador “class”:
Aqui está um breve resumo das funcionalidades disponíveis:
annotateDocument - anota elementos, que são marcados com um atributo “data-annotate”.
sublinhado - elemento sublinhado
destaque - elemento de destaque
greve - elemento de greves
underlineSelected - sublinha o texto selecionado
highlightSelected - destaca o texto selecionado
strikeSelected - elimina o texto selecionado
unannotateElement - remove a anotação do elemento
getTemplates - retorna dicionário de modelos de anotação
setUnderlineOptions - define as configurações para o anotador de sublinhado
setHighlightOptions - define as configurações para o anotador de destaque
setStrikeOptions - define as configurações para o anotador de greve
A classe anotador contém três instâncias da classe AnnotatorImpl para cada função de anotação: sublinhado, destaque e tachado.
tvs.Annotator = function() { this.underliner_ = new tvs.AnnotatorImpl( 'underliner', tvs.Annotator.getTemplates(), tvs.AnnotatorCore.underlinePositioner); this.highlighter_ = new tvs.AnnotatorImpl( 'highlighter', tvs.Annotator.getTemplates(), tvs.AnnotatorCore.highlightPositioner, {opacity: 0.45}); this.striker_ = new tvs.AnnotatorImpl( 'striker', tvs.Annotator.getTemplates(), tvs.AnnotatorCore.strikePositioner); };
As instâncias AnnotatorImpl são criadas com diferentes IDs e objetos auxiliares do posicionador. Os IDs passados são usados posteriormente em nomes de classes CSS e nomes de campos internos, exigindo que os IDs sejam exclusivos. Além disso, uma referência a uma lista de modelos conhecidos é passada (pode ser alterada posteriormente).
Cada objeto posicionador é uma implementação da interface IPositioner que possui apenas o método “getPosition” e se parece com o seguinte:
/** * Underline positioner * @implements {tvs.IPositioner} */ tvs.AnnotatorCore.underlinePositioner = /** @type {!tvs.IPositioner} */ ({ /** * @param {Object} elementRect * @param {number} annotationHeight * @return {{left: number, top: number, width: number, height: number}} */ getPosition: function(elementRect, annotationHeight) { return { width: elementRect.width, height: annotationHeight, left: elementRect.left, top: elementRect.bottom - (elementRect.height * 0.1) }; } });
Isso permite que todos os modelos sejam usados com anotações de texto sublinhado, destacado ou riscado. Quando uma anotação é aplicada a um elemento, a caixa delimitadora do elemento é obtida chamando “getElementRects” conforme mostrado abaixo:
var rects = elemOrEv.getClientRects();
Esse método retorna uma coleção de retângulos que indicam os retângulos delimitadores de cada caixa em um cliente. Depois de passar cada rect para o posicionador de concreto, obteremos os limites de destino.
Modelos de anotação de texto SVG
Como mencionado anteriormente, há apenas um conjunto de modelos que são usados para todos os tipos de anotações de texto SVG. Cada modelo consiste em partes do modelo. Uma peça de modelo é uma entidade que representa o conteúdo da peça, a largura do modelo e o modo de desenho.
Contente
O conteúdo é um conjunto de elementos SVG representados como uma string. Como esse conteúdo não possui um nó SVG raiz onde a largura e a altura da janela de visualização (em pixels) são definidas, o construtor da parte do modelo os aceita como parâmetros. Por exemplo, você pode especificar o tamanho de uma viewport como 100px x 100px e desenhar uma linha para (50, 50) e (25, 25). Depois que a anotação for aplicada, todos os elementos svg serão dimensionados corretamente para o tamanho desejado. O valor do conteúdo pode usar a string “{0}” que será substituída pela cor selecionada pelo usuário.
O SVG a seguir renderiza uma linha diagonal. Estaremos usando isso como uma das partes em um estilo de anotação de exemplo a seguir em breve:
<line x1="0" y1="0" x2="5" y2="5" stroke-width="2" stroke="red" />
Largura
A largura do modelo é uma string que pode ser “*”, “altura” ou qualquer outra coisa:
“*” define a largura de todos os elementos com uma estrela igual entre si
“height” define a largura igual à altura do elemento de anotação
Qualquer outra coisa definida aqui será definida diretamente para as propriedades CSS width e min-width.
Modo Desenhar
O modo Draw é uma string que pode ser “repeat” ou “stretch”. Como os valores indicam, configurá-lo para “repeat” repetirá o conteúdo, enquanto configurá-lo para “stretch” alongará o conteúdo.
Aqui está um exemplo do que podemos alcançar configurando esses três parâmetros:
A anotação de texto no exemplo acima contém 4 partes. A primeira parte é a linha diagonal, com a largura do modelo definida como "altura" e o modo de desenho definido como "repetir". A segunda parte tem a largura do modelo definida como “*” e o modo de desenho definido como “repetir”. A terceira parte está configurada para ter “15px” de largura e ser desenhada no modo “repetir”. Finalmente, a largura da última parte é definida como “*” e seu modo de desenho é definido como “stretch”.
Quando essas larguras são avaliadas, a primeira parte leva 5 pixels (igual à altura do elemento de anotação), a terceira parte leva 15 pixels (conforme definido) e o espaço restante é dividido igualmente entre a segunda e a quarta partes.
Quando o mesmo pedaço de texto é destacado usando o mesmo modelo, é isso que obtemos:
Como você pode ver, a altura do elemento de anotação é maior, assim como a largura da primeira parte (já que a largura do modelo para essa parte é definida como "altura"). Naturalmente, a largura da terceira parte permaneceu inalterada em relação ao exemplo anterior.
A aplicação de um efeito riscado ao mesmo texto com o mesmo modelo produz um resultado muito semelhante ao primeiro. A única diferença é o local onde os elementos de anotação são posicionados:
Embora essas anotações de texto pareçam complexas (pela aparência, com quatro partes distintas), todas elas usam elementos SVG muito simples. Como exemplo adicional, fazer uma linha ondulada requer uma única parte, com o seguinte conteúdo SVG simples:
var t = new tvs.Template(new tvs.SvgTemplatePart( '<line y2="16.00" x2="20" y1="4.00" ' + 'x1="10" stroke-linecap="round" ' + 'stroke-width="5" stroke="{0}" fill="none"/>' + '<line y2="4.00" x2="10" y1="16.00" ' + 'x1="0" stroke-linecap="round" ' + 'stroke-width="5" stroke="{0}" fill="none"/>', 20, 20, 'repeat' ))
Quando esses modelos são avaliados, o conteúdo é redimensionado e “{0}” é substituído pela cor especificada automaticamente. Ainda mais, adicionar novos modelos é tão simples quanto adicioná-los a um objeto JavaScript:
tvs.AnnotatorDictionary.svgTemplates['brush'] = new tvs.Template(new tvs.SvgTemplatePart( svgContent, 50, 50, '*', 'stretch' ));
Resultados
Cada anotação é aplicada anexando um elemento div com posicionamento absoluto à página:
<div class="tvs-annotate-element"> <div class="tvs-wrap-div"> <table> <tr> <td></td> <td></td> <td></td> </tr> </table> </div> </div>
O elemento div é preenchido com uma tabela onde cada célula adicionada corresponde a uma das partes do template. O conteúdo de cada parte do modelo é adicionado como URI de dados codificados Base64, com a cor selecionada aplicada:
tvs.SvgTemplatePart.prototype.getBackground = function(color) { var image = tvs.AnnotatorCore.formatString(this.content, [color]); var encodedSVG = goog.crypt.base64.encodeString(image); return 'data:image/svg+xml;base64,' + encodedSVG; };
Incorporação
Para uma melhor experiência do usuário, especialmente ao tentar usar esta biblioteca JavaScript com áreas de conteúdo editáveis, é importante que o Anotador de Texto conheça os limites do texto atualmente selecionado pelo usuário. Rangy, uma biblioteca JavaScript elegante que lida com alcance e seleção, foi usada para conseguir isso de uma maneira entre navegadores. Rangy fornece uma API baseada em padrões simples para executar tarefas comuns de Intervalo e Seleção de DOM em todos os principais navegadores, abstraindo as implementações totalmente diferentes dessa funcionalidade entre o Internet Explorer e os navegadores compatíveis com DOM. É a única dependência do projeto.
Uma vez que o Text Annotator é incorporado, usá-lo é uma tarefa muito simples:
var annotator = new tvs.Annotator(); annotator.underlineSelected();
Cada elemento anotado é marcado com a classe “tvs-annotated-text” e cada elemento de anotação tem a classe “tvs-annotate-element”. A remoção de anotações é ainda mais simples, uma linha:
annotator.unannotateElement(annotatedElement);
Peculiaridades
Quando a janela é redimensionada, os elementos podem se mover, exigindo que os elementos anotados sejam “atualizados”. Isso é feito pela biblioteca. No entanto; para reduzir o impacto no desempenho, a chamada para a atualização de anotações é limitada:
tvs.AnnotatorImpl = function(id, templates, positioner, options) { // ... this.throttle = new goog.Throttle(goog.bind(this.refreshAllAnnotations, this), 50); tvs.AnnotatorCore.registerForWindowResize( this.id,goog.bind(this.throttle.fire, this.throttle)); }; tvs.AnnotatorImpl.prototype.refreshAllAnnotations = function() { var elems = goog.dom.getElementsByClass(this.getCssClassForAnnotated()); var refFunc = goog.bind(this.refreshAnnotation, this); goog.array.forEach(elems, refFunc); };
Após a atualização, os elementos de anotação podem ser adicionados, redimensionados ou removidos da página conforme necessário.
Conveniência
Para facilitar a anotação de texto estático em uma página, basta um atributo de dados simples no elemento contêiner:
data-annotate='underline squiggly green'
Isso anotará o conteúdo do elemento com um sublinhado verde ondulado.
Conclusão
O que mais posso dizer sobre este tutorial de texto SVG? Uma ferramenta divertida e poderosa foi facilmente implementada. Não acho que nos beneficiaríamos muito garantindo o suporte para o Internet Explorer 8, pois podemos acabar complicando toda a implementação. No entanto, com algumas melhorias e um pouco de trabalho no núcleo, podemos estender a biblioteca para poder produzir bordas decorativas para elementos não textuais. Além disso, pode ser uma tarefa interessante implementar algum mecanismo para salvar e depois restaurar o estado do conteúdo editável de uma anotação.
Por enquanto, as possibilidades são limitadas apenas pela sua imaginação (e recursos do navegador). Talvez você queira linhas de microimpressão, ou gradientes, ou até mesmo animações. Com o Text Annotator, você pode.