Pie - labelLayout

一、饼图label线的布局优化

labelLayoutList为初始布局位置信息;如上图,可看到name1name6是重叠的状态

var labelLayoutList = [
    {
        "x": 23.40783269959437,
        "y": -114.94949619851135,
        "data": {
            "name": "name1",
            "value": 1
        },
        "position": "out",
        "side": "right",
        "height": 17,
        "len": 15,
        "len2": 15,
        "linePoints": [
            [
                2.963332782255974,
                -99.95608365087944
            ],
            [
                3.40783269959437,
                -114.94949619851135
            ],
            [
                18.40783269959437,
                -114.94949619851135
            ]
        ],
        "textAlign": "start",
        "inside": false
    },
    ...
]

1.1 将labelLayoutList以side属性分成两个部分

function avoidOverlap(labelLayoutList, radius, width, height) {
    var leftList = [];
    var rightList = [];

    for (var i = 0; i < labelLayoutList.length; i++) {
        if (isPositionCenter(labelLayoutList[i])) {
            continue;
        }
        if (labelLayoutList[i].side === 'left') {
            leftList.push(labelLayoutList[i]);
        } else {
            rightList.push(labelLayoutList[i]);
        }
    }
}

1.2 用adjustSingleSide函数调整右侧的布局(左侧同理)

function adjustSingleSide(list, radius, dir, width, height) {
    //
}

1.2.1 先调整右侧、再调整左侧

function avoidOverlap(labelLayoutList, radius, width, height) {
    ...

    adjustSingleSide(rightList, radius, 1, width, height);

    // adjustSingleSide(leftList, radius, -1, width, height);
}

1.2.2 adjustSingleSide函数

1.2.3 按属性y进行正向排序

function adjustSingleSide(list, radius, dir, width, height) {
    list.sort(function (a, b) {
        return a.y - b.y;
    });
}

1.2.4 定义label下移上移位置的内部函数shiftDown、shiftUp

function adjustSingleSide(list, radius, dir, width, height) {
    list.sort(function (a, b) {
        return a.y - b.y;
    });

    function shiftDown(start, end, delta, dir) {
        for (var j = start; j < end; j++) {
            list[j].y += delta;

            if (j > start
                && j + 1 < end
                && list[j + 1].y > list[j].y + list[j].height
            ) {
                shiftUp(j, delta / 2);
                return;
            }
        }

        shiftUp(end - 1, delta / 2);
    }

    function shiftUp(end, delta) {
        for (var j = end; j >= 0; j--) {
            list[j].y -= delta;

            if (j > 0
                && list[j].y > list[j - 1].y + list[j - 1].height
            ) {
                break;
            }
        }
    }

}

1.2.5 调整代码实现

function adjustSingleSide(list, radius, dir, width, height) {
    ...
    
    var lastY = Number.NEGATIVE_INFINITY
    var delta;
    var len = list.length;

    list.forEach((d, i) => {
        delta = d.y - lastY;

        if (delta < 0) {
            shiftDown(i, len, -delta, dir);
        }
        lastY = d.y + d.height;
    });

    if (height / 2 - lastY < 0) {
        shiftUp(len - 1, lastY - height);
    }
}

1.2.6 以第1个label开始进行调整步骤

(1)以第1个label为基准,lastY = list[0].y + list[0].height;从第2个label到第8个label(先布局右侧的label)依次查看是否与第1个label有重叠,即delta = list[i].y - lastY,delta < 0则为重叠

(2)若label有重叠,则依次将这些label往下移动-delta值直到没有重叠则停止,即调用shiftDown函数,list[i].y += -delta

(3)停止条件(没有重叠):list[i + 1].y > list[i].y + list[i].height

(4)在下移重叠的label时,下一个label与当前的label没有重叠,则从当前的label到第1个label,依次上移半个(-delta / 2)的高度直到没有重叠则停止,即list[i].y -= (-delta / 2)

(5)停止条件(没有重叠):list[i].y > list[i - 1].y + list[i - 1].height

1.2.7 依次以第2、3、4、5、6、7、8个label开始重复1.2.4的步骤,直到没有重叠

...

1.2.8 调整label.x的位置

function adjustSingleSide(list, radius, dir, width, height) {
    ...
    
    function changeX(list, isDownList, radius, dir) {
        var lastDeltaX = dir > 0
            ? isDownList                // right-side
                ? Number.MAX_VALUE      // down
                : 0                     // up
            : isDownList                // left-side
                ? Number.MAX_VALUE      // down
                : 0;                    // up

        for (var i = 0, l = list.length; i < l; i++) {
            var deltaY = Math.abs(list[i].y);
            var length = list[i].len;
            var length2 = list[i].len2;
            var deltaX = (deltaY < radius + length)
                ? Math.sqrt(
                    (radius + length + length2) * (radius + length + length2) - deltaY * deltaY
                )
                : Math.abs(list[i].x);
            if (isDownList && deltaX >= lastDeltaX) {
                // right-down, left-down
                deltaX = lastDeltaX - 10;
            }
            if (!isDownList && deltaX <= lastDeltaX) {
                // right-up, left-up
                deltaX = lastDeltaX + 10;
            }

            list[i].x = deltaX * dir;
            lastDeltaX = deltaX;
        }
    }


    var upList = [];
    var downList = [];

    for (var i = 0; i < len; i++) {
        if (list[i].y >= 0) {
            downList.push(list[i]);
        } else {
            upList.push(list[i]);
        }
    }

    changeX(upList, false, radius, dir);

    // changeX(downList, true, radius, dir);
}

...

1.2.9 调整polyline的x,y位置

