Back to "Merenneito.js Deep Sukellus: Miten se oikeasti toimii ja miten laajentaa sitä"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

Diagrams JavaScript Mermaid SVG

Merenneito.js Deep Sukellus: Miten se oikeasti toimii ja miten laajentaa sitä

Sunday, 09 November 2025

Johdanto

HUOMAUTUS: Tämä on osa kokeilujani tekoälyllä / tapa käyttää 1000 dollaria Calude Code Web -arvosanoja. Olen syöttänyt tälle BUNCH-lehden, ymmärrykseni, kysymyksiä, joita jouduin tekemään tämän artikkelin. Se on hauska ja täyttää aukon, jota en ole nähnyt paikattavan missään muualla.

Tämä viesti perustuu aiempiin artikkeleihin: Jos et ole jo ehtinyt, katso Lisää merenneito.js kanssa htmx, Teemojen vaihtaminen merenneidolle, ja Merenneitokaavioiden parantaminen Pan/Zoomin ja Exportin avullaTämä syväsukellus selittää toteutusten taustalla olevat sisätilat.

Mermaid.js on aidosti nerokas. Kirjoita yksinkertaista tekstiä, hanki kauniita kaavioita. Älä enää hapuile Vision tai Draw.io:n kanssa, menetä lähdetiedostoja tai säilytä erillisiä kuvatiedostoja. Kaikki elää markdownissa, versioiden hallinnassa koodisi rinnalla.

Mutta halusin tietää miten Se oikeasti toimii konepellin alla.

graph LR
    A[Text] --> B[Magic?]
    B --> C[Beautiful Diagram]

Ja mikä tärkeintä, miten siihen voi kytkeä lisäominaisuuksia, kuten pan/zoom, teemanvaihto ja vientitoiminto, jonka rakensin tälle sivustolle (nyt saatavilla nimellä @mostlylucid/mermaid-enhancements)?

Merenneidon lähdekoodin penkomisen ja todellisten laajennusten rakentamisen jälkeen tässä kaikki, mitä opin siitä, miten merenneito toimii sisäisesti ja miten sitä voidaan laajentaa kunnolla.

Mikä Merenneito Js on?

Merenneito muuttaa tekstin määritelmät kaavioiksi.

Vanha tapa:

  1. Avaa kaaviotyökalu
  2. Luo kaavio
  3. Vie PNG:nä
  4. Sisällytä dokumentteihin
  5. Tarvitsetko päivityksen? Etsi lähdetiedosto, muokkaa, vie uudelleen, korvaa kuva...

Merenneidon tapa:

  1. Kirjoita kaavio tekstiksi
  2. Tehty

Se on vain teksti, joka toimii markdownissa.

Kaaviotyypit

Merenneito tukee naurettavaa määrää kaaviotyyppejä:

graph LR
    A[Flowcharts] --> B[Sequence Diagrams]
    B --> C[Class Diagrams]
    C --> D[State Diagrams]
    D --> E[ER Diagrams]
    E --> F[Gantt Charts]
    F --> G[Pie Charts]
    G --> H[Git Graphs]
    H --> I[User Journeys]
    I --> J[And many more...]

Katso Merenneidon dokumentit koko listaa varten.

Miten merenneito oikeasti toimii: putkisto

Näin käy, kun merenneito tekee kaavion:

graph TD
    A[Text Definition] --> B[Lexer/Tokenizer]
    B --> C[Parser]
    C --> D[AST Built]
    D --> E[Diagram Type Detected]
    E --> F[Type-Specific Renderer]
    F --> G[SVG Generated]
    G --> H[Inserted into DOM]
    H --> I[Your Enhancements Run]

    style A stroke:#059669,stroke-width:3px,color:#10b981
    style D stroke:#2563eb,stroke-width:3px,color:#3b82f6
    style G stroke:#7c3aed,stroke-width:3px,color:#8b5cf6
    style I stroke:#d97706,stroke-width:3px,color:#f59e0b

Murretaan jokainen askel.

Vaihe 1: Tekstin määritelmä

Kaikki alkaa tekstistä. Merenneidon DSL:ään (aluekohtainen kieli) kirjoitetaan kaavioita:

// Flowchart
const diagram = `
graph TD
    A[Start] --> B{Is it working?}
    B -->|Yes| C[Great!]
    B -->|No| D[Debug time]
`;

