#d3.js #tree #bezier #orgchart
#d3.js #дерево #безье #orgchart
Вопрос:
Я создаю организационную диаграмму в D3 на основе диаграммы Бернхарда Зуба D3.js Организационная структура. Организационная диаграмма моделирует организацию, в которой у любого данного человека (представленного белым квадратом) может быть около сотни человек непосредственно под ними (очень плоская древовидная структура с черной кривой Безье, представляющей каждое родительско-дочернее отношение).
А вот увеличение нижней части родительского узла на рисунке выше:
Проблема в том, что связи между дочерними и родительскими узлами имеют тенденцию объединяться вместе, что приводит к очень толстой черной линии с очень постепенным наклоном, что может быть немного неприятно.
Функция, которую я использую для создания ссылок, выглядит следующим образом:
// Diagonal function
var diagonal = d3.svg.diagonal()
.source(function (d) {
return {
x: d.source.x (rectW / 2),
y: d.source.y rectH - 10
};
})
.target(function (d) {
return {
x: d.target.x (rectW / 2),
y: d.target.y 10
};
})
.projection(function (d) {
return [d.x, d.y];
});
Здесь — rectW
ширина каждого узла и rectH
высота каждого узла.
Что я хотел бы сделать, так это внести некоторые небольшие изменения в функцию Безье, используемую для создания ссылок. В частности, я хотел бы немного сгладить контрольные точки, чтобы кривые в начале и конце кривой были более выразительными. Если кто-нибудь может показать мне, как изменить функцию, используемую diagonal()
для генерации кривой Безье, я могу выяснить остальное.
Ответ №1:
Если вы посмотрите на исходный код svg.diagonal, я не вижу прямого способа настроить только контрольные точки. Можно подумать, что для этого можно использовать проекцию, но это преобразует все 4 точки, используемые для генерации пути. Теперь, я думаю, мы могли бы немного повозиться с проекцией и сделать что-то вроде этого:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3@3.5.17" data-semver="3.5.17" src="https://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<script>
var data = [{
source: {
x: 10,
y: 10
},
target: {
x: 200,
y: 200
}
}, {
source: {
x: 50,
y: 50
},
target: {
x: 200,
y: 200
}
}];
var svg = d3.select('body')
.append('svg')
.attr('width', 205)
.attr('height', 205);
var diagonal = d3.svg.diagonal()
.projection(function(d) {
if (!this.times) this.times = 0;
this.times ;
console.log(this.times);
if (this.times === 1) {
return [d.x, d.y];
} else if (this.times === 2) {
return [d.x - 25, d.y]
} else if (this.times === 3) {
return [d.x 25, d.y];
} else if (this.times === 4) {
this.times = 0;
return [d.x, d.y];
}
});
svg.selectAll('path')
.data(data)
.enter()
.append('path')
.attr('d', diagonal)
.style('fill', 'none')
.style('stroke', 'black');
</script>
</body>
</html>
Возможно, я слишком много думаю об этом. Вероятно, вам просто лучше нарисовать дугу самостоятельно:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3@3.5.17" data-semver="3.5.17" src="https://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<script>
var data = [{
source: {
x: 10,
y: 10
},
target: {
x: 200,
y: 200
}
}, {
source: {
x: 200,
y: 10
},
target: {
x: 10,
y: 200
}
}];
var svg = d3.select('body')
.append('svg')
.attr('width', 205)
.attr('height', 205);
svg.selectAll('path')
.data(data)
.enter()
.append('path')
.attr('d', function(d){
var s = d.source,
t = d.target,
m = (s.y t.y) / 2,
p0 = [s.x, s.y],
p1 = [s.x, m],
p2 = [t.x, m],
p3 = [t.x, t.y];
// adjust constrol points
p1[0] -= 25;
p2[0] = 25;
return "M" p0 "C" p1 " " p2 " " p3;
})
.style('fill', 'none')
.style('stroke', 'black');
</script>
</body>
</html>
Комментарии:
1. Спасибо за отличный ответ. Можете ли вы объяснить, почему / как это работает? Что
this
относится к функции проекции?2. @IsaacLyman,
this
относится к самой функции проекции. По сути, я привязываю свойство к функции, чтобы запомнить, сколько раз она вызывалась. О, радости JavaScript 🙂3. Это имеет смысл. Итак, гарантируется ли, что функция проекции будет вызываться четыре раза для любой заданной диагональной кривой?
4. @IsaacLyman, да. Ознакомьтесь с исходным кодом, на который я ссылаюсь. Кроме того, см. Измененный ответ выше. Мой оригинальный не будет работать для нескольких путей.
5. @IsaacLyman, просто думаю об этом вопросе. Хотя мой первоначальный ответ классный и демонстрирует «удовольствие» от JavaScript, я сильно переоцениваю его. Я добавил новый ответ, который намного более понятен.