整活了,让你的编辑器中出现3D佐伊

1,360 阅读3分钟

前言

昨天晚上再 b 站刷视频,看到有个 up 主,使用 nw.js 实现在电脑系统桌面加载佐伊模型,看了一下觉得很有意思,就像能不能在编辑器中加载这个模型呢,试了一下竟然可以。

视频地址:www.bilibili.com/video/BV1EH…

视频里有提取英雄联盟其他英雄模型的教程,你可以根据教程提取你喜欢的英雄模型展示。

效果展示

Kapture 2025-04-30 at 12.44.35.gif

原理

实现原理和我以前实现的 wukong 视频插件原理一样。

把黑神话悟空视频设置为vscode背景,真的太炫酷了

大家都知道 vscode 使用electron 开发的,electron渲染页面实际上是加载的 html,我们只要在源码中找到这个 html文件,把我们的js代码放进去就行了。

使用教程

找到vscode应用程序文件夹,/Applications/Visual Studio Code.app/Contents/Resources/app/out/vs/code/electron-sandbox/workbench,mac 电脑基本都是在这个路径。

windows系统可以点击 vscode 图标,查看所在目录, 然后进入这个目录\resources\app\out\vs\code\electron-sandbox\workbench。。

在当前文件夹下创建model.html文件,把下面代码复制进去。

<!DOCTYPE html>
<html>
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>zoe</title>
	<style>
		html body {
			margin: 0;
		}
	</style>
	<script type="importmap">
		{
			"imports": {
				"three": "https://cdn.jsdelivr.net/npm/three@0.163.0/build/three.module.js",
				"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.163.0/examples/jsm/"
			}
		}
	</script>
</head>
<body>

</body>
<script type="module">
	import * as THREE from 'https://cdn.jsdelivr.net/npm/three@0.163.0/build/three.module.js';
	import { OrbitControls } from 'https://cdn.jsdelivr.net/npm/three@0.163.0/examples/jsm/controls/OrbitControls.js';
	import { GLTFLoader } from 'https://cdn.jsdelivr.net/npm/three@0.163.0/examples/jsm/loaders/GLTFLoader.js';


	const modelPath = './Zoe.glb'; // 替换为你的模型路径

	const scene = new THREE.Scene();
	const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
	const renderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
	renderer.setSize(window.innerWidth, window.innerHeight);
	renderer.setPixelRatio(window.devicePixelRatio);

	renderer.shadowMap.enabled = true;

	document.body.appendChild(renderer.domElement);

	camera.position.set(1, 1, 1);
	camera.lookAt(new THREE.Vector3(0, 0, 0));

	let mixer;
	const loader = new GLTFLoader();
	loader.load(
		modelPath,
		function (gltf) {
			const model = gltf.scene;

			const box = new THREE.Box3().setFromObject(model);
			const size = box.getSize(new THREE.Vector3());
			const scale = (1 / size.y);
			model.scale.set(scale, scale, scale);

			model.position.y = -0.5;

			mixer = new THREE.AnimationMixer(model);
			const action = mixer.clipAction(gltf.animations[0]);
			action.play();

			const meshes = ['Zoe_Base_Mat', "Zoe_Base_Hair_Mat"]

			model.traverse(function (child) {
				if (child instanceof THREE.SkinnedMesh) {
					child.castShadow = true;

					child.visible = meshes.includes(child.material.name);
				}
			});

			scene.add(model);
			renderer.render(scene, camera);
		},
		undefined,
		function (error) {
			console.error('加载失败:', error);
		}
	);


	// 创建控制器
	const controls = new OrbitControls(camera, renderer.domElement);
	controls.enableDamping = true;

	// 环境光
	const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
	scene.add(ambientLight);

	// 点光源
	const pointLight = new THREE.PointLight(0xffffff, 1);
	pointLight.position.set(0, 1, 1);
	pointLight.castShadow = true;
	scene.add(pointLight);

	const shadow = new THREE.Mesh(
		new THREE.PlaneGeometry(100, 100),
		new THREE.ShadowMaterial({ opacity: 0.2 })
	)

	shadow.rotation.x = -Math.PI / 2;
	shadow.position.y = -0.5;
	shadow.receiveShadow = true;
	scene.add(shadow);

	const clock = new THREE.Clock();
	// 动画循环
	function animate() {
		const delta = clock.getDelta();
		requestAnimationFrame(animate);
		renderer.render(scene, camera);
		mixer && mixer.update(delta);
	}
	animate();
