目录

前端模拟终端(一):如果我的这款 IOTerm 不是你想要的
前端模拟终端(二):部分可输入而部分不可修改的多行文本域
前端模拟终端(三):文本显示与自动换行
前端模拟终端(四):显示、输入与光标
前端模拟终端(五):看谁用了 rm -rf / 之历史记录
前端模拟终端(六):快捷输入的好助手、终端的灵魂之补全和提示

文本显示与自动换行

  前面都在说 IOTerm 中使用 div 显示文本,但其实一开始选用的是 pre 元素,因为它可以原模原样的显示,而不会少了空格。

  pre 默认使用等宽字体,这也正好符合终端的需要。但是 pre 默认是不会自动换行的,文本会超出元素范围,也就是 white-spacepre。可以将 white-space 改成 pre-wrap,这样就可以既保留空格又能正常换行了。但是因为默认的 world-break 是在单词之间换行,而不会破开整个单词,这就造成在最后一列凹凸不平,不和终端一样。把 world-break 换成 break-all,就可以在单词中断开了。但是效果依然不佳,在都是英文字母,且字体为等宽字体时,还是会出现最后一列不齐的情况。也就是当本应换行处的字母的后面是标签符号时,最后一个字符会被拉到下一行,即标签符号不会出现在一行开头(除了左括号、引号)。有什么好的办法呢?是的,最初,我选择自己写自动换行。不断地测量以确定插入换行符的位置。

  首先需要一个辅助的元素,它的样式要和显示文本的 prediv 一模一样,至少字体、字号等要一样。然后开始不断地尝试与测量吧。最容易想到的当然就是顺序取字符,测量字符串长度。取第一个字符赋值给辅助元素,获取辅助元素的宽度,短了就继续取,长了就插入换行符。取第二个字符与第一个字符组成字符串赋值为辅助元素,获取辅助元素的宽度,进行比较……这样就是一个 O(n)。慢,是肯定的。

  所以,我用了二分法。一行的最大宽度是已知的,记为 maxWidth。设当前测量的字符串为 subText,其宽度为 subTextWidth。当使用的是等宽字体时,就英文来说,每个字符的宽度是一样的,而终端中输入的最多的就是英文了。那么可以通过以下方式估算插入换行符的位置:

endIndex = Math.floor(subText.length * maxWidth / subTextWidth);

  当字符串中有中文等其他文字时,这种估算也只能获取大概的位置,还需要评估其左右。若其宽度 subTextWidth 小于 maxWidth,则往其右再取一个字符,比较宽度,若长了,则 endIndex 为精确的换行位置。若 subTextWidth 大于 maxWidth,则往其左再取一个字符,比较长度,若短了,则 endIndex - 1 为精确的换行位置。

  考虑到实际处理的字符串和显示出来的字符串是可能不一样的,比如存在高亮时,HTML 标签是占长度但不显示的,以及 <、>、& 等转义字符,在代码实现中,将确定插入换行符位置和处理 HTML 标签以及转义字符分为两个步骤。具体代码如下:

