如何使svg交互式以收集对所描绘元素的评论/注释 [英] How to make an svg interactive to gather comments/annotations on depicted elements
问题描述
我借助
现在,我希望每个节点和每个边缘都是可点击的",以便用户可以将其注释添加到图形的特定元素.我认为可以通过弹出一个模式对话框来完成.该对话框应该知道是从哪个元素触发的,并且应该通过发布请求将textarea的内容发送到某些url.
实现此目标的最佳方法是什么?
包装在 W3C标准Web组件(在所有现代浏览器中均受支持),您可以使其对任何 src ="filename.svg"
-
SVG加载了异步抓取
-
此SO代码段中的节点和边缘均可单击
-
添加您自己的更好的模态,窗口并将其保存到数据库
-
从以下位置尝试SVG: https://graphviz.org/Gallery/directed/Genetic_Programming.html
< 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"
Simple example: How to get SVG document data to be inserted into the DOM?
More complex example:
<graphviz-svg-annotator src="https://graphviz.org/Gallery/directed/fsm.svg"> </graphviz-svg-annotator>
The SVG is loaded with an async fetch
Nodes and Edges are clickable in this SO Snippet
add your own, better modal, window and saving to database
Try the SVGs from: https://graphviz.org/Gallery/directed/Genetic_Programming.html
<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 (seecursor: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屋!