在d3v5的x轴上切换刷牙和平移/缩放

时间:2019-07-04 08:22:50

标签: javascript d3.js zoom brush

我正在使用d3.js版本5,并绘制了一个时间轴,以秒为单位显示持续时间。此时间轴仅显示整个持续时间的60秒间隔,但可以向左/向右平移/缩放以在时间轴中导航。一个人也应该能够使用画笔突出显示多个时间间隔。绘制多个笔刷的代码基于https://bl.ocks.org/NGuernse/4c75a051154cbe08bf80cddefefae22a / https://gist.github.com/NGuernse/4c75a051154cbe08bf80cddefefae22a

现在的问题是,一旦添加代码以在时间轴上绘制画笔,我就无法在时间轴上执行缩放/平移。

类似于上面的链接,我添加了一个禁用画笔功能的开关,作为回报,它还应该启用缩放功能。但是,我只能禁用/启用涂刷功能,而不能重新启用缩放功能。

var margin = {top: 200, right: 40, bottom: 200, left: 40},
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;


// Zoom
var zoom = d3.zoom()
    .extent([[0, 0], [width, height]])
    .scaleExtent([1, 10])
    .translateExtent([[0, 0], [width, height]])
    .on('zoom', zoomed);

// Scale
var xScale = d3.scaleTime()
    .domain([0, 137 * 1000])//domain of [00:00 - 02:17] in seconds
    .range([0, width]);

// Axis
var xAxis = d3.axisBottom(xScale)
    .ticks(d3.timeSecond.every(1))
    .tickSize(-height)
    .tickFormat(d3.timeFormat('%M:%S'))


// Main svg
var svg = d3.select('#block')
    .append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")")

var rect = svg
    .append("rect")
    .attr("width", width)
    .attr("height", height)
    .style("fill", "white")
    .call(zoom);

// axis
var axis = svg.append("g")

var formatSeconds = d3.timeFormat("%S");

// Jump to interval [00:00, 01:00] at start
startTransition();
var ticks = d3.selectAll(".tick text");

// Only display a tick-label if second is dividable by 5
ticks.attr("class", function (d, i) {

    var s = formatSeconds(d)
    if (s % 5 !== 0) d3.select(this).remove();
});

function zoomed() {
    var transform = d3.event.transform;

    var xNewScale = transform.rescaleX(xScale);

    xAxis.scale(xNewScale);
    axis.call(xAxis);
    ticks = d3.selectAll(".tick text")
    // readjust labels in case of zooming
    ticks.attr("class", function (d, i) {
        var s = formatSeconds(d)
        if (s % 5 !== 0) d3.select(this).remove();
    });

}

function startTransition() {

    // to jump to [00:00,01:00] we need to calculate a new scale factor (k)...
    var k = (xScale(137 * 1000) - xScale(0)) / (xScale(60 * 1000) - xScale(0));

    // ...and then a translate to [01:00, 00:00]
    var tx = 0 //- (k * xScale(60));

    var t = d3.zoomIdentity.translate(tx, 0).scale(k);

    // Rescale the axis
    xAxis.scale(t.rescaleX(xScale));
    axis
        .attr("class", "axis axis--x")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis)
        .attr("text-anchor", "middle")
        .selectAll("text")
        .attr("x", 0)


    rect.call(zoom.transform, t);
}

var gBrushes = svg.append('g')
    .attr("height", height)
    .attr("width", width)
    .attr("fill", "none")
    .attr("transform", "translate(" + 0 + "," + 0 + ")")
    .attr("class", "brushes");

var mySelections = {}
var brushCount = 40;

var brushes = [];
newBrush()
drawBrushes()

function newBrush() {
    console.log('newBrush')
    var brush = d3.brushX()
        .extent([[0, 0], [width, height]])

        .on("start", brushstart)
        .on("brush", brushed)
        .on("end", brushend);

    brushes.push({id: brushes.length, brush: brush});


    function brushstart() {
        // Brush start here

    };

    function brushed() {
        let selection = d3.event.selection.map(xScale.invert);
        mySelections[this.id] = {start: selection[0], end: selection[1]};
        // console.log("Selections are: ", mySelections);
    }

    function brushend() {
        // Figure out if our latest brush has a selection
        var lastBrushID = brushes[brushes.length - 1].id;
        var lastBrush = document.getElementById('brush-' + lastBrushID);
        var selection = d3.brushSelection(lastBrush);

        if (!d3.event.sourceEvent) return; // Only transition after input.
        if (!d3.event.selection) return; // Ignore empty selections.
        var d0 = d3.event.selection.map(xScale.invert),
            d1 = d0.map(d3.timeSecond);

        // If empty when rounded, use floor & ceil instead.
        if (d1[0] >= d1[1]) {
            d1[0] = d3.timeSecond.floor(d0[0]);
            d1[1] = d3.timeSecond.offset(d1[0]);
        }

        d3.select(this).transition().call(d3.event.target.move, d1.map(xScale));

        // If it does, that means we need another one
        if (brushes.length < brushCount && selection && selection[0] !== selection[1]) {
            newBrush();
        }

        // Always draw brushes
        drawBrushes();
    }
}

function drawBrushes() {

    var brushSelection = gBrushes
        .selectAll('.brush')
        .data(brushes, function (d) {
            return d.id
        });


    // Set up new brushes
    brushSelection.enter()
        .insert("g", '.brush')
        .attr('class', 'brush')
        .attr('id', function (brush) {
            return "brush-" + brush.id;
        })
        .each(function (brushObject) {
            // call the brush
            brushObject.brush(d3.select(this));


        });


    brushSelection
        .each(function (brushObject) {
            d3.select(this)
                .attr('class', 'brush')
                .selectAll('.overlay')
                .style('pointer-events', function () {
                    var brush = brushObject.brush;
                    if (brushObject.id === brushes.length - 1 && brush !== undefined) {
                        return 'all';
                    } else {
                        return 'none';
                    }
                });
        })

    brushSelection.exit()
        .remove();
}

let toggle = true;

document.getElementById('disable-btn').addEventListener('click', () => {
    toggleBrush();
});

var toggleBrush = function () {
    toggle = !toggle;

    if (!toggle) {
        document.getElementById('disable-btn').innerHTML = '<i class="fa fa-toggle-off"></i> Brushes Off';

        for (let i = 0, len = brushes.length; i < len; i++) {
            d3.select('#brush-' + i).on('.brush', null);
        }

        d3.select('.brushes').selectAll('.selection').style("cursor", "initial");
        d3.select('.brushes').selectAll('.overlay').style("cursor", "initial");
        rect.on(".zoom", null)


    } else {
        document.getElementById('disable-btn').innerHTML = '<i class="fa fa-toggle-on"></i> Brushes On';

        for (let i = 0, len = brushes.length; i < len; i++) {
            brushes[i].brush(d3.select('#brush-' + i));
        }

        rect.call(zoom)
        startTransition();

        d3.select('.brushes').selectAll('.selection').style("cursor", "move");
        d3.select('.brushes').selectAll('.overlay').style("cursor", "crosshair");


    }
};

这是jsfiddle,仅包含具有缩放/平移功能的时间轴:https://jsfiddle.net/prw67fu3/

还有指向当前小提琴的链接,其中包括绘制画笔和启用/禁用画笔的代码。 https://jsfiddle.net/gb8m4tr3/

将切换开关设置为关闭画笔后,应该可以在时间轴上缩放/平移,而不再能够绘制画笔。但是,添加画笔代码后,我不知道如何重新激活缩放功能。

非常感谢您!

0 个答案:

没有答案