SVG 文本教程:Web 上的文本註釋

已發表: 2022-03-11

借助 HTML5 和 CSS3,Web 瀏覽器獲得了許多令人驚嘆的技術:3D 圖形、套接字、線程等等。 有了這些,Web 應用程序可以利用它們所使用的計算機和操作系統的一些最複雜的功能。 Web 瀏覽器為應用程序開發提供了一個強大的多功能生態系統,這從最近大量強大的 Web 應用程序的興起中可以看出,我們離不開這些應用程序。 然而,仍然缺少的東西是 HTML 文本註釋和裝飾的美感。 什麼是文字裝飾? 波浪形的下劃線、粗獷的高光和波浪形的三線劃線是 Web 瀏覽器不提供本機支持的一些內容。 這聽起來可能比有用更複雜,但是 JavaScript 開發人員生成這些樣式的能力可能在諸如電子學習資源和基於 Web 的電子書閱讀器等方面證明是有用的。 此外,這有助於增強圍繞自然設計原則的 Web 應用程序的用戶體驗。 至少,構建這樣的工具很有趣,並且可以深入了解 Web 瀏覽器的許多怪癖。

SVG 文本教程 - 文本註釋

開發人員發現了許多解決 Web 瀏覽器限制的方法。 許多這些變通方法涉及以不太直觀的方式使用 CSS,因為有些在“::after”偽元素中使用圖像。 這行得通,但是為每個樣式-顏色對維護許多圖像通常被證明是困難的。 本文將剖析一個試圖優雅地解決這個問題的 JavaScript 庫。

該庫是開源的,可在 GitHub 上獲得:Text Annotator

概述

在開發這個庫時,特別注意確保與最流行的網絡瀏覽器(包括 IE 9+)的兼容性。 然而,與大多數解決此問題的方法不同,該庫不依賴於特別晦澀的 CSS 技巧。 或更糟糕的是,特殊的 Unicode 符號。 相反,它使用 SVG 來實現更好、更清晰的文本裝飾。

從根本上說,該庫實現了一個註釋器“類”,可用於自動創建 DIV 元素,將它們放置在要註釋的文本下,並用 SVG 圖像填充它們的背景。 可以組合多個 DIV 以進一步自定義裝飾。 該方法是跨瀏覽器兼容的,提供了裝飾元素定位的靈活性,並允許使用自定義模板進行更輕鬆的擴展。

該庫是使用 Google Closure Tools 開發的,因為它是模塊化和跨瀏覽器的,有助於生成緊湊且快速的 JavaScript 代碼,而無需任何額外的依賴。

建築學

該庫被設計為 JavaScript“類”的集合,並通過“類”註釋器向用戶公開所有必要的功能:

文本註釋器庫

以下是可用功能的簡要概述:

  • annotateDocument - 對標有“data-annotate”屬性的元素進行註釋。

  • underline - 下劃線元素

  • highlight - 突出顯示元素

  • 罷工 - 罷工元素

  • underlineSelected - 給選中的文本加下劃線

  • highlightSelected - 突出顯示選定的文本

  • 罷工選擇 - 罷工選定的文本

  • unannotateElement - 從元素中刪除註釋

  • getTemplates - 返回註釋模板的字典

  • setUnderlineOptions - 設置下劃線註釋器的設置

  • setHighlightOptions - 設置高亮註釋器的設置

  • setStrikeOptions - 設置罷工註釋器的設置

註釋器類為每個註釋函數保存 AnnotatorImpl 類的三個實例:下劃線、突出顯示和罷工。

 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); };

AnnotatorImpl 實例是使用不同的 ID 和定位器輔助對象創建的。 傳遞的 ID 稍後用於 CSS 類名稱和內部字段名稱,要求 ID 是唯一的。 此外,還傳遞了對已知模板列表的引用(以後可以更改)。

