范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

一招让妹子尖叫,教你非常酷炫黑客帝国里的矩阵雨

  相信大家都对黑客帝国电影里的矩阵雨印象非常深刻,就是下面这个效果。
  效果非常酷炫,我看了一下相关实现库的代码,也非常简单,核心就是用好命令行的控制字符,这里分享一下。
  在 matrix-rain 的源代码中,总共只有两个文件,ansi.js 和 index.js,非常小巧。 控制字符和控制序列
  ansi.js 中定义了一些命令行的操作方法,也就是对控制字符做了一些方法封装,代码如下: const ctlEsc = `[`; const ansi = {   reset: () => `${ctlEsc}c`,   clearScreen: () => `${ctlEsc}2J`,   cursorHome: () => `${ctlEsc}H`,   cursorPos: (row, col) => `${ctlEsc}${row};${col}H`,   cursorVisible: () => `${ctlEsc}?25h`,   cursorInvisible: () => `${ctlEsc}?25l`,   useAltBuffer: () => `${ctlEsc}?47h`,   useNormalBuffer: () => `${ctlEsc}?47l`,   underline: () => `${ctlEsc}4m`,   off: () => `${ctlEsc}0m`,   bold: () => `${ctlEsc}1m`,   color: c => `${ctlEsc}${c};1m`,    colors: {     fgRgb: (r, g, b) => `${ctlEsc}38;2;${r};${g};${b}m`,     bgRgb: (r, g, b) => `${ctlEsc}48;2;${r};${g};${b}m`,     fgBlack: () => ansi.color(`30`),     fgRed: () => ansi.color(`31`),     fgGreen: () => ansi.color(`32`),     fgYellow: () => ansi.color(`33`),     fgBlue: () => ansi.color(`34`),     fgMagenta: () => ansi.color(`35`),     fgCyan: () => ansi.color(`36`),     fgWhite: () => ansi.color(`37`),     bgBlack: () => ansi.color(`40`),     bgRed: () => ansi.color(`41`),     bgGreen: () => ansi.color(`42`),     bgYellow: () => ansi.color(`43`),     bgBlue: () => ansi.color(`44`),     bgMagenta: () => ansi.color(`45`),     bgCyan: () => ansi.color(`46`),     bgWhite: () => ansi.color(`47`),   }, };  module.exports = ansi;
  这里面 ansi 对象上的每一个方法不做过多解释了。我们看到,每个方法都是返回一个奇怪的字符串,通过这些字符串可以改变命令行的显示效果。
  这些字符串其实是一个个控制字符组成的控制序列。那什么是控制字符呢?我们应该都知道 ASC 字符集,这个字符集里面除了定义了一些可见字符以外,还有很多不可见的字符,就是控制字符。这些控制字符可以控制打印机、命令行等设备的显示和动作。
  有两个控制字符集,分别是 CO 字符集和 C1 字符集。C0 字符集是 0x00 到 0x1F 这两个十六进制数范围内的字符,而 C1 字符集是 0x80 到 0x9F 这两个十六进制数范围内的字符。C0 和 C1 字符集内的字符和对应的功能可以在这里查到,我们不做详细描述了。
  上面代码中,[ 其实是一个组合, 定义了 ESC 键,后跟 [ 表示这是一个控制序列导入器(Control Sequence Introducer,CSI)。在 [ 后面的所有字符都会被命令行解析为控制字符。
  常用的控制序列有这些:
  序列
  功能
  CSI n A
  向上移动 n(默认为 1) 个单元
  CSI n A
  向下移动 n(默认为 1) 个单元
  CSI n C
  向前移动 n(默认为 1) 个单元
  CSI n D
  向后移动 n(默认为 1) 个单元
  CSI n E
  将光标移动到 n(默认为 1) 行的下一行行首
  CSI n F
  将光标移动到 n(默认为 1) 行的前一行行首
  CSI n G
  将光标移动到当前行的第 n(默认为 1)列
  CSI n ; m H
  移动光标到指定位置,第 n 行,第 m 列。n 和 m 默认为 1,即 CSI ;5H 与 CSI 1;5H 等同。
  CSI n J
  清空屏幕。如果 n 为 0(或不指定),则从光标位置开始清空到屏幕末尾;如果 n 为 1,则从光标位置清空到屏幕开头;如果 n 为 2,则清空整个屏幕;如果 n 为 3,则不仅清空整个屏幕,同时还清空滚动缓存。
  CSI n K
  清空行,如果 n 为 0(或不指定),则从光标位置清空到行尾;如果 n 为 1,则从光标位置清空到行头;如果 n 为 2,则清空整行,光标位置不变。
  CSI n S
  向上滚动 n (默认为 1)行
  CSI n T
  向下滚动 n (默认为 1)行
  CSI n ; m f
  与 CSI n ; m H 功能相同
  CSI n m
  设置显示效果,如 CSI 1 m 表示设置粗体,CSI 4 m 为添加下划线。
  我们可以通过 CSI n m 控制序列来控制显示效果,在设置一种显示以后,后续字符都会沿用这种效果,直到我们改变了显示效果。可以通过 CSI 0 m 来清楚显示效果。常见的显示效果可以在SGR (Select Graphic Rendition) parameters 查到,这里受篇幅限制就不做赘述了。
  上面的代码中,还定义了一些颜色,我们看到颜色的定义都是一些数字,其实每一个数字都对应一种颜色,这里列一下常见的颜色。
  前景色
  背景色
  名称
  前景色
  背景色
  名称
  30
  40
  黑色   90
  100
  亮黑色   31
  41
  红色   91
  101
  亮红色   32
  42
  绿色   92
  102
  亮绿色   33
  43
  黄色   93
  103
  亮黄色   34
  44
  蓝色   94
  104
  亮蓝色   35
  45
  品红色(Magenta)   95
  105
  亮品红色(Magenta)   36
  46
  青色(Cyan)   96
  106
  亮青色(Cyan)   37
  47
  白色   97
  107
  亮白色   上面的代码中,使用了 CSI n;1m 的形式来定义颜色,其实是两种效果的,一个是具体颜色值,一个是加粗,一些命令行实现中会使用加粗效果来定义亮色。比如,如果直接定义 CSI 32 m 可能最终展示的是暗绿色,我们改成 CSI 32;1m 则将显示亮绿色。   颜色支持多种格式,上面的是 3-bit 和 4-bit 格式,同时还有 8-bit 和 24-bit。代码中也有使用样例,这里不再赘述了。 矩阵渲染   在 matrix-rain 的代码中,index.js 里的核心功能是 MatrixRain 这个类: class MatrixRain { constructor(opts) { this.transpose = opts.direction === `h`; this.color = opts.color; this.charRange = opts.charRange; this.maxSpeed = 20; this.colDroplets = []; this.numCols = 0; this.numRows = 0; // handle reading from file if (opts.filePath) { if (!fs.existsSync(opts.filePath)) { throw new Error(`${opts.filePath} doesn"t exist`); } this.fileChars = fs.readFileSync(opts.filePath, `utf-8`).trim().split(``); this.filePos = 0; this.charRange = `file`; } } generateChars(len, charRange) { // by default charRange == ascii let chars = new Array(len); if (charRange === `ascii`) { for (let i = 0; i < len; i++) { chars[i] = String.fromCharCode(rand(0x21, 0x7E)); } } else if (charRange === `braille`) { for (let i = 0; i < len; i++) { chars[i] = String.fromCharCode(rand(0x2840, 0x28ff)); } } else if (charRange === `katakana`) { for (let i = 0; i < len; i++) { chars[i] = String.fromCharCode(rand(0x30a0, 0x30ff)); } } else if (charRange === `emoji`) { // emojis are two character widths, so use a prefix const emojiPrefix = String.fromCharCode(0xd83d); for (let i = 0; i < len; i++) { chars[i] = emojiPrefix + String.fromCharCode(rand(0xde01, 0xde4a)); } } else if (charRange === `file`) { for (let i = 0; i < len; i++, this.filePos++) { this.filePos = this.filePos < this.fileChars.length ? this.filePos : 0; chars[i] = this.fileChars[this.filePos]; } } return chars; } makeDroplet(col) { return { col, alive: 0, curRow: rand(0, this.numRows), height: rand(this.numRows / 2, this.numRows), speed: rand(1, this.maxSpeed), chars: this.generateChars(this.numRows, this.charRange), }; } resizeDroplets() { [this.numCols, this.numRows] = process.stdout.getWindowSize(); // transpose for direction if (this.transpose) { [this.numCols, this.numRows] = [this.numRows, this.numCols]; } // Create droplets per column // add/remove droplets to match column size if (this.numCols > this.colDroplets.length) { for (let col = this.colDroplets.length; col < this.numCols; ++col) { // make two droplets per row that start in random positions this.colDroplets.push([this.makeDroplet(col), this.makeDroplet(col)]); } } else { this.colDroplets.splice(this.numCols, this.colDroplets.length - this.numCols); } } writeAt(row, col, str, color) { // Only output if in viewport if (row >=0 && row < this.numRows && col >=0 && col < this.numCols) { const pos = this.transpose ? ansi.cursorPos(col, row) : ansi.cursorPos(row, col); write(`${pos}${color || ``}${str || ``}`); } } renderFrame() { const ansiColor = ansi.colors[`fg${this.color.charAt(0).toUpperCase()}${this.color.substr(1)}`](); for (const droplets of this.colDroplets) { for (const droplet of droplets) { const {curRow, col: curCol, height} = droplet; droplet.alive++; if (droplet.alive % droplet.speed === 0) { this.writeAt(curRow - 1, curCol, droplet.chars[curRow - 1], ansiColor); this.writeAt(curRow, curCol, droplet.chars[curRow], ansi.colors.fgWhite()); this.writeAt(curRow - height, curCol, ` `); droplet.curRow++; } if (curRow - height > this.numRows) { // reset droplet Object.assign(droplet, this.makeDroplet(droplet.col), {curRow: 0}); } } } flush(); } }   还有几个工具方法: // Simple string stream buffer + stdout flush at once let outBuffer = []; function write(chars) { return outBuffer.push(chars); } function flush() { process.stdout.write(outBuffer.join(``)); return outBuffer = []; } function rand(start, end) { return start + Math.floor(Math.random() * (end - start)); }   matrix-rain 的启动代码如下: const args = argParser.parseArgs(); const matrixRain = new MatrixRain(args); function start() { if (!process.stdout.isTTY) { console.error(`Error: Output is not a text terminal`); process.exit(1); } // clear terminal and use alt buffer process.stdin.setRawMode(true); write(ansi.useAltBuffer()); write(ansi.cursorInvisible()); write(ansi.colors.bgBlack()); write(ansi.colors.fgBlack()); write(ansi.clearScreen()); flush(); matrixRain.resizeDroplets(); } function stop() { write(ansi.cursorVisible()); write(ansi.clearScreen()); write(ansi.cursorHome()); write(ansi.useNormalBuffer()); flush(); process.exit(); } process.on(`SIGINT`, () => stop()); process.stdin.on(`data`, () => stop()); process.stdout.on(`resize`, () => matrixRain.resizeDroplets()); setInterval(() => matrixRain.renderFrame(), 16); // 60FPS start();   首先初始化一个 MatrixRain 类,然后调用 start 方法。start 方法中通过 MatrixRain 的 resizeDroplets 方法来初始化要显示的内容。   MatrixRain 类实例中管理着一个 colDroplets 数组,保存这每一列的雨滴。在 resizeDroplets 中我们可以看到,每一列有两个雨滴。   在启动代码中我们还可以看到,每隔 16 毫秒会调用一次 renderFrame 方法来绘制页面。而 renderFrame 方法中,会遍历每一个 colDroplet 中的每一个雨滴。由于每一个雨滴的初始位置和速度都是随机的,通过 droplet.alive 和 droplet.speed 的比值来确定每一次渲染的时候是否更新这个雨滴位置,从而达到每个雨滴的下落参差不齐的效果。当雨滴已经移出屏幕可视范围后会被重置。   每一次渲染,都是通过 write 函数向全局的缓存中写入数据,之后通过 flush 函数一把更新。

朋友总是鼓吹苹果,无脑唱衰华为而且还不讲理,应该如何说服她?你应该告诉他,任正非,孟晚粥也用苹果。人家还无脑,就你这种智障还好意思说人家呢?就告诉你一个道理。而且是不容反驳的。在中华人民共和国大陆销售的一切品牌都是合情合理合法国家允许的产品亚马逊良心发现?来看看海外近期物流状亚马逊新举措让你用更少的钱存更多的货云商跨境了解到,近日亚马逊正在美国试验一项物流新举措,即在其运营中心上游新增一个仓储层,从上游仓库向运营中心供货,以实现更多的无缝补货。目前亚马三星现在几乎消失了,苹果会和他一样退出中国市场吗?三星是什么?苹果不是倒闭了?什么乱七八糟的问题。你真幽默呲牙这么无聊的问题让我怎么回答,你怎么知道三星手机消失了,我正用三星手机s10给你回问题!!为什么美国害怕华为,从而全力打压之前加入抖音小店无货源的商家现在如何?一年过去了,还能入场吗?可以肯定的是,抖音小店作为当下抖音电商体系里面最重要的载体,无论从流量机会还是运营模式上,无疑都是现阶段最适合普通人在抖音赚钱的项目。抖音巨大的流量红利,让抖音小店无货源模式门槛变花呗怎么大面积都被停用了,出什么事了吗?转移战场,天天网上骂人家,凭什么用人家花呗我有个朋友说,余额宝里存个50万,花呗不会停的看我的花呗借呗都停了,花呗用了好多年一直好好的,这个突然一下都给停了四万五还没变化刚看了还是币圈那些事俄罗斯中央银行俄罗斯中央银行正计划对数字卢布的交易收取费用金色财经报道,俄罗斯中央银行(CBR)监管机构金融技术部主任KirillPronin透露,俄罗斯中央银行正计划对数字卢布的对标Postman,高瓴资本加持,ApiPost如何引领APIfirst时代作者徐结怀宋荣荣编辑张康随着数字化浪潮快速席卷各行各业,以及研发前后端分离协作成为主流,架起前后端数据流通桥梁的API,呈现出爆发式增长的态势。为了让开发设计测试迭代协同更加高效,跌至6499元,100倍变焦12GB512GB,体验不输华为Mate40Pro了解行情的都知道,在同等配置的情况下,华为手机的售价往往要比其他国产手机品牌更高,但是你会发现,它们的销量并不差,哪怕就是没有5G功能的华为Mate40Pro和P50Pro,销量依黑五大促借双十一预热,跨境电商用什么吸引消费者?对于国内消费者而言,购物节愈发常态化。为吸引消费者,跨境电商也逐步本土化。双11刚结束,黑五购物节又即将开始,各大跨境电商平台包括亚马逊海外购抖音电商京东国际敦煌网天猫国际Newe耳机不知怎么选?青花瓷国潮风的它你可以来看一下耳机怎么选?这确实是一个大大的问题,现如今市面上的耳机种类品牌数不胜数,但是坑坑也很多,几十元的吧是不敢用,几千元的吧买不起,最怕的就是几百块钱买来的耳机还不够理想,小编也是经过踩谷歌Pixel6a渲染图惊艳亮相,Pixel5沦为白菜机创悲惨新纪录此前谷歌Pixel6系列已经正式上市,其靓丽的外观已经得到不少消费者的认可。现在91mobiles已经曝光了该系列手机中中高端Pixel6a的表现,展示了这款手机的外观设计。与此同
如何评价预热中的联想小新Pro162022,是否值得等待?联想即将发布2022款的小新系列笔记本,旗舰型号为小新Pro16。今天联想开始预热了这款笔记本。该款笔记本屏幕采用了16英寸2。5k屏,支持120Hz高刷新率,5ms灰阶响应时间,苹果的AirPods你觉得怎么样?我从2009年就开始使用蓝牙耳机了。一旦你转向无线世界,就很难再回头了,电话和wifi就是很明显的两个例子。首先,我要说明为什么我更喜欢无线耳机而不是有线耳机。1。无线耳机从你的口如何购买机械键盘,本人心得与建议经过多年的键盘使用经验以及最近购买键盘的一些经验与条友进行分享,看到我的文章相信你买键盘就不在纠结了。一纠结是买薄膜键盘好还是机械键盘好?图1薄膜键盘的按键板1。1所谓的薄膜键盘,洗地机到底有用没?一文告诉你云米CyberPro高品质洗地机有多强大懒是科技创新的动力,也是科技发展追求的目标。尤其是近几年,人类不需要科幻电影式的想象就能享受到科技带给我们的便利,体现在智能家电上尤为明显,例如智能吸尘器和智能扫地机的出现大大提高告别续航翻车苹果关闭iOS15。4验证通道上月31号苹果推送了iOS15。4。1系统,现在苹果已经关闭了前一个版本iOS15。4的验证通道,iOS15。4。1发布仅一周,这就意味着iPhone用户不能再从iOS15。4。1音质优秀,运动自如!英国sanag真无线蓝牙降噪耳机体验对于喜欢运动的我来说,每次跑步都会带上耳机,在酣畅淋漓的运动中感受音乐的韵律。但是传统的耳机长长的耳机线,总是会缠绕在一起,非常麻烦。有时候我也会带蓝牙耳机,但跑步时候也经常掉落。宇宙的真实物理模型解析宇宙的真实物理模型解析文何启真宇宙的每次循环终于奇点亦始于奇点的宇宙大爆炸,奇点是吸收了全部可观测物质能量的超级黑洞,光速是宇宙的速度上限,在观测意义上光速恒定,在数字意义上光速可美国人耐心耗尽,贾跃亭的法拉第进入退市倒计时?去年11月,贾跃亭第一次收到了纳斯达克的退市警告,限期180天,让贾跃亭递交法拉第公司第三季度的财报。贾会计在国内做假财报,到了美国改掉了恶习,创造性发明了不交财报的新套路。今年4为什么人类一直要计算的值?1947年,数学家IvanM。Niven,用微积分和反证法证明了是无理数。既然已证明是无理数,为什么人类一直要计算的值?还有计算的必要吗?回答是肯定的。因为在计算的过程中第一,可以最新综述丨刘大锰教授中国煤层气储层地质与表征技术研究进展能源人都在看,点击右上角加关注本文创新点论文系统阐述了煤层气储层地质在储层孔裂隙结构渗透率跨尺度非均质性流体性质及动态评价等方面的研究进展。煤层气储层地质学研究正在从宏观向微观从定全天保持口腔清新,扉乐FiliX电动牙刷体验每天口腔保持清新,能让人一整天都神清气爽,尤其是在公众场合与人交流中,能给对方留下一个好的印象。而要想保持口腔清新,每天早晚刷牙不能少,毕竟如果对刷牙不重视,容易出现牙龈炎,导致口