Vaihe 2: Lexical Analysis

Lexer jakaa tekstin poletteihin. Esimerkiksi tämä rivi:

A[Start] --> B{Decision}

Tulee kuponkeja, kuten:

[
    { type: 'NODE_ID', value: 'A' },
    { type: 'NODE_TEXT', value: 'Start' },
    { type: 'ARROW', value: '-->' },
    { type: 'NODE_ID', value: 'B' },
    { type: 'NODE_TEXT', value: 'Decision' },
    { type: 'NODE_SHAPE', value: 'diamond' }  // from { }
]

Vaihe 3: Jäsennys ja AST-sukupolvi

Jäsentäjä kuluttaa kuponkeja ja rakentaa Abstract Syntax Treen (AST):

// Simplified AST structure
{
    type: 'flowchart',
    direction: 'TD',
    nodes: [
        { id: 'A', text: 'Start', shape: 'rect' },
        { id: 'B', text: 'Decision', shape: 'diamond' }
    ],
    edges: [
        { from: 'A', to: 'B', type: 'arrow' }
    ]
}

Merenneito käyttää eri jäsennyksiä jokaiselle kaaviotyypille. Ne syntyvät usein kielioppitiedostoista, joissa käytetään Jison (kuten Yacc/Bison for JavaScript).

Vaihe 4: Kaavioiden osoittaminen

Merenneito havaitsee kaaviotyypin ensimmäiseltä riviltä:

// Simplified detection logic
if (text.match(/^\s*graph/)) return 'flowchart';
if (text.match(/^\s*sequenceDiagram/)) return 'sequence';
if (text.match(/^\s*classDiagram/)) return 'class';
// ... etc

Vaihe 5: Tyyppikohtainen renderointi

Jokaisessa kaaviotyypissä on oma renderaattorinsa. Renderaattori ottaa AST-järjestelmän ja tuottaa SVG-elementtejä.

Merenneito käyttää vuokaavioissa Dagre Muille se käyttää mukautettuja algoritmeja tai kirjastoja, kuten Cytoscapea.

// Simplified flowchart renderer
export const draw = function (text, id, version, diagObj) {
    const graph = diagObj.db;  // The AST
    const svg = d3.select(`#${id}`);

    // Render nodes
    graph.getVertices().forEach(vertex => {
        drawNode(svg, vertex);
    });

    // Render edges
    graph.getEdges().forEach(edge => {
        drawEdge(svg, edge);
    });

    // Apply layout algorithm
    dagre.layout(graph);
};

Vaihe 6: SVG-sukupolvi

Renderaattori tuottaa SVG-leiman:

<svg xmlns="http://www.w3.org/2000/svg">
    <g class="node">
        <rect x="0" y="0" width="100" height="50"/>
        <text x="50" y="25">Start</text>
    </g>
    <g class="edge">
        <path d="M 100 25 L 200 25" stroke="#333"/>
    </g>
</svg>

Vaihe 7: DOM:n sijoittaminen

Merenneito löytää kaiken .mermaid elementit ja korvaa ne renderoidulla SVG:llä:

// From mermaid.ts
export const init = async function (config, nodes) {
    const nodesToProcess = nodes || document.querySelectorAll('.mermaid');

    for (const node of nodesToProcess) {
        const id = `mermaid-${Date.now()}-${Math.random()}`;
        const txt = node.textContent;

        const { svg } = await render(id, txt);
        node.innerHTML = svg;
    }
};

Vaihe 8: Jälkiprosessointi (missä tulet mukaan)

Kun merenneito on laittanut SVG:n, voit parantaa sitä. Tässä kaikki lisälaitteeni koukuttavat:

  • Pan/zoom-toiminto
  • Ohjauspainikkeet
  • Vientivalmiudet
  • Aiheen vaihtaminen

Lisää aiheesta alla.

Merenneiton laajennus: Laajennuspisteet

Nyt kun tiedämme, miten merenneito toimii, tutkitaan, miten sitä laajennetaan.

1. Asetukset

Peruslaajennus on konfiguraatio:

import mermaid from 'mermaid';

mermaid.initialize({
    startOnLoad: true,
    theme: 'dark',
    securityLevel: 'loose',
    flowchart: {
        curve: 'basis',
        padding: 15
    }
});

2. Teemamuokkaus

