【发布时间】:2010-11-29 21:55:46
【问题描述】:
非常感谢任何建议。
【问题讨论】:
-
由于答案已经快 3 年了,还有其他可以使用的解决方案或现成的库吗?
-
@Cybrix - 是的,请参阅下面的答案
标签: javascript text html canvas
非常感谢任何建议。
【问题讨论】:
标签: javascript text html canvas
canvas 只是一个绘图表面。您渲染,结果是像素。因此,您需要在鼠标事件期间处理的某种数据结构中跟踪渲染到画布的所有文本的位置。
【讨论】:
如果您需要有可选择的文本,只需创建一个 div 或其他任何内容,并将其放置在您希望文本显示的画布顶部,会容易得多。
canvas 没有任何用于选择文本的内置机制,因此您必须推出自己的文本渲染和选择代码——这可能相当棘手。
【讨论】:
由于画布标签的性质,无法选择在画布元素中绘制的文本。但是有一些解决方法,例如 typefaceJS 中使用的解决方法。
另一种解决方案是使用定位的 div 元素添加文本,而不是使用 strokeText 或 fillText。
【讨论】:
【讨论】:
一个简单的答案是:使用 HTML 或 SVG 代替画布。除非你真的需要画布提供的低级控制程度。
【讨论】:
文本选择有很多组成部分,有些是可视的,有些是非可视的。
首先,使文本可选择,您必须保留一个数组,其中包含文本的位置、文本的内容以及使用的字体。您将将此信息与 Canvas 函数 measureText 一起使用。
通过使用 measureText 和文本字符串,您可以确定单击图像时光标应该落在哪个字母上。
ctx.fillText("My String", 100, 100);
textWidth = ctx.measureText("My String").width;
您仍然需要从“font”属性中解析字体高度, 因为它目前不包含在文本度量中。画布文本对齐 默认到基线。
有了这些信息,您现在就有了一个可以检查的边界框。 如果光标在边界框内,你现在有一个不幸的任务是推导 故意选择了哪个字母;光标的起点应该放在哪里。这可能涉及多次调用 measureText。
这时你就知道光标应该去哪里了;你需要存储你的 文本字符串作为文本字符串,当然在变量中。
一旦定义了范围的起点和终点,就必须绘制 一个选择指标。这可以在一个新层(第二个画布元素)中完成, 或者通过使用 XOR 合成模式绘制一个矩形。它也可以通过 只需清除和重绘填充矩形顶部的文本。
总而言之,Canvas 中的文本选择、文本编辑编程相当费力,重用已编写的组件是明智之举,Bespin 就是一个很好的例子。
如果我遇到其他公开示例,我会编辑我的帖子。我相信 Bespin 使用基于网格的选择方法,可能需要等宽字体。字体渲染的连字、字距调整、双向性和其他高级功能需要额外的编程;这是一个复杂的问题。
【讨论】:
FabricJS 现在能够与画布元素之外的对象进行交互 - 例如,this demo 显示了一个按钮,该按钮链接到画布元素中加载的图像。
通过挂钩任何移动事件、获取元素的边界框并重新定位 HTML 元素,可以在 Raphael 等其他库中使用相同的方法。
【讨论】:
我建议使用EaselJS 库,您可以将每个字母添加为子对象,甚至可以将鼠标事件添加到该对象,这是一个了不起的库,去看看吧!
【讨论】:
首先让我说我不是文本控件方面的专家,但现在我确信这无关紧要,因为我可以帮助您安全进出树林。这些事情本质上是复杂的,需要大量的直觉和关于事情如何运作的知识。但是,您可以检查在 senpai-js/senpai-stage 存储库 here 中运行的代码。
我们应该预先定义一些东西:
/^.$/u
Insert、Selection、Basic(我在我的库中使用SelectionState 枚举并检查舞台上的insertMode 属性)
textScroll,它始终为负数现在我将介绍每个函数来描述它的行为,以准确描述文本框控件应该如何工作。
碰撞检测是个怪物。规范鼠标和触摸事件之间的点移动是一个复杂的问题,本文没有涉及。处理点事件后,您必须对矩形执行某种一般的碰撞检测。这意味着进行 AABB 碰撞。如果文本框精灵本身被旋转,您将不得不“取消旋转”点本身。但是,如果鼠标/触摸点已经在文本框上方,我们会绕过此检查。这是因为一旦您开始选择文本,您希望此函数始终返回 true。然后我们移动到narrowPhase 碰撞,它实际上检查“未转换”的鼠标/触摸点是否在文本框的填充内。如果是,或者文本框处于活动状态,我们会在此处返回一个真实值。
一旦我们知道鼠标/触摸点在我们的文本框的范围内,我们将画布的 css 更改为 cursor: text;。
当我们在文本框上按下鼠标按钮时,我们需要计算插入符号的移动位置。插入符号可以存在于从 0 到 text.length 的范围内。请注意,这并不完全正确,因为 unicode 字符的长度可以是 2。您必须跟踪数组中添加到文本中的每个字符,以断言您没有测量错误的 unicode 字符。计算目标索引意味着遍历当前文本的每个字符并将其附加到一个临时字符串中,每次测量直到测量的宽度大于当前的 textScroll + 测量的 textWidth。
一旦我们确认该点已经下降到文本框的顶部并设置了起点,我们就可以启动“选择”模式。拖动该点应将选择从起始 caretIndex 移动到新计算的结束索引。这是双向的。
here 显示了这方面的一个示例。
Web 按键的解决方案是检查 KeyEvent 上的 key 属性。尽管每个人都说了很多话,但可以通过针对上述 unicode 正则表达式对其进行测试来测试该文本属性。如果匹配,则很可能该键实际上已在键盘上按下。这不包括用于复制和粘贴的组合键,例如 ctrl + c 和 ctrl + v。这些功能是微不足道的,由读者决定如何实现这些功能。
箭头键是少数例外:“ArrowLeft”、“ArrowRight”等。这些键实际上修改了控件的状态,并改变了它的功能。重要的是要记住关键事件只能由当前的focused 控件处理。这意味着您应该检查并确保控件在文本输入期间获得焦点。这当然比我在我的库中编写的代码级别更高,所以这是微不足道的。
下一个需要解决的问题是每个字符输入应该如何修改你的控件的状态。 keyDown 方法识别 selectionState 并根据其状态调用不同的函数。这不是经过优化的伪代码,而是为了清晰起见,非常适合我们描述行为的目的。
selectionStart拼接出来,并将新键插入到文本数组中Normal或Caret
selectionState 再次返回到Normal
0 和 text.length 索引
caretIndex 的选择caretIndex - 1处索引前的一个字符
caretIndex 处索引后的一个字符
0
text.length
caretIndex
Caret 模式下向左或向右移动插入符号时,您应该重新启动闪存机制,以便每次插入符号移动时都能准确显示它们的位置Selection,否则通过将文本切片到插入符号位置,使用ctx.measureText 到插入符号索引来测量插入符号沿文本的距离
Selection 下测量文本的距离仍然很有用,因为我们总是希望用户可以看到文本选择的结尾ctx.save()(基本画布)textScroll 值,该值应为负数midline 值,该值应垂直位于文本框的中间middle 并通过在文本数组上调用 text.join("") 来填充文本【讨论】: