缩放到边界的路径适合

问题描述 投票:0回答:1

我正在创建 svg 路径并将其缩放到特定尺寸。让我们假设

100x100
。我正在使用 d3 根据我的要求调整数据。

但是,我想知道,如何使用

pathfit
实现与 pathStr 完全相同的输出。这是我在下面尝试的。

function degreeToRadian(d) {
    return (d * Math.PI) / 180;
};

function fnData() {
    const data = d3.range(0, 11, 1).map((d) => {
        const angleRadian = degreeToRadian(d);
        return {
            index: d,
            angleDeg: d,
            angleRadian: angleRadian,
            x: Math.cos(angleRadian),
            y: Math.sin(angleRadian)
        }
    });

    return data;
};

const width = 1280;
const height = 720;
const data = fnData();

//------------------------WITH D3------------------------//
const scaleX = d3
    .scaleLinear()
    .range([0, 100])
    .domain(d3.extent(data, (d) => d.x));

const scaleY = d3
    .scaleLinear()
    .range([100, 0])
    .domain(d3.extent(data, (d) => d.y));

const pathStr = d3
    .line()
    .x((d) => scaleX(d.x))
    .y((d) => scaleY(d.y))(data);
  
const pathStr2 = d3.line().x(d=>d.x).y(d=>d.y)(data);  

//------------------------WITH pathfit------------------------//

const base = {
    viewBox: `0 0 ${width} ${height}`,
    preserveAspectRatio: "xMidYMid meet" // the default
};
const pathFitter = new Pathfit(base, undefined, pathStr2).scale_with_aspect_ratio(100, 100);
console.log({d3:pathStr, pathfit:pathFitter});
<script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/pathfit.js"></script>

svg path
1个回答
0
投票

如果您想使用数据范围作为应扩展到 100*100 维度的框,则必须在

viewBox
中设置这些数据。
d3
可以为您提供四个边的坐标:

const [x1, x2] = d3.extent(data, (d) => d.x);
const [y1, y2] = d3.extent(data, (d) => d.y);

viewBox: `${x1} ${y1} ${x2 - x1} ${y2 - y1}`

如果您想保留纵横比,而是将数据在两个方向上拉伸到最大尺寸,则必须设置

preserveAspectRatio: "none"

如果你想翻转数据,你必须提供一个预转换值。这确实有点尴尬,就像

d3
一样。由于使用了SVG语法,所以不能直接指定变换中心,而是需要自己找到正确的变换公式
matrix
。幸运的是,将数据边界精确地保留在适当位置的颠倒翻转相对容易:

`matrix(1 0 0 -1 0 ${y1 + y2})`

function degreeToRadian(d) {
    return (d * Math.PI) / 180;
};

function fnData() {
    const data = d3.range(0, 11, 1).map((d) => {
        const angleRadian = degreeToRadian(d);
        return {
            index: d,
            angleDeg: d,
            angleRadian: angleRadian,
            x: Math.cos(angleRadian),
            y: Math.sin(angleRadian)
        }
    });

    return data;
};

const width = 1280;
const height = 720;
const data = fnData();

//------------------------WITH D3------------------------//
const scaleX = d3
    .scaleLinear()
    .range([0, 100])
    .domain(d3.extent(data, (d) => d.x));

const scaleY = d3
    .scaleLinear()
    .range([100, 0])
    .domain(d3.extent(data, (d) => d.y));

const pathStr = d3
    .line()
    .x((d) => scaleX(d.x))
    .y((d) => scaleY(d.y))(data);
  
const pathStr2 = d3.line().x(d=>d.x).y(d=>d.y)(data);  

//------------------------WITH pathfit------------------------//

const [x1, x2] = d3.extent(data, (d) => d.x);
const [y1, y2] = d3.extent(data, (d) => d.y);

const base = {
    viewBox: `${x1} ${y1} ${x2 - x1} ${y2 - y1}`,
    preserveAspectRatio: "none" // do stretch the result
};
const pathFitter = new Pathfit(
   base,
   undefined,
   pathStr2,
   `matrix(1 0 0 -1 0 ${y1 + y2})`
).scale_with_aspect_ratio(100, 100);

console.log({d3:pathStr, pathfit:pathFitter});
<script type="text/javascript" src="https://d3js.org/d3.v7.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/pathfit.js"></script>

这就是尴尬开始的地方(对我来说,作为

pathfit
库的作者)。在其中的某个地方,中间结果被四舍五入,导致最终结果存在较大 (±5) 的差异。设置更高的
precision
似乎并不能解决这个问题。我没有立即解决这个问题的方法。

© www.soinside.com 2019 - 2024. All rights reserved.