Käsittelin tätä laajasti Teemojen vaihtaminen merenneidolle, mutta tässä on tärkein toteutus:

Teeman vaihtamisen ongelma

Merenneito pitää alustaa teemalla, etkä voi muuttaa sitä sen jälkeen. Mutta jos haluat renderoida kaavioita uudella teemalla, tarvitset alkuperäisen kaavion lähteen, joka merenneito ei varastoidu DOM:iin.

Ratkaisu

Tallenna alkuperäinen sisältö ennen renderointia, palauta ja renderöi teemoja vaihdettaessa:

// From my theme-switcher implementation
const originalData = new Map();

// Save original content before first render
const saveOriginalData = async () => {
    const elements = document.querySelectorAll('.mermaid');
    elements.forEach(element => {
        const id = element.id || `mermaid-${Date.now()}`;
        element.id = id;

        // Store the original diagram source
        if (!originalData.has(id)) {
            originalData.set(id, element.textContent?.trim());
        }
    });
};

// When theme changes, restore and re-render
const loadMermaid = async (theme) => {
    mermaid.initialize({
        startOnLoad: false,
        theme: theme
    });

    const elements = document.querySelectorAll('.mermaid');
    for (const element of elements) {
        const source = originalData.get(element.id);
        if (source) {
            element.innerHTML = '';  // Clear
            element.removeAttribute('data-processed');

            const { svg } = await mermaid.render(
                `mermaid-svg-${element.id}`,
                source
            );
            element.innerHTML = svg;
        }
    }
};

Useita teemantunnistusmenetelmiä (kohteet käsittelevät teemoja eri tavalla):

function detectTheme() {
    // Check various sources
    if (typeof window.__themeState !== 'undefined') {
        return window.__themeState;
    }
    if (localStorage.theme) {
        return localStorage.theme;
    }
    if (document.documentElement.classList.contains('dark')) {
        return 'dark';
    }
    if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
        return 'dark';
    }
    return 'light';
}

Katso koko teemavaihtajan koodi Tarkempia tietoja.

3. Uusinnan jälkeiset parannukset

Täällä todellinen taikuus tapahtuu. Merenneidon renderoinnin jälkeen voit lisätä vuorovaikutteisia ominaisuuksia.

Käsittelin tätä laajasti Merenneitokaavioiden parantaminen Pan/Zoomin ja Exportin avullaKorostan tässä avaintekniikoita.

Kääritään kaavioita

Luo kääresäiliö hallintalaitteille:

function wrapDiagram(element) {
    if (element.closest('.mermaid-wrapper')) {
        return element.closest('.mermaid-wrapper');
    }

    const wrapper = document.createElement('div');
    wrapper.className = 'mermaid-wrapper';
    wrapper.id = `wrapper-${element.id}`;

    element.parentNode.insertBefore(wrapper, element);
    wrapper.appendChild(element);

    return wrapper;
}

Lisätään Pan/Zoom

Käyttäminen svg-pan-zoom:

import svgPanZoom from 'svg-pan-zoom';

const panZoomInstances = new Map();

function initPanZoom(svgElement, diagramId) {
    // Clean up existing instance
    if (panZoomInstances.has(diagramId)) {
        panZoomInstances.get(diagramId).destroy();
        panZoomInstances.delete(diagramId);
    }

    const instance = svgPanZoom(svgElement, {
        zoomEnabled: true,
        controlIconsEnabled: false,
        fit: true,
        center: true,
        minZoom: 0.1,
        maxZoom: 10
    });

    panZoomInstances.set(diagramId, instance);
    return instance;
}

Ohjauspainikkeet

Luo kelluva ohjauspaneeli:

function createControlButtons(container, diagramId) {
    const controlsDiv = document.createElement('div');
    controlsDiv.className = 'mermaid-controls';

    const buttons = [
        { icon: 'bx-fullscreen', title: 'Fullscreen', action: 'fullscreen' },
        { icon: 'bx-zoom-in', title: 'Zoom In', action: 'zoomIn' },
        { icon: 'bx-zoom-out', title: 'Zoom Out', action: 'zoomOut' },
        { icon: 'bx-reset', title: 'Reset', action: 'reset' },
        { icon: 'bx-move', title: 'Pan', action: 'pan' },
        { icon: 'bx-image', title: 'Export PNG', action: 'exportPng' },
        { icon: 'bx-code-alt', title: 'Export SVG', action: 'exportSvg' }
    ];

    buttons.forEach(btn => {
        const button = document.createElement('button');
        button.className = `mermaid-control-btn bx ${btn.icon}`;
        button.setAttribute('data-action', btn.action);
        button.setAttribute('data-diagram-id', diagramId);
        controlsDiv.appendChild(button);
    });

    container.appendChild(controlsDiv);
}