每個定位器對像都是 IPositioner 接口的一個實現,它只有“getPosition”方法,如下所示:

 /** * 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) }; } });

這允許每個模板與下劃線、突出顯示或刪除文本註釋一起使用。 當註釋應用於元素時,通過調用“getElementRects”獲取元素的邊界框,如下所示:

 var rects = elemOrEv.getClientRects();

此方法返回一個矩形集合,這些矩形表示客戶端中每個框的邊界矩形。 將每個矩形傳遞給具體的定位器後,我們將獲得目標邊界。

SVG 文本註釋模板

如前所述,只有一組模板可用於各種 SVG 文本註釋。 每個模板都由模板部分組成。 模板部件是表示部件內容、模板寬度和繪製模式的實體。

內容

內容是一組表示為字符串的 SVG 元素。 由於此內容沒有設置視口寬度和高度(以像素為單位)的根 SVG 節點,因此模板的部分構造函數接受它們作為參數。 例如,您可以將視口的大小指定為 100px x 100px,並在 (50, 50) 和 (25, 25) 之間畫一條線。 應用註釋後,所有 svg 元素將正確調整為所需大小。 內容值可以使用字符串“{0}”,該字符串將替換為用戶選擇的顏色。

以下 SVG 呈現對角線。 我們將在接下來的示例註釋樣式中使用它作為其中的一部分:

 <line x1="0" y1="0" x2="5" y2="5" stroke-width="2" stroke="red" />

寬度

模板寬度是一個字符串,可以是“*”、“height”或其他任何內容:

  • “*”設置所有帶星號的元素的寬度彼此相等

  • “height”設置寬度等於註釋元素的高度

此處設置的任何其他內容將直接設置為 CSS width 和 min-width 屬性。

CSS 屬性

繪圖模式

繪製模式是一個可以是“重複”或“拉伸”的字符串。 如值所示,將其設置為“重複”將重複內容,而將其設置為“拉伸”將拉伸內容。

以下是我們通過配置這三個參數可以實現的示例:

文本註釋

上例中的文本註釋包含 4 個部分。 第一部分是對角線,模板寬度設置為“高度”,繪製模式設置為“重複”。 第二部分的模板寬度設置為“*”,繪製模式設置為“重複”。 第三部分設置為“15px”寬並以“重複”模式繪製。 最後,將最後一部分的寬度設置為“*”,並將其繪製模式設置為“拉伸”。

在評估這些寬度時,第一部分佔用 5 個像素(等於註釋元素的高度),第三部分佔用 15 個像素(按設置),剩餘空間在第二和第四部分之間平均分配。

當使用相同的模板突出顯示相同的文本時,這就是我們得到的:

繪圖模式

如您所知,註釋元素的高度更大,第一部分的寬度也更大(因為該部分的模板寬度設置為“高度”)。 自然地,第三部分的寬度與前面的示例保持不變。

對具有相同模板的相同文本應用刪除效果會產生與第一個非常相似的結果。 唯一的區別是註釋元素的位置:

文本註釋繪製模式

儘管這些文本註釋看起來很複雜(它們的外觀,有四個不同的部分),但它們都使用非常簡單的 SVG 元素。 再舉一個例子,做一條波浪線需要一個單獨的部分,包含以下簡單的 SVG 內容:

 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' ))

評估這些模板時,會調整內容的大小並自動將“{0}”替換為指定的顏色。 更重要的是,添加新模板就像將它們添加到 JavaScript 對像一樣簡單:

 tvs.AnnotatorDictionary.svgTemplates['brush'] = new tvs.Template(new tvs.SvgTemplatePart( svgContent, 50, 50, '*', 'stretch' ));

結果

每個註釋都是通過將具有絕對定位的 div 元素附加到頁面來應用的:

 <div class="tvs-annotate-element"> <div class="tvs-wrap-div"> <table> <tr> <td></td> <td></td> <td></td> </tr> </table> </div> </div>

div 元素填充有一個表格,其中添加的每個單元格都對應於模板中的一個部分。 每個模板部分的內容都添加為 Base64 編碼數據 URI,並應用所選顏色:

 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; };

嵌入

為了獲得更好的用戶體驗,尤其是在嘗試將此 JavaScript 庫用於可編輯內容區域時,文本註釋器了解用戶當前選擇的文本範圍非常重要。 Rangy 是一個處理範圍和選擇的簡潔 JavaScript 庫,已用於以跨瀏覽器的方式實現此目的。 Rangy 提供了一個簡單的基於標準的 API,用於在所有主要瀏覽器中執行常見的 DOM 範圍和選擇任務,抽像出 Internet Explorer 和 DOM 兼容瀏覽器之間該功能的完全不同的實現。 它是項目的唯一依賴項。

嵌入文本註釋器後,使用它是一個非常簡單的壯舉:

 var annotator = new tvs.Annotator(); annotator.underlineSelected();

每個帶註釋的元素都標有“tvs-annotated-text”類,每個註釋元素都有“tvs-annotate-element”類。 刪除註釋更簡單,單行:

 annotator.unannotateElement(annotatedElement);

怪癖

調整窗口大小時,元素可能會四處移動,需要“刷新”帶註釋的元素。 這是由圖書館處理的。 然而; 為了減少對性能的影響,對刷新註釋的調用受到限制:

 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); };

刷新後,可以根據需要從頁面中添加、調整大小或刪除註釋元素。

方便

為了更容易在頁面上註釋靜態文本,容器元素上的一個簡單數據屬性就足夠了:

 data-annotate='underline squiggly green'

這將使用彎曲的綠色下劃線註釋元素的內容。

結論

關於這個 SVG 文本教程,我還能說什麼? 一個有趣但功能強大的工具已輕鬆實現。 我認為確保對 Internet Explorer 8 的支持不會給我們帶來太多好處,因為我們最終可能會使整個實現變得複雜。 但是,通過對核心的一些改進和一些工作,我們可以擴展庫以能夠為非文本元素生成裝飾性邊框。 此外,實現某種機制來保存和稍後恢復註釋的可編輯內容的狀態可能是一項有趣的任務。

就目前而言,可能性僅受您的想像力(和瀏覽器功能)的限制。 也許您想要微縮印刷線條、漸變甚至動畫。 使用文本註釋器,您可以。