feat: replace static cover image with interactive 3D CD jewel case mockup and YouTube video lightbox
This commit is contained in:
@@ -29,11 +29,55 @@
|
|||||||
<div class="flex flex-wrap mx-[-15px]">
|
<div class="flex flex-wrap mx-[-15px]">
|
||||||
<div class="w-full flex-[0_0_auto] !px-[15px] max-w-full">
|
<div class="w-full flex-[0_0_auto] !px-[15px] max-w-full">
|
||||||
<article class="!mt-[-12.5rem]">
|
<article class="!mt-[-12.5rem]">
|
||||||
{{-- Cover Image --}}
|
@php
|
||||||
|
$videoId = null;
|
||||||
|
if ($production->youtube_video_id) {
|
||||||
|
$videoId = $production->youtube_video_id;
|
||||||
|
} elseif ($production->youtube_url) {
|
||||||
|
if (preg_match('%(?:youtube(?:-nocookie)?\.com/(?:[^/]+/.+/|(?:v|e(?:mbed)?)/|.*[?&]v=)|youtu\.be/)([^"&?/ ]{11})%i', $production->youtube_url, $match)) {
|
||||||
|
$videoId = $match[1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@endphp
|
||||||
|
|
||||||
|
{{-- CD Jewel Case 3D Mockup --}}
|
||||||
@if($production->cover_image_url)
|
@if($production->cover_image_url)
|
||||||
<figure class="!rounded-[.4rem] !mb-8 xl:!mb-[3.5rem] lg:!mb-[3.5rem] md:!mb-[3.5rem] overflow-hidden shadow-lg">
|
<div class="cd-mockup-wrapper flex justify-center !mb-12 xl:!mb-16">
|
||||||
<img class="!rounded-[.4rem] w-full" src="{{ $production->cover_image_url }}" alt="{{ $production->translate('title') }}">
|
<div class="cd-case-perspective">
|
||||||
</figure>
|
<div class="cd-case" @if($videoId) id="play-cd-btn" style="cursor: pointer;" title="{{ __('music_productions.youtube_watch_on_youtube') }}" @endif>
|
||||||
|
{{-- Left Spine (Plastic Hinge) --}}
|
||||||
|
<div class="cd-spine"></div>
|
||||||
|
{{-- Cover Graphic --}}
|
||||||
|
<div class="cd-cover" style="background-image: url('{{ $production->cover_image_url }}');">
|
||||||
|
{{-- Glass shine reflection --}}
|
||||||
|
<div class="cd-shine"></div>
|
||||||
|
{{-- Play Button Overlay --}}
|
||||||
|
@if($videoId)
|
||||||
|
<div class="cd-play-overlay">
|
||||||
|
<div class="cd-play-btn">
|
||||||
|
<i class="uil uil-play before:content-['\ea15'] text-white text-[2rem] ml-1"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
|
||||||
|
{{-- YouTube Lightbox Modal --}}
|
||||||
|
@if($videoId)
|
||||||
|
<div id="youtube-lightbox" class="fixed inset-0 z-[9999] flex items-center justify-center bg-black/90 backdrop-blur-md opacity-0 pointer-events-none transition-all duration-300">
|
||||||
|
<div class="absolute inset-0" id="lightbox-bg"></div>
|
||||||
|
<div class="relative w-full max-w-4xl mx-4 aspect-video z-10 scale-95 opacity-0 transition-all duration-300" id="lightbox-content">
|
||||||
|
<button id="lightbox-close" class="absolute -top-12 right-0 text-white hover:text-red-500 transition-colors text-2xl flex items-center gap-1 font-semibold focus:outline-none">
|
||||||
|
<i class="uil uil-times before:content-['\eb50']"></i> {{ t('Kapat') }}
|
||||||
|
</button>
|
||||||
|
<div class="w-full h-full rounded-lg overflow-hidden shadow-2xl bg-black border border-white/10">
|
||||||
|
<iframe id="lightbox-iframe" class="w-full h-full border-0" src="" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@endif
|
@endif
|
||||||
|
|
||||||
{{-- Content & Meta --}}
|
{{-- Content & Meta --}}
|
||||||
@@ -49,7 +93,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{-- Sidebar - Date & Client --}}
|
{{-- Sidebar - Date & Client --}}
|
||||||
<div class="xl:w-2/12 lg:w-2/12 md:w-2/12 w-full flex-[0_0_auto] max-w-full !ml-auto">
|
<div class="xl:w-3/12 lg:w-3/12 md:w-3/12 w-full flex-[0_0_auto] max-w-full !ml-auto">
|
||||||
<ul class="pl-0 list-none">
|
<ul class="pl-0 list-none">
|
||||||
@if($production->production_date)
|
@if($production->production_date)
|
||||||
<li>
|
<li>
|
||||||
@@ -64,17 +108,27 @@
|
|||||||
</li>
|
</li>
|
||||||
@endif
|
@endif
|
||||||
@if($production->spotify_url)
|
@if($production->spotify_url)
|
||||||
<li>
|
<li class="!mb-3">
|
||||||
<h5 class="!mb-1">Spotify</h5>
|
<h5 class="!mb-1">Spotify</h5>
|
||||||
<p>
|
<p>
|
||||||
<a href="{{ $production->spotify_url }}" target="_blank" rel="noopener noreferrer" class="btn btn-sm btn-soft-primary !rounded-[50rem]">
|
<a href="{{ $production->spotify_url }}" target="_blank" rel="noopener noreferrer" class="btn btn-sm btn-soft-primary !rounded-[50rem] w-full text-center">
|
||||||
{{ __('music_productions.spotify_listen_on_spotify') }}
|
{{ __('music_productions.spotify_listen_on_spotify') }}
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
</li>
|
</li>
|
||||||
@endif
|
@endif
|
||||||
|
@if($production->youtube_url)
|
||||||
|
<li class="!mb-3">
|
||||||
|
<h5 class="!mb-1">YouTube</h5>
|
||||||
|
<p>
|
||||||
|
<a href="{{ $production->youtube_url }}" target="_blank" rel="noopener noreferrer" class="btn btn-sm btn-soft-red !rounded-[50rem] w-full text-center" style="background-color: rgba(227, 30, 36, 0.1); color: #e31e24;">
|
||||||
|
{{ __('music_productions.youtube_watch_on_youtube') }}
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
@endif
|
||||||
</ul>
|
</ul>
|
||||||
<a href="{{ route('music-productions.index') }}" class="more hover">
|
<a href="{{ route('music-productions.index') }}" class="more hover mt-4 inline-block">
|
||||||
<i class="uil uil-arrow-left before:content-['\e949'] !mr-1"></i>
|
<i class="uil uil-arrow-left before:content-['\e949'] !mr-1"></i>
|
||||||
{{ __('music_productions.back_to_list') }}
|
{{ __('music_productions.back_to_list') }}
|
||||||
</a>
|
</a>
|
||||||
@@ -134,6 +188,154 @@
|
|||||||
|
|
||||||
@push('styles')
|
@push('styles')
|
||||||
<style>
|
<style>
|
||||||
|
/* 3D CD Jewel Case Mockup Styles */
|
||||||
|
.cd-mockup-wrapper {
|
||||||
|
perspective: 2000px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-case-perspective {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-case {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
aspect-ratio: 1/1;
|
||||||
|
background: rgba(255, 255, 255, 0.08);
|
||||||
|
border: 4px solid rgba(255, 255, 255, 0.35);
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow:
|
||||||
|
-10px 20px 40px rgba(0, 0, 0, 0.3),
|
||||||
|
0 10px 20px rgba(0, 0, 0, 0.15),
|
||||||
|
inset 0 0 15px rgba(255, 255, 255, 0.2);
|
||||||
|
transform: rotateX(15deg) rotateY(-18deg) rotateZ(3deg);
|
||||||
|
transform-style: preserve-3d;
|
||||||
|
transition: all 0.7s cubic-bezier(0.165, 0.84, 0.44, 1);
|
||||||
|
display: flex;
|
||||||
|
overflow: hidden;
|
||||||
|
padding: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-case:hover {
|
||||||
|
transform: rotateX(6deg) rotateY(-6deg) rotateZ(0deg) scale(1.06);
|
||||||
|
border-color: rgba(255, 255, 255, 0.5);
|
||||||
|
box-shadow:
|
||||||
|
-15px 30px 55px rgba(0, 0, 0, 0.4),
|
||||||
|
0 15px 30px rgba(0, 0, 0, 0.2),
|
||||||
|
inset 0 0 25px rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CD Hinge / Plastic Tray Spine */
|
||||||
|
.cd-spine {
|
||||||
|
width: 24px;
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(90deg,
|
||||||
|
rgba(20, 20, 20, 0.85) 0%,
|
||||||
|
rgba(70, 70, 70, 0.9) 20%,
|
||||||
|
rgba(35, 35, 35, 0.85) 45%,
|
||||||
|
rgba(90, 90, 90, 0.7) 65%,
|
||||||
|
rgba(25, 25, 25, 0.9) 100%
|
||||||
|
);
|
||||||
|
border-radius: 6px 0 0 6px;
|
||||||
|
border-right: 1.5px solid rgba(255, 255, 255, 0.15);
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
|
box-shadow: inset 1px 0 3px rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* CD Booklet Cover Art */
|
||||||
|
.cd-cover {
|
||||||
|
flex: 1;
|
||||||
|
height: 100%;
|
||||||
|
background-size: cover;
|
||||||
|
background-position: center;
|
||||||
|
border-radius: 0 6px 6px 0;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: inset 0 0 12px rgba(0, 0, 0, 0.55);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Jewel Case Plastic Glass Reflection */
|
||||||
|
.cd-shine {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 160%;
|
||||||
|
height: 160%;
|
||||||
|
background: linear-gradient(135deg,
|
||||||
|
rgba(255, 255, 255, 0.5) 0%,
|
||||||
|
rgba(255, 255, 255, 0.18) 32%,
|
||||||
|
rgba(255, 255, 255, 0) 32.5%,
|
||||||
|
rgba(255, 255, 255, 0) 48%,
|
||||||
|
rgba(255, 255, 255, 0.12) 48.5%,
|
||||||
|
rgba(255, 255, 255, 0.06) 65%,
|
||||||
|
rgba(255, 255, 255, 0) 65.5%
|
||||||
|
);
|
||||||
|
transform: translate(-30%, -30%) rotate(4deg);
|
||||||
|
pointer-events: none;
|
||||||
|
z-index: 5;
|
||||||
|
transition: all 0.7s cubic-bezier(0.165, 0.84, 0.44, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-case:hover .cd-shine {
|
||||||
|
transform: translate(-18%, -18%) rotate(8deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hover Play Button Overlay */
|
||||||
|
.cd-play-overlay {
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.4);
|
||||||
|
backdrop-filter: blur(2px);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
opacity: 0;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
z-index: 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-case:hover .cd-play-overlay {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-play-btn {
|
||||||
|
width: 68px;
|
||||||
|
height: 68px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #e31e24;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow:
|
||||||
|
0 10px 25px rgba(227, 30, 36, 0.5),
|
||||||
|
0 0 0 0px rgba(227, 30, 36, 0.3);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
transform: scale(0.85);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-case:hover .cd-play-btn {
|
||||||
|
transform: scale(1);
|
||||||
|
animation: pulse-red-ring 2s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pulse-red-ring {
|
||||||
|
0% {
|
||||||
|
box-shadow: 0 0 0 0px rgba(227, 30, 36, 0.7);
|
||||||
|
}
|
||||||
|
70% {
|
||||||
|
box-shadow: 0 0 0 15px rgba(227, 30, 36, 0);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
box-shadow: 0 0 0 0px rgba(227, 30, 36, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Content styling */
|
/* Content styling */
|
||||||
.music-production-content p {
|
.music-production-content p {
|
||||||
margin-bottom: 1rem;
|
margin-bottom: 1rem;
|
||||||
@@ -167,6 +369,65 @@ article {
|
|||||||
from { opacity: 0; transform: translateY(20px); }
|
from { opacity: 0; transform: translateY(20px); }
|
||||||
to { opacity: 1; transform: translateY(0); }
|
to { opacity: 1; transform: translateY(0); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Flatten 3D CD element when lightbox is open to fix stacking context issues */
|
||||||
|
body.lightbox-open .cd-mockup-wrapper,
|
||||||
|
body.lightbox-open .cd-case-perspective,
|
||||||
|
body.lightbox-open .cd-case {
|
||||||
|
transform: none !important;
|
||||||
|
perspective: none !important;
|
||||||
|
transform-style: flat !important;
|
||||||
|
z-index: 1 !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
@endpush
|
@endpush
|
||||||
|
|
||||||
|
@push('scripts')
|
||||||
|
<script>
|
||||||
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
|
const cdBtn = document.getElementById('play-cd-btn');
|
||||||
|
const lightbox = document.getElementById('youtube-lightbox');
|
||||||
|
const lightboxBg = document.getElementById('lightbox-bg');
|
||||||
|
const lightboxContent = document.getElementById('lightbox-content');
|
||||||
|
const lightboxClose = document.getElementById('lightbox-close');
|
||||||
|
const iframe = document.getElementById('lightbox-iframe');
|
||||||
|
const videoId = "{{ $videoId }}";
|
||||||
|
|
||||||
|
if (cdBtn && lightbox && iframe) {
|
||||||
|
cdBtn.addEventListener('click', function() {
|
||||||
|
// Set autoplay & enable javascript API
|
||||||
|
iframe.src = `https://www.youtube.com/embed/${videoId}?autoplay=1&enablejsapi=1`;
|
||||||
|
|
||||||
|
// Show lightbox container with transition
|
||||||
|
lightbox.classList.remove('opacity-0', 'pointer-events-none');
|
||||||
|
document.body.classList.add('lightbox-open');
|
||||||
|
setTimeout(() => {
|
||||||
|
lightboxContent.classList.remove('scale-95', 'opacity-0');
|
||||||
|
}, 50);
|
||||||
|
});
|
||||||
|
|
||||||
|
function closeLightbox() {
|
||||||
|
lightboxContent.classList.add('scale-95', 'opacity-0');
|
||||||
|
lightbox.classList.add('opacity-0', 'pointer-events-none');
|
||||||
|
document.body.classList.remove('lightbox-open');
|
||||||
|
|
||||||
|
// Clear source to halt playback after transition completes
|
||||||
|
setTimeout(() => {
|
||||||
|
iframe.src = "";
|
||||||
|
}, 300);
|
||||||
|
}
|
||||||
|
|
||||||
|
lightboxBg.addEventListener('click', closeLightbox);
|
||||||
|
lightboxClose.addEventListener('click', closeLightbox);
|
||||||
|
|
||||||
|
// Listen for ESC key
|
||||||
|
document.addEventListener('keydown', function(e) {
|
||||||
|
if (e.key === 'Escape' && !lightbox.classList.contains('opacity-0')) {
|
||||||
|
closeLightbox();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
@endpush
|
||||||
@endsection
|
@endsection
|
||||||
|
|||||||
Reference in New Issue
Block a user