3

I am trying to adjust the font size of the text inside a bar based on the size of the rects. The width of the rects (as can be see in the fiddle) changes depending on the data.

What I tried was to adjust the font size based on the width of the rects:

 var bar = svg.selectAll('g')
  .data(tasks)
  .enter()
  .append('g')
  .attr('transform', rectTransform)

  bar.append("rect")
  .attr("rx", 5)
  .attr("ry", 5)
  .attr("y", 0)
  .attr("fill", function(d){return d.color})
  .attr("height", function(d) { return 70; })
  .attr("width", function(d) { 
    return (x(d.endDate) - x(d.startDate)); 
  });

  bar.append("text")
  .attr('y', 10)
  .attr('dy', '.35em')
  .style("font-size", function(){
        return d3.select(this.previousSibling).attr("width") * 0.006 + 'px'
        })
  .text(function(d){return d.taskName})

This end up working poorly, as it worked for some bars and for others the text was either really big or small.

I have also come across the vw metric instead of px for font size, which adjust the font based on the view port, but I am interested in the size of the rects instead of the whole screen, thus would also be a poor choice.

Is there a way to adjust the font-size of the text in the bars based on their size? (their height/width)

dsax7
  • 1,333
  • 22
  • 36

2 Answers2

0

You could use your xscale to generate a font size.

.attr("font-size", d => `${(x(d.endDate) - x(d.startDate))/5}px`)

d3.gantt = function() {
  var FIT_TIME_DOMAIN_MODE = "fit";

  var margin = {
    top: 20,
    right: 40,
    bottom: 20,
    left: 150
  };
  var timeDomainStart = d3.timeDay.offset(new Date(), -3);
  var timeDomainEnd = d3.timeHour.offset(new Date(), +3);
  var timeDomainMode = FIT_TIME_DOMAIN_MODE; // fixed or fit
  var taskTypes = ["D Job", "P Job", "E Job", "A Job", "N Job"];
  var height = document.body.clientHeight - margin.top - margin.bottom - 5;
  var width = document.body.clientWidth - margin.right - margin.left - 5;

  var tickFormat = "%y %b";

  var rectTransform = function(d) {
    return "translate(" + x(d.startDate) + "," + y(d.taskName) + ")";
  };

  var x, y, xAxis, yAxis;

  initAxis();

  var initTimeDomain = function() {
    if (timeDomainMode === FIT_TIME_DOMAIN_MODE) {
      if (tasks === undefined || tasks.length < 1) {
        timeDomainStart = d3.time.day.offset(new Date(), -3);
        timeDomainEnd = d3.time.hour.offset(new Date(), +3);
        return;
      }
      tasks.sort(function(a, b) {
        return a.endDate - b.endDate;
      });
      timeDomainEnd = tasks[tasks.length - 1].endDate;
      tasks.sort(function(a, b) {
        return a.startDate - b.startDate;
      });
      timeDomainStart = tasks[0].startDate;
    }
  };

  function initAxis() {
    x = d3.scaleTime().domain([timeDomainStart, timeDomainEnd]).range([0, width]).clamp(true);

    y = d3.scaleBand().domain(taskTypes).rangeRound([0, height - margin.top - margin.bottom], 0.1);

    xAxis = d3.axisBottom().scale(x).tickFormat(d3.timeFormat(tickFormat))
      .tickSize(8).tickPadding(8);

    yAxis = d3.axisLeft().scale(y).tickSize(0);
  }

  function gantt(tasks) {

    initTimeDomain();
    initAxis();

    var svg = d3.select("body")
      .append("svg")
      .attr("class", "chart")
      .attr("width", width + margin.left + margin.right)
      .attr("height", height + margin.top + margin.bottom)
      .append("g")
      .attr("class", "gantt-chart")
      .attr("transform", "translate(" + margin.left + ", " + margin.top + ")");

    var bar = svg.selectAll('g')
      .data(tasks)
      .enter()
      .append('g')
      .attr('transform', rectTransform)

    bar.append("rect")
      .attr("rx", 5)
      .attr("ry", 5)
      .attr("y", 0)
      .attr("fill", function(d) {
        return d.color
      })
      .attr("height", function(d) {
        return 70;
      })
      .attr("width", function(d) {
        return (x(d.endDate) - x(d.startDate));
      });

    bar.append("text")
      .attr('y', 10)
      .attr('dy', '.35em')
      .attr("font-size", d => `${(x(d.endDate) - x(d.startDate))/5}px`)
      .text(function(d) {
        return d.taskName
      })

    svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0, " + (height - margin.top - margin.bottom) + ")")
      .transition()
      .call(xAxis);

    var legend = svg.selectAll(".legend")
      .data(tasks)
      .enter()
      .append("g")

    legend.append("rect")
      .attr("fill", function(d) {
        return d.color
      })
      .attr("width", 10)
      .attr("height", 15)
      .attr("x", -100)
      .attr("y", function(d, i) {
        return 20 + 20 * i
      })

    legend.append("text")
      .attr("y", function(d, i) {
        return 32 + 20 * i
      })
      .attr("x", -90)
      .text(function(d) {
        return d.taskName
      });

    svg.append("g").attr("class", "y axis").transition().call(yAxis);

    return gantt;

  };


  return gantt;
};

