Last active
November 4, 2024 01:11
-
-
Save aadsm/a66113980ec33b826afe14bc48aed5f9 to your computer and use it in GitHub Desktop.
SVG Template Function
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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