在 SVG 中呈现的 graphviz 节点中的简单颜色循环和突出显示

Simple colour cycling and highlighting in graphviz nodes rendered in SVG

我用graphviz画了一些工程图。他们使用领结方法(在某些重型工程行业中很流行)来使用风险模型。 Graphviz 可以通过这种方式很好地建模风险,尽管它们不是严格意义上的图形。

我的 graphviz 领结导出为 SVG,我将它们直接嵌入 HTML 并将它们发布为静态文件。

某些图形节点在领结模型中称为“威胁”。我将这些威胁节点设为 hyperlink 到其他 HTML 专用于这些威胁的页面。

我在每个威胁页面的顶部都有相同的 SVG 领结的副本,以帮助 reader(我每次都复制它而不是 link。)

一切都按计划进行,但我希望页面更加生动。

  1. 我希望每个相关的威胁节点都用颜色“脉动”(可能从它的 SVG 填充颜色循环到更浅的颜色然后再返回)。 (每页一个威胁。脉冲是为了帮助 reader 记住他们正在查看的威胁。)

我可以在 graphviz 中添加静态节点填充颜色,但我不知道如何使用 SVG 或 CSS 代码。

  1. 如果可能,是否可以在 SVG/CSS 中添加一些通用的东西,以便在指针经过时以用户友好的方式突出显示具有 hyperlink 的每个节点,以表明它有 link 到 HTML 页面?

这是从 graphviz 导出的 SVG,后面是(更短的)graphviz 点代码。我通常不会直接手动编辑 SVG 代码,所以我希望任何更改都尽可能基本。

