Skip to content

Commit c115bb7

Browse files
committed
Finalized Image Slider on Search
1 parent b559535 commit c115bb7

File tree

1 file changed

+98
-49
lines changed

1 file changed

+98
-49
lines changed

resources/views/components/image-slider.blade.php

Lines changed: 98 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,119 @@
1-
@php
2-
$images = collect(File::files(public_path('img/image-slider')))
3-
->filter(fn($file) => in_array($file->getExtension(), ['jpg', 'jpeg', 'png', 'webp']))
4-
->map(
5-
fn($file) => [
6-
'imgSrc' => asset('img/image-slider/' . $file->getFilename()),
7-
'imgAlt' => pathinfo($file->getFilename(), PATHINFO_FILENAME),
8-
],
9-
)
10-
->toArray();
11-
@endphp
1+
<div x-data="slider()" x-init="initializeSlider()" class="relative mx-auto max-w-screen-xl" @mouseenter="pauseAutoRotation" @mouseleave="resumeAutoRotation">
2+
<!-- Image container -->
3+
<div class="overflow-hidden relative">
4+
<div class="flex transition-transform duration-700 ease-in-out" :style="transformStyle">
5+
<template x-for="(image, index) in images" :key="index">
6+
<img :src="'/img/image-slider/' + image" :alt="`Slide ${index + 1}`" class="w-full h-auto object-cover flex-shrink-0 rounded lazyload">
7+
</template>
8+
</div>
9+
</div>
1210

13-
<div x-data="sliderComponent({{ json_encode($images) }})" x-init="init()" class="relative w-full overflow-hidden">
14-
<!-- Previous Button -->
15-
<button type="button"
16-
class="absolute left-2 sm:left-5 top-1/2 z-20 flex items-center justify-center p-2 bg-white/40 rounded-full -translate-y-1/2 text-neutral-600 transition hover:bg-white/60 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-black dark:bg-neutral-950/40 dark:text-neutral-300 dark:hover:bg-neutral-950/60 dark:focus-visible:outline-white"
17-
aria-label="Previous Slide" x-on:click="previous()">
18-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="3" class="w-5 h-5 sm:w-6 sm:h-6 pr-0.5" aria-hidden="true">
19-
<path stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5 8.25 12l7.5-7.5" />
11+
<!-- Navigation buttons -->
12+
<button @click="prevImage" aria-label="Previous"
13+
class="absolute top-1/2 left-4 transform -translate-y-1/2 p-3 bg-white text-black dark:bg-gray-800 dark:text-white rounded-full shadow-md hover:bg-gray-100 focus:outline-none">
14+
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
15+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 19l-7-7 7-7"></path>
2016
</svg>
2117
</button>
2218

23-
<!-- Next Button -->
24-
<button type="button"
25-
class="absolute right-2 sm:right-5 top-1/2 z-20 flex items-center justify-center p-2 bg-white/40 rounded-full -translate-y-1/2 text-neutral-600 transition hover:bg-white/60 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-black dark:bg-neutral-950/40 dark:text-neutral-300 dark:hover:bg-neutral-950/60 dark:focus-visible:outline-white"
26-
aria-label="Next Slide" x-on:click="next()">
27-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke="currentColor" fill="none" stroke-width="3" class="w-5 h-5 sm:w-6 sm:h-6 pl-0.5" aria-hidden="true">
28-
<path stroke-linecap="round" stroke-linejoin="round" d="M8.25 4.5l7.5 7.5-7.5 7.5" />
19+
<button @click="nextImage" aria-label="Next"
20+
class="absolute top-1/2 right-4 transform -translate-y-1/2 p-3 bg-white text-black dark:bg-gray-800 dark:text-white rounded-full shadow-md hover:bg-gray-100 focus:outline-none">
21+
<svg xmlns="http://www.w3.org/2000/svg" class="w-6 h-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
22+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path>
2923
</svg>
3024
</button>
3125

