目录
-
- 摘要
- [1. 引言 - 为什么需要 Canvas?](#1. 引言 - 为什么需要 Canvas?)
-
- [1.1 Canvas 的价值](#1.1 Canvas 的价值)
- [1.2 Canvas 架构](#1.2 Canvas 架构)
- [1.3 Canvas 能力概览](#1.3 Canvas 能力概览)
- [2. Canvas 基础操作](#2. Canvas 基础操作)
-
- [2.1 展示界面 (present)](#2.1 展示界面 (present))
- [2.2 导航页面 (navigate)](#2.2 导航页面 (navigate))
- [2.3 执行脚本 (eval)](#2.3 执行脚本 (eval))
- [2.4 截图 (snapshot)](#2.4 截图 (snapshot))
- [3. 展示 HTML 内容](#3. 展示 HTML 内容)
-
- [3.1 基本用法](#3.1 基本用法)
- [3.2 带样式的界面](#3.2 带样式的界面)
- [3.3 响应式设计](#3.3 响应式设计)
- [4. 展示外部网页](#4. 展示外部网页)
-
- [4.1 基本用法](#4.1 基本用法)
- [4.2 设置尺寸](#4.2 设置尺寸)
- [4.3 嵌入文档](#4.3 嵌入文档)
- [5. 实战案例一:数据仪表盘](#5. 实战案例一:数据仪表盘)
-
- [5.1 场景描述](#5.1 场景描述)
- [5.2 实现代码](#5.2 实现代码)
- [6. 实战案例二:交互式表单](#6. 实战案例二:交互式表单)
-
- [6.1 场景描述](#6.1 场景描述)
- [6.2 实现代码](#6.2 实现代码)
- [7. 实战案例三:报告展示](#7. 实战案例三:报告展示)
-
- [7.1 场景描述](#7.1 场景描述)
- [7.2 实现代码](#7.2 实现代码)
- [8. 高级功能](#8. 高级功能)
-
- [8.1 执行 JavaScript](#8.1 执行 JavaScript)
- [8.2 获取截图](#8.2 获取截图)
- [8.3 A2UI 交互](#8.3 A2UI 交互)
- [9. 最佳实践](#9. 最佳实践)
-
- [9.1 设计原则](#9.1 设计原则)
- [9.2 常见问题](#9.2 常见问题)
- [10. 总结](#10. 总结)
-
- [10.1 核心要点](#10.1 核心要点)
- [10.2 下一步](#10.2 下一步)
- 参考资料
摘要
本文介绍 OpenClaw 框架中的 Canvas 可视化界面功能。从 Canvas 基本概念、核心操作、界面展示到实际应用,全面解析如何通过 AI Agent 创建和管理可视化界面。通过实际案例演示网页嵌入、自定义界面、交互式应用等场景,帮助开发者构建具有可视化能力的智能应用。🎨
1. 引言 - 为什么需要 Canvas?
1.1 Canvas 的价值
| 场景 | 说明 | 示例 |
|---|---|---|
| 可视化展示 | 展示图表、报告 | 数据仪表盘 |
| 交互界面 | 提供用户交互 | 配置面板 |
| 网页嵌入 | 嵌入外部网页 | 文档、工具 |
| 实时预览 | 实时展示结果 | 代码预览 |
1.2 Canvas 架构
OpenClaw Canvas
AI Agent
Canvas Tool
操作类型
present - 展示界面
navigate - 导航URL
eval - 执行JS
snapshot - 截图
渲染界面
获取截图
1.3 Canvas 能力概览
| 能力 | 说明 | Action |
|---|---|---|
| 展示界面 | 展示 HTML/URL | present |
| 导航页面 | 加载指定 URL | navigate |
| 执行脚本 | 运行 JavaScript | eval |
| 截图 | 捕获界面图像 | snapshot |
| A2UI | 交互式界面 | a2ui_push |
2. Canvas 基础操作
2.1 展示界面 (present)
python
canvas(
action="present",
url="https://example.com" # 或使用 html 参数
)
2.2 导航页面 (navigate)
python
canvas(
action="navigate",
url="https://example.com"
)
2.3 执行脚本 (eval)
python
canvas(
action="eval",
javaScript="document.title"
)
2.4 截图 (snapshot)
python
canvas(
action="snapshot",
outputFormat="png" # 或 "jpg"
)
3. 展示 HTML 内容
3.1 基本用法
python
canvas(
action="present",
html="""
<!DOCTYPE html>
<html>
<head>
<title>Hello Canvas</title>
</head>
<body>
<h1>Hello, OpenClaw Canvas!</h1>
<p>这是一个简单的 HTML 页面</p>
</body>
</html>
"""
)
3.2 带样式的界面
python
canvas(
action="present",
html="""
<!DOCTYPE html>
<html>
<head>
<style>
body {
font-family: Arial, sans-serif;
padding: 20px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.card {
background: rgba(255,255,255,0.1);
padding: 20px;
border-radius: 10px;
backdrop-filter: blur(10px);
}
h1 { margin: 0; }
</style>
</head>
<body>
<div class="card">
<h1>📊 数据仪表盘</h1>
<p>实时数据展示</p>
</div>
</body>
</html>
"""
)
3.3 响应式设计
python
canvas(
action="present",
html="""
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
.container {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 20px;
padding: 20px;
}
.item {
background: #f5f5f5;
padding: 20px;
border-radius: 8px;
text-align: center;
}
</style>
</head>
<body>
<div class="container">
<div class="item">项目 1</div>
<div class="item">项目 2</div>
<div class="item">项目 3</div>
<div class="item">项目 4</div>
</div>
</body>
</html>
"""
)
4. 展示外部网页
4.1 基本用法
python
canvas(
action="present",
url="https://docs.openclaw.ai"
)
4.2 设置尺寸
python
canvas(
action="present",
url="https://example.com",
width=800,
height=600
)
4.3 嵌入文档
python
def show_documentation():
"""展示 OpenClaw 文档"""
canvas(
action="present",
url="https://docs.openclaw.ai",
width=1000,
height=700
)
5. 实战案例一:数据仪表盘
5.1 场景描述
创建一个实时数据展示仪表盘。
5.2 实现代码
python
def show_dashboard(data):
"""展示数据仪表盘"""
html = f"""
<!DOCTYPE html>
<html>
<head>
<title>数据仪表盘</title>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<style>
body {{
font-family: 'Segoe UI', Arial, sans-serif;
background: #1a1a2e;
color: white;
padding: 20px;
margin: 0;
}}
.header {{
text-align: center;
margin-bottom: 30px;
}}
.stats {{
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 20px;
margin-bottom: 30px;
}}
.stat-card {{
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 20px;
border-radius: 10px;
text-align: center;
}}
.stat-value {{
font-size: 2em;
font-weight: bold;
}}
.stat-label {{
opacity: 0.8;
}}
.chart-container {{
background: #16213e;
padding: 20px;
border-radius: 10px;
}}
</style>
</head>
<body>
<div class="header">
<h1>📊 实时数据仪表盘</h1>
<p>数据更新时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}</p>
</div>
<div class="stats">
<div class="stat-card">
<div class="stat-value">{data['total_users']}</div>
<div class="stat-label">总用户数</div>
</div>
<div class="stat-card">
<div class="stat-value">{data['active_users']}</div>
<div class="stat-label">活跃用户</div>
</div>
<div class="stat-card">
<div class="stat-value">{data['requests']}</div>
<div class="stat-label">请求数</div>
</div>
<div class="stat-card">
<div class="stat-value">{data['success_rate']}%</div>
<div class="stat-label">成功率</div>
</div>
</div>
<div class="chart-container">
<canvas id="chart"></canvas>
</div>
<script>
const ctx = document.getElementById('chart').getContext('2d');
new Chart(ctx, {{
type: 'line',
data: {{
labels: {data['labels']},
datasets: [{{
label: '请求数',
data: {data['values']},
borderColor: '#667eea',
backgroundColor: 'rgba(102, 126, 234, 0.1)',
fill: true
}}]
}},
options: {{
responsive: true,
plugins: {{
legend: {{
labels: {{ color: 'white' }}
}}
}},
scales: {{
y: {{
ticks: {{ color: 'white' }},
grid: {{ color: 'rgba(255,255,255,0.1)' }}
}},
x: {{
ticks: {{ color: 'white' }},
grid: {{ color: 'rgba(255,255,255,0.1)' }}
}}
}}
}}
}});
</script>
</body>
</html>
"""
canvas(action="present", html=html)
6. 实战案例二:交互式表单
6.1 场景描述
创建一个可交互的配置表单。
6.2 实现代码
python
def show_config_form():
"""展示配置表单"""
html = """
<!DOCTYPE html>
<html>
<head>
<title>配置面板</title>
<style>
body {
font-family: Arial, sans-serif;
background: #f5f5f5;
padding: 20px;
}
.form-container {
max-width: 500px;
margin: 0 auto;
background: white;
padding: 30px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
h2 {
margin-top: 0;
color: #333;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: bold;
color: #555;
}
input, select {
width: 100%;
padding: 10px;
border: 1px solid #ddd;
border-radius: 5px;
box-sizing: border-box;
}
.btn {
background: #667eea;
color: white;
border: none;
padding: 12px 24px;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
}
.btn:hover {
background: #5a6fd6;
}
</style>
</head>
<body>
<div class="form-container">
<h2>⚙️ 配置面板</h2>
<div class="form-group">
<label>模型选择</label>
<select id="model">
<option value="gpt-4">GPT-4</option>
<option value="gpt-3.5-turbo">GPT-3.5 Turbo</option>
<option value="claude-3">Claude 3</option>
</select>
</div>
<div class="form-group">
<label>温度参数</label>
<input type="number" id="temperature" value="0.7" min="0" max="2" step="0.1">
</div>
<div class="form-group">
<label>最大Token数</label>
<input type="number" id="max_tokens" value="2000" min="100" max="8000">
</div>
<button class="btn" onclick="submitConfig()">保存配置</button>
</div>
<script>
function submitConfig() {
const config = {
model: document.getElementById('model').value,
temperature: parseFloat(document.getElementById('temperature').value),
max_tokens: parseInt(document.getElementById('max_tokens').value)
};
console.log('配置已保存:', config);
alert('配置已保存!');
}
</script>
</body>
</html>
"""
canvas(action="present", html=html)
7. 实战案例三:报告展示
7.1 场景描述
展示格式化的报告内容。
7.2 实现代码
python
def show_report(title, content, stats):
"""展示报告"""
html = f"""
<!DOCTYPE html>
<html>
<head>
<title>{title}</title>
<style>
body {{
font-family: 'Georgia', serif;
max-width: 800px;
margin: 0 auto;
padding: 40px;
background: #fafafa;
}}
.report {{
background: white;
padding: 40px;
border-radius: 10px;
box-shadow: 0 2px 20px rgba(0,0,0,0.1);
}}
h1 {{
color: #333;
border-bottom: 3px solid #667eea;
padding-bottom: 10px;
}}
.meta {{
color: #888;
font-size: 0.9em;
margin-bottom: 30px;
}}
.content {{
line-height: 1.8;
color: #444;
}}
.stats-box {{
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
margin: 20px 0;
}}
.stats-box h3 {{
margin-top: 0;
color: #667eea;
}}
.stat-item {{
display: flex;
justify-content: space-between;
padding: 10px 0;
border-bottom: 1px solid #eee;
}}
</style>
</head>
<body>
<div class="report">
<h1>{title}</h1>
<div class="meta">
生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
</div>
<div class="content">
{content}
</div>
<div class="stats-box">
<h3>📊 统计数据</h3>
<div class="stat-item">
<span>总字数</span>
<span>{stats['words']}</span>
</div>
<div class="stat-item">
<span>段落数</span>
<span>{stats['paragraphs']}</span>
</div>
<div class="stat-item">
<span>预计阅读时间</span>
<span>{stats['read_time']} 分钟</span>
</div>
</div>
</div>
</body>
</html>
"""
canvas(action="present", html=html)
8. 高级功能
8.1 执行 JavaScript
python
def update_canvas_data(data):
"""更新 Canvas 中的数据"""
canvas(
action="eval",
javaScript=f"""
document.getElementById('data-display').textContent = '{data}';
"""
)
8.2 获取截图
python
def capture_canvas():
"""截取 Canvas 内容"""
screenshot = canvas(
action="snapshot",
outputFormat="png"
)
return screenshot
8.3 A2UI 交互
python
def push_interactive_ui():
"""推送交互式界面"""
canvas(
action="a2ui_push",
jsonl="""
{"type": "text", "content": "Hello"}
{"type": "button", "label": "Click me", "action": "click"}
"""
)
9. 最佳实践
9.1 设计原则
| 原则 | 说明 |
|---|---|
| 简洁清晰 | 界面简洁,信息清晰 |
| 响应式 | 适配不同尺寸 |
| 性能优化 | 避免过多资源 |
| 用户体验 | 良好的交互反馈 |
9.2 常见问题
| 问题 | 解决方案 |
|---|---|
| 加载慢 | 减少外部资源 |
| 样式错乱 | 使用内联样式 |
| 交互失效 | 检查JS代码 |
| 截图空白 | 等待加载完成 |
10. 总结
10.1 核心要点
| 要点 | 说明 |
|---|---|
| HTML展示 | 使用 present + html 参数 |
| URL展示 | 使用 present + url 参数 |
| 脚本执行 | 使用 eval 执行 JS |
| 截图获取 | 使用 snapshot 获取图像 |
10.2 下一步
- 第52篇:OpenClaw Canvas 导航:URL 加载与控制
- 第53篇:OpenClaw Canvas 执行:JavaScript 注入实战