我在这里做了一个通用示例以缩短篇幅,但我所有的风险图在概念上都是相同的。

    <?xml version="1.0" encoding="UTF-8" standalone="no"?>
    <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
     "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
    <!-- Generated by graphviz version 2.26.3 (20100126.1600)
     -->
    <!-- Title: tiger Pages: 1 -->
    <svg width="864pt" height="354pt"
     viewBox="0.00 0.00 864.00 354.43" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
    <g id="graph1" class="graph" transform="scale(0.44582 0.44582) rotate(0) translate(4 791)">
    <title>tiger</title>
    <polygon fill="white" stroke="white" points="-4,5 -4,-791 1935,-791 1935,5 -4,5"/>
    <!-- ESCAPED \nTIGER -->
    <g id="node1" class="node"><title>ESCAPED \nTIGER</title>
    <a xlink:title="Top Event">
    <ellipse fill="crimson" stroke="crimson" cx="977" cy="-372" rx="148.01" ry="148.01"/>
    <ellipse fill="none" stroke="crimson" cx="977" cy="-372" rx="148.01" ry="148.01"/>
    <ellipse fill="none" stroke="crimson" cx="977" cy="-372" rx="152.01" ry="152.01"/>
    <text text-anchor="middle" x="977" y="-382" font-family="Times Roman,serif" font-size="40.00" fill="white">ESCAPED </text>
    <text text-anchor="middle" x="977" y="-336" font-family="Times Roman,serif" font-size="40.00" fill="white">TIGER</text>
    </a>
    </g>
    <!-- TIGERPIC -->
    <g id="node2" class="node"><title>TIGERPIC</title>
    <a xlink:title="Hazard">
    <image xlink:href="tiger.gif" width="376px" height="203px" preserveAspectRatio="xMinYMin meet" x="789" y="-202.5"/>
    <polygon fill="none" stroke="none" points="1165,-202.5 789,-202.5 789,0.5 1165,0.5 1165,-202.5"/>
    </a>
    </g>
    <!-- ESCAPED \nTIGER&#45;&gt;TIGERPIC -->
    <g id="edge5" class="edge"><title>ESCAPED \nTIGER&#45;&gt;TIGERPIC</title>
    <path fill="none" stroke="black" stroke-width="10" d="M977,-219.683C977,-213.821 977,-207.958 977,-202.096"/>
    </g>
    <!-- SEARCH\nPLAN -->
    <g id="node14" class="node"><title>SEARCH\nPLAN</title>
    <a xlink:href="./search.html" xlink:title="Control">
    <polygon fill="darkgreen" stroke="darkgreen" points="1318,-437 1202,-437 1202,-369 1318,-369 1318,-437"/>
    <polygon fill="none" stroke="darkgreen" points="1318,-437 1202,-437 1202,-369 1318,-369 1318,-437"/>
    <text text-anchor="middle" x="1260" y="-410.5" font-family="Times Roman,serif" font-size="25.00" fill="white">SEARCH</text>
    <text text-anchor="middle" x="1260" y="-380.5" font-family="Times Roman,serif" font-size="25.00" fill="white">PLAN</text>
    </a>
    </g>
    <!-- ESCAPED \nTIGER&#45;&gt;SEARCH\nPLAN -->
    <g id="edge15" class="edge"><title>ESCAPED \nTIGER&#45;&gt;SEARCH\nPLAN</title>
    <path fill="none" stroke="green" stroke-width="10" d="M1128.44,-388.589C1154.59,-391.454 1180.29,-394.269 1201.94,-396.64"/>
    </g>
    <!-- INSURANCE -->
    <g id="node16" class="node"><title>INSURANCE</title>
    <a xlink:href="./insurance.html" xlink:title="Control">
    <polygon fill="darkgreen" stroke="darkgreen" points="1513,-354 1355,-354 1355,-316 1513,-316 1513,-354"/>
    <polygon fill="none" stroke="darkgreen" points="1513,-354 1355,-354 1355,-316 1513,-316 1513,-354"/>
    <text text-anchor="middle" x="1434" y="-327.5" font-family="Times Roman,serif" font-size="25.00" fill="white">INSURANCE</text>
    </a>
    </g>
    <!-- ESCAPED \nTIGER&#45;&gt;INSURANCE -->
    <g id="edge19" class="edge"><title>ESCAPED \nTIGER&#45;&gt;INSURANCE</title>
    <path fill="none" stroke="brown" stroke-width="10" d="M1128.54,-359.731C1204.02,-353.62 1292.18,-346.482 1354.34,-341.45"/>
    </g>
    <!-- GATE\nPASSABLE\n\by\nTIGER -->
    <g id="node4" class="node"><title>GATE\nPASSABLE\n\by\nTIGER</title>
    <a xlink:title="Threat">
    <polygon fill="red" stroke="red" points="202,-743.019 -0.232539,-471.49 404.233,-471.49 202,-743.019"/>
    <polygon fill="none" stroke="red" points="202,-743.019 -0.232539,-471.49 404.233,-471.49 202,-743.019"/>
    <text text-anchor="middle" x="202" y="-599.5" font-family="Times Roman,serif" font-size="25.00" fill="white">GATE</text>
    <text text-anchor="middle" x="202" y="-569.5" font-family="Times Roman,serif" font-size="25.00" fill="white">PASSABLE</text>
    <text text-anchor="middle" x="202" y="-539.5" font-family="Times Roman,serif" font-size="25.00" fill="white">by</text>
    <text text-anchor="middle" x="202" y="-509.5" font-family="Times Roman,serif" font-size="25.00" fill="white">TIGER</text>
    </a>
    </g>
    <!-- GATE\nDESIGN -->
    <g id="node11" class="node"><title>GATE\nDESIGN</title>
    <a xlink:href="./design.html" xlink:title="Control">
    <polygon fill="darkgreen" stroke="darkgreen" points="551,-406 441,-406 441,-338 551,-338 551,-406"/>
    <polygon fill="none" stroke="darkgreen" points="551,-406 441,-406 441,-338 551,-338 551,-406"/>
    <text text-anchor="middle" x="496" y="-379.5" font-family="Times Roman,serif" font-size="25.00" fill="white">GATE</text>
    <text text-anchor="middle" x="496" y="-349.5" font-family="Times Roman,serif" font-size="25.00" fill="white">DESIGN</text>
    </a>
    </g>
    <!-- GATE\nPASSABLE\n\by\nTIGER&#45;&gt;GATE\nDESIGN -->
    <g id="edge7" class="edge"><title>GATE\nPASSABLE\n\by\nTIGER&#45;&gt;GATE\nDESIGN</title>
    <path fill="none" stroke="red" stroke-width="10" d="M342.112,-471.452C378.198,-448.13 414.84,-424.451 443.311,-406.05"/>
    </g>
    <!-- KEEPER\nLEAVES\nGATE\nOPEN -->
    <g id="node5" class="node"><title>KEEPER\nLEAVES\nGATE\nOPEN</title>
    <a xlink:title="Threat">
    <polygon fill="red" stroke="red" points="202,-363.019 45.0223,-91.4903 358.978,-91.4903 202,-363.019"/>
    <polygon fill="none" stroke="red" points="202,-363.019 45.0223,-91.4903 358.978,-91.4903 202,-363.019"/>
    <text text-anchor="middle" x="202" y="-219.5" font-family="Times Roman,serif" font-size="25.00" fill="white">KEEPER</text>
    <text text-anchor="middle" x="202" y="-189.5" font-family="Times Roman,serif" font-size="25.00" fill="white">LEAVES</text>
    <text text-anchor="middle" x="202" y="-159.5" font-family="Times Roman,serif" font-size="25.00" fill="white">GATE</text>
    <text text-anchor="middle" x="202" y="-129.5" font-family="Times Roman,serif" font-size="25.00" fill="white">OPEN</text>
    </a>
    </g>
    <!-- KEEPER\nLEAVES\nGATE\nOPEN&#45;&gt;GATE\nDESIGN -->
    <g id="edge11" class="edge"><title>KEEPER\nLEAVES\nGATE\nOPEN&#45;&gt;GATE\nDESIGN</title>
    <path fill="none" stroke="blue" stroke-width="10" d="M278.478,-231.424C330.2,-264.85 397.187,-308.141 443.078,-337.799"/>
    </g>
    <!-- EATS\nMEMBER\nof\nPUBLIC -->
    <g id="node7" class="node"><title>EATS\nMEMBER\nof\nPUBLIC</title>
    <a xlink:title="Consequence">
    <polygon fill="red" stroke="red" points="1740,-787.019 1566.05,-515.49 1913.95,-515.49 1740,-787.019"/>
    <polygon fill="none" stroke="red" points="1740,-787.019 1566.05,-515.49 1913.95,-515.49 1740,-787.019"/>
    <text text-anchor="middle" x="1740" y="-643.5" font-family="Times Roman,serif" font-size="25.00" fill="white">EATS</text>
    <text text-anchor="middle" x="1740" y="-613.5" font-family="Times Roman,serif" font-size="25.00" fill="white">MEMBER</text>
    <text text-anchor="middle" x="1740" y="-583.5" font-family="Times Roman,serif" font-size="25.00" fill="white">of</text>
    <text text-anchor="middle" x="1740" y="-553.5" font-family="Times Roman,serif" font-size="25.00" fill="white">PUBLIC</text>
    </a>
    </g>
    <!-- LOSS\nof\nREVENUE -->
    <g id="node8" class="node"><title>LOSS\nof\nREVENUE</title>
    <a xlink:title="Consequence">
    <polygon fill="red" stroke="red" points="1740,-407.593 1551,-199.704 1929,-199.704 1740,-407.593"/>
    <polygon fill="none" stroke="red" points="1740,-407.593 1551,-199.704 1929,-199.704 1740,-407.593"/>
    <text text-anchor="middle" x="1740" y="-291.5" font-family="Times Roman,serif" font-size="25.00" fill="white">LOSS</text>
    <text text-anchor="middle" x="1740" y="-261.5" font-family="Times Roman,serif" font-size="25.00" fill="white">of</text>
    <text text-anchor="middle" x="1740" y="-231.5" font-family="Times Roman,serif" font-size="25.00" fill="white">REVENUE</text>
    </a>
    </g>
    <!-- INSPECTION\n\&amp;\nTESTING -->
    <g id="node12" class="node"><title>INSPECTION\n\&amp;\nTESTING</title>
    <a xlink:href="./inspection.html" xlink:title="Control">
    <polygon fill="darkgreen" stroke="darkgreen" points="752,-464 588,-464 588,-366 752,-366 752,-464"/>
    <polygon fill="none" stroke="darkgreen" points="752,-464 588,-464 588,-366 752,-366 752,-464"/>
    <text text-anchor="middle" x="670" y="-437.5" font-family="Times Roman,serif" font-size="25.00" fill="white">INSPECTION</text>
    <text text-anchor="middle" x="670" y="-407.5" font-family="Times Roman,serif" font-size="25.00" fill="white">&amp;</text>
    <text text-anchor="middle" x="670" y="-377.5" font-family="Times Roman,serif" font-size="25.00" fill="white">TESTING</text>
    </a>
    </g>
    <!-- GATE\nDESIGN&#45;&gt;INSPECTION\n\&amp;\nTESTING -->
    <g id="edge8" class="edge"><title>GATE\nDESIGN&#45;&gt;INSPECTION\n\&amp;\nTESTING</title>
    <path fill="none" stroke="red" stroke-width="10" d="M551.533,-385.724C563.07,-388.575 575.46,-391.637 587.725,-394.668"/>
    </g>
    <!-- TRAINING -->
    <g id="node13" class="node"><title>TRAINING</title>
    <a xlink:href="./training.html" xlink:title="Control">
    <polygon fill="darkgreen" stroke="darkgreen" points="737,-348 603,-348 603,-310 737,-310 737,-348"/>
    <polygon fill="none" stroke="darkgreen" points="737,-348 603,-348 603,-310 737,-310 737,-348"/>
    <text text-anchor="middle" x="670" y="-321.5" font-family="Times Roman,serif" font-size="25.00" fill="white">TRAINING</text>
    </a>
    </g>
    <!-- GATE\nDESIGN&#45;&gt;TRAINING -->
    <g id="edge12" class="edge"><title>GATE\nDESIGN&#45;&gt;TRAINING</title>
    <path fill="none" stroke="blue" stroke-width="10" d="M551.533,-358.276C567.844,-354.245 585.861,-349.793 602.817,-345.603"/>
    </g>
    <!-- INSPECTION\n\&amp;\nTESTING&#45;&gt;ESCAPED \nTIGER -->
    <g id="edge9" class="edge"><title>INSPECTION\n\&amp;\nTESTING&#45;&gt;ESCAPED \nTIGER</title>
    <path fill="none" stroke="red" stroke-width="10" d="M752.27,-403.477C775.182,-400.268 800.815,-396.677 826.342,-393.102"/>
    </g>
    <!-- TRAINING&#45;&gt;ESCAPED \nTIGER -->
    <g id="edge13" class="edge"><title>TRAINING&#45;&gt;ESCAPED \nTIGER</title>
    <path fill="none" stroke="blue" stroke-width="10" d="M737.372,-338.437C763.76,-342.132 795.168,-346.532 826.415,-350.908"/>
    </g>
    <!-- DART\nGUN -->
    <g id="node15" class="node"><title>DART\nGUN</title>
    <a xlink:href="./dartgun.html" xlink:title="Control">
    <polygon fill="darkgreen" stroke="darkgreen" points="1475,-573 1393,-573 1393,-505 1475,-505 1475,-573"/>
    <polygon fill="none" stroke="darkgreen" points="1475,-573 1393,-573 1393,-505 1475,-505 1475,-573"/>
    <text text-anchor="middle" x="1434" y="-546.5" font-family="Times Roman,serif" font-size="25.00" fill="white">DART</text>
    <text text-anchor="middle" x="1434" y="-516.5" font-family="Times Roman,serif" font-size="25.00" fill="white">GUN</text>
    </a>
    </g>
    <!-- SEARCH\nPLAN&#45;&gt;DART\nGUN -->
    <g id="edge16" class="edge"><title>SEARCH\nPLAN&#45;&gt;DART\nGUN</title>
    <path fill="none" stroke="green" stroke-width="10" d="M1303.91,-437.319C1331.19,-458.643 1366.04,-485.88 1392.8,-506.797"/>
    </g>
    <!-- DART\nGUN&#45;&gt;EATS\nMEMBER\nof\nPUBLIC -->
    <g id="edge17" class="edge"><title>DART\nGUN&#45;&gt;EATS\nMEMBER\nof\nPUBLIC</title>
    <path fill="none" stroke="green" stroke-width="10" d="M1475.27,-548.036C1508.79,-555.376 1558.05,-566.162 1605.02,-576.445"/>
    </g>
    <!-- INSURANCE&#45;&gt;LOSS\nof\nREVENUE -->
    <g id="edge20" class="edge"><title>INSURANCE&#45;&gt;LOSS\nof\nREVENUE</title>
    <path fill="none" stroke="brown" stroke-width="10" d="M1513.6,-317.832C1550.43,-309.887 1594.7,-300.338 1634.29,-291.8"/>
    </g>
    </g>
    </svg>

