【前端小需求】通过线性回归模拟飞书云文档页宽行为

168 阅读3分钟

场景

场景是这样的,我们目前开发的云文档功能需要添加一个页宽功能,之前我们的页宽是写死的

  • 默认: 180px padding
  • 较宽: 240px padding
  • 全宽: 300px padding

后来说不能够进行简单的写死赋值.然后去参考云文档的老前辈飞书,看看他们的页宽是怎么动态设置的,并且应用到我们自己的云文档上面。于是派生出这个前端需求

分析

一开始我想的是直接从源码上面打断点进行调试,也就是说 在飞书控制width 和 padding的地方打一个document的断点,然后 r -> c -> i -> t 一层层变量调用栈向上排查,然后发现行不通。根本找不到计算页宽的地方。估计是 计算和赋值是两处函数的调用栈

image.png

所以只能用蒙特卡洛模拟,先随机采样一些点

模式\页宽95313662048540
默认31326631344
较宽21316621344
全宽66666644

注: 表格中的是padding

我们可以发现这样几个规律

  • 全宽永远只有是66px和44px两种情况
  • 默认模式 = 较宽 模式 + 100
  • 540以上 全宽>=较宽> = 默认 >=66px ,540 以下 全宽>=较宽> = 默认 >=44px

所以现在我们只需要得到默认模式是怎么计算padding的就可以大概推断出飞书云文档测量padding的行为

尝试

我们接着对飞书页宽paddding和width的点进行采样, 看看他们的关系是什么,x轴是padding,y轴是width

image.png

好,通过图我们可以判断,飞书的页宽行为是三个分段函数,前面的两个函数是固定的,那么我们只需要得到最后一个函数的方程即可,由图可知 大概是一个kx+b 的函数,那么我们只需要求的 这个斜率和截距即可。

const dataPoints = [
    [201, 1235],
    [103, 1041],
    [507, 1847],
    [394, 1622],
    [370, 1573],
    [453, 1741],
    [233, 1301],
    [293, 1420],
    [120, 1071],
    [66, 764],
    [100, 1032],
    [81, 995],
    [90.5, 1014]
];

// 计算线性拟合函数的斜率和截距
function linearFit(data) {
    let sumX = 0;
    let sumY = 0;
    let sumXY = 0;
    let sumXX = 0;
    const n = data.length;

    for (let i = 0; i < n; i++) {
        const [x, y] = data[i];
        sumX += x;
        sumY += y;
        sumXY += x * y;
        sumXX += x * x;
    }

    const slope = (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX);
    const intercept = (sumY - slope * sumX) / n;

    return { slope, intercept };
}

// 获取拟合结果
const fitResult = linearFit(dataPoints);
const { slope, intercept } = fitResult;

// 定义线性拟合函数
function linearFunction(x) {
    return (x - intercept) / slope ;
}

// 示例:计算某个x值对应的拟合值
const yValue = 1286;
const fittedYValue = linearFunction(yValue);
console.log(`线性拟合函数为: y = ${slope.toFixed(2)}x + ${intercept.toFixed(2)}`);
console.log(`当y = ${yValue} 时,拟合的y值为: ${fittedYValue.toFixed(2)}`)

/**
线性拟合函数为: y = 2.12x + 790.85
VM52:52 当y = 1286 时,拟合的y值为: 233.91
*/

得到的结果如下 y = 2.12x + 790.85, 然后我们可以对照实际的行为验证一下,平均差不到 5px,验证通过,然后我们可以简单梳理一下最终的行为

y={x790.82.12,x>96466,964x>54044,x540 \begin{equation} y = \begin{cases} \frac{x - 790.8}{2.12}, & x > 964 \\ 66, & 964 \geq x > 540 \\ 44, & x \leq 540 \end{cases} \end{equation}

其中 x 是 实际的宽度, y是需要设置的边距.

需求完成,代码中引入方程式设置边距即可