</script>

</html>

然后在当前文件夹下创建 model.js 文件,把下面代码复制进去

const box = document.createElement('div');

box.style.width = '200px';
box.style.height = '200px';
box.style.position = 'absolute';
box.style.bottom = '200px';
box.style.right = '40px';
box.style.zIndex = '9999';

const iframe = document.createElement('iframe');
iframe.src = './model.html';
iframe.style.width = '100%';
iframe.style.height = '100%';
iframe.style.border = 'none';
iframe.style.position = 'absolute';
iframe.style.bottom = '0';
iframe.style.right = '0';
iframe.style.pointerEvents = 'none';

box.appendChild(iframe);
document.body.appendChild(box);
// 实现鼠标拖动 iframe
let isDragging = false;
let offset = { x: 0, y: 0 };
box.addEventListener('mousedown', (e) => {
  console.log(333333)
  isDragging = true;
  offset = {
    x: e.clientX - box.getBoundingClientRect().left,
    y: e.clientY - box.getBoundingClientRect().top
  };
});
document.addEventListener('mousemove', (e) => {
  if (isDragging) {
    box.style.left = `${e.clientX - offset.x}px`;
    box.style.top = `${e.clientY - offset.y}px`;
  }
});
document.addEventListener('mouseup', () => {
  isDragging = false;
})

然后修改 workbench.html,把当前 js 引入进去

image.png

<!-- Copyright (C) Microsoft Corporation. All rights reserved. -->
<!DOCTYPE html>
<html>

<head>
	<meta charset="utf-8" />
	<meta http-equiv="Content-Security-Policy" content="
				default-src
					'none'
				;
				img-src
					'self'
					data:
					blob:
					vscode-remote-resource:
					vscode-managed-remote-resource:
					https:
				;
				media-src
					'self'
				;
				frame-src
					'self'
					vscode-webview:
				;
				script-src
					'self'
					'unsafe-eval'
					blob:
				;
				style-src
					'self'
					'unsafe-inline'
				;
				connect-src
					'self'
					https:
					ws:
				;
				font-src
					'self'
					vscode-remote-resource:
					vscode-managed-remote-resource:
					https://*.vscode-unpkg.net
				;
				require-trusted-types-for
					'script'
				;
				trusted-types
					amdLoader
					cellRendererEditorText
					defaultWorkerFactory
					diffEditorWidget
					diffReview
					domLineBreaksComputer
					dompurify
					editorGhostText
					editorViewLayer
					notebookRenderer
					stickyScrollViewLayer
					tokenizeToString
					notebookChatEditController
				;
		" />

	<!-- Workbench CSS -->
	<link rel="stylesheet" href="../../../workbench/workbench.desktop.main.css">

</head>

<body aria-label="">

</body>

<!-- Startup (do not modify order of script tags!) -->
<script src="./workbench.js" type="module"></script>
<script src="./model.js" type="module"></script>

</html>

最后一步把 3d 模型文件Zoe.glb复制进去,再重启 vscode 就行了。

提示

如果想改成自己喜欢的英雄模型,可以使用视频中的教程提取,然后把glb 文件,复制到当前目录下,然后修改 model.html 里模型地址。

image.png

修改源码后,可能会一直提示文件损坏,这个不用管,可以设置不再提示。

大家可以到 github 上下载代码文件和资源文件

github.com/dbfu/zoe

最后祝大家五一劳动节假期快乐