Наличие как мышиных событий, так и барных переходов - PullRequest
1 голос
/ 25 сентября 2019

Мое намерение состоит в том, чтобы создавать график, который будет иметь изменение высоты полосы каждый раз, когда она рисуется / перерисовывается, а затем, когда панель отображается, могут быть события мыши (mouseenter, mouseleave и mousemove), которые будут отображать всплывающую подсказкус информацией о баре, над которым вы зависаете.

Я сделал так, чтобы события мыши работали, но куда мне добавить переход?Если я добавляю переход над '.attr ("высота"), переход работает, но тогда оси X и Y не отрисовываются, и я получаю ошибку "неизвестный тип: mouseenter".Если я помещаю переход за пределы выделения панели и выполняю 'bar.transition () ...', переход не отображается, но оси X и Y отрисовываются и событие мыши работает.

Я что-то упустил?Мое понимание того, как работают вложенные функции D3 / javascript, скудное, поскольку мы никогда не обсуждали их в классе, поэтому я, скорее всего, упускаю из виду основной принцип применения свойств D3 и т. Д.

РЕДАКТИРОВАТЬ: Глядя на это, должен ли я объявить переход выше

.attr('height', function (d)...

И я должен переместить события щелчка вниз, чтобы оно было

bar.on('mouseenter', function (d)...

Текущий код:

function drawHistogram(type = 3, title = 'Total Count by Name - Alphabetically') {
    var url = "histogramData.tsv";

    var margin = 100;
    var width = 1000 - (margin * 2);
    var height = 500 - (margin * 2);

    var x = d3.scaleBand()
        .range([0, width])
        .padding(0.1); // space between bars

    var y = d3.scaleLinear()
        .range([height, 0]);

    var svg = d3.select('#graphBox').append('svg')
        .attr('width', width + margin + margin)
        .attr('height', height + margin + margin)
        .append('g')
        .attr('transform',
            'translate(' + margin + ',' + margin + ')');

    // add graph title and x/y labels
    svg.append('text')
        ...

    var gradient = d3.scaleLinear()
        .domain([0, 50])
        .range(['rgb(253,203,90)', 'rgb(253,56,170)']);

    d3.tsv(url, function (error, data) {
        if (error) throw error;

        // sort data based on type
        if (type == 0) { // name Z to A
            data.sort(function (a, b) {
                return d3.descending(a.name, b.name)
            });
        } else if (type == 1) { // count lowest to highest
            data.sort(function (a, b) {
                return a.count - b.count;
            });
        } else if (type == 2) { // count highest to lowest
            data.sort(function (a, b) {
                return b.count - a.count;
            });
        } // name A to Z

        data.forEach(function (d) {
            d.count = +d.count;
        });

        x.domain(data.map(function (d) {
            return d.name;
        }));

        y.domain([0, d3.max(data, function (d) {
            return d.count;
        })]);

        var bar = svg.selectAll('.bar')
            .data(data)
            .enter()
            .append('rect')
            .attr('class', 'bar')

            .attr('x', function (d) {
                return x(d.name);
            })

            .attr('y', function (d) {
                return y(d.count);
            })
            .attr('width', x.bandwidth())

            // This is the transition code, this transition will allow the bars to dropdown. 
            // If you uncomment this, the bar effect will work as intended, however, my axis
            // are no longer drawn and the mouse events do not work. But the transition works.

            /*
            .transition()
            .duration(5000)
            .delay(function (d, i) {
                return i * 20;
            })
            */

            .attr('height', function (d) {
                return height - y(d.count);
            })

            // give bars horizontal gradient fill
            .style('fill', function (d, i) {
                return gradient(i);
            })

            .on('mouseenter', function (d) {
                bar = d3.select(this)
                    .attr('opacity', 0.5)

                tooltip.style('display', null)
                    .raise()
            })

            .on('mouseleave', function (d) {
                d3.select(this).attr('opacity', 1)
                tooltip.style('display', 'none')
            })

            .on('mousemove', function (d) {
                var xPos = d3.mouse(this)[0] + 5;
                var yPos = d3.mouse(this)[1] - 40;
                tooltip.attr('transform', 'translate(' + xPos + ',' + yPos + ')');
                tooltip.select('#name').text('Name: ' + d.name);
                tooltip.select('#count').text('Count: ' + d.count);
            });

        // I tried to add the transition outside of the bar's creation.
        // and call it here while adding the transition.
        // The transition here does not do anything but the axis are drawn
        // and mouse events work properly. 

        /*
        bar.transition()
            .duration(5000)
            .delay(function (d, i) {
                return i * 20;
            })
        */

        // add x and y axis legends
        svg.append('g')
            ...

        // add a clickable line over the x axis to sort chart alphabetically 
        svg.append('line')
            ...

        // add a clickable line over the y axis to sort chart by value
        svg.append('line')
            ....

    });

    // tooltip item
    var tooltip = svg.append('g')
        .attr('class', 'tooltip')
        .style('display', 'none');

    tooltip.append('rect')
        .attr('width', 80)
        .attr('height', 35)
        .attr('fill', 'white')
        .style('opacity', 1);

    tooltip.append('text')
        .attr('id', 'name')
        .attr('x', '0.5em')
        .attr('dy', '1.3em')
        .style('text-anchor', 'left')

    tooltip.append('text')
        .attr('id', 'count')
        .attr('x', '0.5em')
        .attr('dy', '2.3em')
        .style('text-anchor', 'left')
}

Ответы [ 2 ]

1 голос
/ 26 сентября 2019

Существует небольшое недопонимание в принятом ответе : вы можете подписаться на события мыши в переходах, это не проблема.

Проблема заключается только ввопрос имен методов.По какой-то причине создатель D3 решил присвоить двум разным методам одно и то же имя.Следовательно, on() здесь:

selection.on

не тот же метод on() здесь:

transition.on

Это разные методы, содно и то же имяПри этом transition.on не используется для событий мыши (то есть selection.on), и он принимает только 4 типа:

  • "start"
  • "end"
  • "interrupt"
  • "cancel"

И здесь у вас есть демонстрация, показывающая, что вы можете подписаться на события мыши во время перехода при условии, что вы используете selection.on (в этом примере d3.select(this).on...).Наведите указатель мыши на черный прямоугольник, чтобы не было прослушивателя событий мыши.После запуска перехода прослушиватель событий мыши работает:

const rect = d3.select("rect");
rect.transition()
  .delay(1000)
  .duration(5000)
  .attr("width", 300)
  .on("start", function() {
    d3.select(this).on("mousemove", function() {
      console.log("The mouse is over!");
    })
  })
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg>
  <rect width="100" height="150"></rect>
</svg>

Или вы также можете использовать менее известный transition.selection(), который возвращает выбор, соответствующий переходу.Отличие от приведенного выше фрагмента состоит в том, что слушатель сразу подключается:

const rect = d3.select("rect");
rect.transition()
  .delay(1000)
  .duration(5000)
  .attr("width", 300)
  .selection()
  .on("mousemove", function() {
    console.log("The mouse is over!");
  })
<script src="https://d3js.org/d3.v5.min.js"></script>
<svg>
  <rect width="100" height="150"></rect>
</svg>
0 голосов
/ 26 сентября 2019

Вы не можете подписаться на события мыши в переходах, поэтому у вас должен быть .on перед .transition.

Попробуйте что-то вроде этого:

    var bar = svg.selectAll('.bar')
        .data(data)
        .enter()
        .append('rect')
        .attr('class', 'bar')
        .attr('x', function (d) {
            return x(d.name);
        })
        .attr('y', function (d) {
            return y(d.count);
        })
        .attr('width', x.bandwidth())
        .on('mouseenter', function (d) {
            bar = d3.select(this)
                .attr('opacity', 0.5)

            tooltip.style('display', null)
                .raise()
        })
        .on('mouseleave', function (d) {
            d3.select(this).attr('opacity', 1)
            tooltip.style('display', 'none')
        })
        .on('mousemove', function (d) {
            var xPos = d3.mouse(this)[0] + 5;
            var yPos = d3.mouse(this)[1] - 40;
            tooltip.attr('transform', 'translate(' + xPos + ',' + yPos + ')');
            tooltip.select('#name').text('Name: ' + d.name);
            tooltip.select('#count').text('Count: ' + d.count);
        })
        .transition()
        .duration(5000)
        .delay(function (d, i) {
            return i * 20;
        })
        .attr('height', function (d) {
            return height - y(d.count);
        })
        // give bars horizontal gradient fill
        .style('fill', function (d, i) {
            return gradient(i);
        });
...