唠唠闲话

日常的开发工作中,有时我们需要记录并展示终端操作的过程,无论是为了演示、教学还是文档编写。尽管有多种工具可以完成这项工作,termtosvg 是一个不仅能录制终端会话,还能以动态或静态的SVG文件格式展示的工具,使得分享和展示变得格外方便和直观。

项目地址:https://github.com/nbedos/termtosvg

将 SVG 动画转为 GIF 的过程可以使用 chrome-driver 来自动化浏览器操作,然后捕获动画的屏幕录像,再将其转换为 GIF。以下是一个基本的步骤指南:

安装所需软件

  1. 安装 Chrome 和 ChromeDriver
1
2
3
4
5
wget -O chromedriver.zip http://chromedriver.storage.googleapis.com/`curl -sS chromedriver.storage.googleapis.com/LATEST_RELEASE`/chromedriver_linux64.zip
unzip chromedriver.zip
sudo mv chromedriver /usr/local/bin/
# wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
# sudo gdebi google-chrome-stable_current_amd64.deb

chrome 需安装旧版:

https://vikyd.github.io/download-chromium-history-version/#/

  1. 安装 Python 和依赖库
    1
    pip install selenium pillow imageio

编写脚本

  1. 使用 Selenium 捕获 SVG 动画
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    from selenium import webdriver
    from selenium.webdriver.chrome.service import Service
    from selenium.webdriver.common.by import By
    from selenium.webdriver.chrome.options import Options
    import time
    from PIL import Image
    import imageio

    # 设置 ChromeDriver 路径
    chrome_driver_path = '/path/to/chromedriver'

    # 初始化 Chrome 选项
    chrome_options = Options()
    chrome_options.add_argument('--headless') # 启用无头模式
    chrome_options.add_argument('--disable-gpu')
    chrome_options.add_argument('--window-size=800,600')

    # 创建 Chrome 服务
    service = Service(chrome_driver_path)

    # 初始化 WebDriver
    driver = webdriver.Chrome(service=service, options=chrome_options)

    # 打开包含 SVG 动画的网页
    driver.get('file:///path/to/your/svg_animation.html')

    # 捕获一系列的屏幕截图
    screenshots = []
    for i in range(30): # 假设动画持续 30 帧
    time.sleep(0.1) # 调整捕获频率
    screenshot = driver.get_screenshot_as_png()
    image = Image.open(BytesIO(screenshot))
    screenshots.append(image)

    # 关闭 WebDriver
    driver.quit()

    # 将截图保存为 GIF
    screenshots[0].save('output.gif', save_all=True, append_images=screenshots[1:], duration=100, loop=0)

详细说明

  1. 设置 ChromeDriver 路径和选项

    • 确保 chrome_driver_path 指向正确的 ChromeDriver 可执行文件。
    • 使用 --headless 选项可以使浏览器在后台运行,而不会弹出窗口。
  2. 捕获 SVG 动画

    • 使用 driver.get() 打开包含 SVG 动画的网页。如果 SVG 动画嵌入在 HTML 文件中,可以使用 file:///path/to/your/svg_animation.html 来打开本地文件。
    • 使用 get_screenshot_as_png() 方法捕获当前屏幕的截图,并存储在一个列表中。
  3. 保存为 GIF

    • 使用 PIL 库将截图列表保存为 GIF。save_all=True 表示保存所有帧,append_images=screenshots[1:] 用于添加后续的帧,duration=100 设置每帧的显示时间(毫秒),loop=0 表示无限循环。

这样,你就可以将包含 SVG 动画的网页捕获并转换为 GIF。根据实际情况,可能需要调整捕获帧数和频率以获得理想的结果。

SVG

SVG 是可伸缩矢量图形(Scalable Vector Graphics)的缩写,它是一种基于 XML 语法的图像格式,用于描述二维矢量图形。与传统的点阵图像(如 JPEG 或 PNG)不同,SVG 图像使用数学表达式来描述形状和颜色等,不会因为放大而失真。

