gengxin
This commit is contained in:
@@ -0,0 +1,179 @@
|
||||
function finiteNumber(value, fallback = 0) {
|
||||
const parsed = Number(value)
|
||||
return Number.isFinite(parsed) ? parsed : fallback
|
||||
}
|
||||
|
||||
function clamp(value, minimum, maximum) {
|
||||
return Math.min(maximum, Math.max(minimum, value))
|
||||
}
|
||||
|
||||
const SCENE_WIDTH = 1400
|
||||
const SCENE_HEIGHT = 788
|
||||
const SCENE_ASPECT_RATIO = SCENE_WIDTH / SCENE_HEIGHT
|
||||
|
||||
function getChapterLayoutMetrics(windowInfo = {}, menuRect = {}) {
|
||||
const windowWidth = Math.max(320, finiteNumber(windowInfo.windowWidth, 844))
|
||||
const windowHeight = Math.max(240, finiteNumber(windowInfo.windowHeight, 390))
|
||||
const safeArea = windowInfo.safeArea || {}
|
||||
const safeLeft = clamp(finiteNumber(safeArea.left, 0), 0, windowWidth / 3)
|
||||
const safeRightEdge = clamp(
|
||||
finiteNumber(safeArea.right, windowWidth),
|
||||
windowWidth * 2 / 3,
|
||||
windowWidth,
|
||||
)
|
||||
const safeRight = clamp(windowWidth - safeRightEdge, 0, windowWidth / 3)
|
||||
const statusBarHeight = Math.max(
|
||||
0,
|
||||
finiteNumber(windowInfo.statusBarHeight, 0),
|
||||
)
|
||||
const safeTop = Math.max(
|
||||
0,
|
||||
statusBarHeight,
|
||||
finiteNumber(safeArea.top, 0),
|
||||
)
|
||||
const safeBottomEdge = clamp(
|
||||
finiteNumber(safeArea.bottom, windowHeight),
|
||||
windowHeight * 2 / 3,
|
||||
windowHeight,
|
||||
)
|
||||
const safeBottom = clamp(
|
||||
windowHeight - safeBottomEdge,
|
||||
0,
|
||||
windowHeight / 3,
|
||||
)
|
||||
const menuLeft = finiteNumber(menuRect.left, windowWidth)
|
||||
const hasExplicitMenuTop = Number.isFinite(Number(menuRect.top))
|
||||
&& Number(menuRect.top) >= 0
|
||||
const menuTop = hasExplicitMenuTop ? Number(menuRect.top) : 0
|
||||
const menuBottom = finiteNumber(menuRect.bottom, 0)
|
||||
const explicitMenuHeight = Number(menuRect.height)
|
||||
const menuHeight = Number.isFinite(explicitMenuHeight)
|
||||
&& explicitMenuHeight > 0
|
||||
? explicitMenuHeight
|
||||
: (
|
||||
hasExplicitMenuTop && menuBottom > menuTop
|
||||
? menuBottom - menuTop
|
||||
: 32
|
||||
)
|
||||
const hasMenuRect = menuLeft > 0
|
||||
&& menuLeft < windowWidth
|
||||
&& menuBottom > 0
|
||||
|
||||
// 横屏 rpx 按屏幕宽度换算;576px 高的设备仍属于短高屏。
|
||||
// 这里使用实际 windowHeight,而不是设备型号或像素比。
|
||||
const compactHeight = windowHeight <= 620
|
||||
// Every visible top-bar action has a 48px minimum target. Keep the
|
||||
// calculated row at least as tall so compact landscape phones do not clip
|
||||
// the button above or below the explicit top-bar height.
|
||||
const rowHeight = Math.max(48, menuHeight)
|
||||
const inferredMenuTop = hasMenuRect
|
||||
? (
|
||||
hasExplicitMenuTop
|
||||
? menuTop
|
||||
: Math.max(safeTop, menuBottom - menuHeight)
|
||||
)
|
||||
: safeTop
|
||||
const topPadding = Math.max(
|
||||
safeTop,
|
||||
hasMenuRect
|
||||
? inferredMenuTop - Math.max(0, (rowHeight - menuHeight) / 2)
|
||||
: safeTop + (compactHeight ? 3 : 5),
|
||||
)
|
||||
const bottomPadding = compactHeight ? 4 : 6
|
||||
const borderHeight = 3
|
||||
const topbarHeight = Math.ceil(
|
||||
topPadding + rowHeight + bottomPadding + borderHeight,
|
||||
)
|
||||
const leftInset = safeLeft + (compactHeight ? 10 : 14)
|
||||
const capsuleReserve = hasMenuRect
|
||||
? Math.max(
|
||||
safeRight + (compactHeight ? 10 : 14),
|
||||
windowWidth - menuLeft + (compactHeight ? 8 : 10),
|
||||
)
|
||||
: safeRight + (compactHeight ? 12 : 16)
|
||||
const contentHeight = Math.max(
|
||||
1,
|
||||
windowHeight - topbarHeight - safeBottom,
|
||||
)
|
||||
const layoutWidth = Math.max(
|
||||
1,
|
||||
windowWidth - safeLeft - safeRight,
|
||||
)
|
||||
const sceneColumnWidth = layoutWidth * 0.6
|
||||
const stageWidth = Math.max(
|
||||
1,
|
||||
Math.min(sceneColumnWidth, contentHeight * SCENE_ASPECT_RATIO),
|
||||
)
|
||||
const stageHeight = stageWidth / SCENE_ASPECT_RATIO
|
||||
const markerWidth = compactHeight
|
||||
? clamp(stageWidth * 0.24, 142, 170)
|
||||
: clamp(windowWidth / 750 * 200, 160, 220)
|
||||
const markerHeight = compactHeight
|
||||
? 56
|
||||
: clamp(windowWidth / 750 * 86, 64, 94)
|
||||
|
||||
return {
|
||||
windowWidth,
|
||||
windowHeight,
|
||||
compactHeight,
|
||||
safeLeft,
|
||||
safeRight,
|
||||
safeTop,
|
||||
safeBottom,
|
||||
topPadding,
|
||||
bottomPadding,
|
||||
rowHeight,
|
||||
topbarHeight,
|
||||
leftInset,
|
||||
capsuleReserve,
|
||||
modalTop: Math.max(
|
||||
safeTop + (compactHeight ? 4 : 8),
|
||||
hasMenuRect ? menuBottom + 4 : 0,
|
||||
),
|
||||
stageWidth: Math.round(stageWidth),
|
||||
stageHeight: Math.round(stageHeight),
|
||||
markerWidth: Math.round(markerWidth),
|
||||
markerHeight: Math.round(markerHeight),
|
||||
sceneWidth: SCENE_WIDTH,
|
||||
sceneHeight: SCENE_HEIGHT,
|
||||
sceneAspectRatio: SCENE_ASPECT_RATIO,
|
||||
}
|
||||
}
|
||||
|
||||
function getMarkerPosition(position = {}, metrics = {}) {
|
||||
const widthPercent = finiteNumber(position.widthPercent, 0)
|
||||
const heightPercent = finiteNumber(position.heightPercent, 0)
|
||||
const rawX = finiteNumber(position.xPercent, 50) + widthPercent / 2
|
||||
const rawY = finiteNumber(position.yPercent, 50) + heightPercent / 2
|
||||
const stageWidth = Math.max(1, finiteNumber(metrics.stageWidth, 500))
|
||||
const stageHeight = Math.max(1, finiteNumber(metrics.stageHeight, 280))
|
||||
const markerWidth = finiteNumber(metrics.markerWidth, 168)
|
||||
const markerHeight = finiteNumber(metrics.markerHeight, 64)
|
||||
const horizontalMargin = clamp(
|
||||
markerWidth / 2 / stageWidth * 100 + 1.5,
|
||||
2,
|
||||
48,
|
||||
)
|
||||
const verticalMargin = clamp(
|
||||
markerHeight / 2 / stageHeight * 100 + 2,
|
||||
2,
|
||||
48,
|
||||
)
|
||||
const clampedX = clamp(rawX, horizontalMargin, 100 - horizontalMargin)
|
||||
const clampedY = clamp(rawY, verticalMargin, 100 - verticalMargin)
|
||||
|
||||
return {
|
||||
xPercent: clampedX,
|
||||
yPercent: clampedY,
|
||||
xPx: Math.round(clampedX / 100 * stageWidth),
|
||||
yPx: Math.round(clampedY / 100 * stageHeight),
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
SCENE_WIDTH,
|
||||
SCENE_HEIGHT,
|
||||
SCENE_ASPECT_RATIO,
|
||||
getChapterLayoutMetrics,
|
||||
getMarkerPosition,
|
||||
}
|
||||
Reference in New Issue
Block a user