电子竞技

对电子竞技基本没有了解,不过偶尔玩玩游戏,经常看看直播。

电子竞技和普通的体育竞技有点不同,体育竞技,在一段时间内——特别是赛季进行中,规则和竞技内容都是比较固定不变的。但电子竞技不同,先不弹游戏的变更,只说同一个游戏。

一个电子游戏的竞技,在赛季中,规则还好,胜负的条件基本不会变更。但内容就不一定了,电子游戏会经常迭代更新,甚至赛季中就更新版本。

电子游戏的竞争,看上去就是一场“战争”,所以需要知己知彼。

需要了解游戏的内容,磨练自己的战术体系,探测对手的战术体系。而游戏的迭代更新,容易造就之前搭建的战术体系随着版本变更而不再适用,如果没能迅速在新版本中重新构建自己的战术,那么就很容易出现一支队伍之前战无不胜突然在新版本中就无所适从的状况出现。

虽然自己技术过硬才是重点,但涉及到对抗,那就必须要了解对手,了解他们的英雄体系以及基于这些因素而展现出来的战术体系。越是风光,越被别的竞争对手认真研究。相反,站在顶峰的人,容易只针对自己认为的对手来下功夫,而别的队伍则忽略了。这就容易看到所谓的“黑马”,如果黑马不仅仅是战术新颖,还用有了过硬的技术,那么就会变为太阳而不再是流星。

现代的战争是全方位的战争,成型的电子竞技也是全方位的竞争。选手需有练英雄练技术跟进版本变更,教练等需要基于选手技术以及版本来构建阵容和战术体系,后勤团队需要做好包括队员生理保养、心理调整等的俱乐部后勤支持,还有其它团队需要做好盈利问题。

一代版本一代神,每支队伍在输之前都是最强的组合。

基于ANSI转义序列来构建命令行工具

命令行工具在输出时,如果是简单的进度更新,可以使用 \r\b 来达成刷新行的效果,但如果要更复杂些的如字体颜色、背景颜色、光标位置移动等功能,那就需要使用 ANSI 转移序列了。

已有不少成熟的命令行工具库,如 ReadlineJLinePython Prompt Toolkit,基于这些库创造了如 mycliipython 等好用的工具。

ANSI 转义序列有比较悠久的历史,不同平台支持的功能不完全一致,这里学习到的是比较简单常用的,包括字体颜色、背景色和其它装饰的富文本和光标操作。

