GrapesJS Frame 快速上手:画布帧配置、生命周期事件与多帧编辑完全指南
【免费下载链接】grapesjsFree and Open source Web Builder Framework. Next generation tool for building templates without coding项目地址: https://gitcode.com/GitHub_Trending/gr/grapesjs
想让 GrapesJS 画布同时渲染桌面、平板、手机三套视口?或者在画布上叠一层自己的高亮、间距标注?这两类需求都落在同一个抽象上——GrapesJS Frame(画布帧)。它决定了每个 iframe 视口里装什么、放哪里、多大。这篇指南把 Frame 的配置、事件、API 和 Spot 叠加层一次性讲清楚,直接给结论和可抄的代码。
先搞懂 Frame:画布里的 iframe 容器
Frame 是 GrapesJS 画布(canvas)中承载内容的 iframe 容器模型。每个 Frame 都有独立的 document、独立的 wrapper 组件树和样式表,你的页面样式因此被隔离在 iframe 内,编辑器外层样式不会污染内容——这正是所见即所得能成立的前提。一个页面可以挂多个 Frame,由 Frames 集合 统一托管;未指定尺寸时 Frame 会自动跟随设备与画布,导出时再自动省略。
快速上手:canvas 配置与 Frame 属性速查
canvas 配置速查
编辑器初始化时用canvas对象定制画布初始状态。下面挑出 8 个最常用的配置项,完整字段见 canvas 配置源码。
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
frameContent | string | '<!DOCTYPE html>' | 所有帧加载的初始内容,控制 iframe 文档骨架 |
scripts | (string | object)[] | [] | 注入到 iframe<head>的外部脚本,不会进导出代码 |
styles | (string | object)[] | [] | 注入到 iframe<head>的外部样式,同样不参与导出 |
autoscrollLimit | number | 50 | 拖组件/块逼近画布边缘多少像素后触发自动滚动 |
scrollableCanvas | boolean | false | 为 true 时画布overflow: auto,内容超出可视区可滚动 |
customSpots | boolean | object | 无 | 接管内置 Spot 渲染:{ hover: true }单关,true全关 |
customRenderer | function | 无 | 自定义渲染函数,可把 React 等框架挂到帧 body 上 |
notTextable | string[] | ['button','a',...] | 聚焦后仍不视为"可输入"的选择器清单 |
Frame 的 5 个公开属性
| 属性 | 类型 | 缺省行为 |
|---|---|---|
component | Object | String | wrapper 组件定义;传 HTML 字符串时解析为默认 wrapper 的子组件 |
width | String | 不传则自动取画布宽度 |
height | String | 不传则自动取画布高度 |
x | Number | 水平位置,默认0 |
y | Number | 垂直位置,默认0 |
下面这段演示如何用一个 Frame 定义固定尺寸与位置的视口:
// 页面里挂一个 375x667 的手机视口,内容用 HTML 字符串 const frame = { component: '<div class="hero">Hello Frame</div>', // 解析为 wrapper 子组件 width: '375', height: '667', x: 20, // 距画布左边缘 20px y: 40, // 距画布上边缘 40px };尺寸如何跟随设备
width/height不传时,Frame 内部会打上__aw/__ah自动标记(见 Frame 模型),尺寸继续跟随当前设备与画布,而不是被写死。序列化时这两个标记会让输出里自动删掉width/height,保证"跟随设备"的语义在导出后依旧成立。切设备或页面时,Canvas.updateDevice()会把设备的width/height/minHeight直接写回当前帧。
生命周期事件速查
所有事件都通过editor.on(...)监听,完整定义见 canvas 文档。
帧加载类
| 事件 | 触发时机 | 回调参数 |
|---|---|---|
canvas:frame:load | iframeonload后立即触发 | { window, el, model, view } |
canvas:frame:load:head | head 内脚本等资源加载完成 | { window, el, model, view } |
canvas:frame:load:body | body 用组件渲染完成 | { window, el, model, view } |
canvas:frame:unload | 帧从画布卸载时 | { frame } |
画布状态类
| 事件 | 触发时机 | 回调参数 |
|---|---|---|
canvas:coords | 画布坐标更新 | 无,用getCoords()取 |
canvas:zoom | 缩放值更新 | 无,用getZoom()取 |
canvas:pointer | 指针位置更新 | 无,用getPointer()取 |
canvas:refresh | 画布刷新(refresh()或帧尺寸变化) | 刷新选项对象 |
canvas:update | 画布更新完成 | 无 |
canvas:move:start/move/move:end | 画布移动开始 / 移动中 / 结束 | 无 |
拖放类
| 事件 | 触发时机 | 回调参数 |
|---|---|---|
canvas:dragenter | 有内容被拖进画布 | DataTransfer实例、被拖内容 |
canvas:dragover | 内容正在画布上移动 | 触发事件 |
canvas:dragend | 拖拽操作结束 | 触发事件 |
canvas:dragdata | 每次 dataTransfer 解析时 | DataTransfer、result(改result.content可自定义落点内容) |
canvas:drop | 内容放入画布 | DataTransfer实例、落点模型 |
editor.Canvas API 场景速查
获取帧内元素
需要直接操作 iframe 内部 DOM 时用这组方法(实现在 canvas 模块):
| 方法 | 返回值 | 用途 |
|---|---|---|
getElement() | HTMLElement | 画布容器元素 |
getFrameEl() | HTMLIFrameElement | 主帧的 iframe 节点 |
getWindow() | Window | 主帧的 window 实例 |
getDocument() | HTMLDocument | 主帧的 document |
getBody() | HTMLBodyElement | 主帧的 body |
自定义拖放
startDrag把拖拽源写入编辑器模型,endDrag收尾,中间可配合getLastDragResult()取回拖出来的组件:
const canvas = editor.Canvas; // 以 HTML 字符串作为拖拽源 canvas.startDrag({ content: '<div>dropped</div>' }); // ... 用户把内容拖进画布 ... canvas.endDrag(); // 清理 dragSource 与 dragResult const last = canvas.getLastDragResult(); // 拖出的 Component滚动与缩放
| 方法 | 说明 |
|---|---|
scrollTo(el, opts) | 元素不可见时滚动画布使其可见;opts.force为 true 时即使已可见也强制滚动 |
hasFocus() | 画布是否聚焦,返回布尔值 |
setZoom(value, opts) | 设置缩放(0~100 百分比),返回this可链式 |
getZoom() | 读取当前缩放值 |
setCoords(x, y, opts) | 设置画布位置;opts.toWorld为真时做屏幕→世界坐标换算 |
getCoords() | 读取画布坐标,返回{ x, y } |
刷新与几何
refresh({ spots: true })在手动改动后重排 spots/tools 定位;getRect()返回画布矩形数据(含topScroll、leftScroll),配合getWorldRectToScreen(boxRect)可在世界坐标与屏幕坐标间换算。
Canvas Spots:在画布上叠加交互层
Spot 是画在画布之上、不侵入组件本身的叠加元素,最典型用途就是选中框、hover 高亮和间距提示。
五种内置类型与 Spot 构建属性
内置 Spot 类型共五种:select(选中框)、hover(悬停高亮)、spacing(间距)、target(拖放落点)、resize(缩放手柄)。
| 属性 | 类型 | 说明 |
|---|---|---|
id | String | Spot 唯一标识 |
type | String | Spot 类型,自定义类型也走这里 |
component | Component | Spot 依附的组件(可选) |
componentView | ComponentView | Spot 依附的视图(可选) |
boxRect | Object | 固定矩形,如{ width: 100, height: 100, x: 0, y: 0 } |
上图为 hover 类型 Spot 在画布上的效果,蓝色描边即叠加在组件外的标注层。
四个 Spot 操作
addSpot(props, opts)—— 注意:同 ID(或同 component+type 命中)是更新而非新增,命中后直接set原 Spot 并返回。
// 新建自定义 Spot const spot = canvas.addSpot({ type: 'my-highlight', component: cmp }); // 复用 ID → 更新原 Spot,不会新建 canvas.addSpot({ id: spot.id, component: anotherCmp });getSpots(spotProps)—— 按属性过滤,不传则返回全部。removeSpots(spotProps)—— 传属性过滤、传 Spot 数组、或不传(清空全部)三种写法。hasCustomSpot(type)—— 判断某内置类型是否已被customSpots接管,返回布尔值。
实战:自定义选中高亮
下面是一个可直接跑的片段:接管 hover 渲染、监听 Spot 生命周期,并在选中变化时打/清自定义标注:
const editor = grapesjs.init({ canvas: { customSpots: { hover: true } } // 接管内置 hover Spot }); const canvas = editor.Canvas; // Spot 生命周期监听 editor.on('canvas:spot:add', ({ spot }) => console.log('Spot 新增', spot.id)); editor.on('canvas:spot:remove', ({ spot }) => console.log('Spot 移除', spot.id)); // 选中组件时打自定义标注 editor.on('component:selected', () => { const cmp = editor.getSelected(); cmp && canvas.addSpot({ type: 'my-highlight', component: cmp }); }); // 取消选中时清理,避免标注残留 editor.on('component:deselected', () => canvas.removeSpots({ type: 'my-highlight' }));配合canvas:drop事件还能做到"拖入即高亮":在回调里canvas.addSpot({ type: 'dropped-target', component: dropped })即可。
进阶角落:多帧、嵌套帧与序列化
- 多个 Frame 用
x/y在画布上平铺定位,天然支持多设备同屏预览或多视口布局。 refFrame/refComponent让一个帧引用另一个帧或组件作根,实现帧内再嵌帧(iframe-in-iframe)。- 序列化会剔除
_开头私有键、styles、changesCount等内部字段;临时帧设skipFromStorage: true即可排除出存储。
避坑 FAQ
为什么导出的 JSON 里没有 width/height?因为没显式传过,Frame 走了自动尺寸模式(__aw/__ah标记),toJSON会主动删掉这两个键,让尺寸继续跟随设备。想要固定尺寸就明确传width/height。
同 ID 的 addSpot 为什么是更新而不是新增?addSpot先按属性(或id)getSpots查重,命中就直接set原实例并返回,根本不会new第二个 Spot。想强制新增就换一个type或 component 组合,让它查不到。
如何把外部脚本注入 iframe 而不进导出代码?用canvas配置里的scripts/styles数组,它们只被追加到 iframe 的<head>,源码里明确标注不参与导出;而frame.styles是参与导出的,别混用。
结语
现在你能配置任意多帧视口、监听完整生命周期、用 Spot 叠自定义交互层,还能把脚本干净地注入 iframe。下一步建议去看 Frame 文档 与 Canvas Spot 文档,把customRenderer接进你自己的组件框架。
【免费下载链接】grapesjsFree and Open source Web Builder Framework. Next generation tool for building templates without coding项目地址: https://gitcode.com/GitHub_Trending/gr/grapesjs
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考