@rlawncks125/component-render-ani
| Install | |
|---|---|
npm install @rlawncks125/component-render-ani |
|
| Latest Version: | 0.1.13 |
| Vue: | 3 |
| Last Updated: | Jun 21, 2026 |
| Links: | npm |
Component Render Ani
Reusable Vue canvas animation component.
@rlawncks125/component-render-ani exposes RenderAniCanvas, a canvas-like
component that watches the #render slot and animates newly added Vue components
onto the screen.
Install
npm install @rlawncks125/component-render-ani
Nuxt Usage
Add the Nuxt module to nuxt.config.ts.
export default defineNuxtConfig({
modules: ["@rlawncks125/component-render-ani/nuxt"],
});
Nuxt can then auto-import the component:
<RenderAniCanvas />
The Nuxt module also registers the package stylesheet automatically.
Vue Usage
Register the plugin once in your Vue app entry.
import { createApp } from "vue";
import ComponentRenderAni from "@rlawncks125/component-render-ani";
import App from "./App.vue";
createApp(App).use(ComponentRenderAni).mount("#app");
After registration, Vue/Volar can discover the global component:
<RenderAniCanvas />
If you import the component directly instead of using app.use, import it like this:
import { RenderAniCanvas } from "@rlawncks125/component-render-ani";
Basic Example
Write ordinary Vue components or DOM directly in #render. RenderAniCanvas
observes its top-level VNodes, measures their real DOM, and animates newly added
nodes. Your component data and internal DOM structure stay entirely yours.
<script setup lang="ts">
import { ref } from "vue";
import { RenderAniCanvas } from "@rlawncks125/component-render-ani";
import MyCard from "./MyCard.vue";
const cards = ref([
{
id: "first-card",
title: "First rendered card",
description: "This component is rendered through the canvas animation.",
},
]);
const addCard = () => {
const nextNumber = cards.value.length + 1;
cards.value.push({
id: `card-${nextNumber}`,
title: `Rendered card ${nextNumber}`,
description: "Appending ordinary Vue content triggers the draw sequence.",
});
};
</script>
<template>
<RenderAniCanvas>
<template #controls="{ running, queue }">
<button type="button" :disabled="running" @click="addCard">
Add card
</button>
<span v-if="running || queue.length">
{{ running ? "Drawing" : "" }}
{{ queue.length ? `${queue.length} waiting` : "" }}
</span>
</template>
<template #render>
<MyCard
v-for="card in cards"
:key="card.id"
class="custom-card"
:description="card.description"
:title="card.title"
/>
</template>
</RenderAniCanvas>
</template>
Headless Styling
The package exports CSS at @rlawncks125/component-render-ani/style.css.
If your bundler does not automatically include CSS from the package entry, import it once in your app entry file.
import "@rlawncks125/component-render-ani/style.css";
RenderAniCanvas is mostly headless. It keeps only the classes needed for
measurement, scrolling, column span, and animation. Build the visible layout with
classes.wrap and classes.render. The internal canvas owns scrolling, so
you usually do not need to set overflow classes yourself.
<RenderAniCanvas
:classes="{
wrap: 'h-screen w-screen bg-white',
header: 'px-5 pt-5',
render: 'mx-auto grid w-[min(960px,calc(100vw-32px))] grid-cols-12 gap-4 py-8',
item: 'rounded-lg',
}"
/>
Cursor
Use the cursor prop to change the built-in cursor. Supported types are
pointer, dot, ring, image, and none.
<RenderAniCanvas
:cursor="{
type: 'ring',
size: 28,
class: 'text-blue-500',
}"
/>
Use an image cursor with imageSrc.
<RenderAniCanvas
:cursor="{
type: 'image',
imageSrc: '/cursor.png',
size: 36,
offsetX: -4,
offsetY: -4,
}"
/>
For a fully custom cursor, use the #cursor slot.
<RenderAniCanvas>
<template #cursor="{ clicking }">
<div
class="grid size-8 place-items-center rounded-full bg-black text-white"
:class="{ 'scale-90': clicking }"
>
AI
</div>
</template>
</RenderAniCanvas>
Animation Types
Use animation.type to choose how a component is drawn. Supported types are
draw, instant, spotlight, scan, and typewriter.
<RenderAniCanvas
:animation="{
type: 'spotlight',
speed: 1.4,
}"
/>
<RenderAniCanvas :animation="{ type: 'scan' }" />
<RenderAniCanvas :animation="{ type: 'typewriter' }" />
Internal Structure
RenderAniCanvas renders the user-provided #render components into this
internal structure:
wrap
├─ header
│ └─ #controls slot
└─ canvas frame
├─ canvas
│ └─ render
│ └─ item
│ └─ rendered component
└─ overlay
├─ selection
└─ cursor
The same names are used by the classes and styles props. In most cases,
customizing wrap and render is enough.
<RenderAniCanvas
:classes="{
wrap: 'h-screen w-screen bg-white',
header: 'px-5 pt-5',
render: 'mx-auto grid w-[min(960px,calc(100vw-32px))] grid-cols-12 gap-4 py-8',
item: 'rounded-lg',
}"
/>
wrap: root wrapper for the whole canvas component.header: wrapper around the#controlsslot.canvas: internal scroll container.render: render area and layout container.item: required render item section for span, measurement, ref, and animation.
Every internal element also includes a debug attribute so it is easy to inspect in DevTools:
[data-render-ani-el="wrap"]
[data-render-ani-el="header"]
[data-render-ani-el="canvas"]
[data-render-ani-el="render"]
[data-render-ani-el="item"]
[data-render-ani-el="overlay"]
[data-render-ani-el="selection"]
[data-render-ani-el="spotlight"]
[data-render-ani-el="scan"]
[data-render-ani-el="typewriter"]
[data-render-ani-el="cursor"]
[data-render-ani-el="measure"]
Rendered items also include internal data-render-ani-* attributes for debugging.
Props
<RenderAniCanvas
:animation="{ speed: 1.6 }"
:auto-run="true"
:classes="{
wrap: 'h-screen w-screen',
header: 'px-5 pt-5',
render: 'grid grid-cols-12 gap-4 p-6',
item: 'rounded-lg',
}"
:default-heights="{ MyCard: 240 }"
:fallback-height="180"
:styles="{
wrap: { minHeight: '100vh' },
header: { pointerEvents: 'auto' },
render: { paddingTop: '120px' },
}"
/>
animation: controls animation speed and behavior.autoRun: watches#renderslot changes and draws automatically. Default:true.defaultHeights: initial reserved height bycomponentName.fallbackHeight: reserved height when no default height is found. Default:180.rootAttrs: pass-through attrs for the root wrapper.canvasAttrs: pass-through attrs for the scroll canvas.renderAreaAttrs: pass-through attrs for the render container.renderItemAttrs: pass-through attrs for each rendered item.classes: Tailwind/class customization map forwrap,header,canvas,render, anditem.styles: style customization map forwrap,header,canvas,render, anditem.
Default utility classes are intentionally minimal and are merged with
tailwind-merge, so your custom classes win when they conflict with built-in
classes.
Animation
Use animation.speed to make every built-in animation faster or slower. The
default speed is 1. Higher values are faster.
<RenderAniCanvas :animation="{ speed: 2 }" />
Use animation.type to choose the animation behavior. Current options are
"draw" and "instant". The "draw" type is the default canvas drawing
animation. The "instant" type keeps the same render flow, but skips motion
durations.
<RenderAniCanvas :animation="{ type: 'instant' }" />
You can also override individual durations in milliseconds. These values are
still divided by speed.
<RenderAniCanvas
:animation="{
type: 'draw',
speed: 1.4,
durations: {
cursorMove: 260,
cursorClick: 80,
draw: 420,
mountDelay: 80,
reveal: 180,
scroll: 220,
},
}"
/>
Slots
#render
Put any Vue component or native DOM structure that should be drawn here. Each top-level VNode is one animation unit; everything below it keeps its original DOM hierarchy, attributes, props, and slots. Use stable Vue keys when nodes are added dynamically so append-only changes can be detected reliably.
<template #render>
<HeroSection key="hero" />
<section key="content" class="content">
<h2>Any DOM structure</h2>
<MyCard />
</section>
</template>
#controls
Optional. Use this slot for buttons, status labels, or custom controls.
<template #controls="{ running, queue, resetAll, syncSlottedComponents }">
<button type="button" :disabled="running" @click="resetAll">
Reset
</button>
<button type="button" :disabled="running" @click="syncSlottedComponents">
Sync
</button>
<span>{{ queue.length }} waiting</span>
</template>
Slot props:
interface RenderAniCanvasSlotProps {
addComponent: (definition: ComponentDefinition) => Promise<void>;
queue: DrawItem[];
resetAll: () => void;
runComponents: (definitions: ComponentDefinition[]) => Promise<void>;
runSlottedComponents: () => Promise<void>;
running: boolean;
slots: DrawSlot[];
syncSlottedComponents: () => Promise<void>;
}
Build And Publish
bun install
bun run build:lib
npm publish --access public
Related Packages
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 0.1.13 | ^3.5.0 | ||
| 0.1.12 | ^3.5.0 | ||
| 0.1.11 | ^3.5.0 | ||
| 0.1.9 | ^3.5.0 | ||
| 0.1.8 | ^3.5.0 | ||
| 0.1.7 | ^3.5.0 | ||
| 0.1.6 | ^3.5.0 | ||
| 0.1.5 | ^3.5.0 | ||
| 0.1.4 | ^3.5.0 | ||
| 0.1.3 | ^3.5.0 | ||
| 0.1.2 | ^3.5.0 | ||
| 0.1.1 | ^3.5.0 | ||
| 0.1.0 | ^3.5.0 |