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