Skip to content

快速开始

安装

shell npm i @chamn/engine @chamn/model @chamn/render

你可以在你的机器上的任何地方运行 create astro,因此在开始之前无需创建一个新的空目录。如果你还没有为你的新项目准备一个空目录,向导将会自动为你创建一个。

如果一切顺利,你将看到一个成功信息,随后是一些推荐的后续步骤。现在你的项目已经创建好了,你可以 cd 进入你的新项目目录开始使用 Astro。

由于演示用例使用了 antd 以及 @chamn/demo-page 库(这不是必须的,只是这里做演示使用),所以你还需要安装:

shell npm i antd @ant-design/icons @chamn/demo-page

用法

创建一个 Editor.tsx 文件,将以下代码拷贝进去:

Editor.tsx
import { BasePage, Material } from '@chamn/demo-page';
import { Button, message, Modal } from 'antd';
import React, { useCallback, useEffect, useState } from 'react';
import ReactDOM from 'react-dom';
import ReactDOMClient from 'react-dom/client';
import '@chamn/engine/dist/style.css';
import { Engine, EnginContext, InnerComponentMeta, plugins } from '@chamn/engine';
import { RollbackOutlined } from '@ant-design/icons';
import './index.css';
const { DisplaySourceSchema, DEFAULT_PLUGIN_LIST } = plugins;
const win = window as any;
win.React = React;
win.ReactDOM = ReactDOM;
win.ReactDOMClient = ReactDOMClient;
export const App = () => {
const [ready, setReady] = useState(false);
const [page, setPage] = useState(BasePage);
useEffect(() => {
// 从本地获取 page schema
const localPage = localStorage.getItem('pageSchema');
if (localPage) {
setPage(JSON.parse(localPage));
}
setReady(true);
}, []);
const onReady = useCallback(async (ctx: EnginContext) => {
const designer = await ctx.pluginManager.onPluginReadyOk('Designer');
const reloadPage = async () => {
setTimeout(() => {
const designerExports = designer?.export;
designerExports.reload();
}, 0);
};
// 获取 引擎 工作台对象
const workbench = ctx.engine.getWorkbench();
// 自定义顶部 bar
workbench?.replaceTopBarView(
<div
style={{
width: '100%',
height: '100%',
display: 'flex',
alignItems: 'center',
justifyContent: 'flex-end',
paddingRight: '10px',
}}
>
<div className="logo">Chameleon EG</div>
<a target="_blank" href="https://github.com/hlerenow/chameleon" rel="noreferrer">
<Button style={{ marginRight: '10px' }}>Github </Button>
</a>
<Button
style={{ marginRight: '10px' }}
onClick={async () => {
const res = await ctx.pluginManager.get('History');
res?.export.preStep();
}}
>
<RollbackOutlined />
</Button>
<Button
style={{ marginRight: '10px' }}
onClick={async () => {
const res = await ctx.pluginManager.get('History');
res?.export.nextStep();
}}
>
<RollbackOutlined
style={{
transform: 'rotateY(180deg)',
}}
/>
</Button>
<DisplaySourceSchema pageModel={ctx.engine.pageModel} engineCtx={ctx}>
<Button style={{ marginRight: '10px' }}>Source Code</Button>
</DisplaySourceSchema>
<Button
style={{ marginRight: '10px' }}
onClick={() => {
reloadPage();
}}
>
Refresh Page
</Button>
<Button
style={{ marginRight: '10px' }}
onClick={() => {
const src = '/#/preview';
Modal.info({
closable: true,
icon: null,
width: 'calc(100vw - 100px)',
centered: true,
title: (
<div>
Preview
<Button
size="small"
style={{
float: 'right',
marginRight: '30px',
}}
onClick={() => {
window.open(src);
}}
>
Open in new window
</Button>
</div>
),
content: (
<div
style={{
width: '100%',
height: 'calc(100vh - 200px)',
}}
>
<iframe
style={{
border: '1px solid #e7e7e7',
width: '100%',
height: '100%',
borderRadius: '4px',
overflow: 'hidden',
}}
src={src}
/>
</div>
),
footer: null,
});
}}
>
Preview
</Button>
<Button
type="primary"
onClick={() => {
const newPage = ctx.engine.pageModel.export();
localStorage.setItem('pageSchema', JSON.stringify(newPage));
message.success('Save successfully');
}}
>
Save
</Button>
</div>
);
}, []);
if (!ready) {
return <>loading...</>;
}
return (
<Engine
plugins={DEFAULT_PLUGIN_LIST}
schema={page}
// 传入组件物料, 这里使用内置的基础物料以及 测试物料信
material={[...InnerComponentMeta, ...Material]}
onReady={onReady}
/>
);
};

