phongdt:podcast

This commit is contained in:
Duong Truong Phong
2024-05-31 13:57:40 +07:00
parent c5887d911f
commit 3a7132ea98
7 changed files with 702 additions and 1 deletions
+61
View File
@@ -0,0 +1,61 @@
<script setup lang='ts'>
const props = defineProps<{
thumb: string
title: string
brief?: string
slug: string
createdOn : string
titleClass?: string
clampIntro?: string
}>()
const defaultClass = {
article: [
'group',
'max-w-full',
'grid', 'gap-3',
'overflow-hidden',
'p-4'
],
thumbnail: [
'rounded-3xl', 'shadow-md', 'max-w-full', 'w-full', 'aspect-5/3',
'group-hover:scale-[1.05]',
'duration-500', 'ease-in-out','object-cover'
],
title: [
'font-bold', 'px-4', 'md:px-0',
props.titleClass ? props.titleClass : [
'xl:text-xl', 'text-base'
]
],
brief: [
'text-sm', 'sm:text-base',
'mx-4', 'pb-4', 'md:pb-0', 'md:mx-0',
'border-b', 'border-stone-400', 'md:border-none',props.clampIntro
]
}
</script>
<template>
<NuxtLink :to="slug || '#!'" :title="title">
<article mode="basic" :class="defaultClass.article">
<div class="rounded-sm overflow-hidden relative">
<NuxtImg :src="thumb || '/images/default-thumbnail.jpg'" placeholder fit="cover" :class="defaultClass.thumbnail" loading="lazy" />
<div class="absolute bottom-2 left-0 bg-stone-200/[.56] h-10 flex justify-center items-center w-20 rounded-full">
<span class="icon">
<svg width="30" height="30" viewBox="0 0 25 25" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M12.0131 0.5C5.38869 0.5 0 5.88331 0 12.5C0 19.1167 5.38869 24.5 12.0131 24.5C18.6376 24.5 24.0263 19.1167 24.0263 12.5C24.0263 5.88331 18.6376 0.5 12.0131 0.5ZM16.7889 12.9204L9.78122 17.4204C9.6991 17.4736 9.60426 17.5 9.51041 17.5C9.42829 17.5 9.34518 17.4795 9.2709 17.439C9.10957 17.3511 9.00985 17.1831 9.00985 17V8C9.00985 7.81691 9.10957 7.64891 9.2709 7.56102C9.42927 7.47411 9.62773 7.47945 9.78122 7.57958L16.7889 12.0796C16.9316 12.1714 17.0186 12.3301 17.0186 12.5C17.0186 12.6699 16.9316 12.8286 16.7889 12.9204Z" fill="#FF0000"></path>
</svg>
</span>
<!-- Nghe tập này -->
</div>
</div>
<p class="xs:mt-0.5 xs:text-sm text-sm">{{ utils.dateFormat(createdOn) }}</p>
<h3 :class="defaultClass.title" v-html="title"></h3>
<div v-if='brief' :class="defaultClass.brief" v-html="brief"> </div>
</article>
</NuxtLink>
</template>
+162
View File
@@ -0,0 +1,162 @@
<script setup lang="ts">
import { ref } from 'vue';
const props = defineProps<{
src?: string
}>()
const isMoreControl = ref(false)
const isPlayed = ref(true)
const isVolume = ref(true)
const speedList = ref<{ [key: number]: string }>({
1: '0.5x',
2: '0.75x',
3: '1.0x',
4: '1.25x',
5: '1.50x',
})
const speedIndexDefault = ref(3)
const speedDefault = ref(speedList.value[speedIndexDefault.value])
const volume = ref(1.0);
const audioPlayer = ref<HTMLAudioElement | null>(null);
const currentTime = ref(0);
const duration = ref(0);
function setUpVolums() {
isVolume.value = !isVolume.value
if (audioPlayer.value) {
if (isVolume.value) {
audioPlayer.value.volume = 1;
} else {
audioPlayer.value.volume = 0;
}
}
}
const updateVolume = () => {
if (audioPlayer.value) {
audioPlayer.value.volume = volume.value;
if (volume.value == 0) {
isVolume.value = false
} else {
isVolume.value = true
}
}
};
function chanageSpeed() {
if (speedIndexDefault.value < 5) {
speedIndexDefault.value += 1
if (audioPlayer.value) {
audioPlayer.value.playbackRate += 0.25;
}
speedDefault.value = speedList.value[speedIndexDefault.value]
} else {
if (audioPlayer.value) {
audioPlayer.value.playbackRate = 0.5;
}
speedIndexDefault.value = 1
speedDefault.value = speedList.value[1]
}
}
function togglePlayer() {
isPlayed.value = !isPlayed.value
if (audioPlayer.value) {
if (isPlayed.value) {
audioPlayer.value.pause();
} else {
audioPlayer.value.play();
}
}
}
function replayAndForward(time: number) {
if (audioPlayer.value) {
if (audioPlayer.value.currentTime == audioPlayer.value.duration) {
isPlayed.value = true
} else {
audioPlayer.value.currentTime = audioPlayer.value.currentTime + time
}
}
}
const seekToTime = () => {
if (audioPlayer.value) {
audioPlayer.value.currentTime = currentTime.value;
}
};
const updateCurrentTime = () => {
if (audioPlayer.value) {
currentTime.value = audioPlayer.value.currentTime;
}
}
const updateDuration = () => {
if (audioPlayer.value) {
duration.value = audioPlayer.value.duration;
}
}
const currrentTimeComputed = computed(() => {
return utils.formattedTime(currentTime.value)
})
const durationComputed = computed(() => {
return utils.formattedTime(duration.value)
})
</script>
<template>
<audio :src="src" preload="auto" ref="audioPlayer" @timeupdate="updateCurrentTime" @loadedmetadata="updateDuration" />
<div class="relative">
<div class="w-full">
<input class="w-full accent-[#fff] cursor-pointer" type="range" v-model="currentTime" @input="seekToTime" :max="duration" />
<div class="flex items-center justify-between px-4 ">
<span class="text-[10px] sm:text-lg font-normal font-[Raleway] text-[#fff]">{{ currrentTimeComputed }}</span>
<span class="text-[10px] sm:text-lg font-normal font-[Raleway] text-[#fff]">{{ durationComputed }}</span>
</div>
</div>
<div class="w-full px-4 ">
<div class="flex items-center justify-between">
<div>
<button @click="chanageSpeed"
class="text-[#fff] bg-transparent text-sm sm:text-sm lg:text-lg flex gap-1"><span
class="hidden lg:block">Tốc độ phát </span> <span
class="font-bold text-[12px] sm:text-sm lg:text-lg">{{
speedDefault
}}</span> </button>
</div>
<div>
<button @click="replayAndForward(-5)" class="hover:bg-stone-200 bg-transparent p-2 rounded-full">
<Icon name="ri:replay-5-fill" color="fff" class="text-lg sm:text-2xl md:text-4xl" style="" />
</button>
<button @click="togglePlayer" class="bg-transparent">
<Icon v-if="isPlayed" name="ri:play-circle-fill" color="fff"
class="text-lg sm:text-3xl md:text-6xl" />
<Icon v-if="!isPlayed" name="ri:pause-circle-fill" color="fff"
class="text-lg sm:text-3xl md:text-6xl" />
</button>
<button @click="replayAndForward(5)" class="hover:bg-stone-200 bg-transparent p-2 rounded-full">
<Icon name="ri:forward-5-line" color="fff" class="text-lg sm:text-2xl md:text-4xl" />
</button>
</div>
<div class="flex items-center">
<button class="bg-transparent">
<div class="flex gap-2 ">
<Icon v-if="isVolume" @click="setUpVolums()" name="ri:volume-up-fill" color="fff"
class="text-lg md:text-2xl lg:text-3xl" />
<Icon v-if="!isVolume" @click="setUpVolums()" name="ri:volume-mute-fill" color="fff"
class="text-lg md:text-2xl lg:text-3xl" />
<input v-if="isVolume" class="accent-[#fff] w-12 lg:w-20 cursor-pointer" type="range" v-model="volume"
@input="updateVolume" min="0" max="1" step="0.1" />
</div>
</button>
</div>
</div>
</div>
</div></template>