AI coding demo painting on texture1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68// --- 创建模型和画布纹理 ---
const canvas = document.createElement('canvas');
canvas.width = 1024;
canvas.height = 1024;
const context = canvas.getContext('2d');
context.fillStyle = '#ffffff';
context.fillRect(0, 0, canvas.width, canvas.height);
const texture = new THREE.CanvasTexture(canvas);
texture.minFilter = THREE.LinearFilter;
texture.magFilter = THREE.LinearFilter;
const material = new THREE.MeshStandardMaterial({
map: texture,
roughness: 0.8,
metalness: 0.2,
});
const geometry = new THREE.SphereGeometry(2, 64, 64);
const sphere = new THREE.Mesh(geometry, material);
scene.add(sphere);
// --- 射线投射和鼠标事件 ---
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
let isPainting = false;
function onMouseMove(event) {
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
if (isPainting) {
paint();
}
}
function onMouseDown(event) {
isPainting = true;
paint();
}
function onMouseUp(event) {
isPainting = false;
}
window.addEventListener('mousemove', onMouseMove);
window.addEventListener('mousedown', onMouseDown);
window.addEventListener('mouseup', onMouseUp);
// --- 核心绘制函数 ---
function paint() {
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObject(sphere);
if (intersects.length > 0) {
const intersect = intersects[0];
const uv = intersect.uv;
const x = uv.x * canvas.width;
const y = (1 - uv.y) * canvas.height;
const brushRadius = 20;
context.fillStyle = '#ff0000'; // 红色笔刷
context.beginPath();
context.arc(x, y, brushRadius, 0, Math.PI * 2);
context.fill();
texture.needsUpdate = true;
}
}
EventBus
以发布订阅实现的消息中转 如
- eventbus = new Vue()
- ng emit on
…
常称事件总线
1 | class EventBus { |
Vue Router
Learn OpenGL
OpenGL自身是一个巨大的状态机(State Machine):一系列的变量描述OpenGL此刻应当如何运行。OpenGL的状态通常被称为OpenGL上下文(Context)。我们通常使用如下途径去更改OpenGL状态:设置选项,操作缓冲。最后,我们使用当前OpenGL上下文来渲染。
切换到绘制线段/图形 —> 绘制参数 —> 更新
GLFW是一个专门针对OpenGL的C语言库,它提供了一些渲染物体所需的最低限度的接口。如创建窗口、处理输入(如键盘、鼠标、游戏手柄)以及管理 OpenGL 上下文,GLFW是开发 OpenGL 应用程序的常用工具之一
微前端 - Micro App
Web Component
w3c提出浏览器原生支持的组件规范 通过扩展HTMLElement定义html标签 嵌入内容和逻辑(shadow dom隔离)
- 接入简单不需要子应用改造
- 不支持ie
- Web Component沙箱隔离 性能较好
集成
1 | npm i @micro-zoe/micro-app -S |
主应用入口1
2
3
4// src/index.js
import microApp from '@micro-zoe/micro-app'
microApp.start()
contianer组件1
2
3
4
5
6
7
8
9
10
11<template>
<div>
<h1>子应用</h1>
<!--
name(必传):应用名称
url(必传):应用地址,会被自动补全为http://localhost:3000/index.html
baseroute(可选):基座应用分配给子应用的基础路由,就是上面的 `/my-page`
-->
<micro-app name='learnvue' url='http://localhost:8081/' baseroute="/dentition"></micro-app>
</div>
</template>
微前端-qiankun
微前端场景
- 老系统架构陈旧 重构难度大 部分需要与时俱进的功能得不到升级
- 不同模块需要多个团队参与 尤其专业和技术栈有差异
- A系统若干功能或页面被B系统引用
Techniques, strategies and recipes for building a modern web app with multiple teams that can ship features independently. — Micro Frontends
微前端是一种多个团队通过独立发布功能的方式来共同构建现代化 web 应用的技术手段及方法策略。
qiankun方案缺点
- 改造和适配成本高 如微应用打包、入口生命周期,以及router问题
- js沙箱性能问题
- 不支持激活多个微应用 不支持微应用保活
工作流
父应用:安装微前端组件 —> 预留容器dom —> 用组件提供的方法注册子系统
子应用:暴露生命周期钩子
1 | npm i qiankun -S |
register和start都在container的组件层级上 不一定是在顶级组件上 尤其在使用router和模块懒加载的应用中1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32<template>
<div id="viewport">
<div id="subapp-root"></div>
</div>
</template>
<script>
import { registerMicroApps, start } from 'qiankun'
export default {
name: '',
data () {
return {}
},
mounted () {
// 注册微应用
registerMicroApps([
{
name: 'learnvue', // app name registered
entry: '//localhost:8081',
container: '#viewport',
activeRule: '/dentition'
}
])
if (!window.qiankunStarted) {
window.qiankunStarted = true
start()
}
}
}
</script>
当微应用信息注册完之后,一旦浏览器的 url 发生变化,便会自动触发 qiankun 的匹配逻辑,所有 activeRule 规则匹配上的微应用就会被插入到指定的 container 中,同时依次调用微应用暴露出的生命周期钩子。
如果微应用不是直接跟路由关联的时候,你也可以选择手动加载微应用的方式:1
2
3
4
5
6
7import { loadMicroApp } from 'qiankun';
loadMicroApp({
name: 'app',
entry: '//localhost:7100',
container: '#yourContainer',
});
微应用webpack设置1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21const { defineConfig } = require('@vue/cli-service')
const packageName = require('./package.json').name;
module.exports = defineConfig({
transpileDependencies: true,
devServer: {
port: 8081,
headers: {
// 微前端需要跨域
'Access-Control-Allow-Origin': '*'
}
},
configureWebpack: {
output: {
library: `${packageName}-[name]`, // [name]是打包时trunk名称的占位符
libraryTarget: 'umd', // Universal Module Definition通用模块定义
chunkLoadingGlobal: `webpackJsonp_${packageName}`,
}
}
})
暴露声明周期1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48//main.js
import Vue from 'vue'
import App from './App.vue'
import './public-path'
import router from './router'
Vue.config.productionTip = false
let instance = null;
function render(props = {}) {
const { container } = props;
instance = new Vue({
router,
render: h => h(App)
}).$mount(container ? container.querySelector('#app') : '#app')
}
// Running in qiankun
/* eslint-disable no-undef */
if (window.__POWERED_BY_QIANKUN__) {
__webpack_public_path__ = window.__INJECTED_PUBLIC_PATH_BY_QIANKUN__;
}
// Running standalone
if (!window.__POWERED_BY_QIANKUN__) {
render();
}
export async function bootstrap() {
console.log('[vue] vue app bootstraped');
}
export async function mount(props) {
console.log('[vue] props from main framework', props);
render(props);
}
export async function unmount() {
instance.$destroy();
instance.$el.innerHTML = '';
instance = null;
}
export async function update(props) {
console.log('update props', props)
}
应用间通信
部署
troubleshooting
Uncaught TypeError: Cannot redefine property: $router
官网解决方法是将主应用window.Vue对象换个名字
我的选择是 微应用最好不要用router
Wasm OpenGL项目
(windows)1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22git clone https://github.com/emscripten-core/emsdk.git
cd emsdk
git pull
# need google access
./emsdk.bat install latest
./emsdk.bat activate latest
cd ..
git clone https://github.com/microsoft/vcpkg.git
cd vcpkg
.\bootstrap-vcpkg.bat
# add vcpkg directory into env variable path
cd ../Workspace/WasmOpenGLProject
vcpkg install boost:wasm32-emscripten
vcpkg install glm:wasm32-emscripten
vcpkg install glad:wasm32-emscripten
issues error: building openssl:wasm32-emscripten failed with: BUILD_FAILED
查看了\vcpkg\buildtrees\openssl\install-wasm32-emscripten-dbg-err.log 内容是1
2
3
4Trying to rename Makefile-333 -> Makefile: Permission denied
make[1]: *** [Makefile:2395: depend] Error 13
make: *** [Makefile:2293: build_modules] Error 2
make: *** Waiting for unfinished jobs....
怀疑其他线程访问导致写入失败1
2set VCPKG_MAX_CONCURRENCY=1
vcpkg install openssl:wasm32-emscripten --clean-after-build
The
FindBoostmodule is removed.
cmake 3.30 起移除了FindBoost模块 导致找不到BOOST_DIR Boost_FILESYSTEM_LIBRARY_DEBUG Boost_INCLUDE_DIR等, 实际上面的步骤中相关包已成功安装在D:\Workspace\Github\vcpkg\installed\wasm32-emscripten\include
make Ninja project1
2
3
4
5
6
7
8
9
10
11
12
13
14
15mkdir build
cd build
emcmake cmake -G "Ninja" ^
-DCMAKE_SYSTEM_NAME=Emscripten ^
-DCMAKE_SYSTEM_PROCESSOR=x86 ^
-DCMAKE_TOOLCHAIN_FILE="%VCPKG_HUGE%/scripts/buildsystems/vcpkg.cmake" ^
-DVCPKG_CHAINLOAD_TOOLCHAIN_FILE="%EMSDK%/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake" ^
-DVCPKG_TARGET_TRIPLET=wasm32-emscripten ^
-DCMAKE_C_COMPILER="%EMCC_PATH%" ^
-DCMAKE_CXX_COMPILER="%EMXX_PATH%" ^
-DCMAKE_BUILD_TYPE=Release ^
-DCMAKE_FIND_ROOT_PATH_MODE_PACKAGE=BOTH ^
-DCMAKE_PREFIX_PATH="%VCPKG_HUGE%/installed/wasm32-emscripten" ^
..
cd ..
build wasm1
2cd build
emmake ninja

axios
相比fetch Api, axios 提供了更简洁、更直观的 API,使得发送 HTTP 请求和处理响应更加方便; 自动处理json对象(fetch 可以调用await response.json())1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17axios.post('/api/submit', this.editForm)
.then(response => {
console.log('Form submitted successfully:', response.data);
})
.catch(error => {
console.error('Error submitting form:', error);
});
axios.request({
url: '/api/submit',
method: 'post',
data: this.editForm
}).then(response => {
console.log('Form submitted successfully:', response.data);
}).catch(error => {
console.error('Error submitting form:', error);
});
instance1
2
3
4
5
6
7const instance = axios.create({ baseURL: '/api' });
// Works just like axios(config)
instance({
url: '/users',
method: 'get'
});
拦截器1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21// Add a request interceptor
axios.interceptors.request.use(function (config) {
// Do something before request is sent
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
},
{ synchronous: true, runWhen: () => /* This function returns true */}
);
// Add a response interceptor
axios.interceptors.response.use(function onFulfilled(response) {
// Any status code that lie within the range of 2xx cause this function to trigger
// Do something with response data
return response;
}, function onRejected(error) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
return Promise.reject(error);
});
Python —— FastAPI
WPF Prism
安装依赖1
2
3
4<ItemGroup>
<PackageReference Include="HandyControl" Version="3.5.1" />
<PackageReference Include="Prism.Unity" Version="8.1.97" />
</ItemGroup>
app.xmal中Wpf:Application替换成prism:PrismApplication1
2
3
4
5
6
7
8
9<prism:PrismApplication
x:Class="WpfApp1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApp1"
xmlns:prism="http://prismlibrary.com/">
<Application.Resources>
</Application.Resources>
</prism:PrismApplication>
标记中删除了StartupUri=”MainWindow.xaml”(否则生成两个实例)
cs代码实现两个抽象方法RegisterTypes和CreateShell1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32// 创建程序主窗口
protected override Window CreateShell()
{
return Container.Resolve<MainWindow>();
}
protected override void RegisterTypes(IContainerRegistry container)
{
// 注册导航页面和ViewModel
container.RegisterForNavigation<MainWindow, MainWindowViewModel>();
// 注册单例服务
container.RegisterSingleton<IMyService, MyService>();
// 配置文件基础路径
string programData = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments);
programData = Path.Combine(programData, "xxxx");
// 从注册表读取配置路径
using (var key = Registry.LocalMachine.OpenSubKey($@"SOFTWARE\xxxx"))
{
if (key != null && key.GetValue("SolutionPath") != null)
{
programData = key.GetValue("SolutionPath").ToString();
}
}
// 初始化全局缓存
GlobalCache.Instance.FileBasePath = programData;
GlobalCache.Instance.InitPaths();
}