在你的 VSCode 里深夜放烟火

我从没有见过极光出现的村落

也没有见过有人 在深夜放烟火

见过有人在 vscode 里深夜放烟火么?

今天我们就一起来放一下:

首先,vscode 是基于 electron 实现的,也就是界面都是网页写的。

而且它有一个 decoration 的 api,可以给 editor 的文本加样式:

参数有 before、after 也就是前后加一个伪元素,还有 border、backgound 等样式。

是不是就和写 css 一样?

因为它本来就是 css。

基于这个 api,我们就可以给某段文本加一段样式。

这就是 vscode 里放烟花的原理。

下面我们来写一下:

全局安装 vscode 插件脚手架:

css 复制代码
npm install -g yo generator-code

然后创建 vscode 插件项目:

css 复制代码
yo code

点击调试,会出现新的 vscode 窗口,然后输入 hello world 命令,右下角有 hello world 弹窗:

这就说明插件跑成功了。

然后我们开始写具体的逻辑:

在 src/extension.ts 里注册 fireworks 命令:

javascript 复制代码
import * as vscode from 'vscode';

export function activate(context: vscode.ExtensionContext) {

	const fireworksCommand = vscode.commands.registerCommand('fireworks', () => {
		vscode.window.showInformationMessage('光光光');
	});

	context.subscriptions.push(fireworksCommand);
}

export function deactivate() {}

然后在 package.json 里声明这个命令:

跑起来就是这样的:

然后用下 decaration 的 api:

javascript 复制代码
import * as vscode from 'vscode';

export function activate(context: vscode.ExtensionContext) {

	const fireworksCommand = vscode.commands.registerCommand('fireworks', () => {
		const editor = vscode.window.activeTextEditor;

		if(editor) {
			const decoration = vscode.window.createTextEditorDecorationType({
				before: {
					contentText: '光光光',
					backgroundColor: 'red'
				},
				border: '10px solid green',
				after: {
					contentText: '东东东',
					backgroundColor: 'blue'
				}
			});

			const position1 = new vscode.Position(1, 2);
			const position2 = new vscode.Position(1, 4);

			const range = new vscode.Range(position1, position2);

			editor.setDecorations(decoration, [range]);
		}
	
	});

	context.subscriptions.push(fireworksCommand);
}

export function deactivate() {}

我们用 vscode.window.activeTextEditor 拿到当前的 editor。

创建 decoration 对象,指定 border 还有 before、after 伪元素的样式。

指定开始和结束的 position,然后给这段 range 添加 decoration。

测试下:

可以看到,执行命令后,第 1 行第 2 到 4 列(vscode 里的行列号是从 0 开始的)的文本被加上了边框了,并且前后加了伪元素。

选中的文本复制之后却没有带上前后伪元素。

这个就是伪元素本身的特性:

比较有意思的一点是样式的值其实可以写任意多个样式:

也就是说,在 vscode 的 editor 里能实现各种用 css 能实现的效果。

那放烟花怎么实现呢?

其实就是用 background-image 加载一个 gif 就好了。

这里我们用内嵌 base64 图片的方式:

创建 src/gifs.ts:

javascript 复制代码
const gifData1= 'xxx';

consg gifData2= 'xxx';

export {
    gifData1,
    gifData2
} 

内容比较多,可以从 github 复制:github.com/QuarkGluonP...

然后设置 background-image:

让元素相对定位,伪元素绝对定位,然后设置宽高、background-image。

javascript 复制代码
let cssStr = `position:absolute;width:100px;height:100px;`
cssStr += `background-image: url(${gifData1});`;
cssStr += 'background-size: contain;';
cssStr+= 'background-repeat: no-repeat;'

const decoration = vscode.window.createTextEditorDecorationType({
        before: {
                contentText: '',
                textDecoration: `none;${cssStr};`
        },
        textDecoration: 'none;position:relative;'
});

效果是这样的:

这样就可以在 vscode 里放烟花了。

当然,现在的烟花位置是固定的,我们可以多加几个,并且位置随机一点:

拿到 editor 的文本,按照换行符分割下:

打个断点,断住以后是这样的:

我们根据这个来计算出在哪里加烟花就好了。

可以随机列、随机行放一个。

封装这样一个方法:

javascript 复制代码
function randomRange(start: number, end: number) {
    return Math.ceil(start + Math.random() * (end - start));
}

随机产生从 xxx 到 yyy 的值。

然后每 10 到 20 行,在那行的随机位置,放一个烟花的装饰,随机从两个 gif 里取一个:

javascript 复制代码
import * as vscode from 'vscode';
import { gifData1, gifData2 } from './gifs';

function randomRange(start: number, end: number) {
	return Math.ceil(start + Math.random() * (end - start));
}