Tapahtumavaltuuskunta (Performance!)

Älä liitä kuuntelijoita jokaiseen painikkeeseen. Käytä tapahtuman delegointia:

document.addEventListener('click', (e) => {
    const target = e.target;
    if (!target.classList.contains('mermaid-control-btn')) return;

    const action = target.getAttribute('data-action');
    const diagramId = target.getAttribute('data-diagram-id');
    const panZoom = panZoomInstances.get(diagramId);

    switch (action) {
        case 'zoomIn': panZoom?.zoomIn(); break;
        case 'zoomOut': panZoom?.zoomOut(); break;
        case 'reset': panZoom?.reset(); break;
        // ... etc
    }
});

Vie Toiminnallisuus

Haaste: SVG-elementeissä on dynaamista kokoa, pan/zoom-muunnoksia ja perittyjä tyylejä. Viedäksesi oikein, sinun täytyy:

  1. Kloonaaa SVG:tä
  2. Säilöntämitat
  3. Poista muunnelmat
  4. Muunna PNG:ksi tai SVG:ksi

Käyttäminen html-to-image:

import { toPng, toSvg } from 'html-to-image';

async function exportDiagram(container, format, diagramId) {
    const svgElement = container.querySelector('svg');
    if (!svgElement) return;

    // Clone to avoid modifying original
    const clonedSvg = svgElement.cloneNode(true);

    // Get or calculate viewBox
    let viewBox = clonedSvg.getAttribute('viewBox');
    if (!viewBox) {
        const bbox = svgElement.getBBox();
        viewBox = `${bbox.x} ${bbox.y} ${bbox.width} ${bbox.height}`;
        clonedSvg.setAttribute('viewBox', viewBox);
    }

    // Set explicit dimensions
    const [, , width, height] = viewBox.split(' ').map(Number);
    clonedSvg.setAttribute('width', width);
    clonedSvg.setAttribute('height', height);

    // Remove pan-zoom transforms
    clonedSvg.removeAttribute('style');

    // Create off-screen container
    const temp = document.createElement('div');
    temp.style.position = 'absolute';
    temp.style.left = '-9999px';
    temp.appendChild(clonedSvg);
    document.body.appendChild(temp);

    // Export
    const dataUrl = format === 'png'
        ? await toPng(clonedSvg, { pixelRatio: 2 })
        : await toSvg(clonedSvg);

    downloadFile(dataUrl, `diagram-${Date.now()}.${format}`);

    // Cleanup
    document.body.removeChild(temp);
}

Kriittiset tiedot:

  • Säilytä näkymälaatikko - Kaappaa koko kaavion, ei vain näkyvän osan
  • Näytön ulkopuolinen renderointi - Vältä vaikuttamasta näytettyyn kaavioon
  • Pikselisuhde: 2 - Korkea-DPI PNG:n terävälle viennille

Katso koko vientikoodi Tarkempia tietoja.

Täydellinen tehostamispaketti

Pakkasin kaikki nämä parannukset @mostlylucid/mermaid-enhancements. Katso Merenneidon parannusten julkaiseminen npm-pakettina täydelliset tiedot.

Näin kaikki sopii yhteen:

graph TB
    A[User Initializes] --> B[init Function]
    B --> C[initMermaid]
    B --> D[enhanceMermaidDiagrams]

    C --> E[Theme Detection]
    C --> F[Event Listeners]
    C --> G[Mermaid Rendering]

    E --> E1[Global State]
    E --> E2[LocalStorage]
    E --> E3[DOM Class]
    E --> E4[OS Preference]

    F --> F1[Custom Events]
    F --> F2[Media Query]

    G --> H[Apply Enhancements]
    D --> H

    H --> I[Wrap Diagrams]
    H --> J[Init Pan/Zoom]
    H --> K[Add Controls]

    I --> L[Interactive Diagram]
    J --> L
    K --> L

    L --> M[User Interactions]
    M --> M1[Zoom In/Out]
    M --> M2[Pan]
    M --> M3[Fullscreen]
    M --> M4[Export PNG/SVG]

    style A stroke:#059669,stroke-width:3px,color:#10b981
    style L stroke:#2563eb,stroke-width:3px,color:#3b82f6
    style M stroke:#7c3aed,stroke-width:3px,color:#8b5cf6

