2015-04-17 122 views
0

我已经使用D3创建了强制布局(请参阅下图)。但是,由于某些原因,它在Firefox中无法正常工作,而在Chrome中它完全正常。在Firefox调试器中没有错误,但它只在浏览器右侧显示一行(就像强制布局从不更新一样)。我正在使用本地服务器调试它并在http://localhost:8888/上浏览。D3强制布局适用于Chrome,但不适用于Firefox

我一直在寻找关于在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 

铬:

enter image description here

火狐:

enter image description here

的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 

Ñ odes.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 
+1

Firefox控制台是否可以说出任何内容?如果你包含你的数据文件,这也会更容易。 – davidhwang

+0

好的,我现在已经做到了。不知道Firefox控制台 - 我已经添加了输出。 – pir

+0

您使用的是本地版本的d3吗?它可能已过时。尝试使用 davidhwang

回答

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); 
} 

当您最初设置的current_gradient在Chrome中,它被设置为 “URL(someValue中)”,而在Firefox中是将其设置为“URL(FULLPATH/someValue中)”。所以你需要删除所有的路径信息,而不仅仅是“url()”位。分割斜线并从分割中获取最后一个值可能是最简单的方法。

+2

Chrome中current_gradient的值如下所示:#line-gradient98575,而在Firefox中则类似于:“”http:// localhost:9013 /#line-gradient43891“)透明n“这就是为什么有语法错误。 –

+0

非常感谢您的帮助!现在它的'SyntaxError'已经被删除了,但是很遗憾它太慢而无法使用。这很可能是由于'改变对象的[[Prototype]]会导致你的代码运行非常缓慢;而是在控制台中使用Object.create d3.js:553:4'行创建具有正确初始[[Prototype]]值的对象。我不确定是编辑这个问题还是发布一个新问题。我最终做了后者。如果有兴趣,去看看http://stackoverflow.com/questions/29705499/mutating-the-prototype-causes-slow-performance-in-firefox-for-d3-force-layou – pir

相关问题