English version

前言

将另一个 HTML 页面嵌入到当前页面中 – iframe

我一直渴望拥有一个 Snap! iframe 库, 这个库最好足够通用, 它能够将各种各样的互联网资源通过 iframe 引入到 Snap! 里, 并与 Snap! 进行互操作。

这样一来我们就可以做许多有趣的事情:

  • 以可编程的方式显示 markdown 文档
  • 以可编程的方式播放 mp4 演示视频
  • 以可编程的方式显示在线幻灯片
  • 引入 3D world/game, 并使用 Snap! 对这些 3D 环境进行实时编程
  • 将 MicroBlocks 平台嵌入 Snap! (之前的 MicroBlocks ide 库将成为这个通用 iframe 库的一个特例)

潜在的用例非常多!

我在 CodeLab 群里分享相关演示视频的时候, @Patch 评论说:

给 Snap! 又开了一扇窗

我回复说

我也是这样看待 iframe 库,所以我刚给这个库添加的图标是 emoji 窗户: 🪟

我一直想要一个通用的 iframe 库,迟迟没有动手是因为不大喜欢也不大擅长前端工作,好在有了 ChatGPT! 这些工作变得非常轻松。 通过与 ChatGPT 协作, 我无需操心太多知识细节(这些是我过去不擅长的), 更多精力用于关注和表述想法, 不多时, 就得到了仅凭自己做不出来的东西。

对我而言, ChatGPT 的价值是双重的, 客观上, 它补足了我的知识短板, 更大的价值是主观层面的: 它极大减少了我对不熟悉领域的恐惧, 我之前倾向于回避这些工作。ChatGPT 通过使知识细节变得无关紧要, 让编程重新变得激动人心, 我们有更多精力关注自己的热情(passion)与项目(projects)的图景。

iframe 库

之前的工作一样, 我们希望实现以下 2 个目标:

无需更新平台, 不需要开发者介入, 所有工作都在用户环境中完成(只是一个 Snap! 项目), 这意味着普通用户可以继续延伸这些能力。 (这是终端用户编程的一个例子)

可以充分利用 Snap! 的活性(liveness), 享受高效而愉快的开发体验。

所以整个 iframe 库完全在 Snap! IDE 中构建, “只是一个普通的 Snap! 项目”, 没有对平台进行任何修改。

我们将通过几个例子展示如何使用 iframe 库:

  • 基本使用方式 (引入外部网站)
  • 动态显示 markdown 文档
  • 嵌入 MicroBlocks 项目
  • 与 3D world 互操作(以 Spline 为例)
  • 将消息从 iframe 页面发送到 Snap!

更多的操作细节, 推荐观看这个演示视频

基本使用方式

示例项目(点击运行)

也可以打开多个页面, 每个页面可以通过它的 id 控制它:

动态显示 markdown 文档

示例项目(点击运行)

Snap! 中的帮助文档

Snap! 官方的积木文档是以图片的形式制作的(右键某个积木, 点击 help...):

相比于使用 markdown 编写和显示文档, 这种既不灵活(无法显示视频)也不清晰。

“支持使用 markdown 显示文档” 是我此次构建 iframe 的主要原因。

iframe 可以以多种方式显示 markdown 文档, 诸如使用常规方式编写 markdown 文档, 将其托管在网络上, 然后使用 iframe 打开文档链接它.

我采用了另一种更加 Snap! 的动态方案, 只托管一个通用的 markdown 渲染页面(默认空白), 如果你对它的工作原理感兴趣, 可以查看页面源代码, 使事情生效的代码只有 4 行!

1
2
3
4
        window.addEventListener('message', (event) => {
            console.log('Received message from main page:', event.data);
            document.getElementById('messageContainer').innerHTML = marked.parse(event.data);
        });

通过 postMessage API , 可以在 iframe 页面中接收到 Snap! 页面发送过来的消息。

这个页面显示的 markdown 内容, 是由积木控制的! 这样一来我们就可以在积木环境中,实时控制文档页面的内容。

嵌入 MicroBlocks 项目

