davidardz07 commited on
Commit
7888a2c
·
1 Parent(s): b0b2cd8

soft movements implemented

Browse files
Files changed (2) hide show
  1. index.html +4 -4
  2. script.js +73 -27
index.html CHANGED
@@ -36,14 +36,14 @@
36
  <div>
37
  <label class="block text-sm text-gray-600 mb-1">Text Position</label>
38
  <select id="text-position" class="w-full p-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
39
- <option value="top" selected>Top</option>
40
- <option value="middle">Middle</option>
41
- <option value="bottom">Bottom</option>
42
  </select>
43
  </div>
44
  <div>
45
  <label class="block text-sm text-gray-600 mb-1">Node Width</label>
46
- <input type="range" id="node-width" min="10" max="50" value="20" class="w-full">
47
  </div>
48
  <div>
49
  <label class="block text-sm text-gray-600 mb-1">Link Alpha</label>
 
36
  <div>
37
  <label class="block text-sm text-gray-600 mb-1">Text Position</label>
38
  <select id="text-position" class="w-full p-2 border rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500">
39
+ <option value="left">Left</option>
40
+ <option value="middle" selected>Middle</option>
41
+ <option value="right">Right</option>
42
  </select>
43
  </div>
44
  <div>
45
  <label class="block text-sm text-gray-600 mb-1">Node Width</label>
46
+ <input type="range" id="node-width" min="10" max="80" value="40" class="w-full">
47
  </div>
48
  <div>
49
  <label class="block text-sm text-gray-600 mb-1">Link Alpha</label>
script.js CHANGED
@@ -2,8 +2,8 @@
2
  // Define the d3.sankey layout function
