index.html
<!DOCTYPE html>
<html lang="en">
<head>
<title>xkcd Antigravity</title>
<link rel="icon" type="image/png" href="../../../favicon.png">
<script defer src="../../../pyscript/pyscript.js"></script>
</head>
<body>
<py-config src="../py_config.local.toml">
paths = ["./antigravity.py"]
</py-config>
<strong>Based on xkcd: antigravity https://xkcd.com/353/.</strong>
<py-script>
import antigravity
antigravity.fly()
</py-script>
</body>
</html>
antigravity.py
"""Animate the xkcd comic."""
import random
import sys
from js import DOMParser
from js import document
from js import setInterval
from pyodide.ffi import create_proxy
from pyodide.http import open_url
class Antigravity:
"""Implement antigravity."""
url = "./antigravity.svg"
def __init__(self, target=None, interval=10, append=True, fly=False):
"""Construct antigravity."""
target = target or sys.stdout._out
self.target = (
document.getElementById(target) if isinstance(target, str) else target
)
doc = DOMParser.new().parseFromString(
open_url(self.url).read(), "image/svg+xml"
)
self.node = doc.documentElement
if append:
self.target.append(self.node)
else:
self.target.replaceChildren(self.node)
self.xoffset, self.yoffset = 0, 0
self.interval = interval
if fly:
self.fly()
def fly(self):
"""Schedule a taking off-and-fly."""
setInterval(create_proxy(self.move), self.interval)
def move(self):
"""Move the item."""
char = self.node.getElementsByTagName("g")[1]
char.setAttribute("transform", f"translate({self.xoffset}, {-self.yoffset})")
self.xoffset += random.normalvariate(0, 1) / 20
if self.yoffset < 50:
self.yoffset += 0.1
else:
self.yoffset += random.normalvariate(0, 1) / 20
_auto = Antigravity(append=True)
fly = _auto.fly