first commit
This commit is contained in:
112
terminal/apps/miniprogram/utils/wxMeasureAdapter.js
Normal file
112
terminal/apps/miniprogram/utils/wxMeasureAdapter.js
Normal file
@@ -0,0 +1,112 @@
|
||||
/**
|
||||
* WxMeasureAdapter — 微信小程序 wx.createSelectorQuery 实现 IMeasureAdapter。
|
||||
*
|
||||
* 由于小程序不支持 ResizeObserver,使用 BoundingClientRect 查询容器尺寸,
|
||||
* 并提供 `poll(intervalMs)` 方法做周期性尺寸检测(模拟 resize 通知)。
|
||||
*
|
||||
* 使用方式:
|
||||
* const m = createWxMeasureAdapter(component, '#tc-viewport', 14, 16.8);
|
||||
* m.onResize(() => {
|
||||
* const { widthPx, heightPx } = m.measureContainer();
|
||||
* const { widthPx: cw, heightPx: ch } = m.measureChar();
|
||||
* const cols = Math.max(20, Math.floor(widthPx / cw));
|
||||
* const rows = Math.max(8, Math.floor(heightPx / ch));
|
||||
* // ... resize terminal
|
||||
* });
|
||||
* m.startPoll(500);
|
||||
* // 在 component detached / onUnload 中:
|
||||
* m.stopPoll();
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {object} ctx - 小程序 Component 实例(this),用于 createSelectorQuery 作用域
|
||||
* @param {string} selector - CSS 选择器,如 '#tc-viewport'
|
||||
* @param {number} charWidthPx - 等宽字符宽度像素(由组件在初始化时测量或硬编码)
|
||||
* @param {number} charHeightPx - 行高像素
|
||||
*/
|
||||
function createWxMeasureAdapter(ctx, selector, charWidthPx, charHeightPx) {
|
||||
let _containerW = 0;
|
||||
let _containerH = 0;
|
||||
let _pollTimer = null;
|
||||
const _callbacks = [];
|
||||
|
||||
/** 实现 IMeasureAdapter.measureChar */
|
||||
function measureChar() {
|
||||
return {
|
||||
widthPx: charWidthPx || 8,
|
||||
heightPx: charHeightPx || 16,
|
||||
};
|
||||
}
|
||||
|
||||
/** 实现 IMeasureAdapter.measureContainer */
|
||||
function measureContainer() {
|
||||
return {
|
||||
widthPx: _containerW,
|
||||
heightPx: _containerH,
|
||||
};
|
||||
}
|
||||
|
||||
/** 实现 IMeasureAdapter.onResize */
|
||||
function onResize(cb) {
|
||||
_callbacks.push(cb);
|
||||
return function off() {
|
||||
const idx = _callbacks.indexOf(cb);
|
||||
if (idx >= 0) _callbacks.splice(idx, 1);
|
||||
};
|
||||
}
|
||||
|
||||
/** 主动刷新容器尺寸(异步) */
|
||||
function refresh(onDone) {
|
||||
const query = ctx.createSelectorQuery();
|
||||
query.select(selector).boundingClientRect(function(rect) {
|
||||
if (!rect) return;
|
||||
const newW = rect.width || 0;
|
||||
const newH = rect.height || 0;
|
||||
if (newW !== _containerW || newH !== _containerH) {
|
||||
_containerW = newW;
|
||||
_containerH = newH;
|
||||
for (let i = 0; i < _callbacks.length; i++) {
|
||||
try { _callbacks[i](); } catch(e) { /* isolation */ }
|
||||
}
|
||||
}
|
||||
if (typeof onDone === 'function') onDone({ widthPx: _containerW, heightPx: _containerH });
|
||||
}).exec();
|
||||
}
|
||||
|
||||
/** 启动周期性轮询(模拟 ResizeObserver)。intervalMs 建议 300~1000ms */
|
||||
function startPoll(intervalMs) {
|
||||
stopPoll();
|
||||
const ms = intervalMs || 500;
|
||||
// 立刻执行一次
|
||||
refresh();
|
||||
_pollTimer = setInterval(function() { refresh(); }, ms);
|
||||
}
|
||||
|
||||
/** 停止轮询 */
|
||||
function stopPoll() {
|
||||
if (_pollTimer !== null) {
|
||||
clearInterval(_pollTimer);
|
||||
_pollTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 一次性测量(供初始化使用)。
|
||||
* @param {function} cb - (result: { widthPx, heightPx }) => void
|
||||
*/
|
||||
function measureOnce(cb) {
|
||||
refresh(cb);
|
||||
}
|
||||
|
||||
return {
|
||||
measureChar: measureChar,
|
||||
measureContainer: measureContainer,
|
||||
onResize: onResize,
|
||||
refresh: refresh,
|
||||
startPoll: startPoll,
|
||||
stopPoll: stopPoll,
|
||||
measureOnce: measureOnce,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = { createWxMeasureAdapter: createWxMeasureAdapter };
|
||||
Reference in New Issue
Block a user