first commit

This commit is contained in:
douboer@gmail.com
2026-03-03 13:23:14 +08:00
commit 3b7c1d558a
161 changed files with 28120 additions and 0 deletions

View 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 };