I am trying to animate a series of arrows between text boxes in a html file. The text boxes are receiving json strings sent via API calls. That part is working perfectly. However I am trying to animate four arrows between the boxes to illustrate and syncronize with the data exchanges and I cannot get my arrows to appear. There are no error messages in Chrome inspector and I can see the arrows becoming active and inactive but none appear, which makes me think it is a simple problem, perhaps an opacity switch somewhere. The literature is unclear on possible solutions. What is the problem with the js code that the arrows will not appear or animate? Any assistance would be gratefully received. I have enclosed the relevant html and js of my page.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Privacy API Exchange Flow</title>
<style>
body { font-family: sans-serif; background: #f8f8f8; padding: 1rem;}
h1 { text-align: center; }
.buttons { text-align: center; margin: 1rem 0; }
button {
margin: 0.3rem; padding: 0.5rem 1rem; font-size: 1rem;
border-radius: 4px; cursor: pointer;
}
#statusLine {
text-align: center; font-weight: bold; font-size: 1.1rem;
margin-bottom: 0.5rem; color: #1976d2;
}
.container { position: relative; width: 100%; }
.boxes {
display: flex; justify-content: space-around; margin-top: 1rem;
position: relative; z-index: 2;
}
.box {
width: 28%; background: white; border: 2px solid #444;
border-radius: 6px; padding: 0.5rem;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
.title { font-weight: bold; text-align: center; margin-bottom: 0.5rem;}
pre {
background: #f0f0f0; height: 200px; overflow: auto;
padding: 0.5rem; border-radius: 4px; font-size: 0.8rem;
}
/* Arrow styling */
svg.arrow {
position: absolute; top: 290px; width: 100%; height: 50px;
overflow: visible; z-index: 1;
}
svg.arrow path {
transition: stroke-dashoffset 1s ease-in-out;
stroke: #1976d2; stroke-width: 3; fill: none;
marker-end: url(#arrowhead);
opacity: 1; /* hidden until animated */
}
path.active {
stroke: #4CAF50;
stroke-width: 2px;
stroke-dasharray: 300;
stroke-dashoffset: 300;
opacity: 1;
animation: draw 1s forwards;
}
@keyframes draw {
to { stroke-dashoffset: 0; }
}
</style>
</head>
<body>
<h1>API Exchange</h1>
<div class="buttons">
<button id="runDemo">Run Demo</button>
<button id="resetDemo">Reset Demo</button>
</div>
<div id="statusLine">Ready to start</div>
<div class="container">
<svg id="arrow1" class="arrow" width="100" height="50" viewBox="0 0 100 50">
<path
d= "M 20 50 L 20 20"
fill="none"
stroke="black"
stroke-width="1"
/>
</svg>
<svg id="arrow2" class="arrow" width="100" height="50" viewBox="0 0 100 50">
<path
d= "M 50 20 L 20 20"
fill="none"
stroke="black"
stroke-width="1"
/>
</svg>
<svg id="arrow3" class="arrow" width="100" height="50" viewBox="0 0 100 50">
<path
d= "M 80 30 L 50 30"
fill="none"
stroke="black"
stroke-width="1"
/>
</svg>
<svg id="arrow4" class="arrow" width="100" height="50" viewBox="0 0 100 50">
<path
d= "M 50 40 L 80 40"
fill="none"
stroke="black"
stroke-width="1"
/>
</svg>
<svg class="arrow">
<defs>
<marker id="arrowhead" markerWidth="10" markerHeight="7"
refX="10" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#1976d2" />
</marker>
</defs>
</svg>
<div class="boxes">
<div class="box">
<div class="title">User</div>
<pre id="userBox">// waiting…</pre>
</div>
<div class="box">
<div class="title">Service Provider</div>
<pre id="spBox">// waiting…</pre>
</div>
<div class="box">
<div class="title">Trusted Third Party</div>
<pre id="ttpBox">// waiting…</pre>
</div>
</div>
</div>
<script >
const sp_base = "http://localhost:8080";
const ttp_base = "http://localhost:8090";
const delay = ms => new Promise(res => setTimeout(res, ms));
function show(id, data) {
document.getElementById(id).textContent =
(typeof data === "string" ? data : JSON.stringify(data, null, 2));
}
function setStatus(text) {
document.getElementById("statusLine").textContent = text;
}
function resetDemo() {
show("userBox", "// waiting…");
show("spBox", "// waiting…");
show("ttpBox", "// waiting…");
setStatus("Ready to start");
document.querySelectorAll("svg.arrow path").forEach(p => p.classList.remove("active"));
console.log("Demo Reset");
}
function animateArrow(id) {
const path = document.getElementById(id);
console.log("Arrow animated");
if (!path) {
console.error(`Arrow with id "${id}" not found!`);
return;
}
//Reset Animation
path.style.strokeDashoffset = "300";
void path.offsetWidth; // Force reflow
console.log("Animation Restart");
//trigger animation
path.classList.add("active");
console.log("Animation Triggered");
//remove active status after animation finish
setTimeout(() => path.classList.remove("active"), 1000);
}
// Ensure DOM is ready
document.addEventListener("DOMContentLoaded", () => {
document.getElementById("runDemo").onclick = async () => {
resetDemo();
setStatus("Step 1: User → Service Provider");
show("userBox", "Running demo...");
//await delay(5000);
try {
// Step 1
animateArrow("arrow1");
await delay(5000);
//this logic is working only the animation is missing
const uidResp = await fetch(`${sp_base}/UID/issue`, {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({ dln: dln, sh: shx, audience: "sp"})
}).then(r=>r.json());
show("spBox", uidResp);
await delay(5000);
// Step 2
setStatus("Step 2: Service Provider → User (UID issued)");
animateArrow("arrow2");
show("userBox", { message: "Received signed UID from SP", uid: uidResp });
await delay(5000);
// Step 3
setStatus("Step 3: User → TTP (send UID)");
animateArrow("arrow3");
const ttpResp = await fetch(`${ttp_base}/token/issue`, {
method: "POST",
headers: {"Content-Type": "application/json"},
body: JSON.stringify({
uid: uidResp.uid,
signature: uidResp.signature,
signer: uidResp.signer
})
}).then(r=>r.json());
show("ttpBox", ttpResp);
await delay(5000);
// Step 4
setStatus("Step 4: TTP → User (note issued)");
animateArrow("arrow4");
show("userBox", {
message: "Received note from TTP after verification",
token: ttpResp
});
setStatus("Demo complete ✅");
} catch (err) {
console.error(err);
show("userBox", { error: err.message });
setStatus("Error: " + err.message);
}
};
});
document.getElementById("resetDemo").onclick = resetDemo;
</script>
</body>
</html>

