Files
terminal-lab/terminal/apps/miniprogram/utils/wxMeasureAdapter.js
douboer@gmail.com 3b7c1d558a first commit
2026-03-03 13:23:14 +08:00

113 lines
3.3 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 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 };