Back to Demo

xkcd Antigravity Code

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