Tutorial de Texto SVG: Anotación de Texto en la Web
Publicado: 2022-03-11Con HTML5 y CSS3, los navegadores web adquirieron una serie de tecnologías sorprendentes: gráficos 3D, sockets, subprocesos y más. Con estos, las aplicaciones web pueden aprovechar algunas de las capacidades más sofisticadas de las computadoras y los sistemas operativos en los que se utilizan. El navegador web ofrece un ecosistema robusto y versátil para el desarrollo de aplicaciones, lo cual es evidente por el reciente surgimiento de numerosas aplicaciones web poderosas sin las cuales no podemos vivir. Sin embargo, algo que todavía falta es la belleza de la decoración y la anotación de texto HTML. ¿Qué es la decoración de texto? Los subrayados ondulados, los resaltados rugosos y los tachados ondulados son algunas de las cosas que los navegadores web no brindan soporte nativo. Esto puede sonar más elaborado que útil, pero la capacidad de los desarrolladores de JavaScript para producir estos estilos puede resultar útil en aspectos tales como recursos de aprendizaje electrónico y lectores de libros electrónicos basados en la web. Además, esto puede contribuir a mejorar la experiencia del usuario en aplicaciones web que giran en torno a principios de diseño natural. Como mínimo, crear una herramienta de este tipo es divertido y proporciona información sobre las muchas peculiaridades de un navegador web.
Los desarrolladores han encontrado muchas soluciones a la limitación del navegador web. Muchas de estas soluciones implican el uso de CSS de formas menos intuitivas, ya que algunas usan imágenes en los pseudoelementos "::after". Esto funciona, pero mantener muchas imágenes para cada par de estilo y color a menudo resulta difícil. Este artículo echa un vistazo a la anatomía de una biblioteca de JavaScript que intenta resolver este problema de manera elegante.
La biblioteca es de código abierto y está disponible en GitHub: Text Annotator
Visión de conjunto
Durante el desarrollo de esta biblioteca, se prestó especial atención a garantizar la compatibilidad con los navegadores web más populares (incluido IE 9+). Sin embargo, a diferencia de cómo la mayoría resuelve este problema, la biblioteca no se basa en trucos de CSS específicamente oscuros; o peor, símbolos Unicode especiales. En su lugar, utiliza SVG para lograr decoraciones de texto mucho mejores y más limpias.
Fundamentalmente, la biblioteca implementa una "clase" Annotator que se puede usar para crear automáticamente elementos DIV, colocarlos debajo de los textos que se van a anotar y llenar sus fondos con imágenes SVG. Se pueden combinar múltiples DIV para personalizar aún más las decoraciones. El enfoque es compatible con todos los navegadores, brinda flexibilidad sobre el posicionamiento de los elementos decorativos y permite una extensión más sencilla con plantillas personalizadas.
La biblioteca se ha desarrollado con Google Closure Tools porque es modular y compatible con varios navegadores, lo que ayuda a producir código JavaScript compacto y rápido sin ninguna dependencia adicional.
Arquitectura
La biblioteca ha sido diseñada como una colección de "clases" de JavaScript y expone todas las funcionalidades necesarias para el usuario a través del Anotador de "clase":
Aquí hay un breve resumen de las funcionalidades disponibles:
annotateDocument: anota elementos, que están marcados con un atributo de "anotación de datos".
subrayar - elemento subrayado
resaltar - elemento destacado
huelga - elemento de huelgas
underlineSelected - subraya el texto seleccionado
HighlightSelected - resalta el texto seleccionado
strikeSelected - tacha el texto seleccionado
unannotateElement - elimina la anotación del elemento
getTemplates: devuelve el diccionario de plantillas de anotaciones
setUnderlineOptions: establece la configuración para el anotador de subrayado
setHighlightOptions: establece la configuración para el anotador de resaltado
setStrikeOptions: establece la configuración para el anotador de avisos
La clase anotador contiene tres instancias de la clase AnnotatorImpl para cada función de anotación: subrayar, resaltar y tachar.
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); };
Las instancias de AnnotatorImpl se crean con diferentes ID y objetos auxiliares de posicionamiento. Los ID pasados se usan más tarde en los nombres de clase CSS y los nombres de campos internos, lo que requiere que los ID sean únicos. Además, se pasa una referencia a una lista de plantillas conocidas (se puede cambiar más adelante).
Cada objeto posicionador es una implementación de la interfaz IPositioner que solo tiene el método "getPosition" y tiene el siguiente aspecto:
/** * 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) }; } });
Esto permite que cada plantilla se use con anotaciones de texto subrayado, resaltado o tachado. Cuando se aplica una anotación a un elemento, el cuadro delimitador para el elemento se obtiene llamando a "getElementRects" como se muestra a continuación:
var rects = elemOrEv.getClientRects();
Este método devuelve una colección de rectángulos que indican los rectángulos delimitadores de cada cuadro en un cliente. Después de pasar cada rect al posicionador concreto, obtendremos los límites de destino.
Plantillas de anotación de texto SVG
Como se mencionó anteriormente, solo hay un conjunto de plantillas que se utilizan para todo tipo de anotaciones de texto SVG. Cada plantilla consta de partes de plantilla. Una parte de la plantilla es una entidad que representa el contenido de la parte, el ancho de la plantilla y el modo de dibujo.
Contenido
El contenido es un conjunto de elementos SVG representados como una cadena. Dado que este contenido no tiene un nodo SVG raíz donde se establecen el ancho y el alto de la ventana gráfica (en píxeles), el constructor de partes de la plantilla los acepta como parámetros. Por ejemplo, puede especificar el tamaño de una ventana gráfica como 100 px x 100 px y dibujar una línea a (50, 50) y (25, 25). Después de aplicar la anotación, todos los elementos svg tendrán el tamaño deseado correctamente. El valor del contenido puede usar la cadena "{0}" que se reemplazará con el color seleccionado por el usuario.
El siguiente SVG representa una línea diagonal. Lo usaremos como una de las partes en un estilo de anotación de ejemplo a continuación:
<line x1="0" y1="0" x2="5" y2="5" stroke-width="2" stroke="red" />
Ancho
El ancho de la plantilla es una cadena que puede ser "*", "altura" o cualquier otra cosa:
“*” establece el ancho de todos los elementos con una estrella iguales entre sí
"altura" establece el ancho igual a la altura del elemento de anotación
Cualquier otra cosa establecida aquí se establecerá directamente en las propiedades de ancho CSS y ancho mínimo.
Modo de dibujo
El modo de dibujo es una cadena que puede ser "repetir" o "estirar". Como indican los valores, establecerlo en "repetir" repetirá el contenido, mientras que establecerlo en "estirar" extenderá el contenido.
Aquí hay un ejemplo de lo que podemos lograr configurando estos tres parámetros:
La anotación de texto en el ejemplo anterior contiene 4 partes. La primera parte es la línea diagonal, con el ancho de la plantilla establecido en "altura" y el modo de dibujo establecido en "repetir". La segunda parte tiene el ancho de la plantilla establecido en "*" y el modo de dibujo establecido en "repetir". La tercera parte está configurada para tener "15px" de ancho y dibujarse en modo "repetir". Finalmente, el ancho de la última parte se establece en "*" y su modo de dibujo se establece en "estirar".
Cuando se evalúan estos anchos, la primera parte toma 5 píxeles (igual a la altura del elemento de anotación), la tercera parte toma 15 píxeles (como se establece) y el espacio restante se divide equitativamente entre la segunda y la cuarta parte.
Cuando se resalta el mismo fragmento de texto usando la misma plantilla, esto es lo que obtenemos:
Como puede ver, la altura del elemento de anotación es mayor, al igual que el ancho de la primera parte (ya que el ancho de la plantilla para esa parte se establece en "alto"). Naturalmente, el ancho de la tercera parte se mantuvo sin cambios con respecto al ejemplo anterior.
Aplicar un efecto de tachado al mismo texto con la misma plantilla produce un resultado muy similar al primero. La única diferencia es la ubicación donde se colocan los elementos de anotación:
Aunque estas anotaciones de texto parecen complejas (la forma en que se ven, tienen cuatro partes distintas), todas usan elementos SVG muy simples. Como ejemplo adicional, hacer una línea ondulada requiere una sola parte, con el siguiente contenido SVG simple:
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' ))
Cuando se evalúan estas plantillas, el contenido cambia de tamaño y "{0}" se sustituye automáticamente por el color especificado. Aún más, agregar nuevas plantillas es tan simple como agregarlas a un objeto de JavaScript:
tvs.AnnotatorDictionary.svgTemplates['brush'] = new tvs.Template(new tvs.SvgTemplatePart( svgContent, 50, 50, '*', 'stretch' ));
Resultados
Cada anotación se aplica agregando un elemento div con posicionamiento absoluto a la página:
<div class="tvs-annotate-element"> <div class="tvs-wrap-div"> <table> <tr> <td></td> <td></td> <td></td> </tr> </table> </div> </div>
El elemento div se completa con una tabla donde cada celda agregada corresponde a una de las partes de la plantilla. El contenido de cada parte de la plantilla se agrega como URI de datos codificados en Base64, con el color seleccionado aplicado:
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; };
incrustación
Para una mejor experiencia del usuario, especialmente cuando se trata de utilizar esta biblioteca de JavaScript con áreas de contenido editable, es importante que Text Annotator conozca los límites del texto actualmente seleccionado por el usuario. Rangy, una ordenada biblioteca de JavaScript que se ocupa del rango y la selección, se ha utilizado para lograr esto de forma cruzada. Rangy proporciona una API simple basada en estándares para realizar tareas comunes de rango y selección de DOM en todos los navegadores principales, abstrayendo las implementaciones muy diferentes de esta funcionalidad entre Internet Explorer y los navegadores compatibles con DOM. Es la única dependencia del proyecto.
Una vez que Text Annotator está incrustado, usarlo es una hazaña muy simple:
var annotator = new tvs.Annotator(); annotator.underlineSelected();
Cada elemento anotado está marcado con la clase "tvs-annotated-text" y cada elemento de anotación tiene la clase "tvs-annotate-element". Eliminar anotaciones es aún más simple, una sola línea:
annotator.unannotateElement(annotatedElement);
peculiaridades
Cuando se cambia el tamaño de la ventana, los elementos pueden moverse, lo que requiere que los elementos anotados se "actualicen". Esto es manejado por la biblioteca. Sin embargo; para reducir el impacto en el rendimiento, se acelera la llamada a las anotaciones de actualización:
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); };
Al actualizar, los elementos de anotación se pueden agregar, cambiar de tamaño o eliminar de la página según sea necesario.
Conveniencia
Para facilitar la anotación de texto estático en una página, todo lo que necesita es un atributo de datos simple en el elemento contenedor:
data-annotate='underline squiggly green'
Esto anotará el contenido del elemento con un subrayado verde ondulado.
Conclusión
¿Qué más puedo decir sobre este tutorial de texto SVG? Se ha implementado fácilmente una herramienta divertida pero poderosa. No creo que nos beneficiemos mucho al garantizar la compatibilidad con Internet Explorer 8, ya que, en cambio, podemos terminar complicando toda la implementación. Sin embargo, con algunas mejoras y un poco de trabajo en el núcleo, podemos ampliar la biblioteca para poder producir bordes decorativos para elementos no textuales. Además, puede ser una tarea interesante implementar algún mecanismo para guardar y luego restaurar el estado del contenido editable de una anotación.
Por ahora, las posibilidades solo están limitadas por su imaginación (y las capacidades del navegador). Tal vez desee líneas de microimpresión, degradados o incluso animaciones. Con Text Annotator, puede hacerlo.