Käyttö

npm install @mostlylucid/mermaid-enhancements
import { init } from '@mostlylucid/mermaid-enhancements';
import '@mostlylucid/mermaid-enhancements/styles.css';

await init();

Merenneitokaavioissasi on nyt:

  • Vuorovaikutteinen pan/zoom
  • â € € € € koko näytön valolaatikko
  • PNG/SVG-vienti
  • â € € € € € € € € € € € € €
  • â € â € € € + vastuullinen suunnittelu

Integrointi HTMX:n kanssa

Kuten peitin Lisää merenneito.js kanssa htmx, Merenneito pitää käynnistää uudelleen HTMX-sisältöjen swapien jälkeen:

// On page load
document.addEventListener('DOMContentLoaded', function () {
    mermaid.initialize({ startOnLoad: true });
});

// After HTMX swaps content
document.body.addEventListener('htmx:afterSwap', function(evt) {
    mermaid.run();
});

Parannuspaketti:

import { init, enhanceMermaidDiagrams } from '@mostlylucid/mermaid-enhancements';

// Initial load
await init();

// After HTMX swap
document.body.addEventListener('htmx:afterSwap', async function() {
    await init();  // Re-init Mermaid with current theme
    enhanceMermaidDiagrams();  // Re-apply enhancements
});

Parhaita toimintatapoja, joita opin

Rakennettuaan nämä jutut ja vianjäljiteltyään oudot laatikot, tämä toimii näin:

1. Säilytä aina alkuperäinen sisältö

Merenneito ei säilytä alkuperäistä kaaviolähdettä renderoinnin jälkeen. Sinun on tallennettava se itse:

const originalData = new Map();

// Before first render
element.setAttribute('data-original-code', element.textContent);
originalData.set(element.id, element.textContent);

// When re-rendering
element.innerHTML = originalData.get(element.id);

2. Siivoustoimikunnat

Muistivuodot ovat todellisia. Tuhoa tapaukset ennen uusien luomista:

if (panZoomInstances.has(id)) {
    try {
        panZoomInstances.get(id).destroy();
    } catch (e) {
        console.warn('Failed to destroy:', e);
    }
    panZoomInstances.delete(id);
}

3. Käytä tapahtumavaltuuskuntaa

Älä liitä kuuntelijoita yksittäisiin painikkeisiin:

// ❌ Don't do this
buttons.forEach(btn => {
    btn.addEventListener('click', handler);
});

// ✅ Do this
document.addEventListener('click', (e) => {
    if (e.target.matches('.mermaid-control-btn')) {
        handleClick(e.target);
    }
});

4. Kahva Cloudflare Rocket Loader

Rocket Loader viivästyttää JavaScript-toteutusta. Odota riippuvuuksia:

function waitForDependencies(maxAttempts = 50) {
    return new Promise((resolve) => {
        let attempts = 0;

        const check = () => {
            if (window.mermaid && window.htmx && window.Alpine) {
                resolve();
            } else if (attempts >= maxAttempts) {
                resolve();  // Give up
            } else {
                attempts++;
                setTimeout(check, Math.min(50 * Math.pow(1.2, attempts), 500));
            }
        };

        check();
    });
}

Ja jätä pääkirjoituksesi pois Rocket Loaderista:

<script src="main.js" data-cfasync="false"></script>

5. Ajoitus on kaikki kaikessa

Käyttö requestAnimationFrame paremman ajoituksen kuin mielivaltaisen setTimeout:

// After Mermaid renders
await mermaid.run();

// Wait for paint before enhancing
await new Promise(resolve => {
    requestAnimationFrame(() => {
        requestAnimationFrame(() => {
            enhanceMermaidDiagrams();
            resolve();
        });
    });
});

6. Puolustava SVG-käsittely

SVG:t voivat olla outoja.

const svgElement = container.querySelector('svg');
if (!svgElement) {
    console.warn('No SVG found');
    return;
}

// Clone before modifying
const cloned = svgElement.cloneNode(true);

