Roomify:使用React与无服务器Puter构建AI室内可视化工具
Roomify能在几秒内将2D平面图转换为逼真的3D渲染图。该应用前端采用React 19与TypeScript,后端逻辑、存储和AI生成则依托Puter平台。无需自定义后端,前端通过puter.js SDK直接与Puter API交互。
该架构摒弃了传统服务器:文件上传、项目创建和AI调用均在云端工作者中完成。这简化了中级和高级开发者的部署与扩展流程。
技术栈及其作用
- React 19 + TypeScript:通过Vite实现热模块替换的强类型前端。
- React Router 7:管理首页和可视化页面的路由。
- Tailwind CSS:基于实用类的样式,无需自定义CSS。
- Lucide React:用户界面图标库。
- Puter.js:从浏览器访问文件存储、键值存储、工作者和AI模型。
Puter提供无服务器工作者、键值存储和持久化文件存储。工作者通过puter.action调用,AI则通过puter.ai.generate调用。
架构:从上传到渲染
处理流程:
- 将平面图上传至
puter.file.write。 - 获取文件的公开URL。
- 调用
project.create工作者,将元数据写入键值存储。 - 通过
ai.render3d工作者生成渲染:加载图像、构建提示词、调用Claude/Gemini、保存结果。 - 更新键值存储中的渲染URL并返回前端。
前端使用react-compare-slider对比显示原始平面图和渲染图。
工作者函数:代码与逻辑
JavaScript工作者函数通过Puter CLI上传。
创建项目:
// lib/puter.action.ts
export async function createProject({ item, visibility }) {
const result = await puter.action('project.create', {
item,
visibility,
});
return result;
}
export async function getProjects() {
return await puter.action('project.list');
}
export async function getProjectById({ id }) {
return await puter.action('project.get', { id });
}
生成渲染:
// worker: ai.render3d
export default async ({ sourceImage, style = 'photorealistic' }) => {
const imageUrl = typeof sourceImage === 'string' ? sourceImage : await puter.file.url(sourceImage);
const prompt = `
将此平面图转换为${style}风格的3D室内渲染图。
图像显示房间布局,包括墙壁、窗户和门。
从最佳展示空间的角度生成逼真视图。
添加合适的家具、纹理、照明和阴影。
使其看起来像专业的建筑可视化效果。
`;
const result = await puter.ai.generate({
model: 'claude-3-sonnet',
prompt,
images: [imageUrl],
responseFormat: 'image'
});
const renderedPath = `/projects/${projectId}/rendered.png`;
await puter.file.write(renderedPath, result.image);
const key = `project:${projectId}`;
const project = JSON.parse(await puter.kv.get(key));
project.renderedImage = puter.file.url(renderedPath);
await puter.kv.set(key, JSON.stringify(project));
return {
renderedImage: project.renderedImage,
renderedPath
};
};
键值存储保存元数据:项目名称、所有者、隐私设置和文件链接。
前端:组件与钩子
首页(Home)显示项目列表。Upload组件将文件转换为base64:
// components/Upload.tsx
const handleFileChange = async (e) => {
const file = e.target.files[0];
if (!file) return;
const base64 = await toBase64(file);
onComplete(base64);
};
可视化页面(VisualizerId)使用useEffect实现自动生成:
useEffect(() => {
if (isProjectLoading || hasInitialGenerated.current || !project?.sourceImage) return;
if (project.renderedImage) {
setCurrentImage(project.renderedImage);
hasInitialGenerated.current = true;
return;
}
hasInitialGenerated.current = true;
void runGeneration(project);
}, [project, isProjectLoading]);
useRef防止重复调用。状态本地管理,无需Redux。
解决常见问题
- 跨域问题:开发环境中使用Vite代理。
- Base64大小:单独保存文件,在键值存储中存储链接。
- 重复生成:
useRef+ 检查机制。 - 异步处理:加载状态、骨架屏。
- 移动端UI:Tailwind响应式设计、滑块触摸支持。
验证:文件最大10 MB,支持JPG/PNG格式。生成时间10-15秒,提供加载指示器。
关键要点
- Puter替代后端:工作者、键值存储、文件和AI集成于一个SDK。
- React钩子管理状态,无需外部库。
- 提示词决定质量:详细描述墙壁、家具和照明。
- 架构支持生产环境扩展,无需DevOps。
- 代码开源,适合分叉和实验。
— Editorial Team
暂无评论。