3
  d3.sankey = function() {
4
  var sankey = {},
5
- nodeWidth = 24,
6
- nodePadding = 8,
7
  size = [1, 1],
8
  nodes = [],
9
  links = [];
@@ -57,8 +57,12 @@ d3.sankey = function() {
57
  var curvature = .5;
58
 
59
  function link(d) {
60
- var x0 = d.source.x + d.source.dx,
61
- x1 = d.target.x,
 
 
 
 
62
  xi = d3.interpolateNumber(x0, x1),
63
  x2 = xi(curvature),
64
  x3 = xi(1 - curvature),
@@ -332,13 +336,40 @@ function initDiagram() {
332
  const height = baseHeight + textPadding;
333
  const linkAlpha = parseFloat(document.getElementById('link-alpha').value);
334
 
 
335
  const sankey = d3.sankey()
336
  .nodeWidth(parseInt(document.getElementById('node-width').value))
337
- .nodePadding(8)
338
- .size([width - textPadding, height - textPadding])
339
- .nodes(sankeyData.nodes.map(d => Object.assign({}, d)))
340
- .links(sankeyData.links.map(d => Object.assign({}, d)))
341
- .layout(32); // Increase iterations for better layout
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
342
 
343
  const nodes = sankey.nodes();
344
  const links = sankey.links();
@@ -370,10 +401,16 @@ function initDiagram() {
370
  d3.select(this).attr("stroke-opacity", linkAlpha);
371
  });
372
 
 
 
 
 
 
 
373
  // Add nodes with better styling
374
  const node = svg.append("g")
375
  .selectAll("g")
376
- .data(nodes)
377
  .join("g")
378
  .attr("transform", d => `translate(${d.x},${d.y})`);
379
 
@@ -384,7 +421,7 @@ function initDiagram() {
384
  });
385
 
386
  node.append("rect")
387
- .attr("height", d => d.dy)
388
  .attr("width", d => d.dx)
389
  .attr("fill", d => d.color || "#4f46e5")
390
  .attr("stroke", "#fff")
@@ -394,13 +431,22 @@ function initDiagram() {
394
 
395
  // Add drag behavior
396
  node.call(d3.drag()
397
- .subject(d => ({ x: d.x, y: d.y }))
 
 
 
 
 
 
 
398
  .on("start", function(event) {
399
  d3.select(this).raise();
400
  })
401
  .on("drag", function(event, d) {
402
- // Keep x position fixed, only allow vertical movement
403
- d.y = event.y;
 
 
404
  nodePositions[d.index] = { x: d.x, y: d.y };
405
  d3.select(this).attr("transform", `translate(${d.x},${d.y})`);
406
 
@@ -413,27 +459,27 @@ function initDiagram() {
413
  );
414
 
415
  // Add text with customizable position
416
- let textY, textDy;
417
 
418
  switch(textPosition) {
419
- case 'top':
420
- textY = -3;
421
- textDy = "0em";
422
  break;
423
- case 'bottom':
424
- textY = d => (d.y1 - d.y0) + 15;
425
- textDy = "0em";
426
  break;
427
  default: // middle
428
- textY = d => (d.y1 - d.y0) / 2;
429
- textDy = "0.35em";
430
  }
431
 
432
  node.append("text")
433
- .attr("x", d => (d.x1 - d.x0) / 2)
434
- .attr("y", textY)
435
- .attr("dy", textDy)
436
- .attr("text-anchor", "middle")
437
  .text(d => d.name)
438
  .attr("fill", document.getElementById('text-color').value)
439
  .attr("font-size", "12px")
 
2
  // Define the d3.sankey layout function
3
  d3.sankey = function() {
4
  var sankey = {},
5
+ nodeWidth = 100,
6
+ nodePadding = 80,
7
  size = [1, 1],
8
  nodes = [],
9
  links = [];
 
57
  var curvature = .5;
58
 
59
  function link(d) {
60
+ // Calculate node centers
61
+ var sourceCenter = d.source.dx / 2,
62
+ targetCenter = d.target.dx / 2;
63
+
64
+ var x0 = d.source.x + sourceCenter,
65
+ x1 = d.target.x + targetCenter,
66
  xi = d3.interpolateNumber(x0, x1),
67
  x2 = xi(curvature),
68
  x3 = xi(1 - curvature),
 
336
  const height = baseHeight + textPadding;
337
  const linkAlpha = parseFloat(document.getElementById('link-alpha').value);
338
 
339
+ // Create Sankey generator with increased padding for better distribution
340
  const sankey = d3.sankey()
341
  .nodeWidth(parseInt(document.getElementById('node-width').value))
342
+ .nodePadding(120) // Increased padding for better vertical distribution
343
+ .size([width - textPadding, height - textPadding]);
344
+
345
+ // Create a copy of the data
346
+ const graph = {
347
+ nodes: sankeyData.nodes.map(d => Object.assign({}, d)),
348
+ links: sankeyData.links.map(d => Object.assign({}, d))
349
+ };
350
+
351
+ // Generate initial layout
352
+ sankey.nodes(graph.nodes).links(graph.links).layout(32);
353
+
354
+ // Apply proportional scaling to node heights
355
+ const nodesByX = d3.groups(graph.nodes, d => d.x);
356
+ const ky = d3.min(nodesByX, ([x, nodes]) =>
357
+ (height - (nodes.length - 1) * sankey.nodePadding()) /
358
+ d3.sum(nodes, d => d.value)
359
+ );
360
+
361
+ // Apply the scaling to nodes and links
362
+ graph.nodes.forEach(node => {
363
+ const nodeHeight = node.value * ky;
364
+ node.dy = nodeHeight;
365
+ });
366
+
367
+ graph.links.forEach(link => {
368
+ link.dy = link.value * ky;
369
+ });
370
+
371
+ // Relayout to apply the new heights
372
+ sankey.layout(1);
373
 
374
  const nodes = sankey.nodes();
375
  const links = sankey.links();
 
401
  d3.select(this).attr("stroke-opacity", linkAlpha);
402
  });
403
 
404
+ // Filter out nodes that have no connections
405
+ const connectedNodes = nodes.filter(node =>
406
+ (node.sourceLinks && node.sourceLinks.length > 0) ||
407
+ (node.targetLinks && node.targetLinks.length > 0)
408
+ );
409
+
410
  // Add nodes with better styling
411
  const node = svg.append("g")
412
  .selectAll("g")
413
+ .data(connectedNodes)
414
  .join("g")
415
  .attr("transform", d => `translate(${d.x},${d.y})`);
416
 
 
421
  });
422
 
423
  node.append("rect")
424
+ .attr("height", d => Math.max(d.dy, 1)) // Ensure minimum height of 1px
425
  .attr("width", d => d.dx)
426
  .attr("fill", d => d.color || "#4f46e5")
427
  .attr("stroke", "#fff")
 
431
 
432
  // Add drag behavior
433
  node.call(d3.drag()
434
+ .subject(function(event, d) {
435
+ // Get mouse position relative to the node
436
+ const mousePoint = d3.pointer(event, this);
437
+ return {
438
+ x: d.x, // Keep x position fixed
439
+ y: d.y + mousePoint[1] // Add relative y position
440
+ };
441
+ })
442
  .on("start", function(event) {
443
  d3.select(this).raise();
444
  })
445
  .on("drag", function(event, d) {
446
+ // Calculate new position based on mouse movement
447
+ const yOffset = event.dy; // Use the change in y position
448
+ d.y += yOffset; // Update position incrementally
449
+
450
  nodePositions[d.index] = { x: d.x, y: d.y };
451
  d3.select(this).attr("transform", `translate(${d.x},${d.y})`);
452
 
 
459
  );
460
 
461
  // Add text with customizable position
462
+ let textX, textY, textAnchor;
463
 
464
  switch(textPosition) {
465
+ case 'left':
466
+ textX = -6;
467
+ textAnchor = "end";
468
  break;
469
+ case 'right':
470
+ textX = d => d.dx + 6;
471
+ textAnchor = "start";
472
  break;
473
  default: // middle
474
+ textX = d => d.dx / 2;
475
+ textAnchor = "middle";
476
  }
477
 
478
  node.append("text")
479
+ .attr("x", textX)
480
+ .attr("y", d => d.dy / 2)
481
+ .attr("dy", ".35em")
482
+ .attr("text-anchor", textAnchor)
483
  .text(d => d.name)
484
  .attr("fill", document.getElementById('text-color').value)
485
  .attr("font-size", "12px")