Skip to content

Instantly share code, notes, and snippets.

@aadsm
Last active November 4, 2024 01:11
Show Gist options
  • Save aadsm/a66113980ec33b826afe14bc48aed5f9 to your computer and use it in GitHub Desktop.
Save aadsm/a66113980ec33b826afe14bc48aed5f9 to your computer and use it in GitHub Desktop.
SVG Template Function
const previewInDocument = false;
function toDataUrl(svgnode) {
// SVG is well formed xml, enforce an xml serialization to make sure
// html embeded in foreignObjects are also serialized as xml.
const serializer = new XMLSerializer();
const svgxml = serializer.serializeToString(svgnode);
const svgDataUrl = `data:image/svg+xml;base64,${btoa(svgxml)}`;
return svgDataUrl;
}
function toSvgNode(svgmarkup) {
const container = document.createElementNS("http://www.w3.org/2000/svg", "g");
container.innerHTML = svgmarkup;
if (container.children.length > 1) {
return container;
} else {
return container.firstElementChild;
}
}
function wrapInSvgRoot(svgnode) {
if (svgnode.tagName.toLowerCase() === "svg") {
return svgnode;
} else {
const svgroot = document.createElementNS(
"http://www.w3.org/2000/svg",
"svg"
);
svgroot.append(svgnode);
return svgroot;
}
}
function measureSvg(svgroot) {
const wrapper = document.createElement("div");
wrapper.append(svgroot);
wrapper.style.display = "absolute";
wrapper.style.visibility = "hidden";
wrapper.style.pointerEvents = "none";
wrapper.dataset.preview = "true";
document.body.append(wrapper);
const { width: widthpx, height: heightpx } = svgroot.getBoundingClientRect();
svgroot.remove();
return { widthpx, heightpx };
}
function calculateAndSetSize(svgroot) {
const { widthpx, heightpx } = measureSvg(svgroot);
const width = `${widthpx}px`;
const height = `${heightpx}px`;
svgroot.setAttribute("width", width);
svgroot.setAttribute("height", height);
return { width, height };
}
function generateSvgCss(svgroot) {
const { width, height } = calculateAndSetSize(svgroot);
const style = `
background-image: url(${toDataUrl(svgroot)});
background-size: contain;
padding-left: ${width};
padding-bottom: ${height};
background-repeat: no-repeat;
`;
return style;
}
function svg(svgmarkup) {
const svgnode = toSvgNode(svgmarkup);
consolesvg(svgnode);
}
function consolesvg(svgnode) {
const svgroot = wrapInSvgRoot(svgnode);
const style = generateSvgCss(svgroot);
console.log("%c ", style);
if (previewInDocument) {
document.body.append(svgroot);
}
}
svg.f = function (...args) {
let [templateString, ...templateArgs] = args;
if (
typeof templateString !== "string" ||
!/(^|[^%])%g/g.test(templateString)
) {
return args;
}
let argIndex = -1;
templateString = templateString.replace(
/(?<=(?:^|[^%])%)[discogOf]/g,
function (specifier) {
argIndex++;
if (specifier === "g") {
const css = generateSvgCss(wrapInSvgRoot(templateArgs[argIndex]));
templateArgs.splice(argIndex, 1, css, "");
return "c %c";
} else {
return specifier;
}
}
);
return [templateString, ...templateArgs];
};
function consolelog(...args) {
console.log(...svg.f(...args));
}
svg.node = toSvgNode;
console.svg = consolesvg;
runExample("svg`...`", function () {
svg`<rect width="100%" height="100%" fill="lightblue" />`;
});
runExample("svg`...` (w/ root)", function () {
svg`<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">
<rect width="100%" height="100%" fill="lightblue" />
<foreignObject x="10" y="10" width="180" height="80">
<body>
<input type="text" value="d">
<div style="font-family: Arial; font-size: 16px; color: darkblue;">
Hello, this is embedded HTML content!
</div>
</body>
</foreignObject>
</svg>
`;
});
runExample("svg()", function () {
// https://medium.com/@thatsprettyfaroutman/console-log-svg-animation-91fdf677e922
svg(
`<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 50 55">
<style type="text/css">
.st0{fill:#FDDA3E;}
.st1{fill:#FFFFFF;}
.bounce{
transform-origin: center center;
animation-name: bounce;
animation-duration: 720ms;
animation-timing-function: cubic-bezier(.25,.72,.15,1.17);
animation-iteration-count: infinite;
}
@keyframes bounce {
0%, 50%, 100% {
transform:
rotate3d(0, 0, 0, 0deg)
translate3d(0, 5px, 0);
}
20% {
transform:
rotate3d(0, 0, 1, 4deg)
translate3d(0, 0, 0);
}
70% {
transform:
rotate3d(0, 0, 1, -4deg)
translate3d(0, 0, 0);
}
}
</style>
<g class="bounce">
<circle class="st0" cx="25" cy="25" r="25"/>
<polygon points="44.5,21 3.9,21 3.9,24.3 5.7,24.3 5.7,25.9 7.1,25.9 7.1,27.4 8.8,27.4 8.8,29 20.2,29 20.2,27.4 21.8,27.4
21.8,25.9 23.4,25.9 23.4,24.3 26.7,24.3 26.7,25.9 28.2,25.9 28.2,27.4 29.8,27.4 29.8,29 41.2,29 41.2,27.4 42.8,27.4
42.8,25.9 44.5,25.9 44.5,24.3 44.5,24.2 46.1,24.2 46.1,22.6 46.1,21"/>
</g>
</svg>
`
);
});
runExample("console.svg", function () {
console.svg(
svg.node(
`<svg
width="15"
height="15"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
fill="red"
>
<path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" />
</svg>
`
)
);
});
runExample("consolelog", function () {
const heartsvg = svg.node(
`<svg
width="15"
height="15"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
fill="red"
>
<path d="M12 21.35l-1.45-1.32C5.4 15.36 2 12.28 2 8.5 2 5.42 4.42 3 7.5 3c1.74 0 3.41.81 4.5 2.09C13.09 3.81 14.76 3 16.5 3 19.58 3 22 5.42 22 8.5c0 3.78-3.4 6.86-8.55 11.54L12 21.35z" />
</svg>
`
);
consolelog(
"A %cconsolelog%c test %g text after the svg!",
"font-style: italic",
"",
heartsvg
);
});
// Helper Functions
function runExample(title, example) {
console.group(
"%c%s",
"font-size: x-large; font-weight: bold; margin-top: 18px",
title
);
console.log(
"%c%s",
"font-size: 12px; border-left: 1px solid #666; padding-left: 8px",
formatFunction(example)
);
console.log();
example();
console.groupEnd();
}
function formatFunction(funktion) {
const text = funktion.toString();
const indent = text
.match(/^\s+/gm)
.map((m) => m.length)
.reduce((a, b) => Math.min(a, b), text.length);
return text
.replace(new RegExp(`^\\s{${indent}}`, "gm"), "")
.split("\n")
.slice(1, -1)
.join("\n");
}
function printFunction(funktion) {
console.log(formatFunction(funktion));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment