Vue Reel Player
Vue 3 के लिए फ़ुलस्क्रीन, Instagram/TikTok जैसा वर्टिकल मीडिया प्लेयर, जो @reelkit/vue-reel-player पर बना है।
फ़ीचर
इंस्टॉलेशन
npm install @reelkit/vue-reel-player @reelkit/vue lucide-vue-nextstylesheet को अपने ऐप के entry (या किसी भी कंपोनेंट) में एक बार import करें:
import '@reelkit/vue-reel-player/styles.css';आइकन
डिफ़ॉल्ट कंट्रोल आइकन (बंद, साउंड, नेविगेशन तीर) के लिए lucide-vue-next इस्तेमाल करते हैं। अगर आप कोई और आइकन लाइब्रेरी पसंद करते हैं, तो अपने देने के लिए #controls और #navigation scoped slots इस्तेमाल करें।
Vue 3.3 या नया
ओवरले जेनेरिक कंपोनेंट हैं, जिन्हें Vue 3.3 से सपोर्ट करता है। पुराने Vue पर runtime उनके props और events छोड़ देता है, इसलिए यह पैकेज peer dependency के रूप में vue 3.3 या नया माँगता है।
क्विक स्टार्ट
थंबनेल का grid रेंडर करें और क्लिक किए index पर overlay खोलें। v-model:is-open bind करने का मतलब है कि यूज़र बटन, जेस्चर या Escape से प्लेयर बंद करे, तो parent का ref भी साथ में बदल जाता है।
<script setup lang="ts">
import { ref } from 'vue';
import { ReelPlayerOverlay, type ContentItem } from '@reelkit/vue-reel-player';
import '@reelkit/vue-reel-player/styles.css';
const content: ContentItem[] = [
{
id: '1',
media: [{
id: 'v1',
type: 'video',
src: '/cdn/samples/videos/video-01.mp4',
poster: '/cdn/samples/videos/video-poster-01.jpg',
aspectRatio: 16 / 9,
}],
author: { name: 'Alex Johnson', avatar: '/cdn/samples/avatars/avatar-01.jpg' },
likes: 1234,
description: 'Amazing sunset vibes',
},
{
id: '2',
media: [{
id: 'img1',
type: 'image',
src: '/cdn/samples/images/image-01.jpg',
aspectRatio: 2 / 3,
}],
author: { name: 'Sarah Miller', avatar: '/cdn/samples/avatars/avatar-02.jpg' },
likes: 5678,
description: 'Nature at its finest',
},
{
id: '3',
media: [{
id: 'v2',
type: 'video',
src: '/cdn/samples/videos/video-02.mp4',
poster: '/cdn/samples/videos/video-poster-02.jpg',
aspectRatio: 16 / 9,
}],
author: { name: 'Mike Chen', avatar: '/cdn/samples/avatars/avatar-03.jpg' },
likes: 3456,
description: 'Adventure awaits',
},
];
const isOpen = ref(false);
const startIndex = ref(0);
function openAt(i: number) {
startIndex.value = i;
isOpen.value = true;
}
</script>
<template>
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:6px">
<button
v-for="(item, i) in content"
:key="item.id"
@click="openAt(i)"
style="aspect-ratio:9/16;cursor:pointer;overflow:hidden;padding:0;border:0"
>
<img
:src="item.media[0].poster || item.media[0].src"
style="width:100%;height:100%;object-fit:cover"
/>
</button>
</div>
<ReelPlayerOverlay
v-model:is-open="isOpen"
:content="content"
:initial-index="startIndex"
/>
</template>Scoped slots
आठ scoped slots से आप प्लेयर UI का कोई भी हिस्सा बदल सकते हैं। हर एक को strongly-typed scope object मिलता है। जो slots आप नहीं देते, वे डिफ़ॉल्ट पर लौट जाते हैं।
| Slot | Scope | विवरण |
|---|---|---|
| #controls | { item, soundState, activeIndex, content, onClose } | कस्टम global कंट्रोल बार (बंद, साउंड, शेयर वगैरह) |
| #error | { item, activeIndex, innerActiveIndex } | कस्टम एरर इंडिकेटर (डिफ़ॉल्ट आइकन की जगह) |
| #loading | { item, activeIndex, innerActiveIndex } | कस्टम लोडिंग इंडिकेटर (डिफ़ॉल्ट wave loader की जगह) |
| #navigation | { item, activeIndex, count, onPrev, onNext } | कस्टम prev/next नेविगेशन तीर (डेस्कटॉप) |
| #nestedNavigation | { media, activeIndex, count, onPrev, onNext } | अंदरूनी हॉरिज़ॉन्टल स्लाइडर के कस्टम तीर |
| #nestedSlide | { item, media, index, size, isActive, isInnerActive, slideKey, defaultContent, onReady, onWaiting, onError } | अंदरूनी हॉरिज़ॉन्टल स्लाइडर के अंदर कस्टम स्लाइड कंटेंट |
| #slide | { item, index, size, isActive, slideKey, defaultContent, onReady, onWaiting, onError } | पूरी तरह कस्टम स्लाइड कंटेंट (न देने पर डिफ़ॉल्ट पर लौटता है) |
| #slideOverlay | { item, index, isActive } | हर स्लाइड का overlay (लेखक की जानकारी, लाइक, विवरण वगैरह) |
| #timeline | { item, activeIndex, timelineState, defaultContent } | कस्टम प्लेबैक टाइमलाइन बार। सिर्फ़ तब चलता है जब बिल्ट-इन नियम (timeline मोड + न्यूनतम अवधि) डिफ़ॉल्ट बार रेंडर करते; वही auto/always/never logic दोबारा इस्तेमाल करता है। बिल्ट-इन <TimelineBar /> को लपेटने के लिए defaultContent() इस्तेमाल करें। |
<script setup lang="ts">
import { ref } from 'vue';
import {
ReelPlayerOverlay,
CloseButton,
SoundButton,
type ContentItem,
type SlideOverlaySlotScope,
type ControlsSlotScope,
} from '@reelkit/vue-reel-player';
const isOpen = ref(false);
const content = ref<ContentItem[]>([/* ... */]);
</script>
<template>
<ReelPlayerOverlay v-model:is-open="isOpen" :content="content">
<!-- Custom per-slide overlay: branded caption -->
<template #slideOverlay="{ item, isActive }: SlideOverlaySlotScope">
<div v-if="isActive" style="position:absolute;bottom:80px;left:16px;color:#fff">
<div style="display:flex;align-items:center;gap:8px">
<img :src="item.author.avatar" style="width:40px;height:40px;border-radius:50%" />
<span style="font-weight:600">{{ item.author.name }}</span>
</div>
<p style="margin-top:8px">{{ item.description }}</p>
</div>
</template>
<!-- Custom global controls -->
<template #controls="{ onClose }: ControlsSlotScope">
<div style="position:absolute;top:16px;right:16px;display:flex;gap:8px">
<SoundButton />
<CloseButton :on-click="onClose" />
</div>
</template>
<!-- Custom playback timeline -->
<template #timeline="{ timelineState }: TimelineSlotScope">
<CustomTimelineBar :state="timelineState" />
</template>
</ReelPlayerOverlay>
</template>कस्टम टाइमलाइन
#timeline slot के ज़रिए बिल्ट-इन प्लेबैक बार की जगह अपना scrub UI लगाएँ। Slot सिर्फ़ तब चलता है जब overlay के नियम डिफ़ॉल्ट बार रेंडर करते (वही timeline मोड + timelineMinDurationSeconds), इसलिए आपको इसे दोबारा नहीं लिखना पड़ता। नीचे से सटी स्थिति, safe-area padding और टच डिवाइस पर दूरी पाने के लिए अपने root पर .rk-reel-timeline class दोबारा इस्तेमाल करें।
<script setup lang="ts">
import { shallowRef, onMounted, onBeforeUnmount } from 'vue';
import {
ReelPlayerOverlay,
type TimelineSlotScope,
} from '@reelkit/vue-reel-player';
import { toVueRef, type TimelineController } from '@reelkit/vue';
const trackRef = shallowRef<HTMLDivElement | null>(null);
let dispose: (() => void) | null = null;
const bind = (state: TimelineController) => {
if (trackRef.value) dispose = state.bindInteractions(trackRef.value);
};
onBeforeUnmount(() => dispose?.());
</script>
<template>
<ReelPlayerOverlay :is-open="open" :content="items" timeline="always">
<template #timeline="{ timelineState }: TimelineSlotScope">
<div class="rk-reel-timeline" style="padding: 0 16px" @vue:mounted="bind(timelineState)">
<div ref="trackRef" role="slider" style="height:6px;background:rgba(255,255,255,0.2)">
<div :style="{
width: (timelineState.progress.value * 100) + '%',
height: '100%',
background: 'linear-gradient(90deg, #6366f1, #ec4899)',
}" />
</div>
</div>
</template>
</ReelPlayerOverlay>
</template>