VScode光标动画(类Neovide效果)
教程开始
第 1 步:下载配置文件
一、下载配置文件
首先,下载跟踪游标效果配置文件。
// https://www.reddit.com/r/vscode/comments/11e66xh/i_made_neovide_alike_cursor_effect_on_vscode/
// Configuration
// Set the color of the cursor trail to match the user's cursor color
const Color = "#A052FF" // If set to "default," it will use the theme's cursor color.
// ! default will only reference editorCursor.background
// "workbench.colorCustomizations": {
// "editorCursor.background": "#A052FF",
// }
// Set the style of the cursor to either a line or block
// line option use fill() to draw trail, block option use lineTo to draw trail
const CursorStyle = "block" // Options are 'line' or 'block'
// Set the length of the cursor trail. A higher value may cause lag.
const TrailLength = 8 // Recommended value is around 8
// Set the polling rate for handling cursor created and destroyed events, in milliseconds.
const CursorUpdatePollingRate = 500 // Recommended value is around 500
// Use shadow
const UseShadow = false
const ShadowColor = Color
const ShadowBlur = 15
// imported from https://github.com/tholman/cursor-effects/blob/master/src/rainbowCursor.js
function createTrail(options) {
const totalParticles = options?.length || 20
let particlesColor = options?.color || "#A052FF"
const style = options?.style || "block"
const canvas = options?.canvas
const context = canvas.getContext("2d")
let cursor = { x: 0, y: 0 }
let particles = []
let width,height
let sizeX = options?.size || 3
let sizeY = options?.sizeY || sizeX*2.2
let cursorsInitted = false
// update canvas size
function updateSize(x,y) {
width = x
height = y
canvas.width = x
canvas.height = y
}
// update cursor position
function move(x,y) {
x = x + sizeX/2
cursor.x = x
cursor.y = y
if (cursorsInitted === false) {
cursorsInitted = true
for (let i = 0; i < totalParticles; i++) {
addParticle(x, y)
}
}
}
// particle class
class Particle {
constructor(x, y) {
this.position = { x: x, y: y }
}
}
function addParticle(x, y, image) {
particles.push(new Particle(x, y, image))
}
function calculatePosition() {
let x = cursor.x,y = cursor.y
for (const particleIndex in particles) {
const nextParticlePos = (particles[+particleIndex + 1] || particles[0]).position
const particlePos = particles[+particleIndex].position
particlePos.x = x;
particlePos.y = y;
x += (nextParticlePos.x - particlePos.x) * 0.42
y += (nextParticlePos.y - particlePos.y) * 0.35
}
}
// for block cursor
function drawLines() {
context.beginPath()
context.lineJoin = "round"
context.strokeStyle = particlesColor
const lineWidth = Math.min(sizeX,sizeY)
context.lineWidth = lineWidth
if (UseShadow) {
context.shadowColor = ShadowColor;
context.shadowBlur = ShadowBlur;
}
// draw 3 lines
let ymut = (sizeY-lineWidth)/3
for (let yoffset=0;yoffset<=3;yoffset++) {
let offset = yoffset*ymut
for (const particleIndex in particles) {
const pos = particles[particleIndex].position
if (particleIndex == 0) {
context.moveTo(pos.x, pos.y + offset + lineWidth/2)
} else {
context.lineTo(pos.x, pos.y + offset + lineWidth/2)
}
}
}
context.stroke()
}
// for line cursor
function drawPath() {
context.beginPath()
context.fillStyle = particlesColor
if (UseShadow) {
context.shadowColor = ShadowColor;
context.shadowBlur = ShadowBlur;
}
// draw path
for (let particleIndex=0;particleIndex<totalParticles;particleIndex++) {
const pos = particles[+particleIndex].position
if (particleIndex == 0) {
context.moveTo(pos.x, pos.y)
} else {
context.lineTo(pos.x, pos.y)
}
}
for (let particleIndex=totalParticles-1;particleIndex>=0;particleIndex--) {
const pos = particles[+particleIndex].position
context.lineTo(pos.x, pos.y+sizeY)
}
context.closePath()
context.fill()
context.beginPath()
context.lineJoin = "round"
context.strokeStyle = particlesColor
context.lineWidth = Math.min(sizeX,sizeY)
// for up&down
let offset = -sizeX/2 + sizeY/2
for (const particleIndex in particles) {
const pos = particles[particleIndex].position
if (particleIndex == 0) {
context.moveTo(pos.x, pos.y + offset)
} else {
context.lineTo(pos.x, pos.y + offset)
}
}
context.stroke()
}
function updateParticles() {
if (!cursorsInitted) return
context.clearRect(0, 0, width, height)
calculatePosition()
if (style=="line") drawPath()
else if (style=="block") drawLines()
}
function updateCursorSize(newSize,newSizeY) {
sizeX = newSize
if (newSizeY) sizeY = newSizeY
}
return {
updateParticles: updateParticles,
move: move,
updateSize: updateSize,
updateCursorSize: updateCursorSize
}
}
// cursor create/remove/move event handler
// by qwreey
// (very dirty but may working)
async function createCursorHandler(handlerFunctions) {
// Get Editor with dirty way (... due to vscode plugin api's limit)
/** @type { Element } */
let editor
while (!editor) {
await new Promise(resolve=>setTimeout(resolve, 100))
editor = document.querySelector(".part.editor")
}
handlerFunctions?.onStarted(editor)
// cursor cache
let updateHandlers = []
let cursorId = 0
let lastObjects = {}
let lastCursor = 0
// cursor update handler
function createCursorUpdateHandler(target,cursorId,cursorHolder,minimap) {
let lastX,lastY // save last position
let update = (editorX,editorY)=>{
// If cursor was destroyed, remove update handler
if (!lastObjects[cursorId]) {
updateHandlers.splice(updateHandlers.indexOf(update),1)
return
}
// get cursor position
let {left:newX,top:newY} = target.getBoundingClientRect()
let revX = newX-editorX,revY = newY-editorY
// if have no changes, ignore
if (revX == lastX && revY == lastY && lastCursor == cursorId) return
lastX = revX;lastY = revY // update last position
// wrong position
if (revX<=0 || revY<=0) return
// if it is invisible, ignore
if (target.style.visibility == "hidden") return
// if moved over minimap, ignore
if (minimap && minimap.offsetWidth != 0 && minimap.getBoundingClientRect().left <= newX) return
// if cursor is not displayed on screen, ignore
if (cursorHolder.getBoundingClientRect().left > newX) return
// update corsor position
lastCursor = cursorId
handlerFunctions?.onCursorPositionUpdated(revX,revY)
handlerFunctions?.onCursorSizeUpdated(target.clientWidth,target.clientHeight)
}
updateHandlers.push(update)
}
// handle cursor create/destroy event (using polling, due to event handlers are LAGGY)
let lastVisibility = "hidden"
setInterval(async ()=>{
let now = [],count = 0
// created
for (const target of editor.getElementsByClassName("cursor")) {
if (target.style.visibility != "hidden") count++
if (target.hasAttribute("cursorId")) {
now.push(+target.getAttribute("cursorId"))
continue
}
let thisCursorId = cursorId++
now.push(thisCursorId)
lastObjects[thisCursorId] = target
target.setAttribute("cursorId",thisCursorId)
let cursorHolder = target.parentElement.parentElement.parentElement
let minimap = cursorHolder.parentElement.querySelector(".minimap")
createCursorUpdateHandler(target,thisCursorId,cursorHolder,minimap)
// console.log("DEBUG-CursorCreated",thisCursorId)
}
// update visible
let visibility = count<=1 ? "visible" : "hidden"
if (visibility != lastVisibility) {
handlerFunctions?.onCursorVisibilityChanged(visibility)
lastVisibility = visibility
}
// destroyed
for (const id in lastObjects) {
if (now.includes(+id)) continue
delete lastObjects[+id]
// console.log("DEBUG-CursorRemoved",+id)
}
},handlerFunctions?.cursorUpdatePollingRate || 500)
// read cursor position polling
function updateLoop() {
let {left:editorX,top:editorY} = editor.getBoundingClientRect()
for (handler of updateHandlers) handler(editorX,editorY)
handlerFunctions?.onLoop()
requestAnimationFrame(updateLoop)
}
// handle editor view size changed event
function updateEditorSize() {
handlerFunctions?.onEditorSizeUpdated(editor.clientWidth,editor.clientHeight)
}
new ResizeObserver(updateEditorSize).observe(editor)
updateEditorSize()
// startup
updateLoop()
handlerFunctions?.onReady()
}
// Main handler code
let cursorCanvas,rainbowCursorHandle
createCursorHandler({
// cursor create/destroy event handler polling rate
cursorUpdatePollingRate: CursorUpdatePollingRate,
// When editor instance stared
onStarted: (editor)=>{
// create new canvas for make animation
cursorCanvas = document.createElement("canvas")
cursorCanvas.style.pointerEvents = "none"
cursorCanvas.style.position = "absolute"
cursorCanvas.style.top = "0px"
cursorCanvas.style.left = "0px"
cursorCanvas.style.zIndex = "1000"
editor.appendChild(cursorCanvas)
// create rainbow cursor effect
// thanks to https://github.com/tholman/cursor-effects/blob/master/src/rainbowCursor.js
// we can create trail effect!
let color = Color
if (color == "default") {
color = getComputedStyle(
document.querySelector("body>.monaco-workbench"))
.getPropertyValue("--vscode-editorCursor-background")
.trim()
}
rainbowCursorHandle = createTrail({
length: TrailLength,
color: color,
size: 7,
style: CursorStyle,
canvas: cursorCanvas
})
},
onReady:()=>{},
// when cursor moved
onCursorPositionUpdated: (x,y)=>{
rainbowCursorHandle.move(x,y)
},
// when editor view size changed
onEditorSizeUpdated: (x,y)=>{
rainbowCursorHandle.updateSize(x,y)
},
// when cursor size changed (emoji, ...)
onCursorSizeUpdated: (x,y)=>{
rainbowCursorHandle.updateCursorSize(x,y)
// rainbowCursorHandle.updateCursorSize(parseInt(y/lineHeight))
},
// when using multi cursor... just hide all
onCursorVisibilityChanged: (visibility)=>{
cursorCanvas.style.visibility = visibility
},
// update animation
onLoop: ()=>{
rainbowCursorHandle.updateParticles()
},
})
然后,将该文件存储到 Microsoft VS Code 安装目录同级的 CustomConfig 目录下。
此时,第一步完成。
第 2 步:下载Code插件
二、下载VSCode插件
首先,使用管理员模式打开Microsoft VS Code,然后找到拓展(Extension),搜索Custom CSS and JS Loader插件并下载。
第 3 步:配置效果文件内容
三、复制效果(VSCode编辑器)配置文件
首先,复制一下的代码文件。
// Set the color of the cursor trail to match the user's cursor color
const Color = "#A052FF"
// Set the style of the cursor to either a line or block
const CursorStyle = "block" // Options are 'line' or 'block'
// Set the length of the cursor trail. A higher value may cause lag.
const TrailLength = 8 // Recommended value is around 8
// Set the polling rate for handling cursor created and destroyed events, in milliseconds.
const CursorUpdatePollingRate = 500 // Recommended value is around 500
第三步完毕。
第 4 步:添加效果文件路径
四、在VSCode中配置效果文件地址
首先,以管理员方式允许Microsoft VS Code,将下方代码添加到VSCode中的setting.js文件中。
// Microsoft VS Code 实现 neovide 的光标移动效果
"vscode_custom_css.imports": [
"file:///D:/Study/Microsoft/CustomConfig/index.js"
],
然后,保存setting.js文件后,按 Ctrl + Shift + P 输入 >enable Custom CSS And JS Loader。
最后,关闭 Microsoft VS Code,重新以管理员方式允许。
第 5 步:修改后状况
五、安装后出现以下情况
由于,Custom CSS And JS Loader属于修改 VS Code 的源代码,所以出现这个很正常,点击右上角的齿轮,选以后不再提示即可。
第 6 步:效果展示
六、效果展示
后续补充。
PowerMode - 烟花火焰碎屑
效果图
VScode开屏图标(替换为自定义图片)
教程开始 | Getting Started
第 1 步 :安装VScode插件
一、下载Custom CSS and JS Loader | Install this extension.
首先,使用管理员模式打开Microsoft VS Code,然后找到拓展(Extension),搜索Custom CSS and JS Loader插件并下载。
二、在setting.json添加配置项 | Add to settings.json:
"vscode_custom_css.imports": [""]
1. Ctrl + Shift + P 打开命令面板
2. 输入Open User Settings
3. 配置转换好的图片
- Web配置
.editor-group-watermark > .letterpress{
background-image: url("https://raw.githubusercontent.com/Aikoyori/ProgrammingVTuberLogos/main/VSCode/VSCode-Thick.png") !important;
opacity: .75;
aspect-ratio: 3/2 !important;
}
- 本地资源配置
VERY IMPORTANT: Items in vscode_custom_css.imports must be URLs. Plain file paths are NOT URLs.
-
- Windows File URL Example: file:///C:/Users/MyUserName/Documents/custom.css
-
-
- The C:/ part is REQUIRED.
-
-
- MacOS and Linux File URL Example: file:///Users/MyUserName/Documents/custom.css
- See here for more details.
三、用管理员模式重启VSCode | Restart Visual Studio Code with proper permission to modify itself:
-
- Windows: Restart with Administrator Permission.
- MacOS and Linux: See instructions below.