SVG 的特点主要包括:

  1. 矢量性: 图像是由直线、曲线和形状组成的,这意味着无论怎样缩放图像,都能保持边缘的清晰度,不会出现像素化。
  2. 可修改: 由于 SVG 文件是文本文件,可以使用文本编辑器来编辑它们。你可以很容易地修改图像的颜色、形状和大小。
  3. DOM 接口: SVG 图像是通过 DOM(文档对象模型)的接口来操作的,这使得你可以用 JavaScript 和 CSS 如同操作 HTML 元素一样来操作 SVG 元素。
  4. 兼容性: 大多数现代浏览器都支持 SVG 格式的显示和脚本操作,移动设备亦然。
  5. 互动和动画: SVG 支持在图像内嵌入互动事件处理器,例如鼠标点击、鼠标移动等,还可以使用 SVG 的 <animate> 元素来制作动画效果。
  6. 脚本化与样式化: 可以通过 JavaScript 进行脚本化操作,通过 CSS 进行样式设计,方便与Web页面整合。
  7. 可访问性: 文本格式允许描述图形内容,这对于搜索引擎优化(SEO)和可访问性辅助技术(如屏幕阅读器)来说是非常重要的。
  8. 性能: 对于包含大量形状或路径的复杂图形,SVG 文件的大小可能会比传统位图小很多,特别是在缩放时。
  9. 打印友好: 由于 SVG 是矢量图形,它在高分辨率打印设备上保持高质量。

SVG 特别适合于复杂图形(如地图)、图标、徽标、图表和一些基于线条绘制的艺术作品等。随着 Web 技术的不断发展,SVG 越来越多地用于网页设计中,以其灵活性和功能丰富性受到设计师和开发者的青睐。

TermToSVG

termtosvg 是一个基于 Python 命令行工具,可以捕捉终端会话,并将其转换成高质量的 SVG 动画。但该项目作者从 2020 年开始不再进行维护,仓库目前已归档。

示例

我们以 askchat 为例,演示常用的 termtosvg 参数用法,并对已有模板进行魔改,制作 Mac 终端的版本。

效果如下:

my_session_mac_frame

基本示例

1
termtosvg my_session.svg

录制当前的终端会话,并将其保存为 my_session.svg

设置动画循环之间的暂停时间

1
termtosvg my_session_delayed.svg -D 2000

录制当前会话,并设置动画循环之间的暂停时间为 2 秒:

my_session_delayed

指定终端的大小

默认占满屏幕宽度,效果不好,可通过 -g 参数指定终端的大小。

1
termtosvg my_session_80x24.svg -g 80x24

以 80 列 24 行的大小录制并渲染终端会话。

my_session_80x24

录制静态帧

1
termtosvg -s my_frames/

这将输出静态帧,而不是动画。录制结果将保存到 my_frames 目录中,每一帧都是一个独立的 SVG 文件。

使用模板

内置的模板列表:https://nbedos.github.io/termtosvg/pages/templates.html

1
termtosvg my_session_dracula.svg -t dracula

dracula 模板提供了一种暗色系的显示风格,使用该模板录制并渲染会话:

my_session_dracula

也可以尝试不同的模板,例如 solarized_dark

1
termtosvg my_session_solarized_dark.svg -t solarized_dark
my_session_solarized_dark

-t 后边也可以接自定义的模板文件路径。

更多例子

参看文档:

模板修改指南

在 termtosvg 中,模板是SVG文件,用于嵌入动画。使用模板可以实现以下功能:

  1. 自定义终端颜色主题和字体:用户可以通过模板中的 style 元素定义终端颜色和字体样式,使得动画更符合个人审美或项目需求。
  2. 添加终端用户界面或窗口框架:通过 SVG 模板,可以为动画添加类似实际终端窗口的视觉框架,增加真实感和美观性。
  3. 交互式动画:例如播放/暂停按钮,使用户能够控制动画的播放过程,提高用户体验。

模板结构

TermToSVG 模板结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<?xml version="1.0" encoding="utf-8"?>
<svg id="terminal" baseProfile="full" viewBox="0 0 656 325" width="656" version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<termtosvg:template_settings xmlns:termtosvg="https://github.com/nbedos/termtosvg">
<termtosvg:screen_geometry columns="82" rows="19"/>
<termtosvg:animation type="css"/>
</termtosvg:template_settings>
<style type="text/css" id="generated-style">
<!-- [snip!] -->
</style>
<style type="text/css" id="user-style">
<!-- [snip!] -->
</style>
</defs>
<svg id="screen" width="656" viewBox="0 0 656 323" preserveAspectRatio="xMidYMin meet">
<!-- [snip!] -->
</svg>
<script type="text/javascript" id="generated-js">
<!-- [snip!] -->
</script>
</svg>

模板结构和各部分的作用:

  • svg 元素(ID “terminal”):这是 SVG 文件的最外层容器
  • defs 元素:这是一个定义容器,用来存储 SVG 中使用的定义,如样式和脚本等。包括:
    • termtosvg:template_settings 元素:termtosvg 特有的设置,用于定义终端的尺寸(列和行数)和动画的渲染方法("css"代表使用CSS动画,"waapi"代表使用Web Animations API)。这些设置告诉 termtosvg 如何渲染终端动画。
    • style 元素(ID “generated-style”):这里的样式将被 termtosvg 在生成动画时自动覆盖,通常用于动画效果的实现。
    • 另一个style 元素(ID “user-style”):这里的样式由模板创建者定义,通常包含终端的颜色主题,不会被termtosvg覆盖,允许用户自定义终端的视觉样式。
  • 内部svg 元素(ID “screen”):这个元素是动画实际展示的区域,保持动画内容的适当比例和定位。
  • script 元素(ID “generated-js”):这里的脚本将在使用"waapi"渲染方法时由 termtosvg 覆盖,通常包含动画的控制逻辑,如播放和暂停功能。

模板定制

模板定制的基本思想是,termtosvg会保留它未修改的模板元素。因此,您可以:

  • 通过修改带有id "user-style"的style元素的内容来自定义动画样式:可以自定义动画的颜色主题、字体等样式,以符合个人或项目的视觉需求。
  • 添加新的script元素以在动画中嵌入JavaScript代码:可以通过添加JavaScript代码增加动画的交互性或实现特定功能。
  • 添加其他SVG元素,只要它们不是带有id "screen"的svg元素的子元素:可以自由地添加额外的视觉元素或装饰,只要它们不干扰动画的主要显示区域。

希望这些信息以及默认模板的代码足以帮助您开始进行模板定制。如果需要帮助,随时提交问题

自定义颜色主题或字体

终端颜色主题在带有 id “user-style” 的 style 元素中指定,并且必须定义以下所有类:foreground, background, color0, color1, …, color15。字体相关属性也可以与颜色主题一起指定。下面是一个示例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="terminal" baseProfile="full" viewBox="0 0 656 325" width="656" version="1.1">
<defs>
/* ... 省略 ... */
<style type="text/css" id="generated-style">
/* ... 省略 ... */
</style>
<style type="text/css" id="user-style">
.foreground {fill: #c5c5c5;}
.background {fill: #1c1c1c;}
.color0 {fill: #1c1c1c;}
.color1 {fill: #ff005b;}
/* ... 省略 ... */
.color15 {fill: #e5e5e5;}

font-family: Monaco, monospace;
</style>
</defs>
<svg id="screen" width="656" viewBox="0 0 656 323" preserveAspectRatio="xMidYMin meet">
</svg>
</svg>

自定义终端用户界面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
<svg id="terminal" baseProfile="full" viewBox="0 0 656 325" width="656" version="1.1"
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
/* ... 省略 ... */
</defs>
<rect id="terminalui" class="background" width="100%" height="100%" ry="4.5826941"/>
<circle cx="24" cy="23" r="7" class="color1"/>
<circle cx="44" cy="23" r="7" class="color3"/>
<circle cx="64" cy="23" r="7" class="color2"/>
<svg id="screen" width="656" viewBox="0 0 656 323" preserveAspectRatio="xMidYMin meet">
<!-- [snip!] -->
</svg>
<script type="text/javascript" id="generated-js">
<!-- [snip!] -->
</script>
</svg>

完整示例:window_frame.svg

CSS进度条

完整示例:colors_progress_bar.svg

嵌入JavaScript

在新的script元素中添加代码。

完整示例:window_frame_js

将动画限制为单次循环

对于使用CSS的模板,简单地添加一个自定义样式元素,指定循环次数,如下所示:

1
2
3
4
5
<style type="text/css" id="user-style">
#screen_view {
animation-iteration-count:1;
}
</style>

完整示例在此:gjm8_single_loop

自定义模板

我们基于 window_frame.svg 进行魔改。原始模板的图标配色看着不适应,且边框棱角明显,我们将其修改为 Mac 风格,修改后的模板下载地址:mac_frame

效果对比

原始效果:

1
termtosvg my_session_with_frame.svg -t window_frame
my_session_with_frame
1
2
3
4
# 下载模板
wget -c https://qiniu.wzhecnu.cn/PicBed6/picgo/mac_frame.svg
# 使用模板
termtosvg my_session_mac.svg -t ./mac_frame.svg -g 80x24

显示效果:

my_session_mac_frame

修改内容

主要修改了底部对图标样式的定义,将原始的 class 属性改为 style 属性,并修改了 rxry 属性,使其更加圆润。

原始配置:

1
2
3
4
<rect id="terminalui" class="background" width="100%" height="100%" ry="4.5826941"/>
<circle cx="24" cy="23" r="7" class="color1"/>
<circle cx="44" cy="23" r="7" class="color3"/>
<circle cx="64" cy="23" r="7" class="color2"/>

修改后:

1
2
3
4
<rect id="terminalui" class="background" width="100%" height="100%" rx="10" ry="10"/>
<circle cx="24" cy="23" r="7" style="fill: #ff5f56"/>
<circle cx="44" cy="23" r="7" style="fill: #ffbd2e"/>
<circle cx="64" cy="23" r="7" style="fill: #27c93f"/>

设置快捷方式

通过 alias 配置默认启动参数,举个例子:

1
2
3
4
5
# 先保存模板
mkdir -p ~/.config/termtosvg
wget https://qiniu.wzhecnu.cn/PicBed6/picgo/mac_frame.svg -P ~/.config/termtosvg/
# 设置快捷命令
alias termrec='termtosvg -t $HOME/.config/termtosvg/mac_frame.svg -g 80x24'

需要时运行 termrec my_session_mac.svg 即可。

SVG 转 GIF

有时候,我们可能希望将 SVG 动画转换为更通用的 GIF 格式,以便在更多平台上展示(比如 CSDN)。这可以通过借助图像处理工具 ImageMagick 来实现。以下是详细步骤:

安装 ImageMagick

(实测存在问题,TODO)

ImageMagick 是一款强大的图像处理工具,支持多种格式的转换,包括 SVG 到 GIF。

  • 在 Ubuntu 上,通过以下命令安装:

    1
    sudo apt-get install imagemagick
  • 在 Mac上,使用 Homebrew 进行安装:

    1
    brew install imagemagick

使用 ImageMagick 转换 SVG 为 GIF

ImageMagick 安装完成后,可以使用 convert 命令将 SVG 文件转换为 GIF。

1
convert -delay 20 -loop 0 my_animation.svg my_animation.gif

其中 -delay 20 控制每帧之间的时间间隔(单位是1/100秒),-loop 0 指定 GIF 动画循环播放的次数(0表示无限循环)。

注意事项

  • 转换质量:SVG动画的转换质量可能受多种因素影响,包括SVG的复杂度、转换参数等。可能需要调整 convert 命令的参数来优化输出的 GIF。

  • 性能问题:如果SVG动画过于复杂,转换为GIF时可能会出现性能问题。可以考虑简化 SVG 内容或优化转换参数以提升性能。


以上,这些方法可以帮助你轻松将终端内容录制为 SVG 动画,或者更通用的 GIF 格式,便于在更多平台上展示。