@@ -7,10 +7,26 @@ description: ''
77
88有时候我们希望在画布中嵌入 HTML 内容,例如 YouTube 播放器、CodeSandbox 组件、ShaderToy 等等。
99
10- ## HTML
10+ ## 创建 HTML 容器 {#create-html-container}
1111
1212Excalidraw 并不支持在画布中嵌入 HTML 内容,但 tldraw 支持 [ TLEmbedShape] 。它在网页中将一个 HTML 容器(含 iframe 或其他元素)和画布 ` <svg> ` 元素并排或叠加显示,而不是“完全”在单一画布内部。
1313
14+ HTMLContainer 的职责是把“普通 DOM”内容放到编辑器的合适层(通常是 editor 的 container),并处理与 shape 的位移/缩放/旋转同步(shape 的 transform -> DOM transform)以保证 DOM 元素在画布上的位置和 shape 对齐。
15+
16+ ``` css
17+ .tl-html-container {
18+ position : absolute ;
19+ inset : 0px ;
20+ height : 100% ;
21+ width : 100% ;
22+ pointer-events : none ;
23+ stroke-linecap : round ;
24+ stroke-linejoin : round ;
25+ transform-origin : top left ;
26+ color : var (--tl-color-text-1 );
27+ }
28+ ```
29+
1430![ HTML external content in tldraw] ( /html-in-tldraw.png )
1531
1632在 [ External content sources] 例子中,我们可以看到 tldraw 是这样支持 HTML 内容的:
@@ -41,5 +57,72 @@ export interface HtmlSerializedNode
4157 Partial <HtmlAttributes > {}
4258```
4359
60+ ## 粘贴 URL
61+
62+ 在 [ 课程 24 - 读取剪贴板] 中,我们介绍过如何处理剪贴板中的图片和文本内容。
63+
64+ URL 是特殊的文本,在 tldraw 中:
65+
66+ - 当 URL 被识别为外部链接时,默认处理器会抓取页面的 metadata(og: image 、title、favicon、description),并把这些信息包装成一个 bookmark asset(TLBookmarkAsset)和对应的 shape,使用书签样式渲染
67+ - 当 URL 是可嵌入内容(例如 YouTube、Figma、Google Maps 等),此时使用 ` <iframe> ` 渲染
68+ - 当 URL 是图片或者视频资源时,把它作为媒体 asset(TLImageAsset / TLVideoAsset)去加载并用 ImageShapeUtil 渲染
69+
70+ ``` ts
71+ // @see https://github.com/tldraw/tldraw/blob/main/packages/tldraw/src/lib/ui/hooks/clipboard/pasteUrl.ts#L12
72+ export async function pasteUrl() {
73+ return await editor .putExternalContent ({
74+ type: ' url' ,
75+ point ,
76+ url ,
77+ sources ,
78+ });
79+ }
80+ ```
81+
82+ ### 书签 {#bookmark}
83+
84+ ``` ts
85+ // @see https://github.com/tldraw/tldraw/blob/ef0eba14c5a8baf4f36b3659ac9af98256d3b5dd/packages/tldraw/src/lib/defaultExternalContentHandlers.ts#L249
86+ export async function defaultHandleExternalUrlAsset() {
87+ let meta: {
88+ image: string ;
89+ favicon: string ;
90+ title: string ;
91+ description: string ;
92+ };
93+
94+ const resp = await fetch (url , {
95+ method: ' GET' ,
96+ mode: ' no-cors' ,
97+ });
98+ const html = await resp .text ();
99+ const doc = new DOMParser ().parseFromString (html , ' text/html' );
100+ meta = {
101+ image:
102+ doc .head
103+ .querySelector (' meta[property="og:image"]' )
104+ ?.getAttribute (' content' ) ?? ' ' ,
105+ // title, favicon, description
106+ };
107+
108+ // Create bookmark asset
109+ }
110+ ```
111+
112+ ## 粘贴 HTML 内容
113+
114+ ``` ts
115+ // @see https://github.com/tldraw/tldraw/blob/ef0eba14c5a8baf4f36b3659ac9af98256d3b5dd/packages/tldraw/src/lib/ui/hooks/useClipboardEvents.ts#L200-L204
116+ const handlePasteFromEventClipboardData = async () => {
117+ if (item .type === ' text/html' ) {
118+ things .push ({
119+ type: ' html' ,
120+ source: new Promise ((r ) => item .getAsString (r )) as Promise <string >,
121+ });
122+ }
123+ };
124+ ```
125+
44126[ External content sources ] : https://tldraw.dev/examples/external-content-sources
45127[ TLEmbedShape ] : https://tldraw.dev/reference/tlschema/TLEmbedShape
128+ [ 课程 24 - 读取剪贴板 ] : /zh/guide/lesson-024#clipboard-read
0 commit comments