Skip to content

Instantly share code, notes, and snippets.

@pataruco
Last active June 5, 2020 09:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pataruco/a263d79ff2d9f6cfd7aff6dfb2770596 to your computer and use it in GitHub Desktop.
Save pataruco/a263d79ff2d9f6cfd7aff6dfb2770596 to your computer and use it in GitHub Desktop.
SVG spinner lab
<article>
<h1>SVG Spinner Lab</h1>
<section class="spinner">
<div class="svg-wrapper">
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<g>
<circle cx="50" cy="50" r="45" />
</g>
<g class="checkmark">
<polyline points="35 45, 45 55, 70 30" />
</g>
<g class="cross">
<line x1="30" y1="70" x2="70" y2="30" />
<line x1="30" y1="30" x2="70" y2="70" />
</g>
</svg>
</div>
<menu type="toolbar">
<li>
<button type="button">Success</button>
</li>
<li>
<button type="button">Failure</button>
</li>
<li>
<button type="button">Reset</button>
</li>
</menu>
<dialog>
<p>Animation ended! 💥</p>
<p>Now you might trigger another action</p>
</dialog>
</section>
</article>
// Animation trigger
const successButton = document.querySelector("menu li:first-of-type button");
const failureButton = document.querySelector("menu li:nth-of-type(2) button");
const resetButton = document.querySelector("menu li:last-of-type button");
const svg = document.querySelector("svg");
const setSvgTo = (className) => {
hideDialog();
className
? svg.classList.toggle(className)
: svg.classList.remove(...svg.classList);
};
successButton.addEventListener("click", () => setSvgTo("success"));
failureButton.addEventListener("click", () => setSvgTo("failure"));
resetButton.addEventListener("click", () => setSvgTo(null));
// Animation ended handlers
const checkmark = document.querySelector("svg .checkmark polyline");
const crossLastLine = document.querySelector("svg .cross line:last-of-type");
const dialogBox = document.querySelector("dialog");
const animatedElements = [checkmark, crossLastLine];
const showDialog = (_event) => {
dialogBox.setAttribute("open", true);
};
const hideDialog = (_event) => {
[...dialogBox.attributes].forEach((attr) =>
dialogBox.removeAttribute(attr.name)
);
};
animatedElements.forEach((element) =>
element.addEventListener("animationend", showDialog)
);
/* Known bug on firefox where CSS calc functions
cannot be call on dasharray properties
https://bugzilla.mozilla.org/show_bug.cgi?id=1258270 */
svg circle {
fill: none;
stroke: var(--base-color);
stroke-width: 3;
/* Set the value of dasharray to be the length of the circumference */
/* 2 * π * Radius = C */
/* 2 * 3.14 * 45 = 282.6 */
--stroke-dasharray: calc(2 * var(--svg-radius) * 3.14);
stroke-dasharray: 282.6;
/* Remove a percentage value from circumference */
/* C * 0.25 = circle - 25% */
/* 282.6 * 0.25 = 70.65 */
--stroke-dashoffset: calc(var(--stroke-dasharray) * 0.25);
stroke-dashoffset: 70.65;
transform-origin: center;
animation: spin 1s linear infinite;
}
svg.success circle {
animation: success 1s linear forwards, spin 1s linear infinite;
}
svg .checkmark polyline {
fill: none;
stroke-width: 3;
stroke: var(--success-color);
stroke-dasharray: 100;
stroke-dashoffset: 100;
}
svg.success .checkmark polyline {
animation: success 1s linear forwards 1s;
}
svg .cross line {
stroke-width: 3;
stroke: var(--failure-color);
stroke-dasharray: 100;
stroke-dashoffset: 100;
}
svg.failure circle {
animation: failure 1s linear forwards, spin 1s linear infinite;
}
svg.failure .cross line:first-of-type {
animation: failure 0.5s linear forwards 1s;
}
svg.failure .cross line:last-of-type {
animation: failure 0.5s linear forwards 1.5s;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
@keyframes success {
to {
stroke-dashoffset: 0;
stroke: var(--success-color);
}
}
@keyframes failure {
to {
stroke-dashoffset: 0;
stroke: var(--failure-color);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment