2016-10-01 3 views
2

Я установил диаграмму направленности с помощью d3.js с помощью svg, но в конечном итоге график стал большим, и у него были проблемы с производительностью. Я решил попробовать сделать это на холсте, потому что я читал, что он делает все гадое лучше и быстрее. Но теперь у меня проблема с масштабированием. Я правильно реализовал поведение масштабирования (думаю), но я могу увеличить масштаб только тогда, когда график находится в состоянии покоя. Перед симуляцией поиск режима масштабирования с точки зрения равновесия не работает. Любая идея почему? Или какие-нибудь советы о том, что я должен делать?Масштабирование в симуляции симуляции d3js на холсте

var force = d3.forceSimulation() 
      .force("link", d3.forceLink().id(function(d, i) { return i; })) 
      .force("charge", d3.forceManyBody().strength(-5)) 
      .force("center", d3.forceCenter(width/2, height/2)); 

force.nodes(data.nodes) 
    .on("tick", ticked) 

force.force("link") 
    .links(data.links); 

function ticked(){ 
    context.clearRect(0, 0, width, height); 

    // Draw the links 
    data.links.forEach(function(d) { 
     // Draw a line from source to target. 
     context.beginPath(); 
     context.moveTo(d.source.x, d.source.y); 
     context.lineTo(d.target.x, d.target.y); 
     context.stroke(); 
    }); 
    // Draw the nodes 
    data.nodes.forEach(function(d, i) { 
    // Draws a complete arc for each node. 
    context.beginPath(); 
    context.arc(d.x, d.y, d.radius, 0, 2 * Math.PI, true); 
    context.fill(); 
    }); 
}; 

// now the zooming part 
    canvas.call(d3.zoom().scaleExtent([0.2, 10]).on("zoom", zoomed)) 

    function zoomed(d) { 
    context.save(); 
    context.clearRect(0, 0, width, height); 
    context.translate(d3.event.transform.x, d3.event.transform.y); 
    context.scale(d3.event.transform.k, d3.event.transform.k); 

    // Draw the links ... 
    data.links.forEach(function(d) { 
     context.beginPath(); 
     context.moveTo(d.source.x, d.source.y); 
     context.lineTo(d.target.x, d.target.y); 
     context.stroke(); 
    }); 
    // Draw the nodes ... 
    data.nodes.forEach(function(d, i) { 
     context.beginPath(); 
     context.arc(d.x, d.y, d.radius, 0, 2 * Math.PI, true); 
     context.fill(); 
    }); 
    context.restore(); 
    } 

ответ

2

Вы tick функция не знает о каких-либо преобразований, созданных зумом. Лучший способ сделать это - всегда применять преобразование в тике (которое перед любым масштабированием является identity transform). Это позволяет повторно использовать метод тика для выполнения всего рисования.

var force = d3.forceSimulation() 
    .force("link", d3.forceLink().id(function(d, i) { 
    return d.id; 
    })) 
    .force("charge", d3.forceManyBody().strength(-5)) 
    .force("center", d3.forceCenter(width/2, height/2)); 

force.nodes(data.nodes) 
    .on("tick", ticked); 

force.force("link") 
    .links(data.links) 

var trans = d3.zoomIdentity; //<-- identity transform 
function ticked() { 
    context.save(); 
    context.clearRect(0, 0, width, height); 
    context.translate(trans.x, trans.y); //<-- this always applies a transform 
    context.scale(trans.k, trans.k); 

    // Draw the links 
    data.links.forEach(function(d) { 
    // Draw a line from source to target. 
    context.beginPath(); 
    context.moveTo(d.source.x, d.source.y); 
    context.lineTo(d.target.x, d.target.y); 
    context.stroke(); 
    }); 
    // Draw the nodes 
    data.nodes.forEach(function(d, i) { 
    // Draws a complete arc for each node. 
    context.beginPath(); 
    context.arc(d.x, d.y, 5, 0, 2 * Math.PI, true); 
    context.fill(); 
    }); 

    context.restore(); 
}; 

// now the zooming part 
canvas.call(d3.zoom().scaleExtent([0.2, 10]).on("zoom", zoomed)) 

function zoomed(d) { 
    trans = d3.event.transform; //<-- set to current transform 
    ticked(); //<-- use tick to redraw regardless of event 
} 

полный ход Код:

<!DOCTYPE html> 
 
<html> 
 

 
<head> 
 
    <script data-require="[email protected]" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script> 
 
