@nutui/nutui-react
Version:
京东风格的轻量级移动端 React 组件库,支持一套代码生成 H5 和小程序
207 lines (206 loc) • 9.05 kB
JavaScript
/**
* 响应式缩放系数(--nut-scale-f):结合京东站内原生桥与站外视口规则,
* 写入根节点 CSS 变量(--nut-scale-f / --nut-scale-font / --nut-scale-icon),
* 供布局/字号/icon 等按比例换算(见 calcByProfile)。H5 与 Taro WebView 共用此实现。
*/ import { _ as _async_to_generator } from "@swc/helpers/_/_async_to_generator";
import { _ as _ts_generator } from "@swc/helpers/_/_ts_generator";
import { canUseDom } from "./can-use-dom";
/** 当前基准缩放(来自原生或视口计算) */ var scale = 1;
/** 大字模式下仅 font 场景的相对倍率 */ var LARGE_FONT_RATIO = 1.15;
/** 老年模式下 font/icon/lego 场景的相对倍率 */ var ELDERLY_RATIO = 1.3;
/** 全局当前档位,与 scale 共同参与 calcByProfile */ var profile = 'standard';
/** 仅 large / elderly 有效,其余一律视为 standard */ function normalizeProfile(nextProfile) {
if (nextProfile === 'large' || nextProfile === 'elderly') return nextProfile;
return 'standard';
}
/** 计算 CSS 变量要使用的场景缩放(基准 scale × 场景倍率) */ function getCssSceneScale(scene, baseScale) {
return baseScale * getSceneRatio(scene, profile);
}
/** 将缩放值同步到 :root 的 --nut-scale-f / --nut-scale-font / --nut-scale-icon */ function applyScaleCssVars(nextScale) {
if (!canUseDom) return;
var rootStyle = document.documentElement.style;
rootStyle.setProperty('--nut-scale-f', formatScaleValue(nextScale));
rootStyle.setProperty('--nut-scale-font', formatScaleValue(getCssSceneScale('font', nextScale)));
rootStyle.setProperty('--nut-scale-icon', formatScaleValue(getCssSceneScale('icon', nextScale)));
}
/** >1 保留两位小数字符串;否则取整,与 CSS 消费端约定一致 */ function formatScaleValue(nextScale) {
if (nextScale > 1) {
return (Math.round(nextScale * 100) / 100).toFixed(2);
}
return "".concat(Math.round(nextScale));
}
/** 根据屏宽粗略区分 phone / pad */ function getCurrentDevice() {
if (!canUseDom) return 'phone';
return window.innerWidth >= 600 ? 'pad' : 'phone';
}
/** 在 profile 与 scene 维度上叠加额外倍率(与全局 scale 相乘) */ function getSceneRatio(scene, currentProfile) {
if (currentProfile === 'elderly' && (scene === 'font' || scene === 'icon' || scene === 'lego')) {
return ELDERLY_RATIO;
}
if (currentProfile === 'large' && scene === 'font') {
return LARGE_FONT_RATIO;
}
return 1;
}
/** 输出 px 等单位时的取整规则:缩放>1 或强制时保留两位小数精度 */ function roundByScaleRule(value, baseScale) {
var forceKeepTwoDecimals = arguments.length > 2 && arguments[2] !== void 0 ? arguments[2] : false;
if (forceKeepTwoDecimals || baseScale > 1) {
return Math.round(value * 100) / 100;
}
return Math.round(value);
}
/** 无原生桥时按屏宽推算 scale(含平板与 375 基准窄屏区间) */ function getScaleByViewport() {
if (!canUseDom) return 1;
var deviceWidth = window.innerWidth;
if (!deviceWidth) return 1;
if (deviceWidth >= 600) {
return 1.2;
}
if (deviceWidth >= 375 && deviceWidth < 600) {
var ratio = deviceWidth / 375;
return ratio >= 1.17 ? 1.17 : ratio;
}
return 1;
}
/** 通过 jmfe.callNative 拉取 DongScreenAdapterPlugin;失败返回 null */ function getScaleByNative() {
return _async_to_generator(function() {
var _window_jmfe, _res_data, res, parsed, e;
return _ts_generator(this, function(_state) {
switch(_state.label){
case 0:
if (!canUseDom || !((_window_jmfe = window.jmfe) === null || _window_jmfe === void 0 ? void 0 : _window_jmfe.callNative)) return [
2,
null
];
_state.label = 1;
case 1:
_state.trys.push([
1,
3,
,
4
]);
return [
4,
window.jmfe.callNative('DongScreenAdapterPlugin', 'getScale', JSON.stringify({}), '')
];
case 2:
res = _state.sent();
if ((res === null || res === void 0 ? void 0 : res.status) === '0' && ((_res_data = res.data) === null || _res_data === void 0 ? void 0 : _res_data.scale) !== undefined) {
parsed = Number(res.data.scale);
if (Number.isFinite(parsed) && parsed > 0) {
return [
2,
parsed
];
}
}
return [
3,
4
];
case 3:
e = _state.sent();
return [
3,
4
];
case 4:
return [
2,
null
];
}
});
})();
}
/** 统一获取缩放:站内原生优先,失败则用视口规则 */ function getScaleF() {
return _async_to_generator(function() {
var nativeScale;
return _ts_generator(this, function(_state) {
switch(_state.label){
case 0:
return [
4,
getScaleByNative()
];
case 1:
nativeScale = _state.sent();
if (nativeScale) return [
2,
nativeScale
];
return [
2,
getScaleByViewport()
];
}
});
})();
}
/** 校验后更新内存中的 scale,并写入 --nut-scale-f */ function setScaleF(nextScale) {
var validScale = Number.isFinite(nextScale) && nextScale > 0 ? nextScale : 1;
scale = validScale;
applyScaleCssVars(validScale);
return scale;
}
/** 重新拉取缩放;可选同时切换 profile,避免与当前值相同时重复写 DOM */ function refreshScaleF(nextProfile) {
return _async_to_generator(function() {
var nextScale;
return _ts_generator(this, function(_state) {
switch(_state.label){
case 0:
if (nextProfile) {
setScaleProfile(nextProfile);
}
return [
4,
getScaleF()
];
case 1:
nextScale = _state.sent();
if (!scale || nextScale !== scale) {
setScaleF(nextScale);
}
return [
2,
scale
];
}
});
})();
}
/** 首次计算缩放并订阅 resize;返回卸载函数(SSR 下为空函数) */ export function initScaleF(nextProfile) {
if (!canUseDom) return function() {};
setScaleProfile(nextProfile);
var handler = function() {
refreshScaleF();
};
handler();
window.addEventListener('resize', handler);
return function() {
window.removeEventListener('resize', handler);
};
}
/** 更新全局 profile,并在当前 scale 下重刷 CSS 变量 */ function setScaleProfile(nextProfile) {
profile = normalizeProfile(nextProfile);
// profile 切换后需要重新应用当前缩放值。
setScaleF(scale);
return profile;
}
/**
* 按档位与场景将设计稿基准值换算为实际数值:base × 场景倍率 × 当前 scale,再按规则取整。
*/ export function calcByProfile(baseValue) {
var options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
var _options_profile;
var currentProfile = normalizeProfile((_options_profile = options.profile) !== null && _options_profile !== void 0 ? _options_profile : profile);
var _options_scene;
var scene = (_options_scene = options.scene) !== null && _options_scene !== void 0 ? _options_scene : 'layout';
var currentScale = Number.isFinite(options.scale) && Number(options.scale) > 0 ? Number(options.scale) : scale;
var _options_device;
var device = (_options_device = options.device) !== null && _options_device !== void 0 ? _options_device : getCurrentDevice();
var ratio = getSceneRatio(scene, currentProfile);
var rawValue = baseValue * ratio * currentScale;
var forceKeepTwoDecimals = scene === 'font' && currentProfile === 'large' && device === 'pad';
return roundByScaleRule(rawValue, currentScale, forceKeepTwoDecimals);
}