\u001b 即 ESC 的 ASCII 码,\u001b[0m 是清除之前的设定。

适用于 *nix 的系统。

富文本

前景色

8 色

\u001b[?m,其中 ? ∈ [30, 37]

  • 黑(black):\u001b[30m
  • 红(red):\u001b[31m
  • 绿(green):\u001b[32m
  • 黄(yellow):\u001b[33m
  • 蓝(blue):\u001b[34m
  • 品红(magenta):\u001b[35m
  • 蓝绿(cyan):\u001b[36m
  • 白(white):\u001b[37m
  • 重置(reset) :\u001b[0m

16 色

在 8 色的基础上对字体加粗,颜色加亮,得到另外 8 种,加起来就是 16 色。

\u001b[?;1m,其中 ? ∈ [30, 37]

  • 亮黑(black):\u001b[30;1m
  • 亮红(red):\u001b[31;1m
  • 亮绿(green):\u001b[32;1m
  • 亮黄(yellow):\u001b[33;1m
  • 亮蓝(blue):\u001b[34;1m
  • 亮品红(magenta):\u001b[35;1m
  • 亮蓝绿(cyan):\u001b[36;1m
  • 亮白(white):\u001b[37;1m
const out = process.stdout;

function colors8(pre, post, startCode = 30) {
  const codePointA = 'A'.codePointAt(0);

  let i = 0;
  while (i < 8) {
    const colorCode = startCode + i;
    const char = String.fromCodePoint(codePointA + i);
    out.write(`${pre}${colorCode}${post}${char} `);
    i++;
  }
  console.log('\u001b[0m');
}

function fgColors8() {
  colors8('\u001b[', 'm');
}

function fgColors8Bright() {
  colors8('\u001b[', ';1m');
}

fgColors8();
fgColors8Bright();

继续阅读

浏览器下载 PDF 文件

用浏览器访问一个指向 PDF 文件的链接,具体表现出来的行为会有所区别,最主要的区别就是在浏览器中打开(记作预览)或触发下载(记作下载)。

这些行为与请求响应结果中的 Content-Type 以及 Content-Disposition 这两个 header 有关。

Content-Typeapplication/pdfapplication/octet-stream 以及 application/custom-unknown 和无 Content-Disposition、取 inline 以及 attachment 来做组合实验,得到结果如下:

header Chrome Firefox Safari
Content-Type: application/pdf 预览 预览 预览
Content-Type: application/pdf
Content-Disposition: inline
预览 预览 预览
Content-Type: application/pdf
Content-Disposition: attachment
下载 下载 下载
Content-Type: application/octet-stream 下载 下载 下载
Content-Type: application/octet-stream
Content-Disposition: inline
下载 下载 下载
Content-Type: application/octet-stream
Content-Disposition: attachment
下载 下载 下载
Content-Type: application/custom-unknown 下载 下载 下载
Content-Type: application/custom-unknown
Content-Disposition: inline
下载 下载 下载
Content-Type: application/custom-unknown
Content-Disposition: attachment
下载 下载 下载

Content-Typeapplication/octet-stream 表示浏览器应当把这个文件作为二进制数据来对待。但实际上浏览器可能会自己根据 Content-Disposition 甚至文件内容来做些文件格式探测,从而使用浏览器内置的功能或插件功能来处理。上面用的 application/custom-unknown 就是特意设计一个不是通用的 type 来避免已有场景。

下载时,如果没通过 Content-Disposition 指定文件名(如 Content-Disposition:inline;filename=attach.pdf),那么下载后的文件的名称通过 url 来获取,如 /download.php 得到的是 download.php

Firefox 在触发下载时,如果 Content-Dispositioninline,那么会静默下载,而不会像 attachment 或无该 header 时那样弹出浏览器的保存弹框。

Safari 下载后,如果是 pdf 文件(通过 Content-TypeContent-Disposition 或其它方式识别出来),那么会自动打开该文件。

此处使用 pdf 来作为示例,对于其它类型的文件,浏览器和系统的规则推测应类似。

  • 操作系统:macOS Mojave 10.14.2
  • Chrome: 72.0.3626.121(正式版本) (64 位)
  • Firefox: 65.0.1 (64 位)
  • Safari: 12.0.2 (14606.3.4)

参考

包含块

一个元素的占用面积大小以及布局定位通常受到包含块(Containing Block)的影响,比如 topleft 取百分比时相对的长度是什么长度,widthheight 取百分比时相对的长度是什么长度?
包含块一般情况下是当前元素的最近的祖先块元素1内容区域,但具体还需要分情况来确定。

布局区域

浏览器渲染文档时,对于每一个元素,都会有一个布局盒子,该盒子又被划分为四个区域。

  • 内容区域——Content Area
  • 内边距区域——Padding Area
  • 边框区域——Border Area
  • 外边距区域——Margin Area

box-sizing 设为 border-box,会影响 width 和 height 的计算,但不影响布局区域的划分。

继续阅读

视窗百分比长度

视窗百分比长度是指相对于视窗1的尺寸,包含了 vwvhvminvmax

  • vw:视窗宽度的 1%
  • vh:视窗高度的 1%
  • vminmin(vw, vh)
  • vmaxmax(vh, vh)

其中,视窗一般是指浏览器文档渲染窗口,但对于 iframe 里的文档而言,它们的视窗是 iframe 元素的 内容区域(content area) 的大小。

参考资料

NBSP

&nbsp; ,全称是 non-breaking space,除了表示空白,还含有告诉渲染引擎不要在此换行的意义。

可用于空间足够时和别的文本在同一行内展示,空间不够时,整串字符串(如 0800 3000 9974 这样的通过空白符来让电话号码等更易读的情况)不会在在空白符处换行,而是整串换行(需要把空白符替换为 &nbsp;)。当然,也可以通过设置这个串为不折行来解决:white-space: nowrap;

此为示例代码

参考

React Context

Context 的目的是对于全局的需要多处使用的数据,不希望通过 prop 一层层传递下去,而是可以直接使用。

childContextTypes & getChildContext

该方案在 16.3 后不再是推荐方案。

React 最初提供的 context 方案是提供 context 数据的组件通过静态的 childContextType 来定义 context 中各字段的数据类型,通过 getChildContext 成员方法来提供实际的 context 数据,其中,返回的数据可以搭配 prop 和 state 来提供响应式的数据。而消费 context 数据的组件通过静态的 contextTypes 来指定需要消费的 context 字段的类型集合,然后即可通过 this.context[fieldName] 来使用。

注意,消费 context 的组件只需要定义需要的字段的类型即可,不需要是全部。同时,也不需要是提供 context 的组件的直接子组件。

import PropTypes from 'prop-types';

class Button extends React.Component {
  static contextTypes = {
    color: PropTypes.string
  };

  render() {
    return (
      <button style={{background: this.context.color}}>
        {this.props.children}
      </button>
    );
  }
}

class Message extends React.Component {
  render() {
    return (
      <div>
        {this.props.text} <Button>Delete</Button>
      </div>
    );
  }
}

class MessageList extends React.Component {
  static childContextTypes = {
    color: PropTypes.string
  };

  getChildContext() {
    return {color: "purple"};
  }

  render() {
    const children = this.props.messages.map((message) =>
      <Message text={message.text} />
    );
    return <div>{children}</div>;
  }
}

继续阅读

青春猪头少年不会梦到兔女郎学姐

青春猪头少年不会梦到兔女郎学姐

这部作品是 18 年比较喜欢的一部作品,故事情节以及表达方式上都是容易理解和接受的。渴望被关注、被爱、被认同、融入群体、有三五知己、开始需要直面社会背面的恶,由此而带来的属于青春的烦恼和情感。

由于是小说改编,也还不是完全改编——不知道是否是想留做第二季,所以 TV 版情节上有些不完整,比如比较关键的翔子的故事没能展示出来。但依然是一个好作品,相比之下,《我喜欢的妹妹但不是妹妹》的故事则完全没有吸引力。