function avoidOverlap(labelLayoutList, radius, width, height) {
    ...

    for (var i = 0; i < labelLayoutList.length; i++) {
        if (isPositionCenter(labelLayoutList[i])) {
            continue;
        }
        var linePoints = labelLayoutList[i].linePoints;
        if (linePoints) {
            var dist = linePoints[1][0] - linePoints[2][0];
            if (labelLayoutList[i].x < 0) {
                linePoints[2][0] = labelLayoutList[i].x + 3;
            } else {
                linePoints[2][0] = labelLayoutList[i].x - 3;
            }
            linePoints[1][1] = linePoints[2][1] = labelLayoutList[i].y;
            linePoints[1][0] = linePoints[2][0] + dist;
        }
    }
}

(1)保存线拐点和末端的距离位dist = linePoints[1][0] - linePoints[2][0]

(2)与将线的末端放置在label计算后的x位置附近,即linePoints[2][0] = labelLayoutList[i].x + 3

(3)调整线拐点和末端的y值,即linePoints[1][1] = linePoints[2][1] = labelLayoutList[i].y

(4)调整线拐点的x值,即linePoints[1][0] = linePoints[2][0] + dist

...

1.3 再用adjustSingleSide函数调整左侧的布局(与右侧同理,略)

 

二、完整代码

function isPositionCenter(layout) {
    // center类型的label不需要更改x
    return layout.position === 'center';
}

function adjustSingleSide(list, radius, dir, width, height) {
    list.sort(function (a, b) {
        return a.y - b.y;
    });

    function shiftDown(start, end, delta, dir) {
        console.log('shiftDown============>')
        for (var j = start; j < end; j++) {
            console.log(`${list[j].data.name} start:${start}, j:${j}; ${list[j].y} + ${delta} = ${list[j].y + delta}`)
            list[j].y += delta;

            if (j > start
                && j + 1 < end
                && list[j + 1].y > list[j].y + list[j].height
            ) {
                console.log('下一个label与当前的label没有重叠')
                console.log(`${list[j].data.name} start:${start}, j:${j}; ${list[j + 1].data.name} > ${list[j].data.name} + height`)
                console.log(`${list[j].data.name} start:${start}, j:${j}; ${list[j + 1].y} > ${list[j].y + list[j].height} (${list[j].y} + ${list[j].height})`)
                shiftUp(j, delta / 2);
                return;
            }
        }

        console.log('==========================')
        shiftUp(end - 1, delta / 2);
    }

    function shiftUp(end, delta) {
        console.log('   shiftUp============>')
        for (var j = end; j >= 0; j--) {
            console.log(`   ${list[j].data.name} end:${end}, j:${j}; ${list[j].y} - ${delta} = ${list[j].y - delta}`)
            list[j].y -= delta;

            if (j > 0
                && list[j].y > list[j - 1].y + list[j - 1].height
            ) {
                break;
            }
        }
    }

    function changeX(list, isDownList, radius, dir) {
        var lastDeltaX = dir > 0
            ? isDownList                // right-side
                ? Number.MAX_VALUE      // down
                : 0                     // up
            : isDownList                // left-side
                ? Number.MAX_VALUE      // down
                : 0;                    // up

        for (var i = 0, l = list.length; i < l; i++) {
            var deltaY = Math.abs(list[i].y);
            var length = list[i].len;
            var length2 = list[i].len2;
            var deltaX = (deltaY < radius + length)
                ? Math.sqrt(
                    (radius + length + length2) * (radius + length + length2) - deltaY * deltaY
                )
                : Math.abs(list[i].x);
            if (isDownList && deltaX >= lastDeltaX) {
                // right-down, left-down
                deltaX = lastDeltaX - 10;
            }
            if (!isDownList && deltaX <= lastDeltaX) {
                // right-up, left-up
                deltaX = lastDeltaX + 10;
            }

            list[i].x = deltaX * dir;
            lastDeltaX = deltaX;
        }
    }

    var lastY = Number.NEGATIVE_INFINITY
    var delta;
    var len = list.length;
    var len2 = Math.min(len, 9)
    list.length = len = len2

    list.forEach((d, i) => {
        delta = d.y - lastY;

        if (delta < 0) {
            shiftDown(i, len, -delta, dir);
        }
        lastY = d.y + d.height;
    });

    if (height / 2 - lastY < 0) {
        shiftUp(len - 1, lastY - height);
    }

    var upList = [];
    var downList = [];

    for (var i = 0; i < len; i++) {
        if (list[i].y >= 0) {
            downList.push(list[i]);
        }
        else {
            upList.push(list[i]);
        }
    }

    changeX(upList, false, radius, dir);
    changeX(downList, true, radius, dir);
}

function avoidOverlap(labelLayoutList, radius, width, height) {
    var leftList = [];
    var rightList = [];

    for (var i = 0; i < labelLayoutList.length; i++) {
        if (isPositionCenter(labelLayoutList[i])) {
            continue;
        }
        if (labelLayoutList[i].side === 'left') {
            leftList.push(labelLayoutList[i]);
        } else {
            rightList.push(labelLayoutList[i]);
        }
    }

    adjustSingleSide(rightList, radius, 1, width, height);
    adjustSingleSide(leftList, radius, -1, width, height);

    for (var i = 0; i < labelLayoutList.length; i++) {
        if (isPositionCenter(labelLayoutList[i])) {
            continue;
        }
        var linePoints = labelLayoutList[i].linePoints;
        if (linePoints) {
            var dist = linePoints[1][0] - linePoints[2][0];
            if (labelLayoutList[i].x < 0) {
                linePoints[2][0] = labelLayoutList[i].x + 3;
            }
            else {
                linePoints[2][0] = labelLayoutList[i].x - 3;
            }
            linePoints[1][1] = linePoints[2][1] = labelLayoutList[i].y;
            linePoints[1][0] = linePoints[2][0] + dist;
        }
    }
}

 

 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值