我打造了一个工具,可通过一次 Drag & Drop 生成160种不同的代码组合——操作方法
Source: Dev.to
TL;DR
我已经厌倦了一遍又一遍地编写相同的活动着陆页代码。因此我创建了 PromoKit ——将按钮拖到图片上,选择你的框架,即可获得可直接投产的代码。
10 种框架 × 16 种样式选项。
让我带你了解我是如何打造这个“怪兽”的。
起源故事:“能把那个按钮上移 3 像素吗?”
- 编写 HTML。
- 添加用于定位、按钮样式、悬停效果的 CSS。
- 完成。
随后收到邮件:
- “能把 CTA 按钮稍微向右移动一下吗?” → 调整 CSS → 完成。
- “其实,能把它上移 20 像素吗?” → 再次调整 CSS → 完成。
我构建的:PromoKit
- 上传 你的宣传图片
- 拖拽 按钮、文字和图片覆盖层
- 选择 你的框架和样式方式
- 复制 可直接用于生产的代码
就是这样。再也不需要手动计算坐标。再也不必为 React、Vue 和 Vanilla JS 重复编写相同的代码。
在线演示
https://promotion-page-editor.netlify.app/
源代码
GitHub – seadonggyun4/promo-kit
让我自豪的数字
| 类别 | 细节 |
|---|---|
| 框架 | React, Vue, Svelte, Angular, Solid.js, Preact, Astro, Qwik, Lit, Vanilla HTML |
| 样式选项 | CSS, SCSS, Tailwind, Styled Components, Emotion, CSS Modules, UnoCSS, Panda CSS, … |
| 预设 | 27 种按钮样式 + 5 种文字样式 + 12 种图片叠加 = 44 |
| 组合 | 任意框架 × 任意样式 = 160 种可能的技术栈 |
Source: …
技术深度解析:它是如何构建的
src/
├── features/
│ ├── button-editor/ # Button creation & customization
│ ├── text-editor/ # Text element management
│ ├── image-overlay-editor/ # Image overlay handling
│ ├── download/ # The 2200+‑line code generator
│ └── version-history/ # Undo/redo system
└── shared/
└── store/ # Zustand state management
每个功能都是完全独立的。想要添加新的元素类型?新建一个 feature 文件夹——完成。无需触碰已有代码。
状态管理:带超能力的 Zustand
// Every mutation automatically logs to history
const updateElement = (id, data) => {
set(state => {
const updated = state.elements.map(el =>
el.id === id ? { ...el, ...data } : el
);
// Automatic history tracking!
pushToHistory(updated, 'Style Changed', 'element_style');
return { elements: updated };
});
};
**魔法所在:**用户根本不需要考虑保存状态。每一次操作都会自动可逆。Ctrl+Z 直接生效。
历史记录系统:正确实现的命令模式
interface HistoryStore {
past: EditorSnapshot[]; // Previous states
present: EditorSnapshot; // Current state
future: EditorSnapshot[]; // Redo states
}
每个快照记录:
- 所有元素及其位置和样式
- 背景图(已压缩)
- 当前选中的元素
- 时间戳和操作描述
- 用于视觉区分的操作类型
防抖历史记录
// During drag operations we don't want 60 snapshots per second
debouncedPushToHistory(
elements,
'Position Changed',
'element_move',
500 // ms of inactivity before saving
);
当你拖动按钮时,会在 500 毫秒无操作后才保存——流畅的用户体验,且不影响性能。
代码生成引擎:2200+ 行框架魔法
function generateCode(
framework: Framework,
styling: StylingMethod,
elements: Element[],
options: CodeOptions
): string {
// 1. Detect element types (gradient buttons need special handling)
const hasGradients = elements.some(el => isGradientButton(el.style));
// 2. Generate framework‑specific imports
const imports = generateImports(framework, styling, hasGradients);
// 3. Generate element markup per framework
const markup = elements.map(el =>
generateElementMarkup(el, framework, styling)
);
// 4. Generate styles per styling method
const styles = generateStyles(elements, styling, options);
// 5. Assemble into framework structure
return assembleComponent(framework, imports, markup, styles);
}
输出适配所有情况:
| 框架 | 事件语法 | 样式集成 |
|---|---|---|
| React | onClick={...} | Styled Components、Tailwind 等 |
| Vue | @click="..." | Scoped CSS、CSS Modules |
| Svelte | on:click={...} | “ blocks 或 Tailwind |
| Angular | (click)="..." | ngClass、ngStyle |
| Solid.js, Preact, Astro, Qwik, Lit, Vanilla | 类似约定 | — |
示例输出
React
export const PromoPage: React.FC = () => {
return (
<button onClick={() => {
window.location.href = '/deal';
}}>
Get 50% Off
</button>
);
};
Vue(Script Setup)
<template>
<button @click="handleClick">Get 50% Off</button>
</template>
<script setup>
const handleClick = () => {
window.location.href = '/deal';
};
</script>
可视化历史时间轴
不仅仅是撤销/重做按钮——一个完整的可视化时间轴,您可以:
- 查看每个操作是什么(带图标)
- 点击任意点跳转
- 查看存在多少过去/未来状态
它就像是您设计的 Git。
智能预设
44 个按类别组织的预设:
| 类别 | 数量 | 示例 |
|---|---|---|
| 简约按钮 | 11 | 简洁、极简设计 |
| 渐变按钮 | 10 | 抢眼的渐变 |
| 动画按钮 | 6 | 弹跳、发光、脉冲、抖动、滑动、涟漪 |
每个预设在放置后都可以完全自定义。
实时预览
(当您编辑时,实时预览窗格会更新。)
可访问性内置
- 语义标签(
<button>、<a>等) aria-label属性- 用于扩展描述的
aria-describedby - 需要时的
role属性 - 为键盘用户提供的
:focus-visible样式 - 屏幕阅读器专用内容
响应式代码生成
切换 “responsive”,生成的代码会自动包含媒体查询:
@media (max-width: 768px) {
.button {
transform: scale(0.85);
font-size: 0.9rem;
}
}
@media (max-width: 640px) {
.button {
transform: scale(0.7);
font-size: 0.8rem;
}
}
您的着陆页可在任何地方自动运行。
SEO 元标签
需要 Open Graph 标签?Twitter 卡片?规范 URL?一次切换。完成。
架构选择
Zustand 优于 Redux
- 更简洁的 API
- 内置历史记录跟踪(如上所示)
功能切片设计
- 明确的关注点分离
- 易于扩展
组合优于继承(用于 Hook)
// Base form logic
const baseForm = useButtonForm();
// Extended for animated buttons
const animatedForm = useAnimatedBtn(); // Internally uses useButtonForm
清晰、可测试且遵循 DRY 原则。
动态导入用于循环依赖
const getBackgroundImage = async () => {
const module = await import('./background');
return module.default;
};
杂项代码片段
// Retrieve the uploaded image from the Zustand store
() => {
const { useUploadImageStore } = require('./uploadImageStore');
return useUploadImageStore.getState().uploadedImage;
};
虽然不够优雅,但它巧妙地解决了一个真实的问题。
技术栈
- React 18 with TypeScript
- Vite – blazing‑fast development server
- Zustand – state management
- styled‑components – styling
- dnd‑kit – drag‑and‑drop
- react‑i18next – Korean, English, Japanese support
- JSZip + FileSaver – ZIP downloads
演示与来源
⭐️ 如果你觉得这很酷,请点星。
🐞 如有想看到的缺失框架,请提交 issue。
🔧 非常欢迎提交 Pull Request!
本项目在大量的挫败感、咖啡以及坚信开发者不应重复编写相同 CSS 的信念下构建。