private getLineFeedsIndices(text: string) {// Find the indices where to insert line feeds into the string `text`.//// This function returns a object containing `indices`, `numRows` and// 'colOffset'. The `indices` is an array recording the indices where to// insert line feeds in the string `text`. The `numRows` is the number// of rows after the `text` is inserted with line feeds <br>.// The 'colOffset' is the width in pixel of the last line after the// `text` is inserted with line feeds <br>.// The `startIndex` is the starting index of the string `subText` in the// string `text`.let startIndex = 0;let maxWidth = this.main.panel.offsetWidth;let indices: number[] = [];let subText: string;let subTextWidth: number;let endIndex: number;let line: string;let lineWidth: number;let preState: number;// In each iteration, find a line whose length is less than `maxWidth`.// The line feed <br> will not be inserted into the last line even if// its length is equal to `maxWidth`.while (true) {subText = text.substring(startIndex);this.measurement.innerHTML = escapeText(subText);subTextWidth = this.measurement.offsetWidth;if (subTextWidth <= maxWidth) {break;}// The `endIndex` computed here is a rough index where to insert a// linefeed in string `subText`. And it is also the number of// characters.endIndex = Math.floor(subText.length * maxWidth / subTextWidth);// For an exact index, it is necessary to compare the// relationship between the rough index and its consecutive// indices.preState = 0;while (true) {line = subText.substring(0, endIndex);this.measurement.innerHTML = escapeText(line);lineWidth = this.measurement.offsetWidth;if (lineWidth === maxWidth) {// The `endIndex` here is an exact index where to insert a// line feed into string `subText'. Also, `startIndex`// computed here is an exact index where to insert a// line feed in string `text`.startIndex += endIndex;indices.push(startIndex);break;} else if (lineWidth < maxWidth) {// If a string consists of some narrow characters at// the beginning and wide characters at end, such as// '1234一二三四', the rough index could be smaller.if (preState > 0) {startIndex += endIndex;indices.push(startIndex);break;} else if (preState === 0) {preState = -1;}endIndex++;} else {if (preState < 0) {startIndex += endIndex - 1;indices.push(startIndex);break;} else if (preState === 0) {preState = 1;}endIndex--;}}}return {indices: indices,// Even a empty line '' will be counted as a newline.numRows: 1 + indices.length,colOffset: subTextWidth};
}private autoWrap(html: string) {// Insert HTML tag <br> as line feed.//// The `html` is an escaped and highlighted string that excludes line// feed '\n', '\r\n' or HTML tag <br>. In it, the HTML tag <span> wraps// around the text for highlighting. Note that character '<', '>' and// '&' should be escaped to '&lts', '&gt;' and '&amp;' except for HTML// tags.//// This function returns a string composed by `html` and some line// feeds.this.measurement.innerHTML = html;let text = this.measurement.innerText;let lf = this.getLineFeedsIndices(text);let indices = lf.indices;if (indices === []) {return {wrappedHTML: html,numRows: lf.numRows,colOffset: lf.colOffset};}let wrappedHTML = '';let i = 0;let length = 0;let isHTML = false;let startIndex = 0;let index = indices.shift();while (true) {if (!index) {wrappedHTML += html.substring(startIndex);break;}switch (html.charAt(i)) {case '<':isHTML = true;i++;continue;case '>':isHTML = false;i++;continue;case '&':if (['&lt;', '&gt;'].indexOf(html.substr(i, 4)) !== -1) {i += 3;} else if ('&amp;' === html.substr(i, 5)) {i += 4;}break;}if (isHTML) {i++;continue;}length++;i++;if (length === index) {wrappedHTML += html.substring(startIndex, i) + '<br>';startIndex = i;index = indices.shift();}}return {wrappedHTML,numRows: lf.numRows,colOffset: lf.colOffset};
}
以下才是正文

  没错,上面的方法太麻烦、太长了,不愿看!虽然这比线性查找快多了,但还需要辅助元素进行多次渲染,而且在复制文本时,也会多出一些本来没有的空白符。所以,最终采用的办法是设置样式中的 line-break 属性为 anywhere!是的,你可能没听过这个属性。我在 Bing 和百度中搜索 line break,竟然没看到一篇中文博文介绍该属性的。重要的东西再突出写一下:

line-break: anywhere;

