Tutoriel de texte SVG : Annotation de texte sur le Web
Publié: 2022-03-11Avec HTML5 et CSS3, les navigateurs Web ont acquis un certain nombre de technologies étonnantes : graphiques 3D, sockets, threads, etc. Grâce à ceux-ci, les applications Web peuvent exploiter certaines des fonctionnalités les plus sophistiquées des ordinateurs et des systèmes d'exploitation sur lesquels elles sont utilisées. Le navigateur Web offre un écosystème robuste et polyvalent pour le développement d'applications, comme en témoigne la montée récente de nombreuses applications Web puissantes sans lesquelles nous ne pouvons pas vivre. Cependant, quelque chose qui manque encore est la beauté de l'annotation et de la décoration du texte HTML. Qu'est-ce que la décoration de texte ? Les soulignements ondulés, les reflets accidentés et les barrés ondulés sont quelques-unes des choses que les navigateurs Web ne fournissent pas de support natif. Cela peut sembler plus élaboré qu'utile, mais la capacité des développeurs JavaScript à produire ces styles peut s'avérer utile dans des aspects tels que les ressources d'apprentissage en ligne et les lecteurs de livres électroniques basés sur le Web. De plus, cela peut contribuer à améliorer l'expérience utilisateur dans les applications Web qui s'articulent autour de principes de conception naturels. À tout le moins, la construction d'un tel outil est amusante et donne un aperçu des nombreuses bizarreries d'un navigateur Web.
Les développeurs ont trouvé de nombreuses solutions de contournement à la limitation du navigateur Web. Beaucoup de ces solutions de contournement impliquent l'utilisation de CSS de manière moins intuitive, car certaines utilisent des images dans les pseudo-éléments " :: after ". Cela fonctionne, mais maintenir de nombreuses images pour chaque paire style-couleur s'avère souvent difficile. Cet article examine l'anatomie d'une bibliothèque JavaScript qui tente de résoudre ce problème avec élégance.
La bibliothèque est open source et est disponible sur GitHub : Text Annotator
Aperçu
Lors du développement de cette bibliothèque, une attention particulière a été accordée à la compatibilité avec les navigateurs Web les plus populaires (y compris IE 9+). Cependant, contrairement à la façon dont la plupart résolvent ce problème, la bibliothèque ne s'appuie pas sur des astuces CSS spécifiquement obscures ; ou pire, des symboles Unicode spéciaux. Au lieu de cela, il utilise SVG pour obtenir des décorations de texte bien meilleures et plus propres.
Fondamentalement, la bibliothèque implémente une "classe" Annotator qui peut être utilisée pour créer automatiquement des éléments DIV, les positionner sous les textes à annoter et remplir leurs arrière-plans avec des images SVG. Plusieurs DIV peuvent être combinés pour personnaliser davantage les décorations. L'approche est compatible avec tous les navigateurs, offre une flexibilité sur le positionnement des éléments décoratifs et permet une extension plus facile avec des modèles personnalisés.
La bibliothèque a été développée à l'aide de Google Closure Tools car elle est modulaire et multi-navigateur, ce qui permet de produire du code JavaScript compact et rapide sans aucune dépendance supplémentaire.
Architecture
La bibliothèque a été conçue comme une collection de "classes" JavaScript, et expose toutes les fonctionnalités nécessaires à l'utilisateur via la "classe" Annotator :
Voici un bref aperçu des fonctionnalités disponibles :
annotateDocument - annote les éléments, qui sont marqués avec un attribut "data-annotate".
soulignement - souligne l'élément
surbrillance - élément de surbrillance
grève - élément de grève
underlineSelected - souligne le texte sélectionné
highlightSelected - met en surbrillance le texte sélectionné
strikeSelected - barre le texte sélectionné
unannotateElement - supprime l'annotation de l'élément
getTemplates - renvoie le dictionnaire des modèles d'annotation
setUnderlineOptions - définit les paramètres de l'annotateur de soulignement
setHighlightOptions - définit les paramètres de l'annotateur de surbrillance
setStrikeOptions - définit les paramètres de l'annotateur de grève
La classe annotator contient trois instances de la classe AnnotatorImpl pour chaque fonction d'annotation : souligner, surligner et barrer.
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); };
Les instances d'AnnotatorImpl sont créées avec différents ID et objets d'assistance de positionneur. Les ID passés sont utilisés plus tard dans les noms de classe CSS et les noms de champs internes, nécessitant que les ID soient uniques. En outre, une référence à une liste de modèles connus est transmise (peut être modifiée ultérieurement).
Chaque objet positionneur est une implémentation de l'interface IPositioner qui n'a que la méthode "getPosition" et se présente comme suit :
/** * 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) }; } });
Cela permet à chaque modèle d'être utilisé avec des annotations de texte soulignées, surlignées ou barrées. Lorsqu'une annotation est appliquée à un élément, la boîte englobante de l'élément est obtenue en appelant "getElementRects" comme indiqué ci-dessous :
var rects = elemOrEv.getClientRects();
Cette méthode renvoie une collection de rectangles qui indiquent les rectangles de délimitation pour chaque boîte dans un client. Après avoir passé chaque rectangle au positionneur concret, nous obtiendrons les limites de destination.
Modèles d'annotation de texte SVG
Comme mentionné précédemment, il n'y a qu'un seul ensemble de modèles qui sont utilisés pour toutes sortes d'annotations de texte SVG. Chaque modèle se compose de parties de modèle. Un composant de modèle est une entité qui représente le contenu, la largeur du modèle et le mode de dessin du composant.
Contenu
Le contenu est un ensemble d'éléments SVG représentés sous forme de chaîne. Étant donné que ce contenu n'a pas de nœud SVG racine où la largeur et la hauteur de la fenêtre (en pixels) sont définies, le constructeur de partie du modèle les accepte comme paramètres. Par exemple, vous pouvez spécifier la taille d'une fenêtre comme 100px x 100px et tracer une ligne à (50, 50) et (25, 25). Une fois l'annotation appliquée, tous les éléments svg seront correctement dimensionnés à la taille souhaitée. La valeur du contenu peut utiliser la chaîne "{0}" qui sera remplacée par la couleur sélectionnée par l'utilisateur.
Le SVG suivant rend une ligne diagonale. Nous l'utiliserons comme l'une des parties d'un exemple de style d'annotation suivant prochainement :
<line x1="0" y1="0" x2="5" y2="5" stroke-width="2" stroke="red" />
Largeur
La largeur du modèle est une chaîne qui peut être "*", "hauteur", ou n'importe quoi d'autre :

"*" définit la largeur de tous les éléments avec une étoile égale les uns aux autres
"height" définit la largeur égale à la hauteur de l'élément d'annotation
Tout ce qui est défini ici sera directement défini sur les propriétés CSS width et min-width.
Mode dessin
Le mode de dessin est une chaîne qui peut être soit "répéter" soit "étirer". Comme les valeurs l'indiquent, le régler sur "répéter" répétera le contenu, tandis que le régler sur "étirer" étirera le contenu.
Voici un exemple de ce que nous pouvons obtenir en configurant ces trois paramètres :
L'annotation de texte dans l'exemple ci-dessus contient 4 parties. La première partie étant la ligne diagonale, avec la largeur du modèle définie sur "hauteur" et le mode de dessin défini sur "répéter". La deuxième partie a sa largeur de modèle définie sur "*" et le mode de dessin défini sur "répéter". La troisième partie est définie sur "15px" de large et dessinée en mode "répétition". Enfin, la largeur de la dernière partie est définie sur "*" et son mode de dessin est défini sur "étirer".
Lorsque ces largeurs sont évaluées, la première partie prend 5 pixels (égale à la hauteur de l'élément d'annotation), la troisième partie prend 15 pixels (comme défini) et l'espace restant est également divisé entre les deuxième et quatrième parties.
Lorsque le même morceau de texte est mis en surbrillance à l'aide du même modèle, voici ce que nous obtenons :
Comme vous pouvez le constater, la hauteur de l'élément d'annotation est supérieure, tout comme la largeur de la première partie (puisque la largeur du modèle pour cette partie est définie sur "hauteur"). Naturellement, la largeur de la troisième partie est restée inchangée par rapport à l'exemple précédent.
L'application d'un effet barré au même texte avec le même modèle produit un résultat très similaire au premier. La seule différence est l'emplacement où les éléments d'annotation sont positionnés :
Même si ces annotations de texte semblent complexes (leur apparence, comportant quatre parties distinctes), elles utilisent toutes des éléments SVG très simples. Comme exemple supplémentaire, faire une ligne ondulée nécessite une seule partie, avec le contenu SVG simple suivant :
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' ))
Lorsque ces modèles sont évalués, le contenu est redimensionné et "{0}" est automatiquement remplacé par la couleur spécifiée. De plus, l'ajout de nouveaux modèles est aussi simple que de les ajouter à un objet JavaScript :
tvs.AnnotatorDictionary.svgTemplates['brush'] = new tvs.Template(new tvs.SvgTemplatePart( svgContent, 50, 50, '*', 'stretch' ));
Résultats
Chaque annotation est appliquée en ajoutant un élément div avec un positionnement absolu à la page :
<div class="tvs-annotate-element"> <div class="tvs-wrap-div"> <table> <tr> <td></td> <td></td> <td></td> </tr> </table> </div> </div>
L'élément div est rempli d'un tableau où chaque cellule ajoutée correspond à l'une des parties du modèle. Le contenu de chaque partie de modèle est ajouté en tant qu'URI de données encodées en Base64, avec la couleur sélectionnée appliquée :
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; };
Intégration
Pour une meilleure expérience utilisateur, en particulier lorsque vous essayez d'utiliser cette bibliothèque JavaScript avec des zones de contenu modifiables, il est important que l'annotateur de texte connaisse les limites du texte actuellement sélectionné par l'utilisateur. Rangy, une bibliothèque JavaScript soignée traitant de la plage et de la sélection, a été utilisée pour y parvenir d'une manière multi-navigateur. Rangy fournit une API simple basée sur des normes pour effectuer des tâches courantes de plage et de sélection DOM dans tous les principaux navigateurs, en éliminant les implémentations extrêmement différentes de cette fonctionnalité entre Internet Explorer et les navigateurs compatibles DOM. C'est la seule dépendance du projet.
Une fois Text Annotator intégré, son utilisation est très simple :
var annotator = new tvs.Annotator(); annotator.underlineSelected();
Chaque élément annoté est marqué avec la classe "tvs-annotated-text" et chaque élément d'annotation a la classe "tvs-annotate-element". La suppression des annotations est encore plus simple, une seule ligne :
annotator.unannotateElement(annotatedElement);
bizarreries
Lorsque la fenêtre est redimensionnée, des éléments peuvent se déplacer, nécessitant le « rafraîchissement » des éléments annotés. C'est la bibliothèque qui s'en charge. Toutefois; pour réduire l'impact sur les performances, l'appel à l'actualisation des annotations est limité :
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); };
Lors de l'actualisation, des éléments d'annotation peuvent être ajoutés, redimensionnés ou supprimés de la page selon les besoins.
Commodité
Pour faciliter l'annotation de texte statique sur une page, un simple attribut data sur l'élément conteneur suffit :
data-annotate='underline squiggly green'
Cela annotera le contenu de l'élément avec un soulignement vert ondulé.
Conclusion
Que puis-je dire de plus sur ce tutoriel de texte SVG ? Un outil amusant mais puissant a été facilement mis en œuvre. Je ne pense pas que nous gagnerions beaucoup à assurer la prise en charge d'Internet Explorer 8, car nous pourrions finir par compliquer toute la mise en œuvre. Cependant, avec quelques améliorations et un peu de travail sur le noyau, nous pouvons étendre la bibliothèque pour pouvoir produire des bordures décoratives pour les éléments non textuels. De plus, il peut être intéressant d'implémenter un mécanisme pour sauvegarder et restaurer ultérieurement l'état du contenu modifiable d'une annotation.
Pour l'instant, les possibilités ne sont limitées que par votre imagination (et les capacités du navigateur). Vous souhaitez peut-être des lignes de micro-impression, des dégradés ou même des animations. Avec Text Annotator, c'est possible.