我使用D3创建了一个力布局(见下图)。但是,由于某种原因它在Firefox中不起作用,而在Chrome中它可以很好地工作。 Firefox调试器中没有错误,但它只显示浏览器右侧的一行(就像强制布局永远不会更新一样)。我正在使用本地服务器进行调试并在http://localhost:8888/浏览。
我一直在查看有关stackoverflow兼容性的不同帖子,但我似乎无法找到与我的代码相关的任何内容。如果有人能给我一个首先调试的标题,那就太好了!
编辑:我在帖子底部包含了数据链接以及纯文本的csv文件。数据和代码:https://www.dropbox.com/s/ksh2qk1b5s9lfq5/Network%20View.zip?dl=0
以下是Firefox控制台的输出:
mutating the [[Prototype]] of an object will cause your code to run very slowly; instead create the object with the correct initial [[Prototype]] value using Object.create d3.js:553:4
SyntaxError: An invalid or illegal string was specified d3.js:562:0
铬:
火狐:
的index.html
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.legend {
font-size: 10px;
}
rect {
stroke-width: 2;
}
.node circle {
stroke: white;
stroke-width: 2px;
opacity: 1.0;
}
line {
stroke-width: 4px;
stroke-opacity: 1.0;
//stroke: "black";
}
body {
/* Scaling for different browsers */
-ms-transform: scale(1,1);
-webkit-transform: scale(1,1);
transform: scale(1,1);
}
svg{
position:absolute;
top:50%;
left:0px;
}
</style>
<body>
<script type="text/javascript" src="d3.js"></script>
<script type="text/javascript" src="papaparse.js"></script>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript" src="networkview.js"></script>
</body>
networkview.js
var line_diff = 0.5; // increase from zero if you want space between the call/text lines
var mark_offset = 10; // how many percent of the mark lines in each end are not used for the relationship between incoming/outgoing?
var mark_size = 5; // size of the mark on the line
var legendRectSize = 9; // 18
var legendSpacing = 4; // 4
var recordTypes = [];
var legend;
var text_links_data, call_links_data;
// colors for the different parts of the visualization
recordTypes.push({
text : "call",
color : "#438DCA"
});
recordTypes.push({
text : "text",
color : "#70C05A"
});
recordTypes.push({
text : "balance",
color : "#245A76"
});
// Function for grabbing a specific property from an array
pluck = function (ary, prop) {
return ary.map(function (x) {
return x[prop]
});
}
// Sums an array
sum = function (ary) {
return ary.reduce(function (a, b) {
return a + b
}, 0);
}
maxArray = function (ary) {
return ary.reduce(function (a, b) {
return Math.max(a, b)
}, -Infinity);
}
minArray = function (ary) {
return ary.reduce(function (a, b) {
return Math.min(a, b)
}, Infinity);
}
var data_links;
var data_nodes;
var results = Papa.parse("links.csv", {
header : true,
download : true,
dynamicTyping : true,
delimiter : ",",
skipEmptyLines : true,
complete : function (results) {
data_links = results.data;
dataLoaded();
}
});
var results = Papa.parse("nodes.csv", {
header : true,
download : true,
dynamicTyping : true,
delimiter : ",",
skipEmptyLines : true,
complete : function (results) {
data_nodes = results.data;
data_nodes.forEach(function (d, i) {
d.size = (i == 0)? 200 : 30
d.fill = (d.no_network_info == 1)? "#dfdfdf": "#a8a8a8"
});
dataLoaded();
}
});
function node_radius(d) {
return Math.pow(40.0 * ((d.index == 0) ? 200 : 30), 1 / 3);
}
function node_radius_data(d) {
return Math.pow(40.0 * d.size, 1 / 3);
}
function dataLoaded() {
if (typeof data_nodes === "undefined" || typeof data_links === "undefined") {
//console.log("Still loading")
} else {
CreateVisualizationFromData();
}
}
function isConnectedToOtherThanMain(a) {
var connected = false;
for (i = 1; i < data_nodes.length; i++) {
if (isConnected(a, data_nodes[i]) && a.index != i) {
connected = true;
}
}
return connected;
}
function isConnected(a, b) {
return isConnectedAsTarget(a, b) || isConnectedAsSource(a, b) || a.index == b.index;
}
function isConnectedAsSource(a, b) {
return linkedByIndex[a.index + "," + b.index];
}
function isConnectedAsTarget(a, b) {
return linkedByIndex[b.index + "," + a.index];
}
function isEqual(a, b) {
return a.index == b.index;
}
function tick() {
if (call_links_data.length > 0) {
callLink
.attr("x1", function (d) {
return d.source.x - line_perpendicular_shift(d, 1)[0] + line_radius_shift_to_edge(d, 0)[0];
})
.attr("y1", function (d) {
return d.source.y - line_perpendicular_shift(d, 1)[1] + line_radius_shift_to_edge(d, 0)[1];
})
.attr("x2", function (d) {
return d.target.x - line_perpendicular_shift(d, 1)[0] + line_radius_shift_to_edge(d, 1)[0];
})
.attr("y2", function (d) {
return d.target.y - line_perpendicular_shift(d, 1)[1] + line_radius_shift_to_edge(d, 1)[1];
});
callLink.each(function (d) {
applyGradient(this, "call", d)
});
}
if (text_links_data.length > 0) {
textLink
.attr("x1", function (d) {
return d.source.x - line_perpendicular_shift(d, -1)[0] + line_radius_shift_to_edge(d, 0)[0];
})
.attr("y1", function (d) {
return d.source.y - line_perpendicular_shift(d, -1)[1] + line_radius_shift_to_edge(d, 0)[1];
})
.attr("x2", function (d) {
return d.target.x - line_perpendicular_shift(d, -1)[0] + line_radius_shift_to_edge(d, 1)[0];
})
.attr("y2", function (d) {
return d.target.y - line_perpendicular_shift(d, -1)[1] + line_radius_shift_to_edge(d, 1)[1];
});
textLink.each(function (d) {
applyGradient(this, "text", d)
});
node
.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
if (force.alpha() < 0.05)
drawLegend();
}
function getRandomInt() {
return Math.floor(Math.random() * (100000 - 0));
}
function applyGradient(line, interaction_type, d) {
var self = d3.select(line);
var current_gradient = self.style("stroke")
current_gradient = current_gradient.substring(4, current_gradient.length - 1);
var new_gradient_id = "line-gradient" + getRandomInt();
var from = d.source.size < d.target.size ? d.source : d.target;
var to = d.source.size < d.target.size ? d.target : d.source;
var mid_offset = 0;
var standardColor = "";
if (interaction_type == "call") {
mid_offset = d.inc_calls / (d.inc_calls + d.out_calls);
standardColor = "#438DCA";
} else {
mid_offset = d.inc_texts / (d.inc_texts + d.out_texts);
standardColor = "#70C05A";
}
/* recordTypes_ID = pluck(recordTypes, 'text');
whichRecordType = recordTypes_ID.indexOf(interaction_type);
standardColor = recordTypes[whichRecordType].color;
*/
mid_offset = mid_offset * 100;
mid_offset = mid_offset * 0.6 + 20; // scale so it doesn't hit the ends
lineLengthCalculation = function (x, y, x0, y0) {
return Math.sqrt((x -= x0) * x + (y -= y0) * y);
};
lineLength = lineLengthCalculation(from.px, from.py, to.px, to.py);
if (lineLength >= 0.1) {
mark_size_percent = (mark_size / lineLength) * 100;
defs.append("linearGradient")
.attr("id", new_gradient_id)
.attr("gradientUnits", "userSpaceOnUse")
.attr("x1", from.px)
.attr("y1", from.py)
.attr("x2", to.px)
.attr("y2", to.py)
.selectAll("stop")
.data([{
offset : "0%",
color : standardColor,
opacity : "1"
}, {
offset : Math.round(mid_offset - mark_size_percent / 2) + "%",
color : standardColor,
opacity : "1"
}, {
offset : Math.round(mid_offset - mark_size_percent / 2) + "%",
color : standardColor,
opacity : "1"
}, {
offset : Math.round(mid_offset - mark_size_percent / 2) + "%",
color : "#245A76",
opacity : "1"
}, {
offset : Math.round(mid_offset + mark_size_percent / 2) + "%",
color : "#245A76",
opacity : "1"
}, {
offset : Math.round(mid_offset + mark_size_percent / 2) + "%",
color : standardColor,
opacity : "1"
}, {
offset : Math.round(mid_offset + mark_size_percent / 2) + "%",
color : standardColor,
opacity : "1"
}, {
offset : "100%",
color : standardColor,
opacity : "1"
}
])
.enter().append("stop")
.attr("offset", function (d) {
return d.offset;
})
.attr("stop-color", function (d) {
return d.color;
})
.attr("stop-opacity", function (d) {
return d.opacity;
});
self.style("stroke", "url(#" + new_gradient_id + ")")
defs.select(current_gradient).remove();
}
}
var linkedByIndex;
var width = $(window).width();
var height = $(window).height();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var force;
var callLink;
var textLink;
var link;
var node;
var defs;
var total_interactions = 0;
var max_interactions = 0;
function CreateVisualizationFromData() {
for (i = 0; i < data_links.length; i++) {
total_interactions += data_links[i].inc_calls + data_links[i].out_calls + data_links[i].inc_texts + data_links[i].out_texts;
max_interactions = Math.max(max_interactions, data_links[i].inc_calls + data_links[i].out_calls + data_links[i].inc_texts + data_links[i].out_texts)
}
linkedByIndex = {};
data_links.forEach(function (d) {
linkedByIndex[d.source + "," + d.target] = true;
//linkedByIndex[d.source.index + "," + d.target.index] = true;
});
//console.log(total_interactions);
//console.log(max_interactions);
function chargeForNode(d, i) {
// main node
if (i == 0) {
return -25000;
}
// contains other links
else if (isConnectedToOtherThanMain(d)) {
return -2000;
} else {
return -1200;
}
}
// initial placement of nodes prevents overlaps
central_x = width / 2
central_y = height / 2
data_nodes.forEach(function(d, i) {
if (i != 0) {
connected = isConnectedToOtherThanMain(d);
data_nodes[i].x = connected? central_x + 10000: central_x -10000;
data_nodes[i].y = connected? central_y: central_y;
}
else {data_nodes[i].x = central_x; data_nodes[i].y = central_y;}})
force = d3.layout.force()
.nodes(data_nodes)
.links(data_links)
.charge(function (d, i) {
return chargeForNode(d, i)
})
.friction(0.6) // 0.6
.gravity(0.4) // 0.6
.size([width, height])
.start();
call_links_data = data_links.filter(function(d) {
return (d.inc_calls + d.out_calls > 0)});
text_links_data = data_links.filter(function(d) {
return (d.inc_texts + d.out_texts > 0)});
callLink = svg.selectAll(".call-line")
.data(call_links_data)
.enter().append("line");
textLink = svg.selectAll(".text-line")
.data(text_links_data)
.enter().append("line");
link = svg.selectAll("line");
node = svg.selectAll(".node")
.data(data_nodes)
.enter().append("g")
.attr("class", "node");
defs = svg.append("defs");
node
.append("circle")
.attr("r", node_radius)
.style("fill", function (d) {
return (d.index == 0)? "#ffffff" : d.fill;
})
.style("stroke", function (d) {
return (d.index == 0)? "#8C8C8C" : "#ffffff";
})
svg
.append("marker")
.attr("id", "arrowhead")
.attr("refX", 6 + 7)
.attr("refY", 2)
.attr("markerWidth", 6)
.attr("markerHeight", 4)
.attr("orient", "auto")
.append("path")
.attr("d", "M 0,0 V 4 L6,2 Z");
if (text_links_data.length > 0) {
textLink
.style("stroke-width", function stroke(d) {
return text_width(d)
})
.each(function (d) {
applyGradient(this, "text", d)
});
}
if (call_links_data.length > 0) {
callLink
.style("stroke-width", function stroke(d) {
return call_width(d)
})
.each(function (d) {
applyGradient(this, "call", d)
});
}
force
.on("tick", tick);
}
function drawLegend() {
var node_px = pluck(data_nodes, 'px');
var node_py = pluck(data_nodes, 'py');
var nodeLayoutRight = Math.max(maxArray(node_px));
var nodeLayoutBottom = Math.max(maxArray(node_py));
legend = svg.selectAll('.legend')
.data(recordTypes)
.enter()
.append('g')
.attr('class', 'legend')
.attr('transform', function (d, i) {
var rect_height = legendRectSize + legendSpacing;
var offset = rect_height * (recordTypes.length-1);
var horz = nodeLayoutRight + 15; /* - 2*legendRectSize; */
var vert = nodeLayoutBottom + (i * rect_height) - offset;
return 'translate(' + horz + ',' + vert + ')';
});
legend.append('rect')
.attr('width', legendRectSize)
.attr('height', legendRectSize)
.style('fill', function (d) {
return d.color
})
.style('stroke', function (d) {
return d.color
});
legend.append('text')
.attr('x', legendRectSize + legendSpacing)
.attr('y', legendRectSize - legendSpacing + 3)
.text(function (d) {
return d.text;
})
.style('fill', '#757575');
}
var line_width_factor = 10.0 // width for the widest line
function call_width(d) {
return (d.inc_calls + d.out_calls) / max_interactions * line_width_factor;
}
function text_width(d) {
return (d.inc_texts + d.out_texts) / max_interactions * line_width_factor;
}
function total_width(d) {
return (d.inc_calls + d.out_calls + d.inc_texts + d.out_texts) / max_interactions * line_width_factor + line_diff;
}
function line_perpendicular_shift(d, direction) {
theta = getAngle(d);
theta_perpendicular = theta + (Math.PI / 2) * direction;
lineWidthOfOppositeLine = direction == 1 ? text_width(d) : call_width(d);
shift = lineWidthOfOppositeLine / 2;
delta_x = (shift + line_diff) * Math.cos(theta_perpendicular)
delta_y = (shift + line_diff) * Math.sin(theta_perpendicular)
return [delta_x, delta_y]
}
function line_radius_shift_to_edge(d, which_node) { // which_node = 0 if source, = 1 if target
theta = getAngle(d);
theta = (which_node == 0) ? theta : theta + Math.PI; // reverse angle if target node
radius = (which_node == 0) ? node_radius(d.source) : node_radius(d.target) // d.source and d.target refer directly to the nodes (not indices)
radius -= 2; // add stroke width
delta_x = radius * Math.cos(theta)
delta_y = radius * Math.sin(theta)
return [delta_x, delta_y]
}
function getAngle(d) {
rel_x = d.target.x - d.source.x;
rel_y = d.target.y - d.source.y;
return theta = Math.atan2(rel_y, rel_x);
}
Links.csv
source,target,inc_calls,out_calls,inc_texts,out_texts
0,1,1.0,0.0,1.0,0.0
0,2,0.0,0.0,1.0,3.0
0,3,3.0,9.0,5.0,7.0
0,4,2.0,12.0,9.0,14.0
0,5,5.0,9.0,9.0,13.0
0,6,5.0,17.0,2.0,25.0
0,7,6.0,13.0,7.0,16.0
0,8,7.0,7.0,8.0,8.0
0,9,3.0,10.0,8.0,20.0
0,10,5.0,10.0,6.0,23.0
0,11,8.0,10.0,13.0,15.0
0,12,9.0,18.0,9.0,22.0
0,13,1.0,2.0,2.0,2.0
0,14,11.0,13.0,7.0,15.0
0,15,5.0,18.0,9.0,22.0
0,16,8.0,15.0,13.0,20.0
0,17,4.0,10.0,9.0,26.0
0,18,9.0,18.0,8.0,33.0
0,19,12.0,11.0,4.0,15.0
0,20,4.0,15.0,9.0,25.0
0,21,4.0,17.0,10.0,19.0
0,22,4.0,16.0,12.0,29.0
0,23,6.0,9.0,12.0,20.0
0,24,2.0,2.0,1.0,3.0
0,25,3.0,8.0,10.0,16.0
0,26,3.0,10.0,11.0,22.0
0,27,6.0,14.0,9.0,11.0
0,28,2.0,7.0,8.0,15.0
0,29,2.0,11.0,8.0,15.0
0,30,1.0,8.0,9.0,6.0
0,31,3.0,6.0,7.0,7.0
0,32,4.0,9.0,3.0,12.0
0,33,4.0,4.0,7.0,12.0
0,34,4.0,4.0,5.0,9.0
0,35,2.0,3.0,0.0,7.0
0,36,3.0,7.0,5.0,9.0
0,37,1.0,7.0,5.0,3.0
0,38,1.0,13.0,1.0,2.0
0,39,2.0,7.0,3.0,4.0
0,40,1.0,3.0,2.0,6.0
0,41,0.0,1.0,2.0,1.0
0,42,0.0,0.0,2.0,0.0
0,43,0.0,3.0,1.0,5.0
0,44,0.0,1.0,0.0,2.0
0,45,4.0,1.0,1.0,10.0
0,46,2.0,7.0,3.0,5.0
0,47,5.0,7.0,3.0,5.0
0,48,2.0,5.0,4.0,10.0
0,49,3.0,3.0,5.0,13.0
1,15,10.0,30.0,13.0,37.0
2,8,16.0,9.0,24.0,15.0
2,43,4.0,10.0,9.0,16.0
5,48,3.0,5.0,0.0,4.0
6,37,11.0,25.0,15.0,34.0
8,48,12.0,4.0,7.0,2.0
9,42,25.0,9.0,29.0,15.0
9,45,11.0,3.0,16.0,5.0
12,24,4.0,15.0,13.0,16.0
14,31,18.0,9.0,29.0,12.0
14,33,5.0,10.0,4.0,9.0
15,28,8.0,5.0,16.0,5.0
16,36,14.0,11.0,10.0,19.0
23,38,3.0,11.0,6.0,10.0
26,42,9.0,23.0,17.0,21.0
27,46,12.0,12.0,15.0,21.0
29,39,8.0,15.0,9.0,20.0
29,47,8.0,27.0,19.0,24.0
33,46,6.0,4.0,13.0,13.0
37,43,10.0,12.0,6.0,21.0
Nodes.csv
no_network_info
0
0
0
1
1
0
0
0
0
0
0
1
0
1
0
0
0
1
0
1
1
0
0
0
0
1
0
0
0
0
1
0
1
0
1
1
0
0
0
0
1
1
0
0
1
0
0
0
0
0
答案 0 :(得分:1)
语法错误在于此行:
defs.select(current_gradient).remove();
但是,这是真正的问题。替换:
current_gradient = current_gradient.substring(4, current_gradient.length - 1);
使用:
if (current_gradient.match("http")) {
var parts = current_gradient.split("/");
current_gradient = parts[-1];
} else {
current_gradient = current_gradient.substring(4, current_gradient.length - 1);
}
当您最初在Chrome中设置current_gradient时,它会设置为&#34; url(somevalue)&#34;而在Firefox中,它设置为&#34; url(fullpath / somevalue)&#34;。因此,您需要删除所有路径信息,而不仅仅是&#34; url()&#34;位。拆分斜杠并从拆分中取出最后一个值可能是最简单的方法。