从 0 到 1 开发一个 node 命令行工具
G2 5.0 推出了服务端渲染的能力,为了让开发者更快捷得使用这部分能力,最写了一个 node 命令行工具 g2-ssr-node:用于把 G2 的 spec 转换成 png、jpeg 或者 pdf 等。基本的使用如下:
$ g2-ssr-node g2png -i ./bar.json -o ./bar.png
其中 bar.json是一个如下绘制条形图的 G2 spec:
{"type": "interval","data": [{ "genre": "Sports", "sold": 275 },{ "genre": "Strategy", "sold": 115 },{ "genre": "Action", "sold": 120 },{ "genre": "Shooter", "sold": 350 },{ "genre": "Other", "sold": 150 }],"encode": {"x": "genre","y": "sold"}
}
最后得到如下的图片 bar.png:

那接下来就来看看如何从 0 到 1 实现 g2-ssr-node。
初始化环境
首先新建一个文件夹并且用 npm 初始化项目:
$ mkdir g2-ssr-node && cd g2-ssr-node && npm init -y
在得到的 package.json 中增加一个 bin 字段,该字段指向了包里面的可执行文件,也就是最后运行 $ g2-ssr-node 时候执行的文件。
{"bin": "bin/g2-ssr-node.js",
}
接下来新增 bin 目录、新增 g2-ssr-node.js,并且输入以下的内容:
#!/usr/bin/env nodeconsole.log('hello world!')
其中第一行说明用 node 来执行 g2-ssr-node.js 文件,类似于当运行 $ g2-ssr-node的时候调用 $ node g2-ssr-node.js。
这之后在项目根目录下运行 npm link 会把当前包安装到全局。如果打开控制台输入 g2-ssr-node能打印出 hello world!, 那么说明开发环境已经搭建好了,可以进一步写代码了。
安装 commander
首先安装 commnder 工具包来简化开发命令行的成本,比如帮助解析参数、展现使用错误和实现提示信息等。
npm i commander -D
本文实现的 g2-ssr-node 有一个子命令:g2png,将 G2 的 spec 转换成图片。该子命令有两个参数:
- -i, --input <filename>: 指定包含需要转换的 spec 的文件地址
- -o, --output <filename>: 指定输出图表的文件地址
修改 bin/g2-ssr-node.js 如下用于满足上述需求:
#!/usr/bin/env node
const { Command } = require("commander");
const process = require("node:process");
const { version } = require("../package");
const { g2png } = require("./g2png.js");const program = new Command();program.name("g2-ssr-node").description("CLI for ssr of @antv/g2").version(version);program.command("g2png") // 添加子命令.description("Convert a G2 spec to an PNG image") // 添加对子命令的描述.option("-i, --input <filename>", "filename for the input spec") // 声明参数.option("-o, --output <filename>", "filename for the output image") // 声明参数.action((options) => g2png(options).then(() => process.exit())); // 真正的执行函数program.parse(); // 解析
然后新增 bin/g2png.js这个文件,并且导出g2png(options)这个函数,用于根据指定的配置渲染图片。
// bin/g2png.jsfunction g2png(options) {console.log(options)
}module.exports = { g2png };
如果一切顺利的话,运行如下测试命令:
$ g2-ssr-node g2png -i ./bar.json -o ./bar.png
会在控制台输出:{ input: './bar.json', output: './bar.png' }。
当然也可以运行 g2-ssr-node g2png --help看看控制台输出的帮助信息是否符合预期:
Usage: g2-ssr-node g2png [options]Convert a G2 spec to an PNG imageOptions:-i, --input <filename> filename for the input spec-o, --output <filename> filename for the output image-h, --help display help for command
如果没有问题的话,就可以进入下一步,实现 g2png 函数。
实现 g2png 函数
g2png 主要包含三个步骤:
- 根据 input 地址读取 JSON 内容,解析成 JavaScript 对象,得到需要渲染的 spec。
- 将得到的 spec 通过 renderImage 函数渲染成 node canvas 中的 canvas 对象。
- 将 canvas 对象转换成 png 流并且写入 output 地址。
首先我们安装 node canvas。 node canvas 是一个实现了 canvas 标准的 node 库,主要用于做 canvas 的服务端渲染,更多的 API 参考其文档。
$ npm i canvas -D
修改 bin/g2png.js代码如下:
const fs = require("fs");
const { renderImage } = require("./renderImage.js");function readJSONSync(input) {const data = fs.readFileSync(input, "utf-8");return JSON.parse(data);
}async function g2png({ input, output }) {console.log(`Start converting ${input} to ${output} ...`);// 读取并且转化 Specconst spec = await readJSONSync(input);// 将 Spec 渲染成 node canvas 中的 canvasconst canvas = await renderImage(spec);// 将 canvas 转化成 png 流并且写入 output 地址const out = fs.createWriteStream(output);const stream = canvas.createPNGStream();stream.pipe(out);return new Promise((resolve, reject) => {out.on("finish", () => {console.log(`Convert ${input} to ${output} successfully.`);resolve();}).on("error", () => reject());});
}module.exports = { g2png };
那么接下来我们来看看 renderImage 函数的实现。
实现 renderImage 函数
首先新建 bin/renderImage.js文件,并且输入如下的代码。这段代码将一个 node-canvas 创建的 canvas 对象传给了 G,用于创建一个画布,然后 G2 将 spec 渲染到这个画布上,并且将 canvas 返回。
// bin/renderImage.js
const { createCanvas } = require("canvas");
const { stdlib, render: renderChart } = require("../dist/g2.js");
const { Canvas } = require("../dist/g");
const { Renderer } = require("../dist/g-canvas");async function renderImage(options) {// 创建 canvasconst { width = 640, height = 480, ...rest } = options;const [gCanvas, canvas] = createGCanvas(width, height);// 根据 spec 和 context 渲染图表到 canvas 上const spec = { ...rest, width, height };const context = {canvas: gCanvas,library: stdlib,createCanvas: () => createCanvas(300, 150),};await new Promise((resolve) => renderChart(spec, context, resolve));return canvas;
}function createGCanvas(width, height, type) {const canvas = createCanvas(width, height, type);const offscreenCanvas = createCanvas(1, 1);const renderer = new Renderer();// 移除一些和 DOM 相关的交互const htmlRendererPlugin = renderer.getPlugin("html-renderer");const domInteractionPlugin = renderer.getPlugin("dom-interaction");renderer.unregisterPlugin(htmlRendererPlugin);renderer.unregisterPlugin(domInteractionPlugin);return [new Canvas({width,height,canvas,renderer,offscreenCanvas,devicePixelRatio: 2, // 解决高分辨率屏不清晰的问题}),canvas,];
}module.exports = { renderImage };
这里大家可能注意到了在上面代码中并没有直接从 G2 或者 G 中导入我们需要的函数,那么接下来就来看看为什么。
// renderImage.js
// 从 dist 导出
const { stdlib, render: renderChart } = require("../dist/g2.js");
const { Canvas } = require("../dist/g");
const { Renderer } = require("../dist/g-canvas");
打包 commonjs
JavaScript 常见的模块系统有两种 ESM 和 commonjs,而低版本的 Node 只支持 commonjs。虽然 G2 和 G 都提供了 commonjs 的版本,但是它们的依赖却不一定,比如 D3.js 只提供了 ESM 版本。为了让 g2-ssr-node 能在低版本的 Node 中运行,需要把 G2 和 G 以及它们的依赖都打包成 commonjs。
首先分别创建以下三个文件,并且输入以下的内容:
// @antv/g2.js
export * from '@antv/g2';
// @antv/g.js
export * from '@antv/g';
// @antv/g-canvas.js
export * from '@antv/g-canvas'
然后使用 Rollup 和它的一系列插件来将上述的文件打包成 commonjs 模块:
npm i rollup @rollup/plugin-commonjs @rollup/plugin-json @rollup/plugin-node-resolve @rollup/plugin-terser -D
配置文件如下:
// rollup.config.js
const resolve = require("@rollup/plugin-node-resolve");
const cjs = require("@rollup/plugin-commonjs");
const json = require("@rollup/plugin-json");
const terser = require("@rollup/plugin-terser");const common = {plugins: [resolve(), cjs(), json(), terser()],external: ["@antv/g"],
};module.exports = [{input: "antv/g2.js",output: {file: "dist/g2.js",format: "cjs", // 打包成 commonjs},...common,},{input: "antv/g.js",output: {file: "dist/g2.js",format: "cjs", // 打包成 commonjs},...common,},{input: "antv/g-canvas.js",output: {file: "dist/g2-canvas.js",format: "cjs", // 打包成 commonjs},...common,},
];
然后在控制台输入 npx rollup -c就会发现已经多了一个 dist 文件夹,这之后就可以验证是是否成功了。
验证是否成功
第一步,在项目根目录下创建一个 bar.json文件,并且输入以下内容:
{"type": "interval","data": [{ "genre": "Sports", "sold": 275 },{ "genre": "Strategy", "sold": 115 },{ "genre": "Action", "sold": 120 },{ "genre": "Shooter", "sold": 350 },{ "genre": "Other", "sold": 150 }],"encode": { "x": "genre", "y": "sold" },"viewStyle": { "plotFill": "white" }
}
接下来运行:
$ g2-ssr-node g2png -i ./bar.json -o ./bar.png
如果没有问题的话,就会在项目的根目录出现一张名叫 bar.png的图片。

小结
到这里简单版本的 g2-ssr-node 就已经开发完成了,发包的过程就不在这里赘述了。最后大体的代码结构如下:
- antv- g2.js- g.js- g-canvas.js
- dist- g2.js- g.js- g-canvas.js
- bin- g2-ssr-node.js- g2png.js- renderImage.js
- rollup.config.js
- bar.json
- package.json
除了更多的能力之外(比如将 G2 spec 转换成 jpeg 和 pdf,以及通过 API 的形式调用),还需要考虑 Test、Lint、CI 和文档一些相关的问题。完整的代码、能力和文档可以在 g2-ssr-node 查看。当然感兴趣的小伙伴也可以提 PR,去实现将 G2 spec 转换成 SVG 命令,参考 SVG Output。
最后,g2-ssr-node 算是 G2 5.0 生态中的新成员,而目前 G2 5.0 在收集相关的一些生态,当然也可以在这里提出一些想法,组团来实现。期望大家一起,让 G2 变得更好,让数据可视化社区活跃起来!
相关文章:
从 0 到 1 开发一个 node 命令行工具
G2 5.0 推出了服务端渲染的能力,为了让开发者更快捷得使用这部分能力,最写了一个 node 命令行工具 g2-ssr-node:用于把 G2 的 spec 转换成 png、jpeg 或者 pdf 等。基本的使用如下: $ g2-ssr-node g2png -i ./bar.json -o ./bar.…...
VsCode中使用功能vite创建vue3+js项目报错
VsCode中使用功能vite创建vue3js项目报错 VsCode中使用功能vite创建vue3js项目import模块报错如下处理方法 VsCode中使用功能vite创建vue3js项目import模块报错如下 处理方法 在项目根目录新建jsconfig.json {"compilerOptions": {"baseUrl": "./&q…...
COGVLM论文解读(COGVLM:VISUAL EXPERT FOR LARGE LANGUAGE MODELS)
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、摘要二、引言三、模型方法1、模型思路2、融合公式 四、训练方法总结 前言 2023年5月18日清华&智谱AI发布并开源VisualGLM-6B以来,清华KEG&…...
Flink-时间流与水印
时间流与水印 一、背景二、时间语义1.事件时间(event time)2.读取时间(ingestion time)3.处理时间(processing time) 三、水印-Watermarks1.延迟和正确性2.延迟事件3.顺序流4.无序流5.并行流 四、Windows1.…...
BiLSTM-CRF的中文命名实体识别
项目地址:NLP-Application-and-Practice/11_BiLSTM-ner-bilstm-crf/11.3-BiLSTM-CRF的中文命名实体识别/ner_bilstm_crf at master zz-zik/NLP-Application-and-Practice (github.com) 读取renmindata.pkl文件 read_file_pkl.py # encoding:utf-8import pickle# …...
paddle detection 训练参数
#####################################基础配置##################################### # 检测算法使用YOLOv3,backbone使用MobileNet_v1,数据集使用roadsign_voc的配置文件模板,本配置文件默认使用单卡,单卡的batch_size=1 # 检测模型的名称 architecture: YOLOv3 # 根据…...
用bat制作图片马——一句话木马
效果图 代码 ECHO OFF TITLE PtoR MODE con COLS55 LINES25 color 0A:main cls echo.当前时间:%date% %time% echo.欢迎使用图片马制作工具 echo.请确保图片和php在同一路径下 echo.echo 请将图像文件拖放到此窗口并按 Enter: set /p "imagefile&q…...
json_encode() 返回 false
当 json_encode() 返回 false 时,表示 JSON 编码过程失败。这通常是因为要编码的数据包含了无效的 UTF-8 字符,而默认情况下 json_encode() 会对无效的 UTF-8 字符进行严格的处理 通过添加 JSON_INVALID_UTF8_IGNORE 选项,你告诉 json_encod…...
Android-Jetpack--Hilt详解
善学者尽其理,善行者究其难 一,定义 Hilt是针对dagger2的二次封装依赖注入框架,至于什么是依赖注入,在Android开源框架--Dagger2详解-CSDN博客 中已经讲解,建议大家先去了解Dagger2之后,再来看Hilt。这样就…...
Docker 下载加速
文章目录 方式1:使用 网易数帆容器镜像仓库进行下载。方式2:配置阿里云加速。方式3:方式4:结尾注意 Docker下载加速的原理是,在拉取镜像时使用一个国内的镜像站点,该站点已经缓存了各个版本的官方 Docker 镜…...
1091 Acute Stroke (三维搜索)
题目可能看起来很难的样子,但是看懂了其实挺简单的。(众所周知,pat考察英文水平) 题目意思大概是:给你一个L*M*N的01长方体,求全为1的连通块的总体积大小。(连通块体积大于T才计算在内…...
java elasticsearch 桶聚合(bucket)
Elasticsearch指标聚合,就是类似SQL的统计函数,指标聚合可以单独使用,也可以跟桶聚合一起使用,下面介绍Java Elasticsearch指标聚合的写法。 实例: // 首先创建RestClient,后续章节通过RestClient对象进行…...
【人生苦短,我学 Python】(4)Python 常用内置数据类型 II —— 序列数据类型(str、tuple、list、bytes和bytearray)
目录 简述 / 前言1. str 数据类型(字符串)1.1 str对象1.2 str对象属性和方法1.3 字符串编码1.4 转义字符1.5 字符串的格式化 2. tuple 数据类型(元组)2.1 创建元组对象 3. list 数据类型(列表)3.1 创建列表…...
Android 9.0 系统默认显示电量百分比
Android 9.0 系统默认显示电量百分比 近来收到项目需求需要设备默认显示电量百分比,具体修改参照如下: /frameworks/base/packages/SystemUI/src/com/android/systemui/BatteryMeterView.java private void updateShowPercent() {final boolean showin…...
原神:夏洛蒂是否值得培养?全队瞬抬治疗量不输五星,但缺点也很明显
作为四星冰系治疗角色,夏洛蒂的实战表现可以说相当让人惊喜。不仅有相当有意思的普攻动作以及技能特效,而且她还有治疗和挂冰等功能性。下面就来详细聊聊夏洛蒂是否值得培养。 【治疗量让人惊喜,但也有缺点】 说实话,在使用夏洛蒂…...
Sublime text 添加到鼠标右键菜单,脚本实现
Sublime text 添加到鼠标右键菜单 Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\*\shell\SublimeText] "Open with Sublime Text" "Icon""D:\\Program Files\\Sublime Text\\sublime_text.exe,0" [HKEY_CLASSES_ROOT\*\shell\Subl…...
【算法】离散化 与 哈希 之间的区别
离散化(Discretization)和哈希(Hashing)是两种不同的数据处理技术,用于处理不同类型的问题。 1. 离散化(Discretization): 离散化是将一组连续的数据映射到有限个离散值的过程。主要…...
Android : GPS定位 获取当前位置—简单应用
示例图: MainActivity.java package com.example.mygpsapp;import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat;import android.Manif…...
目标检测——R-CNN算法解读
论文:Rich feature hierarchies for accurate object detection and semantic segmentation 作者:Ross Girshick, Jeff Donahue, Trevor Darrell, Jitendra Malik 链接:https://arxiv.org/abs/1311.2524 代码:http://www.cs.berke…...
基于傅里叶变换的运动模糊图像恢复算法matlab仿真
目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1、傅里叶变换与图像恢复 4.2、基于傅里叶变换的运动模糊图像恢复算法原理 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 %获取角度 img…...
2026年一键生成论文工具对比实测:5款神器从选题到格式全流程护航
写论文的焦虑,是每个科研人和学生都心照不宣的“隐形压力”。选题无从下手,文献检索耗时费力,逻辑框架反复推翻,格式排版让人抓狂,查重降重更是像在和系统玩“猫鼠游戏”。2026年的AI工具早已不是过去那种“打字机”&a…...
GIS工程应用记录(AI辅助编程)
问题的问题:语境坍缩“从各个角度提出问题,AI做出对应积极答复和修改,结果没有什么变化。”这,就是元问题最核心的症状。你尝试了所有你已知的“高级”协作手段,但就像重拳打在棉花上,AI永远在积极回应&…...
Vue2-Verify:解决前端验证码安全性与用户体验平衡问题的技术方案实现
Vue2-Verify:解决前端验证码安全性与用户体验平衡问题的技术方案实现 【免费下载链接】vue2-verify vue的验证码插件 项目地址: https://gitcode.com/gh_mirrors/vu/vue2-verify 在当今Web应用开发中,验证码作为防止自动化攻击的关键安全组件&…...
在线文档协作工具选型必看:14款产品对比(2026版)
一、在线文档协作工具的概念解析及其核心功能 在线文档协作工具是基于云端的文档创建、编辑、共享与协同沟通平台,核心目标是让团队在同一份资料上“实时共同工作”,减少反复传文件、版本混乱与沟通成本。 企业常见的核心能力包括: 多人实…...
Noto字体终极指南:告别“豆腐块“,让全球文字清晰显示
Noto字体终极指南:告别"豆腐块",让全球文字清晰显示 【免费下载链接】noto-fonts Noto fonts, except for CJK and emoji 项目地址: https://gitcode.com/gh_mirrors/no/noto-fonts 在数字世界中,你是否经常看到那些令人困…...
Arduino土壤湿度监测仪制作:从传感器原理到自动灌溉实现
1. 项目概述:用Arduino Uno和LCD屏打造你的土壤湿度监测仪作为一个喜欢在阳台种点番茄、辣椒的业余园丁,我经常为浇水这事儿头疼。浇多了怕烂根,浇少了又怕旱着,光靠手指插土里感觉,实在是不准。后来玩上了Arduino&…...
基于ISDN信令的来电语音播报系统:从原理到树莓派实现
1. 项目概述:一个基于ISDN的来电语音播报系统如果你家里或办公室里还有一台老式的ISDN路由器,别急着把它当电子垃圾处理掉。我最近就利用手头一台闲置的ISDN路由器,折腾出了一个挺有意思的小玩意儿:一个能自动识别来电号码&#x…...
自然语言处理的实战项目:从0到1搭建属于自己的文本分类系统
对于软件测试从业者而言,日常工作中我们每天都会接触大量的文本数据:缺陷管理系统中的bug描述、测试用例的步骤说明、用户反馈的问题报告、需求文档的规格描述,甚至是接口返回的异常信息文本。这些非结构化文本往往隐含着关键业务信息&#x…...
你的差异基因结果可靠吗?用MetaVolcanoR给多个GEO数据集做一次‘交叉验证’吧
你的差异基因结果可靠吗?用MetaVolcanoR给多个GEO数据集做一次"交叉验证"当你在GEO数据库中下载了三个肺癌研究的差异表达结果,却发现三个DEG列表的重叠基因不到20%——这种令人沮丧的场景每天都在全球实验室上演。单项研究的差异分析结果就像…...
如何用嘎嘎降AI处理金融学论文:金融学毕业论文降AI4.8元完整操作教程
如何用嘎嘎降AI处理金融学论文:金融学毕业论文降AI4.8元完整操作教程 第一次用降AI工具有很多不确定——传什么格式、选哪个模式、怎么验收。 这篇教程把金融学论文降AI教程的常见问题都覆盖了,主要基于嘎嘎降AI(www.aigcleaner.com&#x…...