添加 css 让编辑器撑满窗口

index.css
html,
body,
#root {
width: 100%;
height: 100%;
overflow: hidden;
}
body {
margin: 0;
padding: 0;
}
.logo {
height: 100%;
font-size: 20px;
display: flex;
align-items: center;
margin-left: 20px;
font-weight: bolder;
margin-right: auto;
}

配置 render.umd.js

因为渲染画布是在 iframe 中运行,所以需使用 umd 模式的 js,然后让 iframe 加载。在 vite 构建工具下,有以下三种配置方式:

方式一:使用 ?url 后缀导入(推荐)

这是最简单的方式,直接使用 vite 的 ?url 后缀导入文件 URL,无需额外配置:

import renderAsURL from '@chamn/render/dist/index.umd.js?url';
<Engine
plugins={DEFAULT_PLUGIN_LIST}
schema={page}
material={[...InnerComponentMeta, ...Material]}
assetPackagesList={assetPackagesList}
onReady={onReady}
renderJSUrl={renderAsURL}
/>;

或者从 node_modules 路径导入:

import renderAsURL from '../../node_modules/@chamn/render/dist/index.umd.js?url';

方式二:使用 vite-plugin-static-copy 插件

使用 vite-plugin-static-copy 插件在构建时自动将文件拷贝到输出目录:

首先安装依赖:

shell npm i vite-plugin-static-copy

然后在 vite.config.ts 中配置:

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import monacoEditorPlugin from 'vite-plugin-monaco-editor';
import { viteStaticCopy } from 'vite-plugin-static-copy';
export default defineConfig({
plugins: [
react(),
monacoEditorPlugin({}),
viteStaticCopy({
targets: [
{
src: './node_modules/@chamn/render/dist/index.umd.js',
dest: './',
rename: 'render.umd.js',
},
],
}),
],
});

这样构建时会自动将 render.umd.js 拷贝到输出目录的根目录下,然后使用相对路径:

<Engine
plugins={DEFAULT_PLUGIN_LIST}
schema={page}
material={[...InnerComponentMeta, ...Material]}
assetPackagesList={assetPackagesList}
onReady={onReady}
renderJSUrl="./render.umd.js"
/>

方式三:使用 CDN 或自定义路径

如果使用 CDN 或其他自定义路径,可以直接传入 URL:

<Engine
plugins={DEFAULT_PLUGIN_LIST}
schema={page}
material={[...InnerComponentMeta, ...Material]}
assetPackagesList={assetPackagesList}
onReady={onReady}
renderJSUrl="https://cdn.example.com/render.umd.js"
/>

或者使用相对路径:

<Engine
plugins={DEFAULT_PLUGIN_LIST}
schema={page}
material={[...InnerComponentMeta, ...Material]}
assetPackagesList={assetPackagesList}
onReady={onReady}
renderJSUrl="./public/render.umd.js"
/>

配置 monaco-editor 构建

因为引擎使用了 monaco-editor 代码编辑器所以需要配置相应的构建配置

Vite

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import monacoEditorPlugin from 'vite-plugin-monaco-editor';
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), monacoEditorPlugin({})],
});

Webpack

Refenrence

运行

将 Editor.tsx 页面作为你项目中的一个页面,运行,打开对应的地址你将会得到一个编辑页面

Engine thumbail

Good Luck! :)