前端模拟终端(三):文本显示与自动换行相关推荐

  1. 从零开始前端学习[11]:控制文本显示的样式属性

    控制文本显示的样式属性 text-align 文字水平显示方式 line-height 文字垂直显示方式 text-decoration 文本的修饰方式 text-indent 文本的缩进方式 let ...

  2. 前端实现纯css文本显示省略号

    单行文本css显示省略号. width:200px; overflow: hidden; text-overflow:ellipsis; white-space: nowrap; 单行文本需要有固定宽 ...

  3. 前端模拟面试字数过23477万内容|刷题打卡

    原文地址:https://juejin.cn/post/6948576107163549732 前端开发工程师面试 今天我请到两位朋友,面试官(小黄),面试者(小达)来进行如下前端模拟面试:(此处省略 ...

  4. web模拟终端博客系统

    2019独角兽企业重金招聘Python工程师标准>>> 本文由QQ音乐前端团队发表 前段时间做了一个非常有意思的模拟终端的展示页:http://ursb.me/terminal/(没 ...

  5. 计算机应用基础模拟三答案,《计算机应用基础》模拟试卷三(含答案)

    <计算机应用基础>模拟试卷三(含答案) (11页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 14.90 积分 中职立体化精品教材题库系统生成 ...

  6. 前端常见知识点三之HTML

    前端常见知识点三之HTML 1.HTML5 drag api dragstart:事件主体是被拖放元素,在开始拖元素时触发 darg:事件是被拖放元素,在正在拖放时触发 dragenter:事件主体是 ...

  7. css截断长文本显示

    实现 截断长文本显示处理,以前是通过后台的截取,但这种方法容易丢失数据,不利于SEO. 而通过前端css的截断,则灵活多变,可统一运用与整个网站. 这项技术主要运用了text-overflow属性,这 ...

  8. CSS 单行溢出文本显示省略号...的方法(兼容IE FF)(转)

    CSS 单行溢出文本显示省略号...的方法(兼容IE FF)(转) html代码: <div> <p><span>CSS Web Design 我爱CSS-Web标 ...

  9. 计算机应用基础模拟三答案,《计算机应用基础》模拟试卷三答案

    计算机应用基础计算机应用基础 模拟试卷三模拟试卷三 答案答案 得分评卷人 一一 填空题填空题 1 HTTP 2 采样频率 3 机械 光电 光学 4 文件夹 5 TCP IP 6 网卡 7 网络地址 8 ...

最新文章

  1. SQLServer 系统表
  2. 线程常用方法,线程安全和同步锁
  3. kettle内存溢出
  4. SQL 流程控制语句 之四 WAITFOR语句介绍
  5. nginx源码分析之模块初始化
  6. c语言复杂的程序代码,C语言中复杂结构的序列化
  7. 可以救命的生活小常识
  8. JS跨域(ajax跨域、iframe跨域)解决方法及原理详解(jsonp)
  9. 6、Actor,Stage的学习
  10. SpringBoot + Mybatis-puls + ClickHouse增删改查入门教程
  11. MySQL增强版命令行客户端连接工具(mycli)
  12. Linux下如何禁止某个用户登录方法
  13. Atitit path query 路径查询语言 数据检索语言 目录 1.1. List map spel 1 1.2. Html数据 》》Css选择符 1 1.3. Json 》map》
  14. Ubuntu_扩容后没有作用——解决办法是要重新分区
  15. AJax 遍历json对象数组 和Jstl遍历以及json解决日期转换
  16. 新概念51单片机c语言教程考试题,新概念51单片机C语言教程例题.doc
  17. 新商机:“百亿帝国”全健、华林彻底倒下,保健行业遭重创,直销业被重新定义
  18. matlab 模糊提取,[转载]Matlab 的fspecial函数用法 图像模糊、提取边缘
  19. cad工具箱详细讲解_正确使用AutoCAD插件CAD工具箱如何使用?
  20. opencv图像形态学运算

热门文章

  1. argparse简化版图片教程
  2. 移动机器人技术(9)-- 全向移动机器人Modeling and Control
  3. f429 discovery开发版 LVGL移植(带操作系统)
  4. 修复青龙白屏登录界面以及脚本管理等界面白屏问题
  5. 获取linux系统编码,Android获取IMEI码
  6. 五、python-地图可视化篇(黑马程序猿-python学习记录)
  7. 外贸CRM系统管理客户
  8. border-image实现渐变边框
  9. 你的Web3域名 价值究竟何在?
  10. 用数万元购买个人网站域名是否值得?