</head> 
 

 
<body> 
 
    
 
    <canvas width="500" height="500"></canvas> 
 
    
 
    <script> 
 
    
 
    var width = 500, 
 
     height = 500 
 
     canvas = document.querySelector("canvas"), 
 
     context = canvas.getContext("2d"); 
 
     
 
    canvas = d3.select(canvas); 
 
    
 
    var data = { 
 
     "nodes": [{ 
 
     "id": "Myriel", 
 
     "group": 1 
 
     }, { 
 
     "id": "Napoleon", 
 
     "group": 1 
 
     }, { 
 
     "id": "Mlle.Baptistine", 
 
     "group": 1 
 
     }, { 
 
     "id": "Mme.Magloire", 
 
     "group": 1 
 
     }, { 
 
     "id": "CountessdeLo", 
 
     "group": 1 
 
     }, { 
 
     "id": "Geborand", 
 
     "group": 1 
 
     }, { 
 
     "id": "Champtercier", 
 
     "group": 1 
 
     }, { 
 
     "id": "Cravatte", 
 
     "group": 1 
 
     }, { 
 
     "id": "Count", 
 
     "group": 1 
 
     }, { 
 
     "id": "OldMan", 
 
     "group": 1 
 
     }, { 
 
     "id": "Labarre", 
 
     "group": 2 
 
     }, { 
 
     "id": "Valjean", 
 
     "group": 2 
 
     }, { 
 
     "id": "Marguerite", 
 
     "group": 3 
 
     }, { 
 
     "id": "Mme.deR", 
 
     "group": 2 
 
     }, { 
 
     "id": "Isabeau", 
 
     "group": 2 
 
     }, { 
 
     "id": "Gervais", 
 
     "group": 2 
 
     }, { 
 
     "id": "Tholomyes", 
 
     "group": 3 
 
     }, { 
 
     "id": "Listolier", 
 
     "group": 3 
 
     }, { 
 
     "id": "Fameuil", 
 
     "group": 3 
 
     }, { 
 
     "id": "Blacheville", 
 
     "group": 3 
 
     }, { 
 
     "id": "Favourite", 
 
     "group": 3 
 
     }, { 
 
     "id": "Dahlia", 
 
     "group": 3 
 
     }, { 
 
     "id": "Zephine", 
 
     "group": 3 
 
     }, { 
 
     "id": "Fantine", 
 
     "group": 3 
 
     }, { 
 
     "id": "Mme.Thenardier", 
 
     "group": 4 
 
     }, { 
 
     "id": "Thenardier", 
 
     "group": 4 
 
     }, { 
 
     "id": "Cosette", 
 
     "group": 5 
 
     }, { 
 
     "id": "Javert", 
 
     "group": 4 
 
     }, { 
 
     "id": "Fauchelevent", 
 
     "group": 0 
 
     }], 
 
     "links": [{ 
 
     "source": "Napoleon", 
 
     "target": "Myriel", 
 
     "value": 1 
 
     }, { 
 
     "source": "Mlle.Baptistine", 
 
     "target": "Myriel", 
 
     "value": 8 
 
     }, { 
 
     "source": "Mme.Magloire", 
 
     "target": "Myriel", 
 
     "value": 10 
 
     }, { 
 
     "source": "Mme.Magloire", 
 
     "target": "Mlle.Baptistine", 
 
     "value": 6 
 
     }, { 
 
     "source": "CountessdeLo", 
 
     "target": "Myriel", 
 
     "value": 1 
 
     }, { 
 
     "source": "Geborand", 
 
     "target": "Myriel", 
 
     "value": 1 
 
     }, { 
 
     "source": "Champtercier", 
 
     "target": "Myriel", 
 
     "value": 1 
 
     }, { 
 
     "source": "Cravatte", 
 
     "target": "Myriel", 
 
     "value": 1 
 
     }, { 
 
     "source": "Count", 
 
     "target": "Myriel", 
 
     "value": 2 
 
     }, { 
 
     "source": "OldMan", 
 
     "target": "Myriel", 
 
     "value": 1 
 
     }, { 
 
     "source": "Valjean", 
 
     "target": "Labarre", 
 
     "value": 1 
 
     }, { 
 
     "source": "Valjean", 
 
     "target": "Mme.Magloire", 
 
     "value": 3 
 
     }, { 
 
     "source": "Valjean", 
 
     "target": "Mlle.Baptistine", 
 
     "value": 3 
 
     }, { 
 
     "source": "Valjean", 
 
     "target": "Myriel", 
 
     "value": 5 
 
     }, { 
 
     "source": "Marguerite", 
 
     "target": "Valjean", 
 
     "value": 1 
 
     }, { 
 
     "source": "Mme.deR", 
 
     "target": "Valjean", 
 
     "value": 1 
 
     }, { 
 
     "source": "Isabeau", 
 
     "target": "Valjean", 
 
     "value": 1 
 
     }, { 
 
     "source": "Gervais", 
 
     "target": "Valjean", 
 
     "value": 1 
 
     }, { 
 
     "source": "Listolier", 
 
     "target": "Tholomyes", 
 
     "value": 4 
 
     }, { 
 
     "source": "Fameuil", 
 
     "target": "Tholomyes", 
 
     "value": 4 
 
     }, { 
 
     "source": "Fameuil", 
 
     "target": "Listolier", 
 
     "value": 4 
 
     }, { 
 
     "source": "Blacheville", 
 
     "target": "Tholomyes", 
 
     "value": 4 
 
     }, { 
 
     "source": "Blacheville", 
 
     "target": "Listolier", 
 
     "value": 4 
 
     }, { 
 
     "source": "Blacheville", 
 
     "target": "Fameuil", 
 
     "value": 4 
 
     }, { 
 
     "source": "Favourite", 
 
     "target": "Tholomyes", 
 
     "value": 3 
 
     }, { 
 
     "source": "Favourite", 
 
     "target": "Listolier", 
 
     "value": 3 
 
     }, { 
 
     "source": "Favourite", 
 
     "target": "Fameuil", 
 
     "value": 3 
 
     }, { 
 
     "source": "Favourite", 
 
     "target": "Blacheville", 
 
     "value": 4 
 
     }, { 
 
     "source": "Dahlia", 
 
     "target": "Tholomyes", 
 
     "value": 3 
 
     }, { 
 
     "source": "Dahlia", 
 
     "target": "Listolier", 
 
     "value": 3 
 
     }, { 
 
     "source": "Dahlia", 
 
     "target": "Fameuil", 
 
     "value": 3 
 
     }, { 
 
     "source": "Dahlia", 
 
     "target": "Blacheville", 
 
     "value": 3 
 
     }, { 
 
     "source": "Dahlia", 
 
     "target": "Favourite", 
 
     "value": 5 
 
     }, { 
 
     "source": "Zephine", 
 
     "target": "Tholomyes", 
 
     "value": 3 
 
     }, { 
 
     "source": "Zephine", 
 
     "target": "Listolier", 
 
     "value": 3 
 
     }, { 
 
     "source": "Zephine", 
 
     "target": "Fameuil", 
 
     "value": 3 
 
     }, { 
 
     "source": "Zephine", 
 
     "target": "Blacheville", 
 
     "value": 3 
 
     }, { 
 
     "source": "Zephine", 
 
     "target": "Favourite", 
 
     "value": 4 
 
     }, { 
 
     "source": "Zephine", 
 
     "target": "Dahlia", 
 
     "value": 4 
 
     }, { 
 
     "source": "Fantine", 
 
     "target": "Tholomyes", 
 
     "value": 3 
 
     }, { 
 
     "source": "Fantine", 
 
     "target": "Listolier", 
 
     "value": 3 
 
     }, { 
 
     "source": "Fantine", 
 
     "target": "Fameuil", 
 
     "value": 3 
 
     }, { 
 
     "source": "Fantine", 
 
     "target": "Blacheville", 
 
     "value": 3 
 
     }, { 
 
     "source": "Fantine", 
 
     "target": "Favourite", 
 
     "value": 4 
 
     }, { 
 
     "source": "Fantine", 
 
     "target": "Dahlia", 
 
     "value": 4 
 
     }, { 
 
     "source": "Fantine", 
 
     "target": "Zephine", 
 
     "value": 4 
 
     }, { 
 
     "source": "Fantine", 
 
     "target": "Marguerite", 
 
     "value": 2 
 
     }] 
 
    } 
 

 
    var force = d3.forceSimulation() 
 
     .force("link", d3.forceLink().id(function(d, i) { 
 
     return d.id; 
 
     })) 
 
     .force("charge", d3.forceManyBody().strength(-5)) 
 
     .force("center", d3.forceCenter(width/2, height/2)); 
 

 
    force.nodes(data.nodes) 
 
     .on("tick", ticked); 
 
     
 
    force.force("link") 
 
     .links(data.links) 
 
     
 
    var trans = d3.zoomIdentity; 
 
    function ticked() { 
 
     context.save(); 
 
     context.clearRect(0, 0, width, height); 
 
     context.translate(trans.x, trans.y); 
 
     context.scale(trans.k, trans.k); 
 

 
     // Draw the links 
 
     data.links.forEach(function(d) { 
 
     // Draw a line from source to target. 
 
     context.beginPath(); 
 
     context.moveTo(d.source.x, d.source.y); 
 
     context.lineTo(d.target.x, d.target.y); 
 
     context.stroke(); 
 
     }); 
 
     // Draw the nodes 
 
     data.nodes.forEach(function(d, i) { 
 
     // Draws a complete arc for each node. 
 
     context.beginPath(); 
 
     context.arc(d.x, d.y, 5, 0, 2 * Math.PI, true); 
 
     context.fill(); 
 
     }); 
 
     
 
     context.restore(); 
 
    }; 
 

 
    // now the zooming part 
 
    canvas.call(d3.zoom().scaleExtent([0.2, 10]).on("zoom", zoomed)) 
 

 
    function zoomed(d) { 
 
     trans = d3.event.transform; 
 
     ticked(); 
 
    } 
 
    </script> 
 
</body> 
 

 
</html>

+0

Это единственный d3v4 холст заставил графа я нашел до сих пор, который работает и короткие и легко понять! :) – Guntram

Смежные вопросы