diff --git a/extensions/iframe.js b/extensions/iframe.js index 6b6f044d2b..687570356d 100644 --- a/extensions/iframe.js +++ b/extensions/iframe.js @@ -7,6 +7,7 @@ /** @type {HTMLIFrameElement|null} */ let iframe = null; + let overlay = null; const featurePolicy = { accelerometer: "'none'", @@ -39,32 +40,48 @@ let width = -1; // negative means default let height = -1; // negative means default let interactive = true; + let resizeBehavior = "scale"; const updateFrameAttributes = () => { if (!iframe) { return; } + iframe.style.pointerEvents = interactive ? "auto" : "none"; + const { stageWidth, stageHeight } = Scratch.vm.runtime; const effectiveWidth = width >= 0 ? width : stageWidth; const effectiveHeight = height >= 0 ? height : stageHeight; - iframe.style.width = `${effectiveWidth}px`; - iframe.style.height = `${effectiveHeight}px`; - let transform = ""; - transform += `translate(${stageWidth / 2 - effectiveWidth / 2 + x}px,${ - stageHeight / 2 - effectiveHeight / 2 - y - }px)`; - iframe.style.transform = transform; + if (resizeBehavior === "scale") { + iframe.style.width = `${effectiveWidth}px`; + iframe.style.height = `${effectiveHeight}px`; - iframe.style.pointerEvents = interactive ? "auto" : "none"; + iframe.style.transform = `translate(${-effectiveWidth / 2 + x}px, ${ + -effectiveHeight / 2 - y + }px)`; + iframe.style.top = "0"; + iframe.style.left = "0"; + } else { + // As the stage is resized in fullscreen mode, only % can be relied upon + iframe.style.width = `${(effectiveWidth / stageWidth) * 100}%`; + iframe.style.height = `${(effectiveHeight / stageHeight) * 100}%`; + + iframe.style.transform = ""; + iframe.style.top = `${(0.5 - effectiveHeight / 2 / stageHeight) * 100}%`; + iframe.style.left = `${(0.5 - effectiveWidth / 2 / stageWidth) * 100}%`; + } }; + const getOverlayMode = () => + resizeBehavior === "scale" ? "scale-centered" : "manual"; + const createFrame = (src) => { iframe = document.createElement("iframe"); iframe.style.width = "100%"; iframe.style.height = "100%"; iframe.style.border = "none"; + iframe.style.position = "absolute"; iframe.setAttribute("sandbox", "allow-scripts allow-same-origin"); iframe.setAttribute( "allow", @@ -75,11 +92,13 @@ iframe.setAttribute("allowtransparency", "true"); iframe.setAttribute("allowtransparency", "true"); iframe.setAttribute("src", src); - Scratch.renderer.addOverlay(iframe, "scale"); + overlay = Scratch.renderer.addOverlay(iframe, getOverlayMode()); updateFrameAttributes(); }; + Scratch.vm.on("STAGE_SIZE_CHANGED", updateFrameAttributes); + class IframeExtension { getInfo() { return { @@ -191,6 +210,17 @@ }, }, }, + { + opcode: "setResize", + blockType: Scratch.BlockType.COMMAND, + text: "set iframe resize behavior to [RESIZE]", + arguments: { + RESIZE: { + type: Scratch.ArgumentType.STRING, + menu: "resizeMenu", + }, + }, + }, ], menus: { getMenu: { @@ -203,12 +233,17 @@ "width", "height", "interactive", + "resize behavior", ], }, interactiveMenu: { acceptReporters: true, items: ["true", "false"], }, + resizeMenu: { + acceptReporters: true, + items: ["scale", "viewport"], + }, }, }; } @@ -246,6 +281,7 @@ if (iframe) { Scratch.renderer.removeOverlay(iframe); iframe = null; + overlay = null; } } @@ -266,6 +302,8 @@ return height >= 0 ? height : Scratch.vm.runtime.stageHeight; } else if (MENU === "interactive") { return interactive; + } else if (MENU === "resize behavior") { + return resizeBehavior; } else { return ""; } @@ -295,6 +333,17 @@ interactive = Scratch.Cast.toBoolean(INTERACTIVE); updateFrameAttributes(); } + + setResize({ RESIZE }) { + if (RESIZE === "scale" || RESIZE === "viewport") { + resizeBehavior = RESIZE; + if (overlay) { + overlay.mode = getOverlayMode(); + Scratch.renderer._updateOverlays(); + updateFrameAttributes(); + } + } + } } Scratch.extensions.register(new IframeExtension()); diff --git a/website/hello.html b/website/hello.html index 2c0a89cf45..5bd51d8c8f 100644 --- a/website/hello.html +++ b/website/hello.html @@ -35,7 +35,7 @@

It works!

Transparency is supported, so you can display the project behind the frame as long as the page doesn't explicitly set a background color.

You can also use JavaScript and most other Web APIs, like this:

-

Due to browser security measures blocks like "mouse x" and "mouse down?" will not work while the cursor is over the frame unless you disable interactivity.

+

Due to browser security measures, blocks like "mouse x" and "mouse down?" will not work while the cursor is over the frame unless you use the disable interactivity block.

Many websites also block other sites from embedding them for security reasons.