// Ensure viewBox exists
let viewBox = cloned.getAttribute('viewBox');
if (!viewBox) {
    const bbox = svgElement.getBBox();
    viewBox = `${bbox.x} ${bbox.y} ${bbox.width} ${bbox.height}`;
}

Vianetsintävinkit

Lohduta tuotosta

Oikealla alustamisella näet:

Saving original data
Loading mermaid with theme: dark
Mermaid initialized
Enhanced 3 diagrams

Testataan tarkistuslistaa

Parannusten toteuttamisen jälkeen:

  • Kaaviot renderöivät sivulatauksen
  • Pan/zoom-ohjainten työ
  • Kokoruutu avautuu/ sulkeutuu (X, klikkaa ulkopuolelta, ESC)
  • PNG-viennin kokonaiskaavio
  • SVG:n vienti säilyttää vektorit
  • Toimii HTMX-sisältöjen vaihdon jälkeen
  • Aiheen vaihtaminen renderoi oikein
  • Liikkuva reagoiva
  • Tumman tilan muotoilu
  • Näppäimistön saavutettavuus

Yhteiset kysymykset

Kaavio ei renderöi:

  • Tarkista selainkonsoli virheiden varalta
  • Varmista, että merenneito on lastattu (window.mermaid)
  • Tarkista kaavion syntaksi

Pan/zoom ei toimi:

  • Varmenna svg-pan-zoom alustamalla
  • Tarkista ristiriitainen CSS (pointer-events: none)
  • Tarkasta esimerkiksi kartta

Vie vain nurkkaan:

  • Puuttuu näkymälaatikon säilyttäminen
  • Muunnos ei poistettu
  • Tarkista kloonauslogiikka

Teema ei muutu:

  • Alkuperäisiä tietoja ei tallennettu
  • data-processed ei nollausta
  • Tapahtuman kuuntelijat eivät ilmoittautuneet

Suorituskykyä koskevia huomioita

Laiska alustaminen

Älä lataa parannuksia ennen kuin se on tarpeen:

let enhancementsLoaded = false;

async function loadEnhancements() {
    if (enhancementsLoaded) return;

    const { enhanceMermaidDiagrams } = await import('./enhancements.js');
    enhanceMermaidDiagrams();
    enhancementsLoaded = true;
}

// Load on first interaction
document.addEventListener('click', (e) => {
    if (e.target.closest('.mermaid')) {
        loadEnhancements();
    }
}, { once: true });

Intersection Observer

Renderointikaaviot vain näkyvillä:

const observer = new IntersectionObserver((entries) => {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            renderDiagram(entry.target);
            observer.unobserve(entry.target);
        }
    });
}, { rootMargin: '100px' });

document.querySelectorAll('.mermaid').forEach(el => {
    observer.observe(el);
});

Debuuce Renderers

Muutoksesta tai teemamuutoksesta:

let timeout;
window.addEventListener('resize', () => {
    clearTimeout(timeout);
    timeout = setTimeout(() => {
        panZoomInstances.forEach(instance => {
            instance.resize();
            instance.fit();
        });
    }, 250);
});

Johtopäätöksenä

Merenneito.js on fantastinen ulos laatikosta, mutta sen sisäisen toiminnan ymmärtäminen antaa mahdollisuuden rakentaa todella hienoja parannuksia. Tärkeimmät oivallukset:

  1. Renderointiputki: Text → Lexer → Parser → AST → Renderer → SVG
  2. Laajennuspisteet: Konfigi, teemat, renderoinnin jälkeiset parannukset
  3. Tallenna alkuperäiset tiedotMerenneito ei tee sitä puolestasi.
  4. Puhdista resursseja: Muistivuodot ovat todellisia
  5. Tapahtumavaltuuskunta: Parempi suorituskyky
  6. Useita teemojen lähteitä: Sivustot käsittelevät teemoja eri tavalla
  7. Aikataulut: Käyttö requestAnimationFrame

Kaikki käyttämäni tekniikat käytetään tämän sivuston tuotannossa ja paketoidaan @mostlylucid/mermaid-enhancements. Täydellinen lähde on saatavilla osoitteessa Lähinnä lusid-verkkoa/lähinlylusid-merineitoa.

Aiheeseen liittyviä viestejä

Resurssit

Kokeile yllä olevien kaavioiden ohjaimia!

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.