var tasks = [{
    "startDate": new Date(2018, 8),
    "endDate": new Date(2018, 10),
    "taskName": "E Job",
    "color": "blue"
  },
  {
    "startDate": new Date(2018, 4),
    "endDate": new Date(2018, 11),
    "taskName": "D Job",
    "color": "red"
  },
  {
    "startDate": new Date(2018, 7),
    "endDate": new Date(2018, 9),
    "taskName": "N Job",
    "color": "green"
  },
  {
    "startDate": new Date(2018, 1),
    "endDate": new Date(2018, 9),
    "taskName": "A Job",
    "color": "brown"
  },
  {
    "startDate": new Date(2018, 2),
    "endDate": new Date(2018, 4),
    "taskName": "E Job",
    "color": "purple"
  },
];
var gantt = d3.gantt();
gantt(tasks);
html,
body,
#wrapper {
  width: 100%;
  height: 100%;
  margin: 0px;
}

.chart {
  font-family: Arial, sans-serif;
  font-size: 12px;
}

.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}

.bar {
  fill: #33b5e5;
}

.bar-failed {
  fill: #CC0000;
}

.bar-running {
  fill: #CC0000;
}

.bar-succeeded {
  fill: #33b5e5;
}

.bar-killed {
  fill: #ffbb33;
}

#forkme_banner {
  display: block;
  position: absolute;
  top: 0;
  right: 10px;
  z-index: 10;
  padding: 10px 50px 10px 10px;
  color: #fff;
  background: url('http://dk8996.github.io/Gantt-Chart/images/blacktocat.png') #0090ff no-repeat 95% 50%;
  font-weight: 700;
  box-shadow: 0 0 10px rgba(0, 0, 0, .5);
  border-bottom-left-radius: 2px;
  border-bottom-right-radius: 2px;
  text-decoration: none;
}
<script type="text/javascript" src="http://d3js.org/d3.v4.min.js"></script>

Fiddle

ksav
  • 20,015
  • 6
  • 46
  • 66
  • What happens when 'X job' is replaced by real text strings of varying lengths? Or with very long jobs? If this technique is used, a suitable `y` offset should be added so that the text doesn't escape the `rect` objects. – i alarmed alien Oct 13 '18 at 12:45
0

Using ksav method but with the adjustment of using the height of the rect (here constant 70, but use the expression used to set height based on d) and the text in the rect

  bar.append("text")
    .attr('y', 10)
    .attr('dy', '.7em')
    .attr("font-size", d => `${Math.min( (x(d.endDate) - x(d.startDate))/d.taskName.length, 70)}px`)
    .text(d => d.taskName );
rioV8
  • 24,506
  • 3
  • 32
  • 49