Как добавить всплывающую подсказку в существующие круги SVG? - PullRequest
0 голосов
/ 15 мая 2019

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

<g id="cities" class="gradici">
<circle class="first" r="7" />
<circle class="second" r="7 />
</g>

var Citytooltip = svg.selectAll("g.gradici").selectAll("text")
                  .data(naziviGradova)
                  .enter()
                  .append("text")
                  .style("visibility", "hidden")
                  .attr("x", function(d,i){return graddx[i]})
                  .attr("y",function(d,i){return graddy[i]})
                  .text(function(d) {return d;})
                  .attr("font-size", "10px")
                  .attr("fill", "#black");

Мне удается получить при наведении курсора на любой круг, что весь текст виден / скрыт рядом со всеми кругами.

var city= svg.selectAll("#cities circle");

city
   .on("mouseover", (function(){Citytooltip.style("visibility", 
   "visible");}))
   .on("mouseout", (function(){Citytooltip.style("visibility", 
   "hidden");}));

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

Ответы [ 3 ]

0 голосов
/ 16 мая 2019

Если всплывающие подсказки пользовательского агента не собираются его сокращать, нужно будет реализовать некоторые функции самостоятельно.Я решил по-прежнему полагаться на декларативные элементы desc, и мы можем использовать их тривиально, даже с подсказками для рендеринга.

В следующем документе SVG определение всплывающей подсказки используется в качестве шаблона и всякий раз, когда "мышь"«Указатель (все, что может генерировать события« mouse * », на самом деле) входит в элемент, мы извлекаем фрагмент документа (Range), который является содержимым его элемента desc, и копируем это содержимое в группу« contents »/графика всплывающей подсказки.Кроме того, мы рассчитываем позицию, в которой должна отображаться подсказка - на кончике указателя мыши - и изменяем размер фоновой «панели», чтобы она фактически напоминала то, что большинство людей принимает в качестве подсказок.

Вы можете добавить свой собственный стиль и даже анимацию для дальнейшего улучшения желаемого результата.

Более подробное объяснение приведено в комментариях к приведенному ниже коду:

<?xml version="2.0" encoding="utf-8" ?>
<!DOCTYPE svg SYSTEM "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" viewBox="0 0 100 100">
    <style>
        #rich-tooltip {
            pointer-events: none;
        }
        #rich-tooltip .panel {
            fill: silver;
        }
    </style>
    <defs>
        <!-- the actual template, will be removed from the context and always shown as appended at the end of the document body, so that it is rendered above everything else. -->
        <g id="rich-tooltip">
            <rect class="panel" /><!-- just some decorative background -->
            <g class="contents" /><!-- contents of an element's description will be added as child nodes of this element -->
        </g>
    </defs>
    <circle cx="50" cy="50" r="25" fill="yellow">
        <desc><circle cx="10" cy="10" r="5" /><text dominant-baseline="hanging" fill="red">First circle</text></desc>
    </circle>
    <circle cx="70" cy="50" r="40" fill="green">
        <desc><circle cx="10" cy="10" r="5" /><text dominant-baseline="hanging" fill="red">Second circle</text></desc>
    </circle>
    <script>
        const tooltip = document.getElementById("rich-tooltip");
        tooltip.remove(); /// Initial state of the tooltip is "not shown" (removed from DOM tree)
        var timeout; /// We only show the tooltip once the pointer settles and some time passes
        const tooltip_delay = 1000; /// Delay before showing the tooltip once pointer settles
        var last_tooltip_ev; /// Auxilary variable to be able to calculate movement after showing the tooltip, so we don't remove it immediately but only once the pointer actually moved substantially, this is just a nice touch, not otherwise crucial
        const remove_tooltip_move_threshold = 10; /// How many user units (pixels, normall) the cursor may move before tooltip is hidden
        function on_mouse_move_event(ev) {
            if(document.contains(tooltip)) { /// Is the tooltip being shown?
                if(last_tooltip_ev) {
                    if(((x, y) => Math.sqrt(x * x + y * y))(ev.clientX - last_tooltip_ev.clientX, ev.clientY - last_tooltip_ev.clientY) >= remove_tooltip_move_threshold) { /// has the pointer moved far enough from where the tooltip was originally shown?
                        tooltip.remove(); /// Hide the tooltip
                    }
                }
            } else {
                if(timeout) clearTimeout(timeout);
                timeout = setTimeout(show_tooltip, tooltip_delay, ev);
            }
        }
        function show_tooltip(ev) {
            const desc = ev.target.querySelector(":scope > desc");
            if(!desc) { /// Does the element that is under the pointer even have a description?
                tooltip.remove(); /// Hide the tooltip (ignoring the fact it may not be showing in the first place)
                return;
            }
            document.documentElement.appendChild(tooltip);
            const desc_range = document.createRange();
            desc_range.selectNodeContents(desc); /// Select all children of the description element, as `desc_range`
            const contents = tooltip.querySelector(".contents");
            const contents_range = document.createRange();
            contents_range.selectNodeContents(contents); /// Select all children of the tooltip contents element, as `contents_range`
            contents_range.extractContents(); /// Empty tooltip contents
            contents.appendChild(desc_range.cloneContents()); /// Fill contents with previously selected description. We _copy_ the description -- the original should legitimately stay where it was
            const panel = tooltip.querySelector("rect.panel");
            panel.setAttribute("width", contents.getBBox().width);
            panel.setAttribute("height", contents.getBBox().height); /// "Stretch" the panel so that it covers the tooltip contents
            const pt = document.documentElement.createSVGPoint();
            pt.x = ev.clientX;
            pt.y = ev.clientY;
            const view_pt = pt.matrixTransform(document.documentElement.getScreenCTM().inverse()); /// We need to convert mouse pointer coordinates to the SVG viewbox coordinates
            tooltip.setAttribute("transform", `translate(${view_pt.x} ${view_pt.y})`); /// Move the tooltip to appropriate position
            last_tooltip_ev = ev; /// Save the event to be able to calculate distance later (above)
        }
        addEventListener("mouseover", function(ev) { /// When the pointer gets over an element...
            ev.target.addEventListener("mousemove", on_mouse_move_event); /// We will be tracking pointer movements to trigger timeout for showing the tooltip
            ev.target.addEventListener("mouseout", function() { /// Once the pointer gets anywhere but the element itself -- like over its children or other elements...
                ev.target.removeEventListener("mouseout", on_mouse_move_event); /// Cancel the whole mousemove business, the behavior will be setup by whatever element the mouse pointer gets over next anyway
            }, { once: true }); /// Once, for this element, everything else will be setup by another call for "mouseover" listener
        });
    </script>
</svg>

Код будет проще без тайм-аутазапуск и т. д., но шансы хорошо продуманы, и удобная для пользователя реализация всплывающей подсказки будет использовать задержки и компенсировать случайные перемещения указателя, поэтому я подумал, что было бы целесообразно сохранить некоторые скелетные рамки для них, а также продемонстрировать, как это будет происходить.о реализации их.

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

0 голосов
/ 24 мая 2019

Мне удается решить мою проблему, поэтому я оставлю ответ, если кто-то застрянет, как я.

var titleCreate = svg.selectAll("g.gradici circle").append("title").text("tooltip");
for (var i =0; i<naziviGradova.length; i++){                                    
  var textNazivaGradova = naziviGradova[i];
  var title = svg.getElementsByTagName("title");
  title[i].innerHTML = textNazivaGradova;                                    

}

0 голосов
/ 15 мая 2019

Добавьте элементы title, description или metadata в качестве содержимого для элемента circle, чтобы агент пользователя мог выдавать всплывающие подсказки (в зависимости от агента пользователя):

<g id="cities" class="gradici">
    <desc>A group of circles</desc>
    <circle class="first" r="7">
        <desc>First circle</desc>
    </circle>
    <circle class="second" r="7>
        <desc>Second circle</desc>
    </circle>
</g>

Это , определенное SVG 1.1 .

Для современных настольных и мобильных веб-браузеров описания элементов обычно отображаются так, как вы их ожидаете, - в виде всплывающих подсказок, появляющихся при наведении курсора на пользователяих указательное устройство "над элементом circle, который, например, содержит элемент desc.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...