D3 помещает дуговые метки в круговую диаграмму, если места достаточно
Я добавлю текстовый элемент в каждую дугу моей круговой диаграммы (в центре) - как показано в этом примере: http://bl.ocks.org/mbostock/3887235
Но я помещу текстовый элемент, только если места достаточно для всего текста, поэтому я должен сравнить размер моего текстового элемента с "доступным" пространством в каждой дуге.
Я думаю, что могу сделать это с помощью getBBox(), чтобы получить размеры текста... но как я могу получить (и сравнить) размер доступного пространства в каждой дуге.
Спасибо...!
2 ответа
Этот вопрос задавался несколько раз раньше.
Решения, которые я предложил, есть rotate
лейбл, но он никогда не удовлетворял меня. Частично это было ужасным рендерингом шрифтов, сделанным некоторыми браузерами и потерей разборчивости, который приносит и странный flip
когда одна метка пересекает 180°
линия. В некоторых случаях результаты были приемлемыми и неизбежными, например, когда этикетки были слишком длинными.
Одним из других решений, предложенных Ларсом, является размещение меток вне круговой диаграммы. Однако это просто выталкивает метки наружу, предоставляя им больший радиус, но не решает overlap
проблема полностью.
Другое решение на самом деле использует предложенную вами технику: просто удалите метки, которые не подходят.
Скрыть переполненные метки
Сравните оригинал, который имеет >= 65
метка переполняется в Solution, где метка переполнения исчезла.
Уменьшение проблемы
Ключевое понимание заключается в том, чтобы увидеть, что эта проблема заключается в поиске того, содержится ли один выпуклый многоугольник (прямоугольник, ограничивающий прямоугольник) внутри другого выпуклого многоугольника (-ish) (клин).
Задача может быть сведена к тому, чтобы определить, все ли точки прямоугольника лежат внутри клина или нет. Если они это сделают, то прямоугольник находится внутри дуги.
Точка лежит внутри клина
Теперь эта часть проста. Все, что нужно сделать, это проверить:
- Расстояние точки от центра меньше, чем
radius
- Угол, образованный точкой в центре, находится между
startAngle
а такжеendAngle
дуги.
function pointIsInArc(pt, ptData, d3Arc) {
// Center of the arc is assumed to be 0,0
// (pt.x, pt.y) are assumed to be relative to the center
var r1 = d3Arc.innerRadius()(ptData), // Note: Using the innerRadius
r2 = d3Arc.outerRadius()(ptData),
theta1 = d3Arc.startAngle()(ptData),
theta2 = d3Arc.endAngle()(ptData);
var dist = pt.x * pt.x + pt.y * pt.y,
angle = Math.atan2(pt.x, -pt.y); // Note: different coordinate system.
angle = (angle < 0) ? (angle + Math.PI * 2) : angle;
return (r1 * r1 <= dist) && (dist <= r2 * r2) &&
(theta1 <= angle) && (angle <= theta2);
}
Найдите ограничивающий прямоугольник с ярлыками
Теперь, когда мы имеем это в стороне, вторая часть выясняет, каковы четыре угла прямоугольника. Это также легко:
g.append("text")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function(d) { return d.data.age; })
.each(function (d) {
var bb = this.getBBox(),
center = arc.centroid(d);
var topLeft = {
x : center[0] + bb.x,
y : center[1] + bb.y
};
var topRight = {
x : topLeft.x + bb.width,
y : topLeft.y
};
var bottomLeft = {
x : topLeft.x,
y : topLeft.y + bb.height
};
var bottomRight = {
x : topLeft.x + bb.width,
y : topLeft.y + bb.height
};
d.visible = pointIsInArc(topLeft, d, arc) &&
pointIsInArc(topRight, d, arc) &&
pointIsInArc(bottomLeft, d, arc) &&
pointIsInArc(bottomRight, d, arc);
})
.style('display', function (d) { return d.visible ? null : "none"; });
Суть решения находится в each
функция. Сначала мы размещаем текст в нужном месте, чтобы DOM отображал его. Затем мы используем getBBox()
метод, чтобы получить ограничивающую рамку text
в пространстве пользователя. Новое пользовательское пространство создается любым элементом, который имеет transform
Атрибут установлен на него. Этот элемент, в нашем случае, является text
сама коробка. Таким образом, возвращаемая ограничительная рамка относится к центру текста, так как мы установили text-anchor
быть middle
,
Положение text
относительно arc
можно рассчитать, так как мы применили преобразование 'translate(' + arc.centroid(d) + ')'
к этому. Как только у нас есть центр, мы просто вычисляем topLeft
, topRight
, bottomLeft
а также bottomRight
указывает на это и посмотреть, все ли они лежат внутри wedge
,
Наконец, мы определяем, все ли точки лежат внутри клина, и если они не подходят, установите display
CSS свойство для none
,
Рабочая демонстрация
Заметка
Я использую
innerRadius
который, если не ноль, делаетwedge
невыпуклый, который сделает вычисления намного более сложными! Тем не менее, я думаю, что здесь опасность невелика, так как единственный случай, когда она может потерпеть неудачу, - это, и, честно говоря, я не думаю, что это случится часто (у меня были проблемы с поиском этого контрпримера):x
а такжеy
перевернуты иy
имеет отрицательный знак при расчетеMath.atan2
, Это из-за разницы между тем, какMath.atan2
а такжеd3.svg.arc
просмотреть систему координат и направление положительногоy
сsvg
,Система координат для
Math.atan2
Вы не можете сделать это с помощью ограничительной рамки, потому что ограничивающая рамка намного больше, чем клин для секторов круговой диаграммы. То есть, несмотря на то, что клин на внешнем крае будет достаточно широким, чтобы вместить текст, это не значит, что он достаточно широк в фактической позиции текста.
К сожалению, нет простого способа сделать то, что вы пытаетесь сделать (тестирование на уровне пикселей). Смотрите, например, этот вопрос для получения дополнительной информации. Я бы предложил просто поместить текстовые метки вне круговой диаграммы, чтобы вы не столкнулись с этой проблемой.