2014-11-28 7 views
1

Я читал много в последнее время о многоточии и кривых Безье, чтобы иметь возможность рисовать «Речевой пузырь» с d3js: Этот пузырь должен содержаться внутри прямоугольного или квадратного контейнера как так:D3js - Эллиптический речевой пузырь

http://edn.embarcadero.com/article/images/10277/D15PX16.jpg

Но, конечно, он будет содержать маленькую стрелку в левом нижнем углу, чтобы дать такой эффект «речи пузырь». Поскольку этот контейнер будет изменяться по размеру, мне нужны точки, которые будут пересчитаны, когда это произойдет.

Учитывая эти условия, пузырь может быть эллиптическим или почти идеальным кругом в случае квадратного контейнера. Я предполагаю, что я не могу использовать эллипсис, потому что рисование стрелки Речевого пузыря было бы невозможно в этом случае.

Выполняя исследования, я наткнулся на http://spencermortensen.com/articles/bezier-circle/, который, как я думал, станет хорошим местом для начала, но я не могу понять, как начать переводить это в код, а самое главное, как использовать d3js, чтобы помочь мне визуализировать это, особенно, поскольку у меня есть трудность в том, чтобы нарисовать стрелку где-нибудь в левой нижней части моего эллипса.

Я был бы очень благодарен, если бы кто-нибудь мог объяснить мне, как рассчитать очки, которые мне нужны, чтобы нарисовать этот пузырь речи и как D3js может его нарисовать.

Спасибо!

PS: Извините, я не могу быть более конкретным и добавлять изображения, но моя текущая репутация не позволяет мне.

+0

Похоже, используя [эллипс] (http://www.w3schools.com/svg/svg_ellipse.asp), а затем добавить путь для стрелки на вершине, что было бы намного проще. –

+0

Мысль об этом тоже поначалу, но мне нужно, чтобы она была идеальной, поскольку фон мог иметь градиенты, а форма могла иметь границу. Размещение стрелки вручную поверх эллипса было бы немного беспорядочно –

+1

Или вы могли бы просто взять SVG-образ речевого пузыря и получить путь от этого. –

ответ

0
function callout(parameters) { 
    var w = parameters.width || 200, 
      h = parameters.height || 100, 
      a = w/2, 
      b = h/2, 
      o_x = parameters.x0 || 100, 
      o_y = parameters.y0 || 100, 
      m_r = parameters.l || 300, 
      m_w = 10, 
      m_q = parameters.angle * Math.PI/180 || 50 * Math.PI/180, 
      m_q_delta = Math.atan(m_w/(2 * Math.min(w, h))); 

    var d = "M", x, y, 
      d_q = Math.PI/30; // 1/30 -- precision of drawing 

    // now, we are drawing the path step by step 
    for (var alpha = 0; alpha < 2 * Math.PI; alpha += d_q) { 

     if (alpha > m_q - m_q_delta && alpha < m_q + m_q_delta) { //edge 
      x = o_x + m_r * Math.cos(m_q); 
      y = o_y + m_r * Math.sin(m_q); 
      d += "L" + x + "," + y; 
      alpha = m_q + m_q_delta; 
     } else { // ellipse 
      x = a * Math.cos(alpha) + o_x; 
      y = b * Math.sin(alpha) + o_y; 
      d += "L" + x + "," + y + " "; 
     } 
    } 
    d += "Z"; 
    return(d.replace(/^ML/, "M").replace(/ Z$/, "Z")); 
} 

var styles = { 
    board: {width: 1000, height: 1000}, 
    callout: {stroke: "black", "stroke-width": 1, fill: "snow"} 
}; 

var callout_params = { 
    width: 300, 
    height: 100, 
    angle: 20, 
    l: 250, 
    x0: 200, 
    y0: 200 
}; 

var board = d3.select("body").append("svg:svg").attr(styles.board); 
var defs = board.append("svg:defs"); 
var callout = board.append("svg:path").attr(styles.callout).attr("d", callout(callout_params)); 

ДЕМО: http://jsbin.com/kalijumepe/1/

+0

Это выглядит отлично! У меня нет времени, чтобы вникнуть в детали только сейчас, но, похоже, все, что мне нужно! Спасибо, что нашли время, чтобы помочь –

0

DEMO: http://jsbin.com/firacaredi/2/

Вы можете использовать только маркер, а не реконструировать пересечение эллипса с двумя векторами для получения точно правильный пути Безье:

var styles = { 
    board: {width: 500, height: 400}, 

    bubble: {id: "bubble", refX: 0, refY: 0, markerWidth: 200, markerHeight: 200, viewBox: "-4 -4 8 4"}, 
    bubble_ellipse: {fill: "snow", stroke: "none", cx: 0, cy: 0, rx:4, ry: 2}, 

    handle: {id: "handle", refX: 0, refY: 2, "markerWidth": 100, "markerHeight": 20, orient: "auto", viewBox: "0 0 4 4" }, 
    handle_path: {"d": "M 0,0 V 4 L10,1", fill: "snow"}, 

    text: {fill: "black", x: 190, y: 110}, 
    callout: {fill: "snow", stroke: "snow", "stroke-width": 2, "d": "M200,100L130,160", "marker-end": "url(#handle)", "marker-start": "url(#bubble)",} 
}; 

var board = d3.select("body").append("svg:svg").attr(styles.board); 
var defs = board.append("svg:defs"); 
defs.append("svg:marker").attr(styles.handle).append("svg:path").attr(styles.handle_path); 
defs.append("svg:marker").attr(styles.bubble).append("svg:ellipse").attr(styles.bubble_ellipse); 

board.append("svg:path").attr(styles.callout); 
board.append("svg:text").attr(styles.text).text("HI!"); 

Затем, если вам нужна граница, вы можете упасть одна пиксельная тень или место темное, на две пиксели большая форма выноски под световой.

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