☰
GrapesJS Frame 快速上手:画布帧配置、生命周期事件与多帧编辑完全指南
2026/10/8 2:00:26 网站建设 项目流程

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 配置源码。

配置项类型默认值说明
frameContentstring'<!DOCTYPE html>'所有帧加载的初始内容,控制 iframe 文档骨架
scripts(string | object)[][]注入到 iframe<head>的外部脚本,不会进导出代码
styles(string | object)[][]注入到 iframe<head>的外部样式,同样不参与导出
autoscrollLimitnumber50拖组件/块逼近画布边缘多少像素后触发自动滚动
scrollableCanvasbooleanfalse为 true 时画布overflow: auto,内容超出可视区可滚动
customSpotsboolean | object无接管内置 Spot 渲染:{ hover: true }单关,true全关
customRendererfunction无自定义渲染函数,可把 React 等框架挂到帧 body 上
notTextablestring[]['button','a',...]聚焦后仍不视为"可输入"的选择器清单

Frame 的 5 个公开属性

属性类型缺省行为
componentObject | Stringwrapper 组件定义;传 HTML 字符串时解析为默认 wrapper 的子组件
widthString不传则自动取画布宽度
heightString不传则自动取画布高度
xNumber水平位置,默认0
yNumber垂直位置,默认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:loadiframeonload后立即触发{ window, el, model, view }
canvas:frame:load:headhead 内脚本等资源加载完成{ window, el, model, view }
canvas:frame:load:bodybody 用组件渲染完成{ 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(缩放手柄)。

属性类型说明
idStringSpot 唯一标识
typeStringSpot 类型,自定义类型也走这里
componentComponentSpot 依附的组件(可选)
componentViewComponentViewSpot 依附的视图(可选)
boxRectObject固定矩形,如{ 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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询