Reputation: 55
It's me again with my "Wheel of life" thing. But since last time i've got many steps further. I actually arrived at the final step, adding text in sections.
Here is the code I have so far (open in full screen):
d3.select('#step').on('click',function(){
sections = document.getElementById("sections").value;
deuxiemeEtape(sections);
});
function deuxiemeEtape(sections){
var form=d3.select("#form2")
form.append('hr');
form.append('p')
.html('Titres des petites sections')
.style('text-decoration','underline')
for (i=1;i<parseInt(sections)+1;i++){
form.append('label')
.html("Nom de la petite section "+i);
form.append('input')
.attr('type','text')
.attr('id','ps'+i);
}
form.append('hr');
form.append('p')
.html('Titres des sections')
.style('text-decoration','underline');
for (i=1;i<(parseInt(sections)+2)/2;i++){
form.append('label')
.html("Nom de la section "+i);
form.append('input')
.attr('type','text')
.attr('id','gs'+i);
}
d3.select("#circles").attr('disabled','');
d3.select("#sections").attr('disabled','');
d3.select("#step").attr('disabled','');
form.append('button')
.attr("id","create")
.html("Créer")
.on('click', onClickButton);
form.append('button')
.attr("id","reset")
.html("Reset")
.on('click',reloadPage);
}
function reloadPage(){
window.location.reload();
}
const createSvg = (circles, sections) => {
const svg = d3.select('#canvas');
const width = parseInt(svg.attr('width'));
const height = parseInt(svg.attr('height'));
svg.selectAll('g').remove();
const g = svg.append('g')
.attr('transform', `translate(${width / 2},${height / 2})`);
let i;
for (i = parseInt(circles)+1; i >= 0; i--)
{
if (i==circles){
g.append('circle')
.attr('r', 40 + i * 30)
.style('fill', 'blue')
.style('stroke', 'black');
}else if (i==parseInt(circles)+1){
g.append('circle')
.attr('r', 40 + i * 30)
.style('fill', 'red')
.style('stroke', 'black');
} else {
g.append('circle')
.attr('r', 40 + i * 30)
.style('fill', 'white')
.style('stroke', 'black');
}
}
const angle = Math.PI * 2 / sections;
let points = "";
for (i = 0; i <= sections; i++) {
if(i%2==0){
//Regular sections
radius = (circles) * 30 + 70;
} else {
//Small sections
radius = (circles-1) * 30 + 70;
}
const x = radius * Math.sin(angle * i);
const y = radius * -Math.cos(angle * i);
g.append('line')
.attr('x1', 0)
.attr('y1', 0)
.attr('x2', x)
.attr('y2', y)
.style('stroke', 'black');
points += ` ${x},${y}`;
}
// Don't mind this, it's WIP
function onPolygonClick () {
const x = d3.event.layerX - width / 2;
const y = d3.event.layerY - height / 2;
const radius = Math.hypot(x, y);
let clickAngle = Math.atan2(x, -y);
if (clickAngle < 0){
clickAngle = Math.PI * 2 + clickAngle;
}
const circle = Math.abs(Math.ceil((radius - 40) / 30));
const sector = Math.floor(clickAngle / angle)
alert("Cliqué sur cercle "+circle+" / secteur "+sector+"");
}
// Don't mind this, it's WIP
g.append('polygon')
.attr('points', points)
.style('fill', 'white')
.style('stroke', 'black')
.style('fill-opacity', 0.01)
.style('cursor', 'pointer')
.style('display','none')
.on('click', onPolygonClick);
}
function onClickButton () {
const circles = d3.select('#circles').node().value;
const sections = d3.select('#sections').node().value;
createSvg(circles, sections);
}
body{
margin:0;
padding:0;
background-color: rgb(78, 98, 112);
}
#formulaire,#form2{
width:9.3%
}
input{
border-radius: 3px;
margin:1em 0;
}
svg{
border:3px black solid
}
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8">
<title>SVG</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
<div>
<div id="contenu">
<div id="formulaire">
<label>Number of circles</label>
<input type="number" min="1" id="circles" />
<br />
<label>Number of sections</label>
<input type="number" min="1" id="sections" />
<button id="step">Suivant</button>
</div>
<div id="form2">
<!-- Insert inputs here -->
</div>
</div>
<div>
<svg id="canvas" height="750" width="1700">
</svg>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<script src="script.js"></script>
</body>
</html>
So as you can see, the inputs to get the text are added below the previous one. I need to add the text of the small sections in the blue circle, and the regular sections in the red circles ! I can't figure how to do this.
Here is an exemple of how it should look with the text :
Thank you for reading this ! And thank you for answering if you do !
Upvotes: 1
Views: 88
Reputation: 2205
I could achieve it to append text but not making text center aligned exactly.
d3.select('#step').on('click', function() {
sections = document.getElementById("sections").value;
deuxiemeEtape(sections);
});
function deuxiemeEtape(sections) {
var form = d3.select("#form2")
form.append('hr');
form.append('p')
.html('Titres des petites sections')
.style('text-decoration', 'underline')
for (i = 1; i < parseInt(sections) + 1; i++) {
form.append('label')
.html("Nom de la petite section " + i);
form.append('input')
.attr('class', 'sub-sections')
.attr('type', 'text')
.attr('id', 'ps' + i);
}
form.append('hr');
form.append('p')
.html('Titres des sections')
.style('text-decoration', 'underline');
for (i = 1; i < (parseInt(sections) + 2) / 2; i++) {
form.append('label')
.html("Nom de la section " + i);
form.append('input')
.attr('class', 'sections')
.attr('type', 'text')
.attr('id', 'gs' + i);
}
d3.select("#circles").attr('disabled', '');
d3.select("#sections").attr('disabled', '');
d3.select("#step").attr('disabled', '');
form.append('button')
.attr("id", "create")
.html("Créer")
.on('click', onClickButton);
form.append('button')
.attr("id", "reset")
.html("Reset")
.on('click', reloadPage);
}
function reloadPage() {
window.location.reload();
}
const createSvg = (circles, sections) => {
const svg = d3.select('#canvas');
const width = parseInt(svg.attr('width'));
const height = parseInt(svg.attr('height'));
svg.selectAll('g').remove();
const g = svg.append('g')
.attr('transform', `translate(${width / 2},${height / 2})`);
const archThickness = 30;
const fullCircle = 2 * 3.14;
//adding inner sections
const arcSizeSmallSections = fullCircle / sections;
const subSectionInputs = document.querySelectorAll('.sub-sections');
for (let i = 0; i < circles; i++) {
for (let j = 0; j < sections; j++) {
if (i + 1 === circles) {
appendArcs(g, i, j, archThickness, arcSizeSmallSections, 'blue', subSectionInputs[j].value);
} else {
appendArcs(g, i, j, archThickness, arcSizeSmallSections, 'white', i + 1);
}
}
}
// adding outer sections
const mergeSplits = Math.ceil(sections / 2);
const arcSizeSections = fullCircle / sections;
const sectionInputs = document.querySelectorAll('.sections');
const endAngle = 2 * arcSizeSections;
for (let j = 0; j < mergeSplits; j++) {
g.append("path")
.attr("d", d3.arc()
.innerRadius(circles * archThickness)
.outerRadius((circles + 1) * archThickness)
.startAngle(j * endAngle)
.endAngle(((j + 1) * endAngle) > fullCircle ? fullCircle : (j + 1) * endAngle)
)
.attr("id", () => 'section' + circles + j)
.attr('stroke', 'black')
.attr('fill', 'red');
g.append("text")
.attr("class", "monthText")
.attr("dy", 22)
.append("textPath")
.attr("startOffset", "22%")
.style("text-anchor", "middle")
.attr("xlink:href", () => "#section" + circles + j)
.text(() => sectionInputs[j].value);
}
}
function appendArcs(parent, i, j, archThickness, arcSize, color, text) {
parent
.append("path")
.attr("d", d3.arc()
.innerRadius(i * archThickness)
.outerRadius((i + 1) * archThickness)
.startAngle(j * arcSize)
.endAngle((j + 1) * arcSize)
)
.attr("id", () => 'section' + i + j)
.attr('stroke', 'black')
.attr('fill', color);
if (text) {
parent.append("text")
.attr("class", "monthText")
.attr("dy", 22)
.style("text-anchor", "middle")
.append("textPath")
.attr("startOffset", "22%")
.attr("xlink:href", () => "#section" + i + j)
.text(() => text);
}
}
function onClickButton() {
const circles = d3.select('#circles').node().value;
const sections = d3.select('#sections').node().value;
createSvg(parseInt(circles), parseInt(sections));
}
body {
margin: 0;
padding: 0;
background-color: rgb(78, 98, 112);
}
#formulaire,
#form2 {
width: 9.3%
}
input {
border-radius: 3px;
margin: 1em 0;
}
svg {
border: 3px black solid
}
.monthText {
fill: #161414;
font-size: 22px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div>
<div id="contenu">
<div id="formulaire">
<label>Number of circles</label>
<input type="number" min="1" id="circles" />
<br />
<label>Number of sections</label>
<input type="number" min="1" id="sections" />
<button id="step">Suivant</button>
</div>
<div id="form2">
<!-- Insert inputs here -->
</div>
</div>
<div>
<svg id="canvas" height="750" width="1700">
</svg>
</div>
</div>
Added support for dynamic sub sections.
d3.select('#step').on('click', function() {
sections = document.getElementById("sections").value;
deuxiemeEtape(sections);
});
function deuxiemeEtape(sections) {
var form = d3.select("#form2")
form.append('hr');
form.append('p').html('Titres des sections').style('text-decoration', 'underline');
for (i = 1; i <= parseInt(sections); i++) {
let ind = 0,
row = i;
const secGroup = form.append('div').attr('class', 'section');
const sec = secGroup.append('div').attr('class', 'section-info');
sec.append('label').html("Nom de la section " + i);
sec.append('input').attr('type', 'text');
sec.append('button').attr('type', 'button').attr('class', 'ms-2').text('Add Sub section')
.on('click', () => {
const subSecGroup = subSections.append('div').attr('class', 'sub-section');
subSecGroup.append('label').html("Nom de la petite section " + row + ++ind);
subSecGroup.append('input').attr('type', 'text');
subSecGroup.append('button').attr('type', 'button').attr('class', 'ms-2')
.text('Remove').on('click', () => {
subSecGroup.remove();
})
});
const subSections = secGroup.append('div').attr('class', 'ms-5 my-3 sub-sections');
const subSecGroup = subSections.append('div').attr('class', 'sub-section');
subSecGroup.append('label').html("Nom de la petite section " + i + 0);
subSecGroup.append('input').attr('type', 'text');
}
d3.select("#circles").attr('disabled', '');
d3.select("#sections").attr('disabled', '');
d3.select("#step").attr('disabled', '');
form.append('button')
.attr("id", "create")
.html("Créer")
.on('click', onClickButton);
form.append('button')
.attr("id", "reset")
.html("Reset")
.on('click', reloadPage);
}
function reloadPage() {
window.location.reload();
}
const createSvg = (circles) => {
const svg = d3.select('#canvas');
const width = parseInt(svg.attr('width'));
const height = parseInt(svg.attr('height'));
svg.selectAll('g').remove();
const g = svg.append('g')
.attr('transform', `translate(${width / 2},${height / 2})`);
const archThickness = 30;
const fullCircle = 2 * 3.14;
//adding inner sections
const subSectionInputs = document.querySelectorAll('.sub-section input');
const arcSizeSmallSections = fullCircle / subSectionInputs.length;
for (let i = 0; i < circles; i++) {
for (let j = 0; j < subSectionInputs.length; j++) {
if (i + 1 === circles) {
appendArcs(g, i, j, archThickness, arcSizeSmallSections, 'blue', subSectionInputs[j].value);
} else {
appendArcs(g, i, j, archThickness, arcSizeSmallSections, 'white', i + 1);
}
}
}
// adding outer sections
const sections = document.querySelectorAll('.section');
let prevAngle = 0;
for (let j = 0; j < sections.length; j++) {
const subSections = sections[j].querySelectorAll('.sub-section input');
const endAngle = prevAngle + (subSections.length * arcSizeSmallSections);
const text = sections[j].querySelector('.section-info input').value;
g.append("path")
.attr("d", d3.arc()
.innerRadius(circles * archThickness)
.outerRadius((circles + 1) * archThickness)
.startAngle(prevAngle)
.endAngle(endAngle)
)
.attr("id", () => 'section' + circles + j)
.attr('stroke', 'black')
.attr('fill', 'red');
g.append("text")
.attr("class", "monthText")
.attr("dy", 22)
.append("textPath")
.attr("startOffset", "22%")
.style("text-anchor", "middle")
.attr("xlink:href", () => "#section" + circles + j)
.text(() => text);
prevAngle = endAngle;
}
}
function appendArcs(parent, i, j, archThickness, arcSize, color, text) {
parent
.append("path")
.attr("d", d3.arc()
.innerRadius(i * archThickness)
.outerRadius((i + 1) * archThickness)
.startAngle(j * arcSize)
.endAngle((j + 1) * arcSize)
)
.attr("id", () => 'section' + i + j)
.attr('stroke', 'black')
.attr('fill', color);
if (text) {
parent.append("text")
.attr("class", "monthText")
.attr("dy", 22)
.style("text-anchor", "middle")
.append("textPath")
.attr("startOffset", "22%")
.attr("xlink:href", () => "#section" + i + j)
.text(() => text);
}
}
function onClickButton() {
const circles = d3.select('#circles').node().value;
const sections = d3.select('#sections').node().value;
createSvg(parseInt(circles), parseInt(sections));
}
body {
margin: 0;
padding: 0;
background-color: rgb(78, 98, 112);
}
input {
border-radius: 3px;
margin: 1em 0;
}
svg {
border: 3px black solid
}
.monthText {
fill: #161414;
font-size: 22px;
}
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-eOJMYsd53ii+scO/bJGFsiCZc+5NDVN2yr8+0RDqr0Ql0h+rP48ckxlpbzKgwra6" crossorigin="anonymous">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div class="container">
<div id="contenu" class="row">
<div id="formulaire" class="col-6">
<label>Number of circles</label>
<input type="number" min="1" id="circles" />
</div>
<div class="col-6">
<label>Number of sections</label>
<input type="number" min="1" id="sections" />
</div>
</div>
<div class="row my-2"> <button type="button" class="btn btn-primary col-2 m-auto" id="step">Suivant</button> </div>
<div id="form2">
<!-- Insert inputs here -->
</div>
<div>
<svg id="canvas" height="750" width="1700"></svg>
</div>
</div>
Upvotes: 2
Reputation: 7210
Here is a simple routine to compute an arc path and draw a text along:
const svg = d3.select('svg');
const arcPath = (centerX, centerY, radius, angle, length) => {
const sector = length / radius;
const start = angle - sector / 2;
const end = angle + sector / 2;
const startX = centerX + radius * Math.sin(start);
const startY = centerY - radius * Math.cos(start);
const endX = centerX + radius * Math.sin(end);
const endY = centerY - radius * Math.cos(end);
return `M ${startX},${startY}
A ${radius},${radius} 1 0 1 ${endX},${endY}`;
}
const angleRad = 45 * Math.PI / 180;
const path = arcPath(100,100,50,angleRad,70);
svg.select('path').attr('d', path);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg width="200" height="200">
<path fill="none" stroke="none" id="my-path"/>
<text>
<textPath href="#my-path" textLength="70" >
BLABLA
</textPath>
</text>
</svg>
Upvotes: 1