Programmatically generate SVG (vector) images, animations, and interactive Jupyter widgets
1<!DOCTYPE html> 2<head> 3<meta charset="utf-8"> 4</head> 5<body> 6<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" 7 width="400" height="200" viewBox="-200.0 -100.0 400 200" onload="svgOnLoad(event)"> 8<defs> 9</defs> 10<rect x="-200" y="-100" width="400" height="200" fill="#eee" /> 11<circle cx="0" cy="0" r="40" fill="green" /> 12<circle cx="0" cy="0" r="0" fill="silver" stroke="gray"> 13<animate attributeName="cx" dur="8s" values="-100;0;100;0;-100" keyTimes="0;0.25;0.5;0.75;1" repeatCount="indefinite" fill="freeze" /> 14<animate attributeName="cy" dur="8s" values="0;-100;0;100;0" keyTimes="0;0.25;0.5;0.75;1" repeatCount="indefinite" fill="freeze" /> 15<animate attributeName="r" dur="8s" values="0;40;0;40;0" keyTimes="0;0.25;0.5;0.75;1" repeatCount="indefinite" fill="freeze" /> 16<animate attributeName="stroke-width" dur="8s" values="0;5;0;5;0" keyTimes="0;0.25;0.5;0.75;1" repeatCount="indefinite" fill="freeze" /> 17</circle> 18<text x="0" y="1" font-size="30" fill="yellow" text-anchor="middle" dominant-baseline="central">0<animate attributeName="visibility" dur="8s" values="visible;hidden;hidden" keyTimes="0;0.25;1" repeatCount="indefinite" fill="freeze" /></text> 19<text x="0" y="1" font-size="30" fill="yellow" text-anchor="middle" dominant-baseline="central">1<animate attributeName="visibility" dur="8s" values="hidden;visible;hidden;hidden" keyTimes="0;0.25;0.5;1" repeatCount="indefinite" fill="freeze" /></text> 20<text x="0" y="1" font-size="30" fill="yellow" text-anchor="middle" dominant-baseline="central">2<animate attributeName="visibility" dur="8s" values="hidden;hidden;visible;hidden;hidden" keyTimes="0;0.25;0.5;0.75;1" repeatCount="indefinite" fill="freeze" /></text> 21<text x="0" y="1" font-size="30" fill="yellow" text-anchor="middle" dominant-baseline="central">3<animate attributeName="visibility" dur="8s" values="hidden;hidden;visible;visible" keyTimes="0;0.5;0.75;1" repeatCount="indefinite" fill="freeze" /></text> 22<g id="scrub"> 23<path d="M-168.0,90.0 L168.0,90.0" stroke="#ccc" stroke-width="4" stroke-linecap="round" /> 24<rect x="-168.0" y="90.0" width="0" height="0.001" stroke="#05f" stroke-width="4" stroke-linejoin="round"> 25<animate attributeName="width" dur="8s" values="0;336" keyTimes="0;1" repeatCount="indefinite" fill="freeze" /> 26</rect> 27<g id="scrub-capture" data-xmin="-168.0" data-xmax="168.0" data-totaldur="8" data-startdelay="0" data-enddelay="0" data-pauseonload="0"> 28<rect x="-170.0" y="80.0" width="340" height="20" fill="rgba(255,255,255,0)" /> 29<circle cx="-168.0" cy="90.0" r="6" fill="#05f" id="scrub-knob" visibility="hidden"> 30<animate attributeName="cx" dur="8s" values="-168.0;168.0" keyTimes="0;1" repeatCount="indefinite" fill="freeze" /> 31</circle> 32</g> 33<g id="scrub-play" visibility="hidden"> 34<rect x="-191.0" y="86.0" width="8" height="8" fill="#05f" stroke="#05f" stroke-width="8" stroke-linejoin="round" /> 35<path d="M-191.0,86.0 v8.0 l8.0,-4.0 Z" fill="#eee" /> 36</g> 37<g id="scrub-pause" visibility="hidden"> 38<rect x="-191.0" y="86.0" width="8" height="8" fill="#05f" stroke="#05f" stroke-width="8" stroke-linejoin="round" /> 39<rect x="-190.0" y="86.0" width="2.0" height="8.0" fill="#eee" /> 40<rect x="-186.0" y="86.0" width="2.0" height="8.0" fill="#eee" /> 41</g> 42</g> 43<script>/*<![CDATA[*/ 44/* Animation playback controls generated by drawsvg */ 45/* https://github.com/cduck/drawsvg/ */ 46function svgOnLoad(event) { 47 /* Support standalone SVG or embedded in HTML or iframe */ 48 if (event && event.target && event.target.ownerDocument) { 49 svgSetup(event.target.ownerDocument); 50 } else if (document && document.currentScript 51 && document.currentScript.parentElement) { 52 svgSetup(document.currentScript.parentElement); 53 } 54} 55function svgSetup(doc) { 56 var svgRoot = doc.documentElement || doc; 57 var scrubCapture = doc.getElementById("scrub-capture"); 58 /* Block multiple setups */ 59 if (!scrubCapture || scrubCapture.getAttribute("svgSetupDone")) { 60 return; 61 } 62 scrubCapture.setAttribute("svgSetupDone", true); 63 var scrubContainer = doc.getElementById("scrub"); 64 var scrubPlay = doc.getElementById("scrub-play"); 65 var scrubPause = doc.getElementById("scrub-pause"); 66 var scrubKnob = doc.getElementById("scrub-knob"); 67 var scrubXMin = parseFloat(scrubCapture.dataset.xmin); 68 var scrubXMax = parseFloat(scrubCapture.dataset.xmax); 69 var scrubTotalDur = parseFloat(scrubCapture.dataset.totaldur); 70 var scrubStartDelay = parseFloat(scrubCapture.dataset.startdelay); 71 var scrubEndDelay = parseFloat(scrubCapture.dataset.enddelay); 72 var scrubPauseOnLoad = parseFloat(scrubCapture.dataset.pauseonload); 73 var paused = false; 74 var dragXOffset = 0; 75 var point = svgRoot.createSVGPoint(); 76 77 function screenToSvgX(p) { 78 var matrix = scrubKnob.getScreenCTM().inverse(); 79 point.x = p.x; 80 point.y = p.y; 81 return point.matrixTransform(matrix).x; 82 }; 83 function screenToProgress(p) { 84 var matrix = scrubKnob.getScreenCTM().inverse(); 85 point.x = p.x; 86 point.y = p.y; 87 var x = point.matrixTransform(matrix).x; 88 if (x <= scrubXMin) { 89 return scrubStartDelay / scrubTotalDur; 90 } 91 if (x >= scrubXMax) { 92 return (scrubTotalDur - scrubEndDelay) / scrubTotalDur; 93 } 94 return (scrubStartDelay/scrubTotalDur 95 + (x - dragXOffset - scrubXMin) 96 / (scrubXMax - scrubXMin) 97 * (scrubTotalDur - scrubStartDelay - scrubEndDelay) 98 / scrubTotalDur); 99 }; 100 function currentScrubX() { 101 return scrubKnob.cx.animVal.value; 102 }; 103 function pause() { 104 svgRoot.pauseAnimations(); 105 scrubPlay.setAttribute("visibility", "visible"); 106 scrubPause.setAttribute("visibility", "hidden"); 107 paused = true; 108 }; 109 function play() { 110 svgRoot.unpauseAnimations(); 111 scrubPause.setAttribute("visibility", "visible"); 112 scrubPlay.setAttribute("visibility", "hidden"); 113 paused = false; 114 }; 115 function scrub(playbackFraction) { 116 var t = scrubTotalDur * playbackFraction; 117 /* Stop 10ms before end to avoid loop (>=1ms needed on FF) */ 118 var limit = scrubTotalDur - 10e-3; 119 if (t < 0) t = 0; 120 else if (t > limit) t = limit; 121 svgRoot.setCurrentTime(t); 122 }; 123 function mousedown(e) { 124 svgRoot.pauseAnimations(); 125 if (e.target == scrubKnob) { 126 dragXOffset = screenToSvgX(e) - currentScrubX(); 127 } else { 128 dragXOffset = 0; 129 } 130 scrub(screenToProgress(e)); 131 /* Global document listeners */ 132 document.addEventListener('mousemove', mousemove); 133 document.addEventListener('mouseup', mouseup); 134 e.preventDefault(); 135 }; 136 function mouseup(e) { 137 dragXOffset = 0; 138 document.removeEventListener('mousemove', mousemove); 139 document.removeEventListener('mouseup', mouseup); 140 if (!paused) { 141 svgRoot.unpauseAnimations(); 142 } 143 e.preventDefault(); 144 }; 145 function mousemove(e) { 146 scrub(screenToProgress(e)); 147 }; 148 scrubPause.addEventListener("click", pause); 149 scrubPlay.addEventListener("click", play); 150 scrubCapture.addEventListener("mousedown", mousedown); 151 scrubContainer.setAttribute("visibility", "visible"); 152 scrubKnob.setAttribute("visibility", "visible"); 153 if (scrubPauseOnLoad) { 154 pause(); 155 scrub(0); 156 } else { 157 play(); 158 } 159}; 160svgOnLoad(); 161/*]]>*/</script> 162</svg> 163</body> 164</html>