有些项目涉及到软硬件协作, 在 Snap! 中嵌入 MicroBlocks 项目, 就无需额外打开 MicroBlocks 页面刷入或查看硬件程序。

示例项目(点击运行)

与 3D world 互操作(以 Spline 为例)

参考 Snap! 中的 Spline 库

FAQ

可以拿 iframe 库做什么?

部署你自己的 web 应用, 使用 iframe 将其引入 Snap! , 然后你就可以为其提供一个高度可定制的图形化编程环境!

在过去, 这通常是一个完整的开发项目(有时是一个公司的商业项目), 现在,它只是一个用户级别的 Snap! 项目!

接下来做什么?

目前 Snap! 与 iframe 页面可以通过 postMessage 互相发送消息, 这些消息是异步非阻塞的。

  • Snap! -> iframe
    • Snap! 一侧: 使用 postMessage 积木给特定 iframe 页面发送消息
    • iframe 一侧: 使用 window.addEventListener('message', (event) => {}) 接收 Snap! 消息
  • iframe -> Snap!
    • Snap! 一侧: 使用 handle postMessage 积木接收 iframe 消息
    • iframe 一侧: 使用 window.parent.postMessage(msg, "*") 发送字符串消息

有时候我们想要同步语义的消息, 诸如调用并等待结果

Dynatalk 致力于对象之间的交流, 尤其关心不同语言/环境之间的互操作。

我打算制作 dynatalk-over-postmessage: 将 dynatalk 构建在 postMessage 之上。 一旦完成, dynatalk 的所有能力在 iframe 库中使用。

更新: dynatalk-over-postmessage 已完成, Spline 库使用了它.

iframe 页面支持哪些权限?

目前的 iframe 页面支持以下权限: allow = "geolocation; microphone; camera; bluetooth; serial"

ChatGPT:列出 iframe allow 的所有选项

Snap! 中的 iframe 页面如何做到可缩放和可拖拽?

使用了 jQuery UI, 具体而言:

html

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<div
  id="snap_iframe_container"
  style="left: 30%; top: 200px; width: 500px; height: 300px; padding: 1em"
>
  <iframe
    allow="geolocation; microphone; camera; bluetooth; serial"
    src="https://wwj718.github.io"
    class="ui-widget-content"
    id="snap_iframe_myiframe"
    style="width: 100%; height: 100%"
  ></iframe>
</div>

css:

1
2
#resizable { width: 150px; height: 150px; padding: 0.5em; }
.ui-resizable-helper { border: 20px solid #EFEFEF; margin: -20px; }

js:

1
$("#snap_iframe_container").resizable({ helper: "ui-resizable-helper" }).draggable();

如何将其转化为标准的 Snap! 库?

处于教育目的, 我们使用 JavaScript function 来制作 iframe 库. 这样做,

  • 好处是: “一切都只是一个普通 Snap! 项目”, 用户可以在 IDE 中理解所有事情
  • 坏处是: 需要开启 “JavaScript 扩展"才能用

为了将基于 JavaScript function 的 iframe 库转化为开箱可用的内置积木库, 我们需要做一下工作:

  1. 将 iframe 库中所有的 JavaScript function 整理到一个 JavaScript 文件中, 并将 JavaScript 文件放在 Snap! 代码仓库里
  2. 将 iframe 库文件(以xml结尾) 当到 Snap! 代码仓库里

目前 iframe 已成为了 Snap! 中文版 内置库.

参考例子

可参考 microblocks 库(它引用了 ble 目录里的 JavaScript 文件), 是通过 autoloading JavaScript files 机制实现的.

如何控制 iframe 页面中的游戏?

通常的游戏设备都由鼠标/键盘控制, Snap! 无法直接触发鼠标/键盘事件.

一种解决方案是, 使用 MicroBlocks 的鼠标键盘库(使用rpi pico板子), 消息的流向是:

Snap! -> MicroBlocks 板子 -> 计算机

示例项目(点击运行)

提醒: 在触发按键之前, 确保将鼠标聚焦在 iframe 页面(点击 iframe 页面)

参考