export function activate(context: vscode.ExtensionContext) {

	const fireworksCommand = vscode.commands.registerCommand('fireworks', () => {
		const editor = vscode.window.activeTextEditor;

		if(editor) {
			let cssStr = `position:absolute;width:150px;height:100px;`
			cssStr += 'background-size: contain;';
			cssStr+= 'background-repeat: no-repeat;'

			const text = editor.document.getText();
			const lines = text.split('\n');


			for(let i = 0; i < lines.length; i+= randomRange(10, 20)) {
				const line = lines[i];

				cssStr += `background-image: url(${Math.random() > 0.5 ? gifData1 : gifData2});`;

				const j = Math.random() * lines[i].length;

				const position1 = new vscode.Position(i, j);
				const position2 = new vscode.Position(i, j + 1);
		
				const range = new vscode.Range(position1, position2);

				const decoration = vscode.window.createTextEditorDecorationType({
					before: {
						contentText: '',
						textDecoration: `none;${cssStr};`
					},
					textDecoration: 'none;position:relative;'
				});
	
				editor.setDecorations(decoration, [range]);

			}
		}
	
	});

	context.subscriptions.push(fireworksCommand);
}

export function deactivate() {}

效果是这样的:

放烟火之后如何去掉呢?

其实 decoration 有个 deispose 方法是用来做这个的:

所以我们把 decoration 收集起来,注册个新 command 用来去掉烟花,并且在每次放烟花前把之前的去掉:

javascript 复制代码
import * as vscode from 'vscode';
import { gifData1, gifData2 } from './gifs';

function randomRange(start: number, end: number) {
	return Math.ceil(start + Math.random() * (end - start));
}

let decorations: vscode.TextEditorDecorationType[] = [];

export function activate(context: vscode.ExtensionContext) {
	function disposeAllDecorations() {
		decorations.forEach(item => {
			item.dispose();
		});
		decorations = [];
	}
	const stopFireworksCommand = vscode.commands.registerCommand('stop-fireworks', disposeAllDecorations);

	context.subscriptions.push(stopFireworksCommand);

	const fireworksCommand = vscode.commands.registerCommand('fireworks', () => {
		const editor = vscode.window.activeTextEditor;

		if(editor) {
			let cssStr = `position:absolute;width:150px;height:100px;`
			cssStr += 'background-size: contain;';
			cssStr+= 'background-repeat: no-repeat;'

			const text = editor.document.getText();
			const lines = text.split('\n');

			disposeAllDecorations();
			for(let i = 0; i < lines.length; i+= randomRange(10, 20)) {
				const line = lines[i];

				cssStr += `background-image: url(${Math.random() > 0.5 ? gifData1 : gifData2});`;

				const j = Math.random() * lines[i].length;

				const position1 = new vscode.Position(i, j);
				const position2 = new vscode.Position(i, j + 1);
		
				const range = new vscode.Range(position1, position2);

				const decoration = vscode.window.createTextEditorDecorationType({
					before: {
						contentText: '',
						textDecoration: `none;${cssStr};`
					},
					textDecoration: 'none;position:relative;'
				});
	
				editor.setDecorations(decoration, [range]);

				decorations.push(decoration);
			}
		}
	
	});

	context.subscriptions.push(fireworksCommand);
}

当然,在 package.json 里也得注册下这个 command:

测试下:

确实去掉了。

然后我们加个快捷键:

json 复制代码
"keybindings": [
  {
    "command": "fireworks",
    "key": "ctrl+y",
    "mac": "cmd+y"
  },
  {
    "command": "stop-fireworks",
    "key": "ctrl+shift+y",
    "mac": "cmd+shift+y"
  }
]

在 windows 下是 ctrl + y 在 mac 下是 cmd + y。

测试下:

这样就可以方便的放烟花和停止了。

代码上传了 github:github.com/QuarkGluonP...

总结

我们实现了在 vsocde 里放烟花的效果。

vscode 是基于 electron 实现的,看到的界面都是网页,并且 vscode 提供了 decoration 的 api 可以给一段文本加装饰。

这个装饰可以写任意的 css。

我们注册了 fireworks 和 stop-fireworks 两个命令。

在 fireworks 的时候拿到文本,给随机行列加上一个装饰,也就是通过 before 伪元素加上 background-image 设置一个 gif。

然后 stop-fireworks 的时候调用 dispose 去掉装饰。

这样,深夜里就可以在 vscode 里自己放烟火了。

相关推荐
崔庆才丨静觅10 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606111 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了11 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅11 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅11 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅12 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment12 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅12 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊12 小时前
jwt介绍
前端
爱敲代码的小鱼12 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax