稀土掘金技术社区 2024年11月14日
VSCode 天命人:边打代码边体验黑神话悟空
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文介绍了一个VSCode插件,它利用TextEditorDecorationType展示逐帧动画,将《黑神话:悟空》的打斗场景融入到代码编辑过程中。插件通过ffmpeg将视频转换为图片序列,然后在用户输入代码时,逐帧显示这些图片,营造出打字时战斗的效果。插件还包含连击计数和自动停止动画等功能,让编程过程更加有趣。用户可以参考文章提供的步骤,一步步实现这个插件,并将其应用到自己的VSCode环境中,体验不一样的编程乐趣。

🐒插件核心原理是利用VSCode的TextEditorDecorationType展示逐帧动画,通过ffmpeg将视频转换为图片序列,实现代码输入时触发动画效果。

💻插件通过预加载动画帧,将所有图片存储到内存中,避免每次播放动画时都读取文件,提高效率。

⌨️插件监听用户输入事件,每当用户输入代码时,就会触发动画播放,并重置用户不活动计时器和增加连击计数。

⏱️插件加入连击的概念,并在用户停止输入一段时间后自动停止动画,避免持续占用系统资源。

🔗插件提供GitHub仓库链接,用户可以访问该仓库查看插件代码,并亲自体验插件功能。

原创 小明大白菜 2024-11-14 08:30 重庆

点击关注公众号,“技术干货”及时达!

点击关注公众号,“技术干货” 及时达!

《黑神话:悟空》最近很火,明天就要正式上线了!小明我为了提前感受一下这款游戏的激燃氛围,做了个 VSCode 小扩展,让你在写代码时也能体验到《黑神话:悟空》里的打斗快感。

接下来,我会带你一步步实现这个插件,让你的编程旅程不再单调!

原理

这个插件的核心原理是利用 VSCode 的 TextEditorDecorationType 来展示逐帧动画。由于 VSCode 不支持直接播放 MP4 视频,我们需要先将视频转成图片序列,然后通过装饰的方式来显示这些图片。

具体操作是这样的:我们先用 ffmpeg 命令将视频拆解成一帧帧的图片,命令如下:

ffmpeg -i ./wukong.mp4 -vf "fps=10,scale=300:150" -compression_level 9 ./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 <= 100; 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 | undefinedlet currentFrame = 1async 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 = falselet inactivityTimer: NodeJS.Timeout | undefinedlet comboCount = 0function 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

点击关注公众号,“技术干货” 及时达!


阅读原文

跳转微信打开

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

VSCode插件 黑神话:悟空 动画 编程 ffmpeg
相关文章