32-
<!-- Slides -->
33-
<div class="relative w-full aspect-[16/9]">
34-
<template x-for="(slide, index) in slides" :key="index">
35-
<div x-show="currentSlideIndex == index + 1" class="absolute inset-0" x-transition.opacity.duration.1000ms>
36-
<img class="w-full h-full object-cover rounded" x-bind:src="slide.imgSrc" x-bind:alt="slide.imgAlt" />
37-
</div>
38-
</template>
39-
</div>
40-
4126
<!-- Indicators -->
42-
<div class="absolute bottom-3 md:bottom-5 left-1/2 z-20 flex -translate-x-1/2 gap-2 sm:gap-3 bg-white/75 px-2 py-1.5 rounded dark:bg-neutral-950/75">
43-
<template x-for="(slide, index) in slides" :key="index">
44-
<button class="w-3 h-3 sm:w-4 sm:h-4 cursor-pointer rounded-full transition" x-on:click="currentSlideIndex = index + 1"
45-
x-bind:class="[currentSlideIndex === index + 1 ? 'bg-neutral-600 dark:bg-neutral-300' : 'bg-neutral-600/50 dark:bg-neutral-300/50']"
46-
x-bind:aria-label="'Go to Slide ' + (index + 1)"></button>
27+
<div class="absolute bottom-4 left-1/2 transform -translate-x-1/2 flex space-x-2">
28+
<template x-for="(image, index) in images" :key="index">
29+
<button
30+
:class="{
31+
'bg-warning-500 dark:bg-warning-200': currentImage === index,
32+
'bg-gray-300 dark:bg-gray-700': currentImage !== index
33+
}"
34+
@click="goToImage(index)" aria-label="Go to" class="w-3 h-3 rounded-full focus:outline-none">
35+
</button>
4736
</template>
4837
</div>
4938
</div>
5039

5140
@push('scripts')
5241
<script>
53-
function sliderComponent(images) {
42+
function slider() {
5443
return {
55-
slides: images,
56-
currentSlideIndex: 1,
57-
init() {
58-
setInterval(() => {
59-
this.next();
60-
}, 20000); // Rotate every 20 seconds
44+
images: [],
45+
currentImage: 0,
46+
intervalId: null,
47+
isPaused: false,
48+
49+
initializeSlider() {
50+
this.loadImages();
51+
if (this.images.length > 1) {
52+
this.shuffleImages();
53+
this.startAutoRotation();
54+
}
55+
},
56+
57+
loadImages() {
58+
try {
59+
const allImages = @json(scandir(public_path('img/image-slider'))).filter(image => !image.startsWith('.'));
60+
const allowedExtensions = ['png', 'webp', 'jpg', 'jpeg'];
61+
this.images = allImages.filter(image => {
62+
const extension = image.split('.').pop().toLowerCase();
63+
return allowedExtensions.includes(extension);
64+
});
65+
} catch (error) {
66+
console.error("Error loading images: ", error);
67+
this.images = [];
68+
}
69+
},
70+
71+
shuffleImages() {
72+
for (let i = this.images.length - 1; i > 0; i--) {
73+
const j = Math.floor(Math.random() * (i + 1));
74+
[this.images[i], this.images[j]] = [this.images[j], this.images[i]];
75+
}
76+
},
77+
78+
prevImage() {
79+
this.currentImage = (this.currentImage === 0) ? this.images.length - 1 : this.currentImage - 1;
80+
this.resetAutoRotation();
6181
},
62-
previous() {
63-
this.currentSlideIndex = this.currentSlideIndex > 1 ? this.currentSlideIndex - 1 : this.slides.length;
82+
83+
nextImage() {
84+
this.currentImage = (this.currentImage === this.images.length - 1) ? 0 : this.currentImage + 1;
85+
this.resetAutoRotation();
86+
},
87+
88+
goToImage(index) {
89+
this.currentImage = index;
90+
this.resetAutoRotation();
6491
},
65-
next() {
66-
this.currentSlideIndex = this.currentSlideIndex < this.slides.length ? this.currentSlideIndex + 1 : 1;
92+
93+
startAutoRotation() {
94+
this.intervalId = setInterval(() => {
95+
if (!this.isPaused) {
96+
this.nextImage();
97+
}
98+
}, 5000);
99+
},
100+
101+
pauseAutoRotation() {
102+
this.isPaused = true;
67103
},
104+
105+
resumeAutoRotation() {
106+
this.isPaused = false;
107+
},
108+
109+
resetAutoRotation() {
110+
clearInterval(this.intervalId);
111+
this.startAutoRotation();
112+
},
113+
114+
get transformStyle() {
115+
return `transform: translateX(-${this.currentImage * 100}%)`;
116+
}
68117
};
69118
}
70119
</script>

0 commit comments

Comments
 (0)