如何使svg交互式以收集对所描绘元素的评论/注释 [英] How to make an svg interactive to gather comments/annotations on depicted elements

查看:56
本文介绍了如何使svg交互式以收集对所描绘元素的评论/注释的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我借助

现在,我希望每个节点和每个边缘都是可点击的",以便用户可以将其注释添加到图形的特定元素.我认为可以通过弹出一个模式对话框来完成.该对话框应该知道是从哪个元素触发的,并且应该通过发布请求将textarea的内容发送到某些url.

实现此目标的最佳方法是什么?

解决方案

包装在 W3C标准Web组件(在所有现代浏览器中均受支持),您可以使其对任何 src ="filename.svg"

通用

 < graphviz-svg-annotator src ="fsm.svg"></graphviz-svg-annotator>< graphviz-svg-annotator src ="Linux_kernel_diagram.svg"<//graphviz-svg-annotator>< style>svg .annotate {cursor:pointer}</style>< script>customElements.define('graphviz-svg-annotator',类扩展了HTMLElement {Constructor(){让loadSVG = async(src,container = this.shadowRoot)=>{container.innerHTML =`< style>:host {display:inline-block}:: slotted(svg){width:100%; height:200px}</style>< slot name ="svgonly">正在加载$ {src}</slot>`;this.innerHTML = await(等待fetch(src)).text();//在lightDOM中加载完整的XML让svg = this.querySelector("svg");svg.slot ="svgonly";//仅在shadowDOM插槽中显示SVG零件svg.querySelectorAll('g [id * ="node"],g [id * ="edge"]').forEach(g => {让label = g.querySelector("text")?. innerHTML ||无标签";让形状= g.querySelectorAll("*:not(title):not(text)");let fill =(color ="none")=>Shapes.forEach(x => x.style.fill = color);让提示符="请注释:ID:"+ g.id +"标签:"+标签;g.classList.add("annotate");g.onmouseenter = evt =>fill("lightgreen");g.onmouseleave = evt =>充满();g.onclick = evt =>g.setAttribute("annotation",window.prompt(prompt));})}super().attachShadow({mode:'open'});loadSVG("//graphviz.org/Gallery/directed/"+this.getAttribute("src"));}});</script>  

详细信息:

  • this.innerHTML = ... 将完​​整的XML注入组件 ligthDOM
    (因为该元素具有shadowDOM,所以lightDOM在浏览器中不可见)

  • 但是您只需要SVG部分(graphviz XML包含太多数据)...,并且您不希望屏幕闪烁.这就是为什么我将XML .. invisible ..放在lightDOM中的原因

  • shadowDOM < slot> 仅用于反射 < svg>

  • 使用此方法,仍然可以从全局CSS 设置< svg> 的样式(请参见 cursor:pointer )

  • 在屏幕上< g> 上有多个SVG,ID值可能会冲突.
    完整的SVG可以通过以下方式移动到shadowDOM:

      let svg = container.appendChild(this.querySelector("svg"))); 

    但是您不能再使用全局CSS设置SVG的样式,因为全局CSS不能设置shadowDOM的样式

I create directed graphs like the following from wikidata with the help of networkx and nxv. The result is an svg file which might be embedded in some html page.

Now I want that every node and every edge is "clickable", such that a user can add their comments to specific elements of the graph. I think this could be done with a modal dialog popping up. This dialog should know from which element it was triggered and it should send the content of the textarea to some url via a post request.

What would be the best way to achieve this?

解决方案

Wrapped in a W3C standard Web Component (supported in all Modern Browsers) you can make it generic for any src="filename.svg"

<graphviz-svg-annotator src="fsm.svg"></graphviz-svg-annotator>
<graphviz-svg-annotator src="Linux_kernel_diagram.svg"></graphviz-svg-annotator>
<style>
  svg .annotate { cursor:pointer }
</style>
<script>
  customElements.define('graphviz-svg-annotator', class extends HTMLElement {
    constructor() {
      let loadSVG = async ( src , container = this.shadowRoot ) => {
        container.innerHTML = `<style>:host { display:inline-block }
                               ::slotted(svg)  { width:100%;height:200px }
                               </style>
                               <slot name="svgonly">Loading ${src}</slot>`;
        this.innerHTML = await(await fetch(src)).text(); // load full XML in lightDOM
        let svg = this.querySelector("svg");
        svg.slot = "svgonly"; // show only SVG part in shadowDOM slot
        svg.querySelectorAll('g[id*="node"],g[id*="edge"]').forEach(g => {
          let label  = g.querySelector("text")?.innerHTML || "No label";
          let shapes = g.querySelectorAll("*:not(title):not(text)");
          let fill   = (color = "none") => shapes.forEach(x => x.style.fill = color);
          let prompt = "Please annotate: ID: " + g.id + " label: " + label; 
          g.classList.add("annotate");
          g.onmouseenter = evt => fill("lightgreen");
          g.onmouseleave = evt => fill();
          g.onclick = evt => g.setAttribute("annotation", window.prompt(prompt));
        })
      }
      super().attachShadow({ mode: 'open' });
      loadSVG("//graphviz.org/Gallery/directed/"+this.getAttribute("src"));
    }});
</script>

Detailed:

  • this.innerHTML = ... injects the full XML in the component ligthDOM
    (because the element has shadowDOM, the lightDOM is not visible in the Browser)

  • But you only want the SVG part (graphviz XML has too much data)... and you don't want a screen flash; that is why I put the XML .. invisible.. in lightDOM

  • A shadowDOM <slot> is used to only reflect the <svg>

  • with this method the <svg> can still be styled from global CSS (see cursor:pointer)

  • With multiple SVGs on screen <g> ID values could conflict.
    The complete SVG can be moved to shadowDOM with:

     let svg = container.appendChild( this.querySelector("svg") );
    

    But then you can't style the SVG with global CSS any more, because global CSS can't style shadowDOM

这篇关于如何使svg交互式以收集对所描绘元素的评论/注释的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