こんにちは世界

Circular Text

3D Rotating circular text.

Demo

Null — Null — Null —
<script lang="ts">
  import * as Null from "#lib/index.js";
</script>

<Null.CircularText text={"Null — Null — Null —"} size={32} radius={26} />

CircularText.svelte

<script lang="ts">
    import type { Snippet } from "svelte";

    let {
        text = "",
        size = 160,
        radius = 64,
        duration = 12,
        reverse = false,
        pauseOnHover = true,
        children,
    }: {
        text?: string;
        size?: number;
        radius?: number;
        duration?: number;
        reverse?: boolean;
        pauseOnHover?: boolean;
        children?: Snippet;
    } = $props();

    const id = $props.id();
    const pathId = `circle-${id}`;
</script>

<div
    class="circular-text"
    class:reverse
    class:pause-on-hover={pauseOnHover}
    style={`--duration: ${Math.max(0.1, duration)}s; width: ${size}px; height: ${size}px;`}
>
    <svg viewBox={`0 0 ${size} ${size}`} role="img" aria-label={text}>
        <defs>
            <path
                id={pathId}
                d={`M ${size / 2},${size / 2} m -${radius},0 a ${radius},${radius} 0 1,1 ${radius * 2},0 a ${radius},${radius} 0 1,1 -${radius * 2},0`}
            />
        </defs>

        <text>
            <textPath href={`#${pathId}`} startOffset="0%">
                {text}
            </textPath>
        </text>
    </svg>

    {#if children}
        <div class="center">
            {@render children()}
        </div>
    {/if}
</div>

<style>
    .circular-text {
        position: relative;
        display: grid;
        place-items: center;
        color: currentColor;
    }

    svg {
        width: 100%;
        height: 100%;
        overflow: visible;
        animation: spin var(--duration) linear infinite;
    }

    text {
        fill: currentColor;
        font-size: 10px;
        font-weight: 600;
        letter-spacing: 2px;
        text-transform: uppercase;
    }

    .reverse svg {
        animation-direction: reverse;
    }

    .pause-on-hover:hover svg {
        animation-play-state: paused;
    }

    .center {
        position: absolute;
        inset: 25%;
        display: grid;
        place-items: center;
        text-align: center;
    }

    @keyframes spin {
        to {
            transform: rotate(360deg);
        }
    }

    @media (prefers-reduced-motion: reduce) {
        svg {
            animation-play-state: paused;
        }
    }
</style>