feat(auth): 添加iframe高度自适应传递功能

This commit is contained in:
JiaJun
2026-04-17 17:50:28 +08:00
parent d8073ff640
commit 7cb5153aa8
2 changed files with 150 additions and 20 deletions

View File

@@ -23,19 +23,32 @@
.layout {
display: grid;
grid-template-columns: 360px minmax(0, 1fr);
min-height: 100vh;
height: 100vh;
}
.panel {
padding: 24px;
background: #ffffff;
border-right: 1px solid #e5e7eb;
overflow: auto;
}
.preview {
padding: 24px;
width: 500px;
height: 800px;
overflow: auto;
}
.preview-frame {
width: 800px;
height: 300px;
overflow: hidden;
border: 1px solid #d1d5db;
border-radius: 16px;
background: #ffffff;
}
h1 {
margin: 0 0 8px;
font-size: 20px;
@@ -98,14 +111,15 @@
font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
white-space: pre-wrap;
min-height: 160px;
max-height: min(360px, calc(100vh - 260px));
overflow: auto;
}
iframe {
display: block;
width: 100%;
height: calc(100vh - 48px);
border: 1px solid #d1d5db;
border-radius: 16px;
background: #ffffff;
height: 100%;
border: 0;
}
@media (max-width: 960px) {
@@ -118,7 +132,8 @@
border-bottom: 1px solid #e5e7eb;
}
iframe {
.preview-frame {
width: 100%;
height: 70vh;
}
}
@@ -149,23 +164,46 @@
</aside>
<main class="preview">
<iframe id="playx-frame" title="PlayX App" src="/"></iframe>
<iframe id="playx-frame" title="PlayX App" src="/"></iframe>
</main>
</div>
<script>
const frame = document.getElementById('playx-frame')
const previewFrame = document.getElementById('preview-frame')
const usernameInput = document.getElementById('username')
const languageInput = document.getElementById('language')
const sendButton = document.getElementById('send')
const reloadButton = document.getElementById('reload')
const logNode = document.getElementById('log')
const iframeOrigin = window.location.origin
const maxLogEntries = 80
const logEntries = []
const heightLogDelay = 300
let pendingHeightLog = null
let heightLogTimer = null
function appendLog(message, data) {
const stamp = new Date().toLocaleTimeString()
const payload = data ? ` ${JSON.stringify(data, null, 2)}` : ''
logNode.textContent = `[${stamp}] ${message}${payload}\n${logNode.textContent}`
logEntries.unshift(`[${stamp}] ${message}${payload}`)
logEntries.length = Math.min(logEntries.length, maxLogEntries)
logNode.textContent = logEntries.join('\n')
logNode.scrollTop = 0
}
function scheduleHeightLog(message) {
pendingHeightLog = message
if (heightLogTimer != null) {
window.clearTimeout(heightLogTimer)
}
heightLogTimer = window.setTimeout(() => {
appendLog('Received PLAYX_HEIGHT from iframe', pendingHeightLog)
pendingHeightLog = null
heightLogTimer = null
}, heightLogDelay)
}
function buildPayload() {
@@ -181,11 +219,9 @@
function sendContext() {
const message = buildPayload()
frame.contentWindow?.postMessage(message, iframeOrigin)
appendLog('Sent IFRAME_CONTEXT to iframe', message)
}
frame.addEventListener('load', () => {
appendLog('Iframe loaded', {src: frame.getAttribute('src')})
})
sendButton.addEventListener('click', () => {
@@ -194,23 +230,24 @@
reloadButton.addEventListener('click', () => {
frame.contentWindow?.location.reload()
appendLog('Requested iframe reload')
})
window.addEventListener('message', (event) => {
if (event.source === frame.contentWindow && event.data?.type === 'PLAYX_READY') {
appendLog('Received PLAYX_READY from iframe', {
origin: event.origin,
data: event.data,
})
sendContext()
return
}
appendLog('Received postMessage on host page', {
origin: event.origin,
data: event.data,
})
if (event.source === frame.contentWindow && event.data?.type === 'PLAYX_HEIGHT') {
const height = Number(event.data?.payload?.height)
if (!Number.isFinite(height) || height <= 0) {
return
}
scheduleHeightLog(event.data)
return
}
})
</script>
</body>

View File

@@ -10,8 +10,10 @@ import {useUserStore} from '@/store/user.ts'
import type {HostContextMessage} from '@/types'
const HOST_READY_MESSAGE = 'PLAYX_READY'
const HOST_HEIGHT_MESSAGE = 'PLAYX_HEIGHT'
const HOST_READY_INTERVAL = 500
const HOST_READY_RETRY_LIMIT = 20
const HOST_HEIGHT_REPORT_INTERVAL = 250
const TEST_BOOTSTRAP_ENABLED = import.meta.env.VITE_BYPASS_IFRAME_CONTEXT === 'true'
const TEST_BOOTSTRAP_USERNAME = '+60777777777'
const TEST_BOOTSTRAP_LANGUAGE = 'zh'
@@ -142,6 +144,97 @@ export function AuthGuide({children}: PropsWithChildren) {
clearUserInfo()
}, [authBootstrapQuery.isError, clearUserInfo])
useEffect(() => {
if (window.parent === window) {
return
}
let frameId = 0
let timeoutId: number | null = null
let lastReportedHeight = 0
let lastReportedAt = 0
const measureHeight = () =>
Math.max(
document.body.scrollHeight,
document.documentElement.scrollHeight,
document.body.offsetHeight,
document.documentElement.offsetHeight,
)
const postHeight = (height: number) => {
lastReportedHeight = height
lastReportedAt = Date.now()
window.parent.postMessage(
{
type: HOST_HEIGHT_MESSAGE,
payload: {
height,
},
},
'*',
)
}
const flushPostHeight = () => {
frameId = 0
const nextHeight = measureHeight()
if (nextHeight === lastReportedHeight) {
return
}
const elapsed = Date.now() - lastReportedAt
if (elapsed >= HOST_HEIGHT_REPORT_INTERVAL) {
postHeight(nextHeight)
return
}
if (timeoutId != null) {
return
}
timeoutId = window.setTimeout(() => {
timeoutId = null
postHeight(measureHeight())
}, HOST_HEIGHT_REPORT_INTERVAL - elapsed)
}
const schedulePostHeight = () => {
if (frameId) {
window.cancelAnimationFrame(frameId)
}
frameId = window.requestAnimationFrame(() => {
flushPostHeight()
})
}
schedulePostHeight()
const resizeObserver = new ResizeObserver(() => {
schedulePostHeight()
})
resizeObserver.observe(document.body)
resizeObserver.observe(document.documentElement)
window.addEventListener('load', schedulePostHeight)
window.addEventListener('resize', schedulePostHeight)
return () => {
resizeObserver.disconnect()
window.removeEventListener('load', schedulePostHeight)
window.removeEventListener('resize', schedulePostHeight)
if (frameId) {
window.cancelAnimationFrame(frameId)
}
if (timeoutId != null) {
window.clearTimeout(timeoutId)
}
}
}, [])
if (!username || authBootstrapQuery.isPending) {
return (
<div className="flex min-h-screen items-center justify-center bg-[#08070E] px-6 text-center text-[14px] text-white/68">