mirror of
https://github.com/motajs/template.git
synced 2026-10-29 20:30:20 +08:00
docs: 验证渲染系统可以嵌入文档
This commit is contained in:
parent
e05cc55b95
commit
3cc0fa9bf6
50
docs/.vitepress/components/demos/basicRender.vue
Normal file
50
docs/.vitepress/components/demos/basicRender.vue
Normal file
@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<container :width="480" :height="320">
|
||||
<g-rect
|
||||
:x="40"
|
||||
:y="40"
|
||||
:width="160"
|
||||
:height="90"
|
||||
:fillStyle="color"
|
||||
strokeStyle="#9dc0ff"
|
||||
:lineWidth="2"
|
||||
@click="onClick"
|
||||
/>
|
||||
<g-circle
|
||||
:x="320"
|
||||
:y="110"
|
||||
:radius="48"
|
||||
fillStyle="#e0a42f"
|
||||
strokeStyle="#ffe3a3"
|
||||
:lineWidth="2"
|
||||
@click="onClick"
|
||||
/>
|
||||
<text
|
||||
:x="40"
|
||||
:y="200"
|
||||
:text="'点击次数:' + count"
|
||||
fillStyle="#e8f0ff"
|
||||
/>
|
||||
<text
|
||||
:x="40"
|
||||
:y="240"
|
||||
:text="'点击颜色:' + color"
|
||||
fillStyle="#8fb6ff"
|
||||
/>
|
||||
</container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
/** 点击次数 */
|
||||
const count = ref<number>(0);
|
||||
/** 矩形当前的填充色,点击后轮换 */
|
||||
const color = ref<string>('#2f6fed');
|
||||
|
||||
/** 点击图形时更新计数与颜色 */
|
||||
function onClick(): void {
|
||||
count.value++;
|
||||
color.value = color.value === '#2f6fed' ? '#2fbf71' : '#2f6fed';
|
||||
}
|
||||
</script>
|
||||
120
docs/.vitepress/components/demos/uiSystem.vue
Normal file
120
docs/.vitepress/components/demos/uiSystem.vue
Normal file
@ -0,0 +1,120 @@
|
||||
<template>
|
||||
<container :width="480" :height="320">
|
||||
<text :x="40" :y="24" text="打开 UI" fillStyle="#e8f0ff" />
|
||||
<g-rect
|
||||
:x="40"
|
||||
:y="40"
|
||||
:width="150"
|
||||
:height="56"
|
||||
fillStyle="#2f6fed"
|
||||
strokeStyle="#9dc0ff"
|
||||
:lineWidth="2"
|
||||
@click="openUi"
|
||||
/>
|
||||
<text :x="240" :y="24" text="关闭 UI" fillStyle="#e8f0ff" />
|
||||
<g-rect
|
||||
:x="240"
|
||||
:y="40"
|
||||
:width="150"
|
||||
:height="56"
|
||||
fillStyle="#3a2f5f"
|
||||
strokeStyle="#b09dff"
|
||||
:lineWidth="2"
|
||||
@click="closeUi"
|
||||
/>
|
||||
<text
|
||||
:x="40"
|
||||
:y="140"
|
||||
:text="'栈长度:' + stackSize"
|
||||
fillStyle="#8fb6ff"
|
||||
/>
|
||||
<text
|
||||
:x="40"
|
||||
:y="180"
|
||||
:text="active ? '已打开' : '未打开'"
|
||||
fillStyle="#8fb6ff"
|
||||
/>
|
||||
<UIRenderer />
|
||||
</container>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, defineComponent, h, onUnmounted } from 'vue';
|
||||
import {
|
||||
UIController,
|
||||
GameUI,
|
||||
IUIDefaultPropsBase,
|
||||
IUIInstance
|
||||
} from '@motajs/system';
|
||||
|
||||
const controller = new UIController<IUIDefaultPropsBase>();
|
||||
controller.setPropsBaseCreator((instance, ctrl) => ({
|
||||
instance,
|
||||
controller: ctrl
|
||||
}));
|
||||
|
||||
/** 当前打开的 UI 实例,打开与关闭必须复用同一个变量 */
|
||||
let instance: IUIInstance | null = null;
|
||||
|
||||
/** 最小 UI 组件,用渲染函数构造,不依赖运行时模板编译器 */
|
||||
const DemoPanel = defineComponent({
|
||||
name: 'PocDemoPanel',
|
||||
setup() {
|
||||
return () =>
|
||||
h('container', { x: 240, y: 140, width: 220, height: 150 }, [
|
||||
h('g-rect', {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 220,
|
||||
height: 150,
|
||||
fillStyle: '#1d2b45',
|
||||
strokeStyle: '#7aa2ff',
|
||||
lineWidth: 2
|
||||
}),
|
||||
h('text', {
|
||||
x: 16,
|
||||
y: 40,
|
||||
text: '这是一个最小 UI',
|
||||
fillStyle: '#e8f0ff'
|
||||
}),
|
||||
h('text', {
|
||||
x: 16,
|
||||
y: 80,
|
||||
text: '由 UIController 打开',
|
||||
fillStyle: '#8fb6ff'
|
||||
})
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
const ui = new GameUI('poc-minimal-ui', DemoPanel);
|
||||
|
||||
/** 把 controller.render() 挂进同一棵渲染树 */
|
||||
const UIRenderer = defineComponent({
|
||||
name: 'PocUIRenderer',
|
||||
setup() {
|
||||
return () => controller.render();
|
||||
}
|
||||
});
|
||||
|
||||
const active = computed<boolean>(() => controller.active.value);
|
||||
const stackSize = computed<number>(() => controller.stack.length);
|
||||
|
||||
/** 打开最小 UI,已打开时不重复入栈 */
|
||||
function openUi(): void {
|
||||
if (instance) return;
|
||||
instance = controller.open(ui, {});
|
||||
}
|
||||
|
||||
/** 关闭最小 UI,复用打开时保存的实例 */
|
||||
function closeUi(): void {
|
||||
if (instance) {
|
||||
controller.close(instance);
|
||||
instance = null;
|
||||
}
|
||||
}
|
||||
|
||||
onUnmounted(() => {
|
||||
controller.closeAll();
|
||||
});
|
||||
</script>
|
||||
84
docs/.vitepress/components/renderDemo.vue
Normal file
84
docs/.vitepress/components/renderDemo.vue
Normal file
@ -0,0 +1,84 @@
|
||||
<template>
|
||||
<div :style="{ background: props.background, overflow: 'hidden' }">
|
||||
<canvas ref="canvasRef"></canvas>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Component, onMounted, onUnmounted, ref } from 'vue';
|
||||
|
||||
/** demo 模块的导出形态,只要求默认导出组件 */
|
||||
interface DemoModule {
|
||||
default: Component;
|
||||
}
|
||||
|
||||
/** 动态导入一个 demo 组件的加载函数 */
|
||||
type DemoLoader = () => Promise<DemoModule>;
|
||||
|
||||
/** 包装组件的外部参数 */
|
||||
interface RenderDemoProps {
|
||||
/** 动态导入 demo 组件的加载函数 */
|
||||
loader: DemoLoader;
|
||||
/** 逻辑宽度 */
|
||||
width?: number;
|
||||
/** 逻辑高度 */
|
||||
height?: number;
|
||||
/** 缩放比例 */
|
||||
scale?: number;
|
||||
/** 画布容器背景色 */
|
||||
background?: string;
|
||||
}
|
||||
|
||||
/** 可清理的 app 引用,仅声明清理所需的 unmount */
|
||||
interface AppHandle {
|
||||
unmount(): void;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<RenderDemoProps>(), {
|
||||
width: 480,
|
||||
height: 320,
|
||||
scale: 1,
|
||||
background: '#101418'
|
||||
});
|
||||
|
||||
const canvasRef = ref<HTMLCanvasElement | null>(null);
|
||||
|
||||
/** 组件是否已经卸载,用于拦截动态导入完成后的竞态 */
|
||||
let disposed = false;
|
||||
/** 渲染器的清理入口,在渲染器创建后、挂载 app 之前赋值 */
|
||||
let dispose: (() => void) | null = null;
|
||||
|
||||
onMounted(async () => {
|
||||
const { MotaRenderer } = await import('@motajs/render');
|
||||
const { createRendererFor } = await import('@motajs/render-vue');
|
||||
const demo = await props.loader();
|
||||
if (disposed || !canvasRef.value) return;
|
||||
|
||||
const canvas = canvasRef.value;
|
||||
const renderer = new MotaRenderer({
|
||||
canvas,
|
||||
width: props.width,
|
||||
height: props.height
|
||||
});
|
||||
let appRef: AppHandle | null = null;
|
||||
dispose = () => {
|
||||
if (appRef) {
|
||||
appRef.unmount();
|
||||
}
|
||||
renderer.destroy();
|
||||
};
|
||||
if (props.scale !== 1) {
|
||||
renderer.setScale(props.scale);
|
||||
}
|
||||
const app = createRendererFor(renderer).createApp(demo.default);
|
||||
appRef = app;
|
||||
app.mount(renderer);
|
||||
// 一个包装实例只服务一个 demo,loader 变更不触发重挂载
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
disposed = true;
|
||||
dispose?.();
|
||||
dispose = null;
|
||||
});
|
||||
</script>
|
||||
@ -1,7 +1,30 @@
|
||||
import { defineConfig } from 'vitepress';
|
||||
import { MermaidMarkdown, MermaidPlugin } from 'vitepress-plugin-mermaid';
|
||||
import vuejsx from '@vitejs/plugin-vue-jsx';
|
||||
import { join } from 'path';
|
||||
|
||||
/** 文档站内的自定义渲染标签,与主站 vite.config.ts 保持一致 */
|
||||
const custom = [
|
||||
'container',
|
||||
'custom',
|
||||
'text',
|
||||
'image',
|
||||
'shader',
|
||||
'comment',
|
||||
'custom-container',
|
||||
'map-render',
|
||||
'animate',
|
||||
'damage',
|
||||
'graphics',
|
||||
'icon',
|
||||
'winskin'
|
||||
];
|
||||
|
||||
/** 判断一个标签是否为自定义渲染标签 */
|
||||
function isCustomElement(tag: string): boolean {
|
||||
return custom.includes(tag) || tag.startsWith('g-');
|
||||
}
|
||||
|
||||
// https://vitepress.dev/reference/site-config
|
||||
export default defineConfig({
|
||||
title: 'HTML5 魔塔样板 V2.B',
|
||||
@ -298,9 +321,29 @@ export default defineConfig({
|
||||
label: '中文'
|
||||
}
|
||||
},
|
||||
vue: {
|
||||
template: {
|
||||
compilerOptions: {
|
||||
isCustomElement
|
||||
}
|
||||
}
|
||||
},
|
||||
vite: {
|
||||
// @ts-expect-error 类型错误
|
||||
plugins: [MermaidPlugin()],
|
||||
plugins: [MermaidPlugin(), vuejsx({ isCustomElement })],
|
||||
resolve: {
|
||||
alias: [
|
||||
{
|
||||
find: /^@motajs\/(.+)$/,
|
||||
replacement: `${process.cwd()}/packages/$1/src`
|
||||
},
|
||||
{
|
||||
find: /^@user\/(.+)$/,
|
||||
replacement: `${process.cwd()}/packages-user/$1/src`
|
||||
}
|
||||
],
|
||||
dedupe: ['vue']
|
||||
},
|
||||
optimizeDeps: {
|
||||
include: ['mermaid']
|
||||
},
|
||||
|
||||
26
docs/poc/render-demo.md
Normal file
26
docs/poc/render-demo.md
Normal file
@ -0,0 +1,26 @@
|
||||
# 渲染器内嵌演示(临时技术验证)
|
||||
|
||||
本页是「文档内嵌可运行示例」的**临时技术验证页**,不进入导航;POC 通过后再由用户决定是否保留。
|
||||
|
||||
## 覆盖范围
|
||||
|
||||
- 渲染基础:容器、矩形、圆、文字,以及点击图形后计数与颜色的变化。
|
||||
- UISystem:`UIController` 打开 / 关闭一个最小 UI,页面上的栈长度与打开状态随之变化。
|
||||
|
||||
两个示例都只在浏览器中挂载;构建期预渲染会跳过它们,因此不会触碰 `document`。
|
||||
|
||||
<ClientOnly>
|
||||
<RenderDemo :loader="basicLoader" :width="480" :height="320" />
|
||||
<RenderDemo :loader="uiLoader" :width="480" :height="320" />
|
||||
<template #fallback>
|
||||
渲染示例需要浏览器环境。
|
||||
</template>
|
||||
</ClientOnly>
|
||||
|
||||
<script setup>
|
||||
import RenderDemo from '../.vitepress/components/renderDemo.vue';
|
||||
|
||||
const basicLoader = () =>
|
||||
import('../.vitepress/components/demos/basicRender.vue');
|
||||
const uiLoader = () => import('../.vitepress/components/demos/uiSystem.vue');
|
||||
</script>
|
||||
16
docs/tsconfig.json
Normal file
16
docs/tsconfig.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"extends": "../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"baseUrl": "..",
|
||||
"ignoreDeprecations": "6.0",
|
||||
"paths": {
|
||||
"@motajs/*": ["./packages/*/src"],
|
||||
"@user/*": ["./packages-user/*/src"]
|
||||
}
|
||||
},
|
||||
"include": [
|
||||
"./.vitepress/**/*.ts",
|
||||
"./.vitepress/**/*.vue",
|
||||
"../src/types/vite-env.d.ts"
|
||||
]
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user