§10 · Интерактивный веб-экспорт
В отличие от остальных форматов (которые сериализуют один кадр), этот путь даёт живую, перетаскиваемую конструкцию в браузере: тяните свободные точки / глайдеры / слайдеры — зависимая геометрия пересчитывается прямо на странице. Под капотом — JSXGraph (его рендерер — SVG), а AnimaGeo транспилирует граф конструкции, а не картинку. Это единственный экспорт, который не может выдать ни один «скриншот-из-GeoGebra» инструмент — он требует семантического графа, которым AnimaGeo уже владеет.
Зачем
Цель — встраивать построения как управляемые подвижные чертежи в любой веб-проект (React / Vue / Svelte / vanilla / без сборки), с понятной системой сигналов и действий в терминах конструкции (имена элементов + AnimaGeo-типы), а не движка рендеринга. Слои:
| Слой | Что |
|---|---|
| Python (библиотека) | exportJSXGraph(output="spec") → animageo-board/v1 (декларативные данные) + JSON-схема |
@animageo/runtime | createBoard(spec, container, opts) → BoardHandle: сигналы + действия; JSXGraph инъектируется; без глобалей |
<animageo-board> | Web Component — сигналы как CustomEvent, действия как методы |
| адаптеры | тонкие шаблоны React / Vue / Svelte / vanilla (~30 строк) |
Живой пример
Это настоящая JSXGraph-доска, построенная из спеки, которую
выдал Python (exportJSXGraph(output="spec") на
ex_general.ggb). Перетащите точки A, B, C
или глайдер G — зависимая геометрия и состояние внизу обновляются live.
— состояние конструкции (сигнал change):
…
file://), браузер
заблокирует загрузку модулей/спеки. Запустите сервер гайда
(PYTHONPATH=. python3.13 docs/guide/server/serve.py) и откройте
страницу по http://127.0.0.1:8765/.
Экспорт: html / js / spec
scene.loadGGB("scene.ggb", style="style.json", px_size=[800, 600])
scene.exportJSXGraph("board.html") # самодостаточная страница (по умолч.)
scene.exportJSXGraph("board.js", output="js") # фрагмент для встраивания
scene.exportJSXGraph("board.json", output="spec") # декларативный animageo-board/v1
output | Что это | Когда |
|---|---|---|
"html" | самодостаточная страница (CDN JSXGraph + MathJax) | быстро показать / iframe |
"js" | фрагмент-скрипт initBoard + create | вставить в готовую страницу |
"spec" | декларативный JSON (animageo-board/v1), без JS-строк | интеграция в приложение через рантайм |
"json" | legacy-спека (несёт JS-строки) | обратная совместимость |
"moodle" | <jsxgraph>-блок для filter_jsxgraph | Moodle |
CLI: python -m animageo scene.ggb -o board.html (или
-o board.json --format jsxgraph для спеки).
Контракт animageo-board/v1
Спека — это eval-free структурированные данные: консьюмеру не
нужно исполнять JS, чтобы построить доску. JSON-схема поставляется в пакете
(animageo/exporters/jsxgraph/board.schema.json, доступна через
load_schema()) и реальные экспорты валидируются против неё в тестах.
{
"format": "animageo-board/v1",
"boundingbox": [-8, 6, 8, -6],
"inputs": [
{ "name": "A", "kind": "point", "x": -2, "y": -1 },
{ "name": "G", "kind": "glider", "on": "f", "t": 0.4, "x": 1.2, "y": 0.7 }
],
"elements": [
{ "name": "M", "role": "live", "kind": "midpoint",
"engine": "midpoint", "parents": [{"ref":"A"}, {"ref":"B"}], "attrs": {} },
{ "name": "f", "role": "static", "kind": "Function",
"engine": "curve", "parents": [[/*xs*/],[/*ys*/]], "tracksDrag": false }
],
"coverage": [ /* live vs static по каждому элементу */ ]
}
Формы parents[]: {"ref": name} · число ·
[x, y] · [[xs],[ys]] · {"fn": body}
(живая функция-координата) · {"js": expr} (escape-hatch). Полная
таблица ключей — §reference.
Рантайм: сигналы и действия
import { createBoard } from '@animageo/runtime';
const spec = await fetch('/board.json').then(r => r.json());
const board = createBoard(spec, document.getElementById('box'));
// сигналы наружу
board.on('ready', () => console.log('built', board.getState()));
board.on('change', ({ name, value, state }) => updateApp(state)); // драг (непрерывно)
board.on('commit', ({ name, value }) => save(name, value)); // конец драга
// действия внутрь
board.setState({ A: { x: 1, y: 2 } }); // управляемый режим
| Сигнал | Когда |
|---|---|
ready | доска построена (после typeset MathJax) |
change | свободная точка / слайдер / глайдер движется (непрерывно) |
commit | драг завершён |
viewchange | pan / zoom |
error | один элемент не построился |
Действия: getState/setState, getValue/setValue,
getElement, reset, setVisible/setStyle,
destroy. Словарь — в терминах конструкции (имена + AnimaGeo-типы),
не JSXGraph. JSXGraph инъектируется как peer-зависимость; нет глобалей;
несколько досок на странице независимы.
Web Component и адаптеры
Наименьший общий знаменатель для интеграции без фреймворка —
<animageo-board>: сигналы → CustomEvent,
действия → методы элемента.
<animageo-board id="b" spec="/board.json" debounce="50"
style="display:block;width:640px;height:480px"></animageo-board>
<script type="module">
import '@animageo/web-component'; // регистрирует <animageo-board>
const el = document.getElementById('b');
el.addEventListener('animageo:change', (e) => console.log(e.detail.state));
// el.setState({ A: { x: 1, y: 2 } });
</script>
В каталоге web/adapters/ — тонкие шаблоны
useAnimageoBoard() (React-хук), composable (Vue), action (Svelte) и
vanilla. Это примеры, не ядро: каждый проект дописывает ~30 строк под
свой стек.
Live vs static
Экспорт честно делит элементы на живые и замороженные и пишет
coverage-отчёт (логируется на INFO: N live, M static).
- Live (пересчёт на драге): точки/глайдеры/слайдеры, midpoint, segment, line/parallel, ray, vector, circle (в т.ч. радиус от слайдера/расстояния), semicircle, polygon (с рёбрами), perpendicular, серединный перпендикуляр, биссектриса, angle, ellipse/hyperbola/parabola/conic, reflection, intersection, rotate/translate. Декорации тоже живые: стрелки векторов и штрихи равенства на сегментах.
- Static (сэмплированные кривые): функции, неявные кривые,
коники из уравнения, дуги, секторы, локусы, касательная из точки. Если такой
элемент зависит от драгаемого входа, он не следует за драгом — это
помечается в coverage (
tracksDrag: false).
Ограничения
- DSL-точки
Point(x, y)не интерактивны: они несут input-команду и не детектируются как свободные входы. Лучшие результаты — на GGB-импортированных сценах (свободные точки → draggable, точки на кривой → глайдеры, числа → слайдеры). - Статические кривые не ресэмплятся при pan/zoom: аналитическое
определение не отправляется в браузер. Рантайм эмитит
viewchange— хост может перезапросить спеку для нового bbox. Live-элементы точны при любом зуме. - Два движка. Live-элементы считаются в JS (JSXGraph), static — заморожены из Python. Coverage-отчёт делает это явным, а не молчаливым.
docs/jsxgraph_web_integration_audit.md
и docs/jsxgraph_web_integration_plan.md; код — каталог web/.