本文概述
借助HTML5和CSS3, Web浏览器获得了许多惊人的技术:3D图形, 套接字, 线程等。有了这些, Web应用程序就可以利用它们所使用的计算机和操作系统的某些最复杂的功能。 Web浏览器为应用程序开发提供了一个强大的通用生态系统, 最近出现的许多强大的Web应用程序离不开我们的见证。但是, 仍然缺少HTML文本注释和修饰的美。什么是文字装饰? Web浏览器不提供本机支持的一些内容是弯弯曲曲的下划线, 凹凸不平的高光和波浪状的删除线。这听起来可能比精心设计要有用得多, 但是JavaScript开发人员产生这些样式的能力在诸如电子学习资源和基于Web的电子书阅读器等方面可能很有用。而且, 这可以有助于增强围绕自然设计原理的Web应用程序中的用户体验。至少, 构建这样的工具很有趣, 并且可以洞察Web浏览器的许多怪异模式。
开发人员发现了许多针对Web浏览器限制的解决方法。这些变通办法中的许多都以不太直观的方式涉及CSS的使用, 如某些在” :: after”伪元素中使用图像。这可行, 但是为每个样式-颜色对维护许多图像通常很困难。本文介绍了JavaScript库的结构, 该库试图优雅地解决此问题。
该库是开源的, 可在GitHub上使用:Text Annotator
概述
在开发该库时, 特别注意确保与大多数流行的Web浏览器(包括IE 9+)兼容。但是, 与大多数解决此问题的方式不同, 该库并不依赖于晦涩难懂的CSS技巧;或更糟糕的是, 特殊的Unicode符号。相反, 它使用SVG来实现更好, 更整洁的文本修饰。
从根本上讲, 该库实现了一个Annotator”类”, 该类可用于自动创建DIV元素, 将其放置在要注释的文本下, 并用SVG图像填充其背景。可以组合多个DIV, 以进一步自定义装饰。该方法是跨浏览器兼容的, 在装饰元素的位置上提供了灵活性, 并允许使用自定义模板更轻松地扩展。
该库是使用Google Closure Tools开发的, 因为它是模块化和跨浏览器的, 可帮助生成紧凑而快速的JavaScript代码, 而无需任何其他依赖。
架构
该库被设计为JavaScript”类”的集合, 并通过”类”注释器向用户提供了所有必要的功能:
以下是可用功能的简要概述:
-
annotateDocument-带注释的元素, 这些元素标记有”数据-注释”属性。
-
下划线-下划线元素
-
高亮-高亮元素
-
罢工-罢工元素
-
underlineSelected-对选定的文本加下划线
-
highlightSelected-突出显示选定的文本
-
StrikeSelected-删除选定的文本
-
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和定位器辅助对象创建的。稍后在CSS类名称和内部字段名称中使用传递的ID, 要求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属性。
绘画模式
绘制模式是可以”重复”或”拉伸”的字符串。如值所示, 将其设置为” repeat”将重复内容, 而将其设置为” stretch”将拉伸内容。
这是我们可以通过配置这三个参数来实现的示例:
上例中的文本注释包含4个部分。第一部分是对角线, 模板宽度设置为”高度”, 绘制模式设置为”重复”。第二部分的模板宽度设置为” *”, 绘制模式设置为”重复”。第三部分设置为” 15px”宽, 并以”重复”模式绘制。最后, 最后一个部分的宽度设置为” *”, 其绘制模式设置为”拉伸”。
当评估这些宽度时, 第一部分占用5个像素(等于注释元素的高度), 第三部分占用15个像素(已设置), 剩余空间在第二和第四部分之间平均分配。
当使用相同的模板突出显示相同的文本时, 我们得到的是:
如你所知, 注解元素的高度更大, 第一部分的宽度也更大(因为该部分的模板宽度设置为” height”)。当然, 第三部分的宽度与上一个示例相同。
对具有相同模板的相同文本应用删除线效果会产生与第一个非常相似的结果。唯一的区别是注释元素的放置位置:
即使这些文本注释看起来很复杂(它们的外观, 由四个不同部分组成), 它们都使用非常简单的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 style="position: absolute; height: 5px; width: 178px; left: 8px; top: 447.2px; opacity: 0.9;" class="tvs-annotate-element">
<div style="height: 5px; width: 178px;" class="tvs-wrap-div">
<table>
<tr>
<td style="width: 10px; min-width: 10px; background-image: url(data:image/svg+xml;base64, ...); background-size: auto 100%;"></td>
<td style="width: 50%; background-image: url(data:image/svg+xml;base64, ...); background-size: auto 100%;"></td>
<td style="width: 50%; background-image: url(data:image/svg+xml;base64, ...); background-size: auto 100%;"></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-annotate-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属性即可:
data-annotate='underline squiggly green'
这将用一个绿色的下划线标记元素的内容。
总结
关于此SVG文本教程, 我还能说什么?一个有趣而强大的工具已轻松实现。我认为, 确保对Internet Explorer 8的支持不会给我们带来很多好处, 相反, 我们可能最终会使整个实现复杂化。但是, 通过一些改进和在核心上的一些工作, 我们可以扩展该库以能够为非文本元素生成装饰性边框。此外, 实施某种机制来保存并随后恢复注释的可编辑内容的状态可能是一项有趣的任务。
就目前而言, 可能性仅受你的想象力(和浏览器功能)限制。也许你需要缩微线条, 渐变或动画。使用文本注释器, 你可以。