Отличный ответ. Потрясающий пример. Заслуживает много очков

ользую принудительное расположение для создания ориентированного графа. Его представленный на холсте. Мой пример пример наhttp://jsbin.com/vuyapibaqa/1/edit?html,output

Теперь я вдохновлен
https://bl.ocks.org/mattkohl/146d301c0fc20d89d85880df537de7b0#index.html

Мало ресурсов в d3 svg, что-то похожее я пытаюсь получить на холсте.

http://jsfiddle.net/zhanghuancs/a2QpA/

http://bl.ocks.org/mbostock/1153292 https://bl.ocks.org/ramtob/3658a11845a89c4742d62d32afce3160
http://bl.ocks.org/thomasdobber/9b78824119136778052f64a967c070e0 Рисование нескольких ребер между двумя узлами с помощью d3.

Хочу добавить эллиптическую дугу, соединяющую край стрелкой. Как этого добиться на холсте.

Мой код:

<!DOCTYPE html>
<html>
<head>
        <title>Sample Graph Rendring Using Canvas</title>
        <script src="https://rawgit.com/gka/randomgraph.js/master/randomgraph.js"></script>
        <script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
    <script>
        var graph = {}//randomgraph.WattsStrogatz.beta(15, 4, 0.06);

    graph.nodes = [{"label":"x"} , {"label":"y"}];
    graph.edges = [{source:0,target:1},{source:0,target:1},
                   {source:1,target:0}]

        var canvas = null
        var width = window.innerWidth,
            height = window.innerHeight;
        canvas = d3.select("body").append("canvas").attr("width",width).attr("height",height);

        var context = canvas.node().getContext("2d");


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

        force.nodes(graph.nodes);
        force.force("link").links(graph.edges).distance(200);

        var detachedContainer = document.createElement("custom");
            dataContainer = d3.select(detachedContainer);

        link = dataContainer.selectAll(".link").data(graph.edges)
              .enter().append("line").attr("class", "link")
              .style("stroke-width", 2)

        node = dataContainer.selectAll(".node").data(graph.nodes)
              .enter().append("g");

          var circles = node.append("circle")
              .classed("circle-class", true)
              .attr("class", function (d){ return "node node_" + d.index;})
              .attr("r", 5)
              .attr("fill", 'red')
              .attr("strokeStyle", 'black');

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

            // draw links
            link.each(function(d) {
              context.strokeStyle = "#ccc";
              /***** Elliptical arcs *****/
              context.stroke(new Path2D(linkArc(d)));
              /***** Elliptical arcs *****/
            });

            context.lineWidth = 2;
            node.each(function(d) {

              context.beginPath();
              context.moveTo(d.x, d.y);
              var r = d3.select(this).select("circle").node().getAttribute('r');   

              d.x = Math.max(30, Math.min(width - 30, d.x));
              d.y = Math.max(30, Math.min(height - 30, d.y));         
              context.closePath();
              context.arc(d.x, d.y, r, 0, 2 * Math.PI);

              context.fillStyle = d3.select(this).select("circle").node().getAttribute('fill');
              context.strokeStyle = d3.select(this).select("circle").node().getAttribute('strokeStyle');
              context.stroke();
              context.fill();

              context.beginPath();
              context.arc(d.x + 15, d.y-20, 5, 0, 2 * Math.PI);
              context.fillStyle = "orange";
              context.strokeStyle = "orange";
              var data = d3.select(this).data();
              context.stroke();
              context.fill();
              context.font = "10px Arial";
              context.fillStyle = "black";
              context.strokeStyle = "black";
              context.fillText(parseInt(data[0].index),d.x + 10, d.y-15);
            });

        });

        circles.transition().duration(5000).attr('r', 20).attr('fill', 'orange');

        canvas.node().addEventListener('click',function( event ){
           console.log(event)
            // Its COMING ANY TIME INSIDE ON CLICK OF CANVAS
        });

        /***** Elliptical arcs *****/
        function linkArc(d) {
          var dx = d.target.x - d.source.x,
              dy = d.target.y - d.source.y,
              dr = Math.sqrt(dx * dx + dy * dy);
          return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
        }
        /***** Elliptical arcs *****/
    </script>
</body>
</html>  

 Sumeet06 окт. 2017 г., 15:18
Я дал попробовать, но не сработало, вы можете помочь?
 Sumeet06 окт. 2017 г., 15:59
