yumi-flutter/lib/modules/room_game/utils/room_game_viewport.dart
2026-04-27 14:11:26 +08:00

143 lines
4.2 KiB
Dart

import 'dart:convert';
import 'package:flutter/material.dart';
const double roomGameDesignWidth = 750;
double resolveRoomGameAvailableHeight(
BuildContext context, {
double reservedTop = 12,
}) {
final mediaQuery = MediaQuery.maybeOf(context);
if (mediaQuery == null) {
return 0;
}
final bottomInset =
mediaQuery.viewInsets.bottom > mediaQuery.padding.bottom
? mediaQuery.viewInsets.bottom
: mediaQuery.padding.bottom;
final availableHeight =
mediaQuery.size.height -
mediaQuery.padding.top -
bottomInset -
reservedTop;
if (!availableHeight.isFinite || availableHeight <= 0) {
return mediaQuery.size.height;
}
return availableHeight.clamp(0.0, mediaQuery.size.height).toDouble();
}
double clampRoomGameHeight(
double value,
double maxHeight, {
double minHeight = 0,
}) {
if (!value.isFinite || value <= 0) {
return maxHeight > 0 ? maxHeight : 0;
}
if (!maxHeight.isFinite || maxHeight <= 0) {
return value;
}
if (minHeight > maxHeight) {
return maxHeight;
}
return value.clamp(minHeight, maxHeight).toDouble();
}
String resolveRoomGameScreenMode(
Map<String, dynamic> launchParams, {
required bool fullScreen,
}) {
final candidates = <String>[
launchParams['screenMode']?.toString() ?? '',
launchParams['screen_mode']?.toString() ?? '',
launchParams['displayMode']?.toString() ?? '',
launchParams['gameScreenMode']?.toString() ?? '',
];
for (final candidate in candidates) {
final trimmed = candidate.trim();
if (trimmed.isNotEmpty) {
return trimmed.toLowerCase();
}
}
return fullScreen ? 'full' : 'half';
}
String buildRoomGameNativeFitScript({
required double visibleHeight,
required double expectedHeight,
}) {
final options = jsonEncode(<String, dynamic>{
'visibleHeight': _roundMetric(visibleHeight),
'expectedHeight': _roundMetric(expectedHeight),
});
return '''
(function() {
const options = $options;
const fitKey = '__roomGameNativeViewportFit';
function applyFit() {
try {
const visibleHeight = Number(options.visibleHeight) || window.innerHeight || 0;
const expectedHeight = Number(options.expectedHeight) || 0;
if (!document.body || !visibleHeight || !expectedHeight) {
return;
}
const rawScale = visibleHeight / expectedHeight;
const scale = Math.min(1, rawScale);
window[fitKey] = {
scale: scale,
visibleHeight: visibleHeight,
expectedHeight: expectedHeight,
compact: scale < 0.999
};
if (scale >= 0.999) {
document.documentElement.style.overflow = '';
document.body.style.overflow = '';
document.body.style.transform = '';
document.body.style.transformOrigin = '';
document.body.style.width = '';
document.body.style.minHeight = '';
document.documentElement.removeAttribute('data-room-game-native-fit');
return;
}
document.documentElement.setAttribute('data-room-game-native-fit', 'true');
document.documentElement.style.overflow = 'hidden';
document.body.style.overflow = 'hidden';
document.body.style.transform = 'scale(' + scale + ')';
document.body.style.transformOrigin = 'top left';
document.body.style.width = (100 / scale) + '%';
document.body.style.minHeight = expectedHeight + 'px';
} catch (_) {}
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', applyFit, { once: true });
} else {
applyFit();
}
[50, 250, 800, 1600, 3000].forEach(function(delay) {
window.setTimeout(applyFit, delay);
});
if (!window.__roomGameNativeFitResizeReady) {
window.__roomGameNativeFitResizeReady = true;
window.addEventListener('resize', applyFit, true);
window.addEventListener('orientationchange', function() {
window.setTimeout(applyFit, 120);
}, true);
}
})();
''';
}
double _roundMetric(double value) {
if (!value.isFinite) {
return 0;
}
return double.parse(value.toStringAsFixed(2));
}