请求:将Office EllipticalArcTo参数转换为ECMA-376 arcTo参数(求JS实现)
椭圆弧参数转换:EllipticalArcTo → Office Open XML arcTo
参数对应说明
先明确两个接口的核心参数定义:
- Office插件EllipticalArcTo:
x, y(弧终点坐标)、A(椭圆水平半径)、B(椭圆垂直半径)、C(椭圆旋转角度,单位:度)、D(扫掠方向:0=顺时针,1=逆时针) - Office Open XML arcTo:
hR(水平半径)、wR(垂直半径)、stAng(起始角度,单位:度,从x轴正方向逆时针计算)、swAng(扫掠角度,正=逆时针,负=顺时针)
JS实现代码
function convertEllipticalArcToOOXML(startX, startY, endX, endY, hR, wR, rotAngleDeg, sweepDir) { // 旋转角度转弧度 const rotAngleRad = rotAngleDeg * Math.PI / 180; // 计算起点到终点的半向量 const dx = (endX - startX) / 2; const dy = (endY - startY) / 2; // 逆旋转:将椭圆转为轴对齐状态,简化计算 const x1 = Math.cos(rotAngleRad) * dx + Math.sin(rotAngleRad) * dy; const y1 = -Math.sin(rotAngleRad) * dx + Math.cos(rotAngleRad) * dy; // 修正椭圆半径:若起点终点距离超过椭圆覆盖范围,按比例放大半径避免计算异常 const x1Sq = x1 * x1; const y1Sq = y1 * y1; const hRSq = hR * hR; const wRSq = wR * wR; let scale = x1Sq / hRSq + y1Sq / wRSq; if (scale > 1) { scale = Math.sqrt(scale); hR *= scale; wR *= scale; } // 计算轴对齐椭圆的中心坐标 const hRSqNew = hR * hR; const wRSqNew = wR * wR; const numerator = hRSqNew * wRSqNew - hRSqNew * y1Sq - wRSqNew * x1Sq; const denominator = hRSqNew * y1Sq + wRSqNew * x1Sq; const k = Math.sqrt(Math.max(numerator / denominator, 0)); let cxPrime = k * hR * y1 / wR; let cyPrime = -k * wR * x1 / hR; // 根据扫掠方向调整中心位置 if (sweepDir === 0) { cxPrime = -cxPrime; cyPrime = -cyPrime; } // 将中心坐标转换回原旋转坐标系 const cx = Math.cos(rotAngleRad) * cxPrime - Math.sin(rotAngleRad) * cyPrime + (startX + endX) / 2; const cy = Math.sin(rotAngleRad) * cxPrime + Math.cos(rotAngleRad) * cyPrime + (startY + endY) / 2; // 计算起始角度(中心到起点的连线与x轴夹角,扣除椭圆旋转角度) let startAngleRad = Math.atan2(startY - cy, startX - cx) - rotAngleRad; // 修正角度到0-2π范围 startAngleRad = (startAngleRad + 2 * Math.PI) % (2 * Math.PI); const startAngleDeg = startAngleRad * 180 / Math.PI; // 计算扫掠角度 let endAngleRad = Math.atan2(endY - cy, endX - cx) - rotAngleRad; endAngleRad = (endAngleRad + 2 * Math.PI) % (2 * Math.PI); let sweepAngleRad = endAngleRad - startAngleRad; // 根据扫掠方向调整角度正负 if (sweepDir === 0) { sweepAngleRad = sweepAngleRad > 0 ? sweepAngleRad - 2 * Math.PI : sweepAngleRad; } else { sweepAngleRad = sweepAngleRad < 0 ? sweepAngleRad + 2 * Math.PI : sweepAngleRad; } const sweepAngleDeg = sweepAngleRad * 180 / Math.PI; return { hR: hR, wR: wR, stAng: startAngleDeg, swAng: sweepAngleDeg }; }
使用示例
// 示例:当前路径起点(100,100),EllipticalArcTo参数为终点(200,200)、水平半径50、垂直半径30、旋转30度、顺时针方向 const ooxmlArcParams = convertEllipticalArcToOOXML(100, 100, 200, 200, 50, 30, 30, 0); console.log(ooxmlArcParams); // 输出包含hR、wR、stAng、swAng的参数对象
核心逻辑说明
- 坐标逆旋转:把带旋转的椭圆转为轴对齐状态,大幅简化角度和中心的计算
- 半径修正:避免因起点终点距离过大导致的椭圆无法覆盖的计算错误
- 角度校准:基于椭圆中心计算起始角度,结合扫掠方向调整扫掠角度的正负,完全匹配OOXML规范要求
内容的提问来源于stack exchange,提问作者Кobak
相关产品推荐
相关产品推荐