Нечто подобное, мы можем преобразовать то же самое в холст.
 altocumulus06 окт. 2017 г., 15:56
I раздвоенный ваш JSBin и добавил эллиптические дуги, что довольно просто (ищите/***** Elliptical arcs *****/ Комментарии). Я уже начал вставлять это в ответ, когда впервые прочитал ваш вопрос, но воздержался от публикации, как только понял, что маркеры стрелок отсутствуют. Если вы хотите их тоже, это будет гораздо более сложный подвиг.
 Sumeet06 окт. 2017 г., 15:58
 altocumulus05 окт. 2017 г., 22:40
Это может быть полезно:«Рисовать путь в Canvas с данными SVG Path (пути SVG к путям Canvas)», Это делает рисование линий довольно простым. Вам все равно нужно будет выяснить, как рисовать маркеры.

Ответы на вопрос(1)

Решение Вопроса
Нарисуйте дугу от круга к кругу с помощью стрелокОсновная проблема

y1 и x2, y2. Вы захотите контролировать величину изгиба, которая не зависит от расстояния между точками (то есть, то же количество изгиба, если расстояние между точками составляет 100 или 10 пикселей).

Таким образом, входы

x1,y1 // as start
x2,y2 // as end
bend  // as factor of distance between points 
      // negative bends up (to right) 
      // positive bends down (to left of line)
arrowLen  // in pixels
arrowWidth // in pixels,
arrowStart // boolean if arrow at start
arrowEnd   // boolean if arrow at end.
Основные шаги методаНайдите среднюю точку между двумя конечными точками.Получить расстояние между точкамиПолучить нормализованный вектор от начала до конца.Повернуть на 90 градусовУмножьте расстояние на изгиб на повернутую норму и добавьте к средней точке, чтобы найти среднюю точку на дугеС 3 точками найдите радиус круга, который будет соответствовать всем 3 точкам.Используйте радиус, чтобы найти центр дугиОт центра найдите направление к началу и концуИспользуйте стрелку len, чтобы найти угловую длину стрелок, теперь у нас есть радиусНарисуйте дугу изнутри стрелки или начало / конец (в зависимости от того, показаны стрелки)Нарисуйте стрелку из точки с плоской стороной вдоль линии от центра дугиДополнительные проблемы

Я предполагаю, что вы хотите, чтобы линии проходили от одного круга к другому. Таким образом, вы хотите указать центры кругов и радиус кругов. Для этого потребуются два дополнительных аргумента: один для начального радиуса окружности и один для конечного.

Существует также проблема того, что делать, когда две точки находятся близко друг к другу (т.е. они перекрываются). Существует не реальное решение, кроме как не рисовать линии и стрелки, если они не подходят.

Решение как демо

Демонстрация состоит из кругов, которые меняют размер с течением времени, есть 6 дуг с различными значениями изгиба 0,1,0,3, 0,6 и -0,1, -0,3, -0,6. Переместите мышь, чтобы изменить положение конечных кругов.

Функция, которая делает все это называетсяdrawBend и я поместил там много комментариев. Есть также несколько закомментированных строк, которые позволяют вам изменить то, как изменяются дуги при изменении расстояния между началом и концом. Если раскомментировать один, установка переменнойb1 (где вы назначаете х3, у3 среднюю точку на дуге) Вы ДОЛЖНЫ закомментировать другие задания

Решение для определения радиуса и центра дуги является сложным, и, скорее всего, из-за симметрии существует лучшее решение. Эта часть найдет круг, который будет соответствовать любым 3 точкам (если не все на линии), так что может иметь другие применения для вас.

Обновить Я нашел гораздо лучший метод определения радиуса дуги и, следовательно, центральной точки. Симметрия предоставила очень удобный набор подобных треугольников, и, таким образом, я мог сократить функцию на 9 линий. Я обновил демо.

Дуга рисуется как обводка, а стрелки - как заливка.

Это довольно быстро, но если вы планируете рисовать много сотен в режиме реального времени, вы можете оптимизировать, используя дугу из, а затем и обратно, делить некоторые кальки. Дуга от начала до конца будет сгибаться по-другому, если вы поменяете местами начало и конец, и есть много значений, которые остаются неизменными, так что вы можете получить две дуги примерно для 75% загрузки ЦП на чертеже 2

const ctx = canvas.getContext("2d");