这是领结的 graphviz 点代码:

digraph tiger {

    overlap=scale;
    splines=true;
    // clusters=dotted;
    ordering=out;
    size="12,12!"
    
    bgcolor=white;
    rankdir=LR;
    
    root="ESCAPED \nTIGER" // for twopi and circo
    
    // Middle nodes
    
    "ESCAPED \nTIGER" [tooltip="Top Event", fontsize=40, width=2, height=2, shape=doublecircle, style=filled, color=crimson, fontcolor=white];
    
    "TIGERPIC" [label="", image="tiger.gif", tooltip="Hazard", fontsize=40, width=2, height=2, shape=rectangle, color=none];
    
    // Lining up!
    
    {rank=same; "GATE\nPASSABLE\n\by\nTIGER" "KEEPER\nLEAVES\nGATE\nOPEN"}
    
    {rank=same; "EATS\nMEMBER\nof\nPUBLIC" "LOSS\nof\nREVENUE"}
    
    {rank=same; "ESCAPED \nTIGER" "TIGERPIC"}
    
    // Top Event and Risk!
    
    
    "ESCAPED \nTIGER" -> "TIGERPIC" [arrowhead = none, penwidth=10];
    
    // Threats and consequences
    
    "GATE\nPASSABLE\n\by\nTIGER" [tooltip="Threat", color=red, shape=triangle, style=filled, fontcolor=white, fontsize=25];
    "KEEPER\nLEAVES\nGATE\nOPEN"  [tooltip="Threat", color=red, shape=triangle, style=filled, fontcolor=white, fontsize=25];
    "EATS\nMEMBER\nof\nPUBLIC"  [tooltip="Consequence", color=red, shape=triangle, style=filled, fontcolor=white, fontsize=25];
    "LOSS\nof\nREVENUE"  [tooltip="Consequence", color=red, shape=triangle, style=filled, fontcolor=white, fontsize=25];
    
    // Controls!
    
    "GATE\nDESIGN" [URL="./design.html", tooltip="Control", shape=box, style=filled, color=darkgreen, fontcolor=white, fontsize=25];
    "INSPECTION\n\&\nTESTING" [URL="./inspection.html", tooltip="Control", shape=box, style=filled, color=darkgreen, fontcolor=white, fontsize=25];
    "TRAINING" [URL="./training.html", tooltip="Control", shape=box, style=filled, color=darkgreen, fontcolor=white, fontsize=25];
    "SEARCH\nPLAN" [URL="./search.html", tooltip="Control", shape=box, style=filled, color=darkgreen, fontcolor=white, fontsize=25];
    "DART\nGUN" [URL="./dartgun.html", tooltip="Control", shape=box, style=filled, color=darkgreen, fontcolor=white, fontsize=25];
    "INSURANCE" [URL="./insurance.html", tooltip="Control", shape=box, style=filled, color=darkgreen, fontcolor=white, fontsize=25];
    
    // Joining up 
    
    "GATE\nPASSABLE\n\by\nTIGER" -> "GATE\nDESIGN" -> "INSPECTION\n\&\nTESTING" -> "ESCAPED \nTIGER" [arrowhead = none, penwidth=10, color=red];
    "KEEPER\nLEAVES\nGATE\nOPEN" -> "GATE\nDESIGN" -> "TRAINING" -> "ESCAPED \nTIGER" [arrowhead = none, penwidth=10, color=blue];
    "ESCAPED \nTIGER" -> "SEARCH\nPLAN" -> "DART\nGUN" -> "EATS\nMEMBER\nof\nPUBLIC" [arrowhead = none, penwidth=10, color=green];
    "ESCAPED \nTIGER" -> "INSURANCE" -> "LOSS\nof\nREVENUE" [arrowhead = none, penwidth=10, color=brown];
}
    
//---------------------
// ----- The End! -----
//---------------------

以下是通过 2 个步骤用脉动颜色为威胁设置动画的方法:

第 1 步:添加 id 属性

为每个以 threat 开头的威胁添加一个 id 属性,例如 threat1threat2, ...:[=​​12=]

"GATE\nPASSABLE\n\by\nTIGER" [tooltip="Threat", id="threat1", ...];
"KEEPER\nLEAVES\nGATE\nOPEN"  [tooltip="Threat", id="threat2", ...];

这将使用此属性作为 graphviz svg 输出中的 id。

第 2 步:添加 CSS 动画

将 CSS 添加到包含 svg 的 html 页面,根据这些 id 对属性进行动画处理。以下 CSS 将使威胁的颜色在红色和黄色之间跳动:

g [id*="threat"] polygon {
    -webkit-animation: pulse 2s;
    -webkit-animation-iteration-count: infinite;
    -webkit-animation-direction: alternate;
    animation: pulse 2s;
    animation-iteration-count: infinite;
    animation-direction: alternate;
}
@-webkit-keyframes pulse {
    from {fill: red;}
    to {fill: yellow;}
}
@keyframes pulse {
    from {fill: red;}
    to {fill: yellow;}
} 

关于你的第二个问题,鼠标指针在浏览器中经过超链接时应该已经变成了一个手形图标。否则,您也可以使用上述技术为这些链接添加一些特殊效果。