3 votes

Comment se débarrasser des petites lignes entre les rectangles de la toile ?

heatmap

Je suis très novice en D3 et comme vous pouvez le voir dans l'image ci-dessus, il y a de minuscules lignes/écarts entre chaque rectangle dont j'aimerais me débarrasser, ceci est dessiné sur un élément de toile avec chaque rectangle commençant là où le dernier se termine en utilisant D3.js suivant este tutoriel presque exactement moins l'ajout des espaces entre chaque carré.

J'ai essayé

this.canvas.imageSmoothingQuality = 'low';

draw() {
  const canvas = d3
    .select(this.chartContainer.nativeElement)
    .append('canvas')
    .attr('width', this.width)
    .attr('height', this.height)
    .attr(
      'transform',
      'translate(' + this.margin.left + ',' + this.margin.top + ')'
    );

  this.canvas = canvas.node().getContext('2d');
  this.clearCanvas();
  this.canvas.imageSmoothingQuality = 'low';
  const elements = this.shadowContainer.selectAll('custom.rect');

  const _this = this;
  elements.each(function(d, i) {
    const node = d3.select(this);
    // Here you retrieve the colour from the individual in-memory node and set the fillStyle for the canvas paint
    _this.canvas.fillStyle = node.attr('color');
    // Here you retrieve the position of the node and apply it to the fillRect context function which will fill and paint the square.

    _this.canvas.fillRect(
      Number(node.attr('x')),
      Number(node.attr('y')),
      Number(node.attr('width')),
      Number(node.attr('height'))
    );
  });
}

private dataBind(value) {
  const customBase = document.createElement('custom');
  this.shadowContainer = d3.select(customBase);
  const {
    viewModes: {
      heatMap: {
        data,
        chartOptions: { engagementStatus, xAxis, yAxis }
      }
    }
  } = value;

  const x = this.d3
    .scaleBand()
    .range([0, this.width])
    .domain(xAxis.categories);

  this.shadowContainer
    .append('g')
    .style('font-size', 11)
    .attr('class', 'x-axis')
    .call(this.d3.axisTop(x).tickSize(0))
    .select('.domain')
    .remove();

  this.shadowContainer
    .selectAll('.x-axis text')
    .style('text-anchor', 'start')
    .attr('transform', function(d) {
      return `translate(8, -8)rotate(-90)`;
    });

  const y = this.d3
    .scaleBand()
    .domain(d3.reverse(yAxis.categories))
    .range([this.height, 0]);

  const color = this.d3
    .scaleLinear()
    .domain([-2, -1, 0, 1])
    // @ts-ignore
    .range(['#5b717d', '#ffb957', '#ee6b56', '#40a050']);

  const join = this.shadowContainer
    .selectAll('custom.rect')
    .data(data, function(d) {
      return `${d.Date}:${d.Member}`;
    });

  const enterSelection = join
    .enter()
    .append('custom')
    .attr('class', 'rect')
    .attr('x', d =>
      this.getCorrectDatePosition(
        d.Date,
        x,
        xAxis.categories[0].split('/').length
      )
    )
    .attr('y', function(d) {
      return y(d.Member);
    })
    .attr('width', 24)
    .attr('height', 24);

  join
    .merge(enterSelection)
    .attr('width', x.bandwidth())
    .attr('height', y.bandwidth())
    .attr('color', function(d) {
      return color(d.score);
    });

  const exitSelection = join
    .exit()
    .transition()
    .attr('width', 0)
    .attr('height', 0)
    .remove();
}

4voto

Andrew Reid Points 16844

C'est probablement un problème provenant de vos balances. Il peut survenir avec SVG ou canvas et se produit lorsqu'on traite des coordonnées qui nécessitent un tracé à des fractions de pixel.

Voici une démonstration avec SVG :

var data = d3.range(20);

var x = d3.scaleBand()
  .range([10,250])
  .domain(data)

var svg = d3.select("body")
  .append("svg")
  .attr("width", 500);

var rect = svg.selectAll("rect")
 .data(data)
 .enter()
 .append("rect")
 .attr("x", d=>x(d) )
 .attr("y", 50)
 .attr("width", x.bandwidth())
 .attr("height",100)
 .attr("fill","crimson")

svg.transition()
  .attrTween("tween", function() {
    var i = d3.interpolate(250,480)
    return function(t) {
       x.range([50,i(t)])
       rect.attr("x",d=>x(d))
           .attr("width", x.bandwidth());

       return "";
    }
  })
  .duration(10000);

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

Et un avec Canvas :

var data = d3.range(20);

var x = d3.scaleBand()
  .range([10,250])
  .domain(data)

var canvas = d3.select("body")
  .append("canvas")
  .attr("width", 500);

var rect = d3.create("div").selectAll("rect")
 .data(data)
 .enter()
 .append("rect")
 .attr("x", d=>x(d) )
 .attr("y", 50)
 .attr("width", x.bandwidth())
 .attr("height",100)
 .attr("fill","crimson")

canvas.transition()
  .attrTween("tween", function() {
    var i = d3.interpolate(250,480)
    var context = canvas.node().getContext("2d");
    return function(t) {
       x.range([50,i(t)])
       context.fillStyle = "#fff";
       context.fillRect(0,0,550,300);
       rect.attr("x",d=>x(d))
           .attr("width", x.bandwidth())
           .each(function() {
              var node = d3.select(this);
              context.fillStyle = "crimson"
              context.fillRect(
                 +node.attr("x"),
                 +node.attr("y"),
                 +node.attr("width"),
                 +node.attr("height"))
           })

       return "";
    }
  })
  .duration(10000);

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

La solution consiste à s'impliquer un peu plus dans la définition du domaine et de l'étendue de la gamme. Commencez par la largeur de bande souhaitée, un nombre entier de pixels, et définissez la plage de sorte que la différence entre les valeurs minimale et maximale soit égale au nombre de valeurs dans le domaine * la largeur de bande.

Donc, au lieu de :

 const x = this.d3
  .scaleBand()
  .range([0, this.width])
  .domain(xAxis.categories);

Vous auriez :

 const length = 10; // length of a box side
 const x = this.d3
  .scaleBand()
  .domain(xAxis.categories)
  .range([0,xAxis.categories * length])

Vous pouvez également calculer length ci-dessus de manière dynamique, par exemple en utilisant : Math.floor(width/xAxis.categories)

En utilisant l'approche ci-dessus et un exemple légèrement inventé pour faciliter la transition, nous supprimons le modèle alias/moire. Comme nous n'utilisons que des pixels entiers, la transition saute lorsque chaque barre augmente en largeur d'un pixel entier en même temps, à mesure que de l'espace se libère dans la gamme :

var data = d3.range(20);
var length = 30;

var x = d3.scaleBand()
  .range([10,data.length*length])
  .domain(data)

var canvas = d3.select("body")
  .append("canvas")
  .attr("width", 500);

var rect = d3.create("div").selectAll("rect")
 .data(data)
 .enter()
 .append("rect")
 .attr("x", d=>x(d) )
 .attr("y", 50)
 .attr("width", x.bandwidth())
 .attr("height",100)
 .attr("fill","crimson")

canvas.transition()
  .attrTween("tween", function() {
    var i = d3.interpolate(250,480)
    var context = canvas.node().getContext("2d");
    return function(t) {
       length = Math.floor(i(t)/data.length)
       x.range([10,length*data.length+10])

       context.fillStyle = "#fff";
       context.fillRect(0,0,550,300);
       rect.attr("x",d=>x(d))
           .attr("width", x.bandwidth())
           .each(function(d,i) {
              var node = d3.select(this);
              context.fillStyle = d3.schemeCategory10[i%10];
              context.fillRect(
                 +node.attr("x"),
                 +node.attr("y"),
                 +node.attr("width"),
                 +node.attr("height"))
           })

       return "";
    }
  })
  .duration(10000);

<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X