quickshell-web/src/components/marquee/Marquee.astro
2025-06-10 15:37:58 -07:00

131 lines
3.9 KiB
Text

---
import { Icon } from "astro-icon/components";
const videos = [
{
author: '<a href="https://github.com/soramanew">soramane</a>',
source: "https://github.com/caelestia-dots/shell",
path: "/assets/showcase/soramane.mp4",
},
{
author: '<a href="https://outfoxxed.me">outfoxxed</a>',
source: "https://git.outfoxxed.me/outfoxxed/nixnew/src/branch/master/modules/user/modules/quickshell",
path: "/assets/showcase/outfoxxed.mp4",
},
{
author: '<a href="https://github.com/pfaj/">pfaj</a> and <a href="https://github.com/bdebiase">bdebiase</a>',
path: "/assets/showcase/pfaj-bdeblase.mp4",
},
{
author: '<a href="https://github.com/flickowoa">flicko</a>',
source: "https://github.com/flickowoa/zephyr",
path: "/assets/showcase/flicko.mp4",
},
{
author: '<a href="https://vaxry.net">vaxry</a>',
path: "/assets/showcase/vaxry.mp4",
},
];
---
<div class="marquee">
<div class="marquee-scroll">
<div id="marquee-scroll-left" class="marquee-scroll-arrow">
<div><Icon name="caret-left"/></div>
</div>
<div id="marquee-scroll-right" class="marquee-scroll-arrow">
<div><Icon name="caret-right"/></div>
</div>
</div>
<div id="marquee-content" class="marquee-content" data-scroll="0" data-media-index="0">
{videos.map(({ author, source, path }, index) => <div class="marquee-item">
<div>
<video
data-media-index={index}
data-media-author={author}
id="showcase-video"
class="marquee-item-spacing marquee-item-content"
muted
controls
playsinline
>
<source src={path} type="video/mp4"/>
</video>
<p>
Configuration by <Fragment set:html={author}/>
{source && <>(<a href={source}>source code</a>)</>}
</p>
</div>
</div>)}
</div>
</div>
<script>
// marquee
const videoCount = 5; // last array index
const marquee = document.getElementById("marquee-content")!;
marquee.style.setProperty("--scroll", "0")
// autoplay
window.addEventListener("load", autoplayInit, false);
const videos = document.getElementsByClassName("marquee-item-content") as HTMLCollectionOf<HTMLVideoElement>;
function autoplayInit() {
videos[0].play();
};
const intersectionOptions = {
root: marquee,
rootMargin: "0px",
threshold: 0.0,
};
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
const video = entry.target as HTMLVideoElement;
if (!entry.isIntersecting) {
video.pause();
video.removeAttribute("autoplay")
video.currentTime = 0;
} else {
video.play();
}
});
},intersectionOptions);
for (const video of videos) {
observer.observe(video);
video.addEventListener("ended", () => {
// The "ended" event might just mean its buffering.
if (video.duration !== 0 && video.currentTime === video.duration) nextVideo();
});
}
const onVisibilityChange = () => {
for(const video of videos){
if(document.hidden){
video.pause();
} else {
video.play();
}
}
}
document.addEventListener("visibilitychange", onVisibilityChange);
function offsetCarousel(offset: number) {
const currentIndex = Number(marquee.getAttribute("data-media-index"));
let nextIndex = currentIndex + offset;
if (nextIndex < 0) nextIndex += videoCount;
nextIndex = nextIndex % videoCount;
marquee.setAttribute("data-media-index", `${nextIndex}`);
marquee.style.setProperty("--scroll", `-${nextIndex*100}%`)
}
function prevVideo() { offsetCarousel(-1) }
function nextVideo() { offsetCarousel(1) }
// left-right buttons
document.getElementById("marquee-scroll-left")!.addEventListener("mousedown", prevVideo);
document.getElementById("marquee-scroll-right")!.addEventListener("mousedown", nextVideo);
</script>