VSCode 天命人:边打代码边体验黑神话悟空
点击关注公众号,“技术干货”及时达!
《黑神话:悟空》最近很火,明天就要正式上线了!小明我为了提前感受一下这款游戏的激燃氛围,做了个 VSCode 小扩展,让你在写代码时也能体验到《黑神话:悟空》里的打斗快感。
接下来,我会带你一步步实现这个插件,让你的编程旅程不再单调!
原理这个插件的核心原理是利用 VSCode 的 TextEditorDecorationType 来展示逐帧动画。由于 VSCode 不支持直接播放 MP4 视频,我们需要先将视频转成图片序列,然后通过装饰的方式来显示这些图片。
具体操作是这样的:我们先用 ffmpeg 命令将视频拆解成一帧帧的图片,命令如下:
ffmpeg-i./wukong.mp4-vf"fps=10,scale=300:150"-compression_level9./res/frames/frame%03d.png
这个命令会把 wukong.mp4 视频按每秒 10 帧的速度导出成 300x150 像素的图片,存放在 res/frames 文件夹下。然后,我们在 VSCode 中利用这些图片逐帧展示动画,营造出打字时打斗的效果。
接下来,我会带你一步步实现这个有趣的插件,让你的编程过程变得更有意思!
第一步:初始化项目首先,我们要创建一个新的 VSCode 插件项目。如果你还没有设置好开发环境,可以参考 VSCode 官方文档 来创建你的第一个插件项目。
假设你已经创建了一个插件项目,现在我们直接进入编码阶段。
第二步:预加载动画帧在展示动画之前,我们需要先加载所有的动画帧。这里假设我们有 100 帧图片,存放在插件的 res/frames 文件夹下。
首先,我们来写一个函数,用来加载这些帧图片并存储到内存中,方便后续使用。
const frameCache: { [key: number]: string } = {}
async function preloadFrames(extensionPath: string) {
for (let i = 1; i i++) {
const frameUrl = getFrameUrl(i, extensionPath)
frameCache[i] = frameUrl
}
}
这个函数的逻辑很简单:我们遍历 100 个帧图片,依次读取并存储到 frameCache 对象中。这样在后面播放动画时,就可以直接使用缓存好的图片,而不用每次都去读取文件。
其中的 getFrameUrl 函数负责生成每一帧图片的路径,并将其转换为 Base64 格式的 URL:
function getFrameUrl(frameNumber: number, extensionPath: string): string {
const paddedFrameNumber = frameNumber.toString().padStart(3, '0')
const imagePath = path.join(extensionPath, 'res', 'frames', `frame${paddedFrameNumber}.png`)
try {
const imageBuffer = fs.readFileSync(imagePath)
const base64Image = imageBuffer.toString('base64')
return `data:image/png;base64,${base64Image}`
} catch (error) {
console.error(`读取图片失败: ${imagePath}`, error)
return ''
}
}
这里我们通过 fs.readFileSync 读取每一帧图片,然后将其转换为 Base64 格式,这样就可以直接在 HTML 中使用图片数据。
第三步:监听输入事件有了预加载的动画帧,现在我们需要监听用户在编辑器中的输入事件,每当用户输入代码时,就触发动画播放。
vscode.workspace.onDidChangeTextDocument(event = {
if (event.contentChanges.length 0) {
updateAnimation(event.document.uri)
resetInactivityTimer()
incrementComboCount()
}
})
当文档内容发生变化时,我们会调用 updateAnimation 来更新动画。同时,我们还重置了用户不活动计时器,并增加连击计数。
第四步:展示动画核心部分来了!我们要通过 VSCode 的 TextEditorDecorationType API 来在编辑器中显示动画。
let animationDecoration: vscode.TextEditorDecorationType | undefined
let currentFrame = 1
async function showNextFrame(editor: vscode.TextEditor) {
if (!isAnimating) return
const frameUrl = frameCache[currentFrame]
if (frameUrl) {
if (animationDecoration) {
animationDecoration.dispose()
}
animationDecoration = vscode.window.createTextEditorDecorationType({
after: {
contentText: comboCount 0 ? `${comboCount}x` : '',
margin: '0 0 0 1em',
width: '200px',
height: '100px',
textDecoration: `
none;
position: absolute;
left: -0.5rem;
top: 2rem;
z-index: 1;
pointer-events: none;
background-image: url(${frameUrl});
background-size: 100% 100%;
background-repeat: no-repeat;
`
},
rangeBehavior: vscode.DecorationRangeBehavior.ClosedClosed
})
const lastLine = editor.document.lineAt(editor.document.lineCount - 1)
const range = new vscode.Range(lastLine.range.end, lastLine.range.end)
editor.setDecorations(animationDecoration, [{ range }])
currentFrame = (currentFrame % 100) + 1
}
}
这里的 showNextFrame 函数负责将动画帧显示在编辑器中。通过设置 TextEditorDecorationType,我们可以自定义每一帧动画的样式和位置。
第五步:处理动画的停止和连击为了让动画更有趣,我们加入了连击的概念,并且在用户停止输入一段时间后自动停止动画。
let isAnimating = false
let inactivityTimer: NodeJS.Timeout | undefined
let comboCount = 0
function resetInactivityTimer() {
if (inactivityTimer) clearTimeout(inactivityTimer)
inactivityTimer = setTimeout(() = {
stopAnimation()
resetComboCount()
}, 3000) // 3秒后停止动画并重置连击计数
}
function stopAnimation() {
isAnimating = false
if (animationDecoration) {
animationDecoration.dispose()
animationDecoration = undefined
}
}
function incrementComboCount() {
comboCount++
showNextFrame(vscode.window.activeTextEditor!)
}
function resetComboCount() {
comboCount = 0
showNextFrame(vscode.window.activeTextEditor!)
}
resetInactivityTimer 会在用户停止输入超过 3 秒后停止动画,并重置连击计数。这样,我们就不会一直占用资源来播放动画了。
第六步:将插件激活到 VSCode 中现在,我们的动画逻辑已经写好了。接下来,我们要把它整合到 VSCode 插件的主入口中。
import * as vscode from 'vscode'
import { activateFightAnimation } from './fightAnimation'
export const activate = async (context: vscode.ExtensionContext) = {
try {
console.log('"VSCode Funny" 插件已激活!')
// 激活打斗动画功能
activateFightAnimation(context)
} catch (err) {
console.warn('插件激活失败', err)
}
}
在插件的 activate 函数中,我们调用 activateFightAnimation 来启动我们的打字动画功能。
最后一步:配置和运行插件至此,插件的代码已经完成。你可以通过以下步骤来运行并测试这个插件:
打开 VSCode,进入插件的项目目录。运行 npm install 来安装依赖。使用 F5 启动插件调试环境。在调试窗口中输入代码,享受打字战斗动画带来的乐趣!结语:彩蛋时间 ??这个插件不仅能让你在敲代码时体验到《黑神话》的程序吗喽热血,还“故意”留了一个 bug——动画的位置 bug。
如果你对这个扩展感兴趣,或者想亲自体验一下这个插件,可以去我的 GitHub 仓库看看:https://github.com/nicepkg/vscode-funny
点击关注公众号,“技术干货”及时达!
阅读原文
网站开发网络凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求...
请立即点击咨询我们或拨打咨询热线:13245491521 13245491521 ,我们会详细为你一一解答你心中的疑难。 项目经理在线