简体   繁体   English

在折线+条形图中使用d3笔刷时出现问题(折线+条形图放大)

[英]issue while using d3 brush in a line+bar chart (zoom in line+bar chart)

I am trying to use brush to zoom my custom chart in d3.js but its not behaving the way it should.Any idea what could be wrong here is js fiddle http://fiddle.jshell.net/saurabh_nitc10/od8gfsd3/9/ 我正在尝试使用画笔在d3.js中缩放我的自定义图表,但它没有应有的方式。任何想法可能出问题的地方是js小提琴http://fiddle.jshell.net/saurabh_nitc10/od8gfsd3/9/

like in this fiddle http://fiddle.jshell.net/CjaD3/1/ after brushing the bars are going out of yaxis. 就像在这个小提琴http://fiddle.jshell.net/CjaD3/1/刷完条之后,yaxis消失了。 any idea 任何的想法

what is wrong with the existing fiddle.Its not behaving the way it is supposed to do after zooming.please help.I just updated the fiddle 现有小提琴有什么问题。它没有按照缩放后的方式运行。请帮助。我只是更新了小提琴

here is the plugin I have created. 这是我创建的插件。

(function($) {
$.dualAxis = {};
var xMapObject=[];
var svg = '';
var focus = '';
var tip = '';
function DualAxis(element, options) {
    this.$element = $(element);
    this.options = options;
    this.margin = {top: 20, right: 80, bottom: 30, left: 40},
    width = $(this.$element.selector).width() - this.margin.left - this.margin.right,
    height = $(this.$element.selector).height() - this.margin.top - this.margin.bottom;
    this.options.height = (this.options.height == null? height: this.options.height);
    this.options.width = (this.options.width == null? width: this.options.width);
    this.rangeMax = this.getMaxX().length*100;
    if (this.rangeMax > 14000) this.rangeMax = 14000;
    this.enabled = true;
}

DualAxis.prototype = {
    draw: function(){
        this.options.data.bar.forEach(function(d) {
            d.value = +d.value;
        });
        this.options.data.line.forEach(function(d) {
            d.value = +d.value;
        });
        tip = d3.tip()
          .attr('class', 'd3-tip')
          .html(function(d) { 
            var name= xMapObject[d.date];if(name == undefined){name=d.date;}
            var table = '<table class="table table-condensed">'
                        + '<thead>'
                        + '<tr><th colspan="2" style="text-align:center"class="city">'+name+'</th></tr>'
                        + '</thead>'
                        + '<tbody>'
                        + '<tr><td>Total Sales</td><td class="visits">'+d.value+'</td></tr>'
                        + '</tbody>'
                        + '</table>';
            return table; 
          })
          .style({border: '1px solid #fff', 'box-shadow': '1px 1px 4px rgba(0,0,0,0.5)', 'border-radius': 'none','background':'#fff','color':'#555'})
          .offset([-12, 0]);
        // if(this.options.data.events.length > 1){
            // xOffset = (this.options.width/this.options.data.events.length) + 40;
        // } else {
            // xOffset = this.options.width/2;
        // }
        xOffset = 62;
        svg = this.getSvg();
        svg == null ? svg = d3.select(this.$element.selector).append("svg"): svg;
        svg = svg
        .attr("height", this.options.height+this.margin.bottom+this.margin.top)
        .attr("width",this.options.width+this.margin.right+this.margin.left)
        .attr('class',this.options.svgClassName);
        svg.append("defs").append("clipPath")
            .attr("id", "clip")
            .append("rect")
            .attr("width", this.options.width)
            .attr("height", this.options.height);
        focus = svg.append("g").attr("class", "focus").attr("transform", "translate(" + xOffset + "," + 40 + ")");
        svg.call(tip);
        //this.drawBackground(focus);

        this.drawRect(focus,tip);
        this.drawXAxis(focus);
        this.drawY1Axis(focus);
        this.drawY2Axis(focus);
        this.drawLine(focus);
        this.drawLineLow(focus);
        this.drawLineMedium(focus);
        this.drawLineHigh(focus);
        if (this.options.showLegend)this.drawLegend(focus);
        this.zoomBehaviour(focus);
    },
    drawRect: function(svg,tip){
        x = this.getX();
        x2 = this.getX();
        y1 = this.getY1();
        y2 = this.getY2();
        height = this.options.height - (this.options.height / 3);
        transTime = 0;
        if(this.options.animate) transTime = 1000;

        svg.selectAll("rect.bar").data(this.options.data.bar).enter().append("rect").attr("class","bar").attr("width", this.options.width/this.options.data.bar.length).attr("x", function (d) {
            return x(d.date);
        }).attr("y", height).attr("height", 0).style("fill", function (d,i) {
            return "#89A54E";
        }).on('mouseover', tip.show)
        .on('mouseout', tip.hide).transition().duration(transTime).attr("y", function (d) {
            if(isNaN(y1(d.value))) return 0;return y1(d.value);
        }).attr("height", function (d) {
             if(isNaN(y1(d.value))) return 0; return (height - y1(d.value));
        }).style("fill", function(d) { return "#89A54E";}).attr("rx","1.5").attr("ry","1.5");
    },

    drawXAxis:function(svg){
        svg.append("g").attr("class", "x axis").attr("transform", "translate(0," + height + ")").call(this.getXAxis()).append("text").attr("transform", "translate(" + xOffset + "," + 40 + ")").style("text-anchor", "end").text(this.options.xAxisText).style("font-weight", "bold");
    },
    drawY1Axis:function(svg){

        svg.append("g").attr("class", "y axis").call(this.getY1Axis()).append("text").attr('id','y1AxisText').attr("transform", "rotate(-90)").attr("y",-36 ).attr("x", -(this.options.height/5)).style("text-anchor", "end").style("fill", "#266866").style("font-weight", "bold").style("letter-spacing", "1px").text(this.options.y1AxisText);
    },
    drawY2Axis:function(svg){
        svg.append("g").attr("class", "y axis").call(this.getY2Axis()).attr("transform", "translate(" + this.options.width + " ,0)")    .append("text").attr('id','y2AxisText').attr("transform", "rotate(-90)").attr("y",47 ).attr("x", -(this.options.height/5)).style("text-anchor", "end").style("fill", "#266866").style("font-weight", "bold").style("letter-spacing", "1px").text(this.options.y2AxisText);
    },
    drawLegend: function(svg){
        legend = svg.append("g").attr("class", "legend").attr('transform', 'translate(-30,'+(height+50)+')');
        max = d3.max(this.options.values,function(d){return d.length;});
        legend.selectAll('rect').data(this.options.values).enter().append("rect").attr("x", function(d,i){return (i*149);}).attr("y", "0").attr("width", 12).attr("height", 12).style("fill", function(d,i){return color(i);});
        legend.selectAll('text').data(this.options.values).enter().append("text").attr("x", function (d, i) {return (i*149+15);}).attr("y", "10").text(function (d) {return d;});
    },
    getSvg: function(){
        return this.options.svg;
    },
    getXAxis:function(){
        //var x = d3.scale.ordinal().rangeRoundBands([0, this.options.width], 0);
        return  d3.svg.axis().scale(x).tickFormat(d3.time.format("%b'%y")).orient("bottom");
        //return d3.svg.axis().scale(x).tickValues(x.domain().filter(function(d, i) {return !(i % 10); })).orient("bottom");
    },
    getY1Axis:function(){
        return d3.svg.axis().scale(y1).orient("left").tickFormat(d3.format(".2s"));
    },
    getY2Axis:function(){
        return d3.svg.axis().scale(y2).orient("right").tickFormat(d3.format(".2s"));
    },
    getMaxX: function(){ //for ordinal we dont know what to scale 

        return  this.options.data.dateForxAxis.map(function (d) {
            return d.date;});
    },
    getMaxY1: function(){
        return d3.max(this.options.data.bar, function (d) {return d.value;});
    },
    getMaxY2: function(){
        return d3.max(this.options.data.line, function (d) {return d.value;});
    },
    getX: function(){
        return d3.time.scale().range([0, this.options.width]).domain(d3.extent(this.options.data.dateForxAxis, function(d) { return d.date; }));
    },
    getY1: function(){
        return d3.scale.linear().range([this.options.height - (this.options.height / 3), 0]).domain([0, this.getMaxY1()]);
    },
    getY2: function(){
        return d3.scale.linear().range([this.options.height - (this.options.height / 3), 0]).domain([0, this.getMaxY2()]);
    },
    drawBackground: function(vis){
        vis.append("rect").attr("x", 0).attr("y", 0).attr("width", this.options.width ).attr("height", this.options.height - 50).style("fill", "grey").attr("transform", "translate(0,0)").style("opacity", "0").attr("class","background").attr("id", "background");
    },
    zoomBehaviour: function(vis){
        var that = this;
        //zoomBehaviour = d3.behavior.zoom().scaleExtent([1, 1]).on("zoom", zoom);
        brush = d3.svg.brush().x(x2).on("brush", brushed);
        width = this.options.width;
        vis.append("g").attr("class","x brush").call(brush).selectAll("rect").attr("y",-6).attr("height",this.options.height+7);
        function brushed(){
            x.domain(brush.empty() ? x2.domain() : brush.extent());
            vis.selectAll("rect.bar")
            .attr("transform", function(d) { return "translate(" + x(d.date) + ",0)"; })
             vis.select(".x.axis").call(that.getXAxis());
             //vis.select(".line").attr("d", that.getLine());
        }
    },
    drawLine:function(svg){
        svg.append('path').datum(this.options.data.line).attr("class", "line").attr("d", this.getLine());
    },
    getLine:function(){
        return d3.svg.line().interpolate("basis") .x(function(d) {return x(d.date); }).y(function(d) { return y2(d.value); });
    },
    drawLineLow:function(svg){
        svg.append('path').datum(this.options.data.future).attr("class", "lineLow").attr("d", this.getLineLow()).style("stroke-dasharray", ("3, 3"));
    },
    getLineLow:function(){
        return d3.svg.line().interpolate("basis") .x(function(d) {return x(d.date); }).y(function(d) { return y2(d.low); });
    },
    drawLineMedium:function(svg){
        svg.append('path').datum(this.options.data.future).attr("class", "lineMedium").attr("d", this.getLineMedium());
    },
    getLineMedium:function(){
        return d3.svg.line().interpolate("basis") .x(function(d) {return x(d.date); }).y(function(d) { return y2(d.medium); });
    },
    drawLineHigh:function(svg){
        svg.append('path').datum(this.options.data.future).attr("class", "lineHigh").attr("d", this.getLineHigh()).style("stroke-dasharray", ("3, 3"));
    },
    getLineHigh:function(){
        return d3.svg.line().interpolate("basis") .x(function(d) {return x(d.date); }).y(function(d) { return y2(d.high); });
    },

};
$.fn.dualAxis = function(options){

    options = $.extend({}, $.fn.dualAxis.defaults, options);
    if(options.data === '' || options.data === null){
        var err = 'dualAxis Error: Data Attribute Cannot be Empty or Null.';
        (typeof(console) != 'undefined' && console.error) ? 
            console.error(err) : 
            alert(err);
    }
    function elementOptions(ele, options) {
        return $.metadata ? $.extend({}, options, $(ele).metadata()) : options;
    };
    function get(ele) {
        var gb = $.data(ele, 'gb');
        if (!gb) {
            gb = new DualAxis(ele,elementOptions(ele, options));
            $.data(ele, 'gb', gb);
        }
        return gb;
    }
    var gb = get(this);
    gb.draw();
};

$.fn.dualAxis.defaults = {
        svgClassName: "dualAxis",
        svg: null,
        xAxisText: 'x-axis',
        y1AxisText: 'y1-axis',
        y2AxisText: 'y2-axis',
        maxX: null,
        maxyY: null,
        height:null,
        width:null,
        animate: true,
        color: ["#3366cc", "#dc3912", "#ff9900", "#109618"],
        showLegend:true,
        data: '',
    };
})(jQuery);

here is the format of data 这是数据格式

{
"bar": [
    {
        "date": "50_2012",
        "value": "88787"
    },
    {
        "date": "155_2012",
        "value": "50573"
    },
     {
        "date": "155_2013",
        "value": "5057"
    }
],
"dateForxAxis": [
    {
        "date": "45_2012"
    },
    {
        "date": "155_2012"
    },
    {
        "date": "260_2013"
    }
],
"future": [
    {
        "high": "87878",
        "medium": "55535",
        "low": "1212"
    },
    {
        "high": "187878",
        "medium": "255535",
        "low": "14212"
    }
],
"line": [
    {
        "date": "50_2012",
        "value": "8787"
    },
    {
        "date": "60_2012",
        "value": "47474"
    },
     {
        "date": "168_2012",
        "value": "49474"
    }
]
};

calling the plugin 调用插件

$('#dualAxis').dualAxis({
            data: viewData1,
            xAxisText: 'Time',
            y1AxisText:'Transactions',
            y2AxisText:'Sales',
            animate:true,
            showLegend:false
        });

Please suggest what Am I doing wrong.when it is zooming it should zoom even for line bar and the three other lines. 请提出我做错了什么。缩放时,即使是线条和其他三行也应该缩放。

In the brushed method, you are translating the bars instead of setting a new x position (causing them to be translated based on their original x position outside the graph). 在拉毛方法中,您是在平移条形而不是设置新的x位置(以使其根据图形外部的原始x位置平移)。 Re-setting the x position in the brushed method will place the bars correctly when scaling: 在缩放方法中重新设置x位置将在缩放时正确放置条形图:

vis.selectAll("rect.bar").attr("x", function (d) {
  return x(d.date);
});

A fork of your jsfiddle with this change at http://fiddle.jshell.net/brendaz/zr6kkgaa/ 您的jsfiddle的分支,位于http://fiddle.jshell.net/brendaz/zr6kkgaa/

Every thing looks fine except that you are not updating width of rect.bar elements in brushed() function. 除了不更新brushed()函数中rect.bar元素的宽度外,其他所有内容看起来都不错。 you are only updating x co-ordinate. 您仅更新x坐标。 rect.bar element width in your case is always constant. rect.bar元素的宽度始终是恒定的。 And i also noticed that when you drag the brush, elements is getting out of the confined area because of extra padding of left and right side(may be it is intentional). 而且我还注意到,当您拖动画笔时,由于左侧和右侧的额外填充(可能是故意的),元素正脱离限制区域。

So just fix the with problem and you are good to go. 因此,只需解决问题,您就可以开始工作了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM