Skip to content

Commit

Permalink
feat: Warp Background Component, forked from Magic UI.
Browse files Browse the repository at this point in the history
  • Loading branch information
Whbbit1999 committed Jan 4, 2025
1 parent f468f2a commit 49d8e38
Show file tree
Hide file tree
Showing 4 changed files with 216 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<template>
<div class="w-full">
<WarpBackground>
<UiCard class="mx-auto w-72">
<UiCardContent class="flex flex-col gap-2 p-4">
<UiCardTitle>Congratulations on Your Promotion!</UiCardTitle>
<UiCardDescription>
Your hard work and dedication have paid off. We&apos;re thrilled to see you take this
next step in your career. Keep up the fantastic work!
</UiCardDescription>
</UiCardContent>
</UiCard>
</WarpBackground>
</div>
</template>

<script lang="ts" setup></script>

<style></style>
41 changes: 41 additions & 0 deletions components/content/inspira/ui/warp-background/Beam.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<template>
<div
v-motion
:style="{
'--x': `${props.x}`,
'--width': `${props.width}`,
'--aspect-ratio': `${ar}`,
'--background': `linear-gradient(hsl(${hue} 80% 60%), transparent)`,
}"
class="absolute left-[var(--x)] top-0 translate-y-[100cqmax] [aspect-ratio:1/var(--aspect-ratio)] [background:var(--background)] [width:var(--width)]"
:initial="{
x: -50,
y: 200,
}"
:enter="{
x: -50,
y: -100,
transition: {
duration: props.duration,
delay: props.delay,
repeat: Infinity,
ease: 'linear',
},
}"
></div>
</template>

<script lang="ts" setup>
interface Props {
width: string | number;
x: string | number;
delay: number;
duration: number;
}
const props = defineProps<Props>();
const hue = computed(() => Math.floor(Math.random() * 360));
const ar = computed(() => Math.floor(Math.random() * 10) + 1);
</script>

<style></style>
117 changes: 117 additions & 0 deletions components/content/inspira/ui/warp-background/WarpBackground.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
<template>
<div :class="cn('relative rounded border p-2 md:p-20', props.class)">
<div
:style="{
'--perspective': `${props.perspective}px`,
'--grid-color': props.gridColor,
'--beam-size': `${props.beamSize}%`,
}"
class="pointer-events-none absolute left-0 top-0 size-full overflow-hidden [clip-path:inset(0)] [container-type:size] [perspective:var(--perspective)] [transform-style:preserve-3d]"
>
<!-- TOP -->
<div
class="absolute [background-size:var(--beam-size)_var(--beam-size)] [background:linear-gradient(var(--grid-color)_0_1px,_transparent_1px_var(--beam-size))_50%_-0.5px_/var(--beam-size)_var(--beam-size),linear-gradient(90deg,_var(--grid-color)_0_1px,_transparent_1px_var(--beam-size))_50%_50%_/var(--beam-size)_var(--beam-size)] [container-type:inline-size] [height:100cqmax] [transform-origin:50%_0%] [transform-style:preserve-3d] [transform:rotateX(-90deg)] [width:100cqi]"
>
<Beam
v-for="(beam, index) in topBeams"
:key="`top-${index}`"
:width="`${props.beamSize}%`"
:x="`${beam.x * props.beamSize}%`"
:delay="beam.delay"
:duration="beamDuration"
/>
</div>
<!-- BOTTOM -->
<div
class="absolute top-full [background-size:var(--beam-size)_var(--beam-size)] [background:linear-gradient(var(--grid-color)_0_1px,_transparent_1px_var(--beam-size))_50%_-0.5px_/var(--beam-size)_var(--beam-size),linear-gradient(90deg,_var(--grid-color)_0_1px,_transparent_1px_var(--beam-size))_50%_50%_/var(--beam-size)_var(--beam-size)] [container-type:inline-size] [height:100cqmax] [transform-origin:50%_0%] [transform-style:preserve-3d] [transform:rotateX(-90deg)] [width:100cqi]"
>
<Beam
v-for="(beam, index) in bottomBeams"
:key="`bottom-${index}`"
:width="`${props.beamSize}%`"
:x="`${beam.x * props.beamSize}%`"
:delay="beam.delay"
:duration="beamDuration"
/>
</div>
<!-- LEFT -->
<div
class="absolute left-0 top-0 [background-size:var(--beam-size)_var(--beam-size)] [background:linear-gradient(var(--grid-color)_0_1px,_transparent_1px_var(--beam-size))_50%_-0.5px_/var(--beam-size)_var(--beam-size),linear-gradient(90deg,_var(--grid-color)_0_1px,_transparent_1px_var(--beam-size))_50%_50%_/var(--beam-size)_var(--beam-size)] [container-type:inline-size] [height:100cqmax] [transform-origin:0%_0%] [transform-style:preserve-3d] [transform:rotate(90deg)_rotateX(-90deg)] [width:100cqh]"
>
<Beam
v-for="(beam, index) in leftBeams"
:key="`left-${index}`"
:width="`${props.beamSize}%`"
:x="`${beam.x * props.beamSize}%`"
:delay="beam.delay"
:duration="beamDuration"
/>
</div>
<!-- RIGHT -->
<div
class="absolute right-0 top-0 [background-size:var(--beam-size)_var(--beam-size)] [background:linear-gradient(var(--grid-color)_0_1px,_transparent_1px_var(--beam-size))_50%_-0.5px_/var(--beam-size)_var(--beam-size),linear-gradient(90deg,_var(--grid-color)_0_1px,_transparent_1px_var(--beam-size))_50%_50%_/var(--beam-size)_var(--beam-size)] [container-type:inline-size] [height:100cqmax] [transform-origin:100%_0%] [transform-style:preserve-3d] [transform:rotate(-90deg)_rotateX(-90deg)] [width:100cqh]"
>
<Beam
v-for="(beam, index) in rightBeams"
:key="`right-${index}`"
:width="`${props.beamSize}%`"
:x="`${beam.x * props.beamSize}%`"
:delay="beam.delay"
:duration="beamDuration"
/>
</div>
</div>

<div class="relative">
<slot />
</div>
</div>
</template>

<script lang="ts" setup>
import { cn } from "@/lib/utils";
import Beam from "./Beam.vue";
interface Props {
perspective?: number;
beamsPerSide?: number;
beamSize?: number;
beamDelayMax?: number;
beamDelayMin?: number;
beamDuration?: number;
gridColor?: string;
class?: string;
}
const props = withDefaults(defineProps<Props>(), {
perspective: 100,
beamsPerSide: 3,
beamSize: 5,
beamDelayMax: 3,
beamDelayMin: 0,
beamDuration: 3,
gridColor: "hsl(var(--border))",
});
const beamDuration = computed(() => props.beamDuration * 1000);
const beamDelayMax = computed(() => props.beamDelayMax * 1000);
const beamDelayMin = computed(() => props.beamDelayMin * 1000);
function generateBeams() {
const beams = [];
for (let i = 0; i < props.beamsPerSide; i++) {
const x = Math.floor((i * Math.floor(100 / props.beamSize)) / props.beamsPerSide);
const delay = Math.random() * beamDelayMax.value - beamDelayMin.value + beamDelayMin.value;
beams.push({ x, delay });
}
return beams;
}
// generateBeams
const topBeams = generateBeams();
const bottomBeams = generateBeams();
const leftBeams = generateBeams();
const rightBeams = generateBeams();
</script>
39 changes: 39 additions & 0 deletions content/2.components/warp-background.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
title: Warp Background
description: A container component that applies a warp animation effect to its children
navBadges:
- value: New
type: lime
---

::ComponentLoader{label="Preview" componentName="WarpBackgroundDemo" type="examples" id="warp-background"}
::

## API

| Prop Name | Type | Default | Description |
| -------------- | -------- | ---------------------- | ------------------------------------- |
| `perspective` | `number` | `100` | The perspective of the warp animation |
| `beamsPerSide` | `number` | `3` | The number of beams per side |
| `beamSize` | `number` | `5` | The size of the beams |
| `beamDelayMax` | `number` | `3` | The maximum delay of the beams |
| `beamDelayMin` | `number` | `0` | The minimum delay of the beams |
| `beamDuration` | `number` | `3` | The duration of the beams |
| `gridColor` | `string` | `"hsl(var(--border))"` | The color of the grid lines |

## Component Code

You can copy and paste the following code to create these components:

::CodeGroup
::CodeViewer{filename="WarpBackground.vue" language="vue" componentName="WarpBackground" type="ui" id="warp-background"}
::

::CodeViewer{filename="Beam.vue" language="vue" componentName="Beam" type="ui" id="warp-background"}
::
::

## Credits

- Credits to [Whbbit1999](https://github.com/Whbbit1999) for this component.
- Inspired and ported from [Magic UI WarpBackground](https://magicui.design/docs/components/warp-background).

0 comments on commit 49d8e38

Please sign in to comment.