const mouse  = {x : 0, y : 0, button : false}
function mouseEvents(e){
	mouse.x = e.pageX;
	mouse.y = e.pageY;
	mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
}
["down","up","move"].forEach(name => document.addEventListener("mouse"+name,mouseEvents));




// x1,y1 location of a circle start
// x2,y2 location of the end circle
// bend factor. negative bends up for, positive bends down. If zero the world will end 
// aLen is Arrow head length in pixels
// aWidth is arrow head width in pixels
// sArrow boolean if true draw start arrow
// eArrow  boolean if true draw end  arrow
// startRadius = radius of a circle if start attached to circle
// endRadius = radius of a circle if end attached to circle
function drawBend(x1, y1, x2, y2, bend, aLen, aWidth, sArrow, eArrow, startRadius, endRadius){
    var mx, my, dist, nx, ny, x3, y3, cx, cy, radius, vx, vy, a1, a2;
    var arrowAng,aa1,aa2,b1;
    // find mid point
    mx = (x1 + x2) / 2;  
    my = (y1 + y2) / 2;
    
    // get vector from start to end
    nx = x2 - x1;
    ny = y2 - y1;
    
    // find dist
    dist = Math.sqrt(nx * nx + ny * ny);
    
    // normalise vector
    nx /= dist;
    ny /= dist;
    
    // The next section has some optional behaviours
    // that set the dist from the line mid point to the arc mid point
    // You should only use one of the following sets
    
    //-- Uncomment for behaviour of arcs
    // This make the lines flatten at distance
    //b1 =  (bend * 300) / Math.pow(dist,1/4);

    //-- Uncomment for behaviour of arcs
    // Arc bending amount close to constant
    // b1 =  bend * dist * 0.5

    b1 = bend * dist

    // Arc amount bend more at dist
    x3 = mx + ny * b1;
    y3 = my - nx * b1;
   
    // get the radius
    radius = (0.5 * ((x1-x3) * (x1-x3) + (y1-y3) * (y1-y3)) / (b1));

    // use radius to get arc center
    cx = x3 - ny * radius;
    cy = y3 + nx * radius;

    // radius needs to be positive for the rest of the code
    radius = Math.abs(radius);

    


    // find angle from center to start and end
    a1 = Math.atan2(y1 - cy, x1 - cx);
    a2 = Math.atan2(y2 - cy, x2 - cx);
    
    // normalise angles
    a1 = (a1 + Math.PI * 2) % (Math.PI * 2);
    a2 = (a2 + Math.PI * 2) % (Math.PI * 2);
    // ensure angles are in correct directions
    if (bend < 0) {
        if (a1 < a2) { a1 += Math.PI * 2 }
    } else {
        if (a2 < a1) { a2 += Math.PI * 2 }
    }
    
    // convert arrow length to angular len
    arrowAng = aLen / radius  * Math.sign(bend);
    // get angular length of start and end circles and move arc start and ends
    
    a1 += startRadius / radius * Math.sign(bend);
    a2 -= endRadius / radius * Math.sign(bend);
    aa1 = a1;
    aa2 = a2;
   
    // check for too close and no room for arc
    if ((bend < 0 && a1 < a2) || (bend > 0 && a2 < a1)) {
        return;
    }
    // is there a start arrow
    if (sArrow) { aa1 += arrowAng } // move arc start to inside arrow
    // is there an end arrow
    if (eArrow) { aa2 -= arrowAng } // move arc end to inside arrow
    
    // check for too close and remove arrows if so
    if ((bend < 0 && aa1 < aa2) || (bend > 0 && aa2 < aa1)) {
        sArrow = false;
        eArrow = false;
        aa1 = a1;
        aa2 = a2;
    }
    // draw arc
    ctx.beginPath();
    ctx.arc(cx, cy, radius, aa1, aa2, bend < 0);
    ctx.stroke();

    ctx.beginPath();

    // draw start arrow if needed
    if(sArrow){
        ctx.moveTo(
            Math.cos(a1) * radius + cx,
            Math.sin(a1) * radius + cy
        );
        ctx.lineTo(
            Math.cos(aa1) * (radius + aWidth / 2) + cx,
            Math.sin(aa1) * (radius + aWidth / 2) + cy
        );
        ctx.lineTo(
            Math.cos(aa1) * (radius - aWidth / 2) + cx,
            Math.sin(aa1) * (radius - aWidth / 2) + cy
        );
        ctx.closePath();
    }
    
    // draw end arrow if needed
    if(eArrow){
        ctx.moveTo(
            Math.cos(a2) * radius + cx,
            Math.sin(a2) * radius + cy
        );
        ctx.lineTo(
            Math.cos(aa2) * (radius - aWidth / 2) + cx,
            Math.sin(aa2) * (radius - aWidth / 2) + cy
        );
        ctx.lineTo(
            Math.cos(aa2) * (radius + aWidth / 2) + cx,
            Math.sin(aa2) * (radius + aWidth / 2) + cy
        );
        ctx.closePath();
    }
    ctx.fill();
}



/** SimpleUpdate.js begin **/
// short cut vars 
var w = canvas.width;
var h = canvas.height;
var cw = w / 2;  // center 
var ch = h / 2;
var globalTime = new Date().valueOf();  // global to this 

// main update function
function update(timer){
    globalTime = timer;
    if(w !== innerWidth || h !== innerHeight){  // resize if needed
      cw = (w = canvas.width = innerWidth) / 2;
      ch = (h = canvas.height = innerHeight) / 2;
    }    
    ctx.setTransform(1,0,0,1,0,0); // reset transform
    ctx.globalAlpha = 1;           // reset alpha
    ctx.clearRect(0,0,w,h);

    var startRad = (Math.sin(timer / 2000) * 0.5 + 0.5) * 20 + 5;
    var endRad = (Math.sin(timer / 7000) * 0.5 + 0.5) * 20 + 5;
    ctx.lineWidth = 2;
    ctx.fillStyle = "white";
    ctx.strokeStyle = "black";
    ctx.beginPath();
    ctx.arc(cw,ch,startRad,0,Math.PI * 2);
    ctx.fill();
    ctx.stroke();
    ctx.beginPath();
    ctx.arc(mouse.x,mouse.y,endRad,0,Math.PI * 2);
    ctx.fill();
    ctx.stroke();

    ctx.lineWidth = 2;
    ctx.fillStyle = "black";
    ctx.strokeStyle = "black";
    
    
    
    drawBend(cw,ch,mouse.x,mouse.y,-0.1,10,10,true,true,startRad + 1,endRad + 1);
    drawBend(cw,ch,mouse.x,mouse.y,-0.3,10,10,true,true,startRad + 1,endRad + 1);
    drawBend(cw,ch,mouse.x,mouse.y,-0.6,10,10,true,true,startRad + 1,endRad + 1);
    drawBend(cw,ch,mouse.x,mouse.y,0.1,10,10,true,true,startRad + 1,endRad + 1);
    drawBend(cw,ch,mouse.x,mouse.y,0.3,10,10,true,true,startRad + 1,endRad + 1);
    drawBend(cw,ch,mouse.x,mouse.y,0.6,10,10,true,true,startRad + 1,endRad + 1);


    requestAnimationFrame(update);
}
requestAnimationFrame(update);
canvas { position : absolute; top : 0px; left : 0px; }
<canvas id="canvas"></canvas>

 Blindman6709 окт. 2017 г., 04:21
@Sumeet Если у вас все еще есть вопрос, выходящий за рамки этого вопроса, вы можете задать другой, или, может быть, вы можете добавить более подробную информацию в этом вопросе, поскольку у вас еще есть время на получение вознаграждения, а кто-то другой может предоставить ответ, более подходящий для ваших нужд ,
 Sumeet08 окт. 2017 г., 17:14
Как добавить это в мой пример программы @ Blindman67
 Sumeet08 окт. 2017 г., 08:34
Спасибо за ответ ! Я собираюсь принять это. Я изучаю холст, и d3 сейчас только началась. Так что его немного доработано, чтобы понять твой код мне сейчас. Можете ли вы помочь мне добавить метку по краям и визуализировать в принудительном макете
 Sumeet09 окт. 2017 г., 16:57
Спасибо @ Bilndman67 Ваш ответ правильный, проблема со мной, так как я мало знаю о d3 и canvas. На самом деле я хочу показать текстовую метку по краю и визуализировать с принудительной версткой. У меня есть еще один вопрос, именно так я хочу достичьstackoverflow.com/questions/46609700/...
 meetamit13 окт. 2017 г., 23:05
Отличный ответ. Потрясающий пример. Заслуживает много очков

Ваш ответ на вопрос