2015-04-17 125 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

相關問題