Javascript D3js指令在重新渲染时有大量重复代码

Javascript D3js指令在重新渲染时有大量重复代码,javascript,angularjs,svg,d3.js,Javascript,Angularjs,Svg,D3.js,我对使用D3有点陌生,并尝试创建散点图。我似乎有很多重复的代码,用于调整窗口大小和数据更新时的渲染。我不确定这是D3和更新数据的本质,还是我忽略了一个非常明显的更新模式 该指令看起来很长,但这只是因为我在updateWindow()(用于调整浏览器大小)、render(用于初始呈现和插入SVG元素)和update(用于在数据更改时更新元素属性)中重复了类似的代码。这是我的指令,目前: app.directive('scatterPlot', function ($window, inputSer

我对使用D3有点陌生,并尝试创建散点图。我似乎有很多重复的代码,用于调整窗口大小和数据更新时的渲染。我不确定这是D3和更新数据的本质,还是我忽略了一个非常明显的更新模式

该指令看起来很长,但这只是因为我在
updateWindow()
(用于调整浏览器大小)、
render
(用于初始呈现和插入SVG元素)和
update
(用于在数据更改时更新元素属性)中重复了类似的代码。这是我的指令,目前:

app.directive('scatterPlot', function ($window, inputService, dataHandler) {
    return {
        restrict: 'E',
        scope: {
            input: '=',
            display: '='
        },
        link: function (scope, element, array) {

            var container = angular.element(document.querySelector('section.output'))[0];
            var filterCount = 0;
            var data = undefined;

            // ********** ********** ************ // 
            // ********* ************ *********** //  
            // ******** ************** ********** //  
            // ******* **************** ********* //  
            // ******* WATCH AND RENDER ********* // 
            scope.$on('inputChange', function(event, isDeleted, isEmpty, isComplete, id, value) {
                // CHECK FOR MATCHES //
                console.log('event: ', event);
                console.log('isDeleted: ', isDeleted, ', isEmpty: ', isEmpty, ', isComplete: ', isComplete, ', id: ', id, ', value: ', value);
                if (isDeleted) {
                    filterCount -= 1;
                    console.log('filterCount: ', filterCount);
                } else {
                    if (filterCount == 0) {
                        dataHandler.matchQuery(id, value).then(function(matches) {
                            data = matches;
                            filterCount += 1;
                            return render(matches);
                        });  
                    } else {
                        dataHandler.matchQuery(id, value, data).then(function(matches) {
                            data = matches;
                            filterCount += 1;
                            if (filterCount > 0) {
                                return update(data);
                            }

                        });   
                    }  
                }
            });

            var formatAsMillions = d3.format('$' + "s");
            var formatAsYears = function(d) { return Math.floor(d/365) + ' Years'; };

            function update(data) {

                var el = element[0];

                var margin = {
                    top: container.clientHeight / 12, 
                    right: container.clientWidth / 7, 
                    bottom: container.clientHeight / 5, 
                    left: container.clientWidth / 11
                };

                var w = (container.clientWidth - margin.left - margin.right) * 0.9;
                var h = (container.clientHeight - margin.top - margin.bottom) * 0.9;

                var input = data;

                var xScale = d3.scale.linear()
                    .domain([0, d3.max(input, function(d) { return d["ctc"]; })])
                    .range([0, w])
                    .nice();

                var yScale = d3.scale.linear()
                    .domain([0, d3.max(input, function(d) { return d["ttc"]; })])
                    .range([h, 0])
                    .nice();

                var rScale = d3.scale.linear()
                    .domain([0, d3.max(input, function(d) { return d["effective"]; })])
                    .range([2, 10]);

                var xAxis = d3.svg.axis()
                    .scale(xScale)
                    .orient('bottom')
                    .ticks(5)
                    .tickFormat(formatAsMillions);

                var max= xScale.domain()[1];

                var yAxis = d3.svg.axis()
                    .scale(yScale)
                    .orient('left')
                    .ticks(8)
                    .tickFormat(formatAsYears)
                    .tickFormat(function (days) {
                          if (max < 50) {
                            return d3.format("2.1f")(days) + " d";
                          }
                          else if (max >= 50 && max < 100) {
                            return d3.format("2.1f")(days/7) + " w";
                          }
                          else if (max >= 100 && max < 500) {
                            return d3.format("2.1f")(days/(365/12)) + " m";
                          }
                          else if (max >= 500){
                            return d3.format("2.1f")(days/365) + " y";
                          } 
                     });

                // *********** //
                // SVG ELEMENT //  
                var svg = d3.select('svg')
                    .attr('class', 'scatter')
                    .attr('width', w + margin.left + margin.right)
                    .attr('height', h + margin.top + margin.bottom)
                    .append('g')
                    .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

                // add X axis
                d3.select('.xAxis')
                    .data(input)
                    .remove()
                    .exit()

                svg.append('g')
                    .attr('class', 'xAxis')
                    .attr('transform', 'translate(0,' + h + ')')
                    .call(xAxis);

                // add Y axis
                d3.select('.yAxis')
                    .data(input)
                    .remove()
                    .exit()

                svg.append('g')
                    .attr('class', 'yAxis')
                    .call(yAxis);

                // DATA JOIN
                // Join new data with old elements, if any.  
                var circle = d3.selectAll('circle')
                    .data(input, function(d) {return d.id;})
                    .remove()
                    .exit();

                // UPDATE
                // Update old elements as needed.
                circle.attr('class', 'update')
                    .transition()
                    .duration(300)
                    .attr('cx', function(d) { return xScale(d["ctc"]); })
                    .attr('cy', function(d) { return yScale(d["ttc"]); })
                    .attr('r', function(d) { return rScale(d["effective"]); }); 
            }

            function render(matches) {
                console.log(matches.length);
                // ********** ********** ************ // 
                // ********* ************ *********** //  
                // ******** ************** ********** //  
                // ******** **************** ******** //  
                // ******** BASIC ATTRIBUTES ******** //
                var input = matches;
                var el = element[0];
                var effectiveScale = d3.scale.linear()
                    .domain([1, d3.max(input, function(d) {return d["effective"]; }) ])
                    .range(["#35ca98", "#029765", "#006432", "#001700"]);

                var margin = {
                    top: container.clientHeight / 12, 
                    right: container.clientWidth / 7, 
                    bottom: container.clientHeight / 5, 
                    left: container.clientWidth / 11
                };

                var w = (container.clientWidth - margin.left - margin.right) * 0.9;
                var h = (container.clientHeight - margin.top - margin.bottom) * 0.9;



                var xScale = d3.scale.linear()
                    .domain([0, d3.max(input, function(d) { return d["ctc"]; })])
                    .range([0, w])
                    .nice();

                var yScale = d3.scale.linear()
                    .domain([0, d3.max(input, function(d) { return d["ttc"]; })])
                    .range([h, 0])
                    .nice();

                var rScale = d3.scale.linear()
                    .domain([0, d3.max(input, function(d) { return d["effective"]; })])
                    .range([2, 15]);

                var xAxis = d3.svg.axis()
                    .scale(xScale)
                    .orient('bottom')
                    .ticks(5)
                    .tickFormat(formatAsMillions);

                var yAxis = d3.svg.axis()
                    .scale(yScale)
                    .orient('left')
                    .ticks(8)
                    .tickFormat(formatAsYears);

                // *********** //
                // SVG ELEMENT //  
                var svg = d3.select(el)
                    .append('svg')
                    .attr('class', 'scatter')
                    .attr('width', w + margin.left + margin.right)
                    .attr('height', h + margin.top + margin.bottom)
                    .append('g')
                    .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');

                // add X axis
                svg.append('g')
                    .attr('class', 'xAxis')
                    .attr('transform', 'translate(0,' + h + ')')
                    .call(xAxis);

                // add Y axis
                svg.append('g')
                    .attr('class', 'yAxis')
                    .call(yAxis);

                //Create the tooltip label
                var tooltip = d3.select('body')
                    .append('div')
                    .attr('class', 'tooltip')
                    .style('position', 'absolute')
                    .style('z-index', '10')
                    .style('visibility', 'hidden')

                // add circles in group
                var circles = svg.append('g')
                    .attr('class','circles')
                    .attr('clip-path','url(#chart-area)');

                // add individual circles
                var circle = circles.selectAll('circle')
                    .data(input, function(d) {return d.id;})
                    .enter()
                    .append('circle')
                    .attr('class', 'circle')
                    .attr('cx', function(d) { return xScale(d["ctc"]); })
                    .attr('cy', function(d) { return yScale(d["ttc"]); })
                    .attr('r', function(d) { return rScale(d["effective"]); })
                    .attr('fill', function(d, i) { return effectiveScale(d["effective"])})
                    .on('mouseover', function(d) {
                        tooltip.style('visibility', 'visible');
                        return tooltip.text(d["technology"]);
                     })
                    .on("mousemove", function(){ return tooltip.style("top",
                        (d3.event.pageY-10)+"px").style("left",(d3.event.pageX+10)+"px");})
                    .on("mouseout", function(){return tooltip.style("visibility", "hidden");})

                    // NEW SCOPE VALUE ON CLICK //
                    .on('click', function(d) {
                        scope.display = d;
                        console.log(d);
                        scope.$apply();
                        return d;
                    });


                // append clip path
                svg.append('clipPath')
                    .attr('id','chart-area')
                    .append('rect')
                    .attr('class', 'rect')
                    .attr('x', 0)
                    .attr('y', 0)
                    .attr('width', w)
                    .attr('height', h);

                // add text
                svg.selectAll('text')
                    .data(input, function(d) {return d.id;})
                    .enter()
                    .append('text')
                    .attr('class', 'label')
                    .attr('clip-path','url(#chart-area)')
                    .attr('x', function(d) { return xScale(d["ctc"]); })
                    .attr('y', function(d) { return yScale(d["ttc"]) - rScale(d["effective"]) - 9; })
                    //.text(function(d) { return d["technology"]; });


                // ********** ********** ************ // 
                // ********* ************ *********** //  
                // ******** ************** ********** //  
                // ******** ************** ********** //  
                // ******** UPDATE WINDOW *********** //
                angular.element($window).bind('resize', function() {
                    updateWindow();
                });    


                function updateWindow() {

                    var w = container.clientWidth - margin.left - margin.right;
                    var h = (container.clientHeight - margin.top - margin.bottom) * 0.9;

                    var xScale = d3.scale.linear()
                        .domain([0, d3.max(input, function(d) { return d["ctc"]; })])
                        .range([0, w])
                        .nice();

                    var yScale = d3.scale.linear()
                        .domain([0, d3.max(input, function(d) { return d["ttc"]; })])
                        .range([h, 0])
                        .nice();

                    var xAxis = d3.svg.axis()
                        .scale(xScale)
                        .orient('bottom')
                        .ticks(5)
                        .tickFormat(formatAsMillions);


                    var yAxis = d3.svg.axis()
                        .scale(yScale)
                        .orient('left')
                        .ticks(8)
                        .tickFormat(formatAsYears);

                    // add X axis
                    d3.select('.xAxis')
                        .attr('transform', 'translate(0,' + h + ')')
                        .call(xAxis);

                    // add Y axis
                    d3.select('.yAxis')
                        .call(yAxis);

                    /// adjust svg element width/height
                    d3.select('.scatter')
                        .attr('width', w + margin.left + margin.right)
                        .attr('height', h + margin.top + margin.bottom)
                        .select('g')
                        .attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
                        .attr('width', w + margin.left + margin.right)
                        .attr('height', h + margin.top + margin.bottom);

                    // adjust circle location
                    d3.select('.circles')
                        .attr('clip-path','url(#chart-area)')
                        .selectAll('circle')
                        .attr('cx', function(d) { return xScale(d["ctc"]); })
                        .attr('cy', function(d) { return yScale(d["ttc"]); })
                        .attr('r', function(d) { return rScale(d["effective"]); });

                    // adjust clip path
                    d3.select('.rect')
                        .attr('x', 0)
                        .attr('y', 0)
                        .attr('width', w)
                        .attr('height', h);     
                }
            };     
        }
    };
});
app.directive('scatterPlot',函数($window,inputService,dataHandler){
返回{
限制:'E',
范围:{
输入:'=',
显示:'='
},
链接:函数(作用域、元素、数组){
var container=angular.element(document.querySelector('section.output'))[0];
var filterCount=0;
var数据=未定义;
// ********** ********** ************ // 
// ********* ************ *********** //  
// ******** ************** ********** //  
// ******* **************** ********* //  
//*******观看并渲染*********//
作用域:$on('inputChange',函数(事件、isDeleted、isEmpty、isComplete、id、值){
//检查是否匹配//
console.log('event:',event);
log('isDeleted:',isDeleted',isEmpty:',isEmpty',isComplete:',isComplete',id:',id',value:',value);
如果(已删除){
过滤器计数-=1;
log('filterCount:',filterCount);
}否则{
if(filterCount==0){
matchQuery(id,值)。然后(函数(匹配){
数据=匹配;
过滤器计数+=1;
返回渲染(匹配);
});  
}否则{
matchQuery(id、值、数据)。然后(函数(匹配){
数据=匹配;
过滤器计数+=1;
如果(过滤器计数>0){
返回更新(数据);
}
});   
}  
}
});
var formatAsMillions=d3.format(“$”+“s”);
var formatAsYears=函数(d){return Math.floor(d/365)+‘Years’;};
功能更新(数据){
var el=元素[0];
var保证金={
顶部:container.clientHeight/12,
右:container.clientWidth/7,
底部:container.clientHeight/5,
左:container.clientWidth/11
};
var w=(container.clientWidth-margin.left-margin.right)*0.9;
var h=(container.clientHeight-margin.top-margin.bottom)*0.9;
var输入=数据;
var xScale=d3.scale.linear()
.domain([0,d3.max(输入,函数(d){返回d[“ctc”];})])
.范围([0,w])
.nice();
var yScale=d3.scale.linear()
.domain([0,d3.max(输入,函数(d){返回d[“ttc”];})])
.范围([h,0])
.nice();
var rScale=d3.scale.linear()
.domain([0,d3.max(输入,函数(d){返回d[“有效”];})])
.范围([2,10]);
var xAxis=d3.svg.axis()
.scale(xScale)
.orient('底部')
.滴答声(5)
.格式(百万格式);
var max=xScale.domain()[1];
var yAxis=d3.svg.axis()
.刻度(yScale)
.orient('左')
.滴答声(8)
.tickFormat(formatAsYears)
.1格式(函数(天){
如果(最大值<50){
返回d3.格式(“2.1f”)(天)+“d”;
}
否则,如果(最大值>=50&&max<100){
返回d3.格式(“2.1f”)(天/7)+“w”;
}
否则,如果(最大值>=100&&max<500){
返回d3.格式(“2.1f”)(天/(365/12))+“m”;
}
否则,如果(最大值>=500){
返回d3.格式(“2.1f”)(天/365)+“y”;
} 
});
// *********** //
//SVG元素//
var svg=d3。选择('svg')
.attr('class','scatter')
.attr('width',w+margin.left+margin.right)
.attr('height',h+margin.top+margin.bottom)
.append('g')
.attr('transform','translate('+margin.left+','+margin.top+'));
//添加X轴
d3.选择(“.xAxis”)
.数据(输入)
.删除()
.exit()
append('g')
.attr('class','xAxis')
.attr('transform','translate(0',+h+'))
.呼叫(xAxis);
//添加Y轴
d3.选择(“.yAxis”)
.数据(输入)
.删除()
.exit()
append('g')
.attr('class','yAxis')
.呼叫(yAxis);
//数据连接
//加入新da