128 lines
3.8 KiB
Vue
128 lines
3.8 KiB
Vue
<script setup lang="ts">
|
|
import { computed, useTemplateRef } from "vue";
|
|
import { useElementBounding, useElementSize, useWindowSize } from "@vueuse/core";
|
|
import type { PlanningSlotOut } from "../api/types.gen";
|
|
import PlanningCover from "./PlanningCover.vue";
|
|
|
|
defineProps<{ entry: PlanningSlotOut }>();
|
|
|
|
const track = useTemplateRef<HTMLElement>("track");
|
|
const stack = useTemplateRef<HTMLElement>("stack");
|
|
|
|
const { top: trackTop, height: trackHeight } = useElementBounding(track);
|
|
const { height: stackHeight } = useElementSize(stack);
|
|
const { height: windowHeight } = useWindowSize();
|
|
|
|
// The games follow the scroll: kept in the middle of the screen, but never
|
|
// leaving the slot (stuck to its top/bottom when it's only partly visible)
|
|
const offset = computed(() => {
|
|
const wanted = windowHeight.value / 2 - stackHeight.value / 2 - trackTop.value;
|
|
const max = Math.max(0, trackHeight.value - stackHeight.value);
|
|
return Math.min(max, Math.max(0, wanted));
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<div class="planning-multi h-full rounded-md p-[2px]" :style="{ '--slot-color': entry.color }">
|
|
<div class="planning-multi-inner relative h-full rounded-[5px] p-2 flex flex-col overflow-hidden">
|
|
<div class="planning-multi-title font-black uppercase text-sm tracking-widest mb-2">
|
|
<i class="mdi mdi-star-four-points mr-1"></i>{{ entry.title }}
|
|
</div>
|
|
|
|
<div ref="track" class="relative flex-1">
|
|
<div ref="stack" class="absolute inset-x-0 flex flex-col gap-3"
|
|
:style="{ transform: `translateY(${offset}px)` }">
|
|
<component :is="game.store_url ? 'a' : 'div'" v-for="game in entry.games" :key="game.name"
|
|
:href="game.store_url || undefined" target="_blank" rel="noopener noreferrer"
|
|
class="block aspect-[2/3] rounded overflow-hidden shadow-lg bg-base-200
|
|
transition-transform hover:scale-[1.05]">
|
|
<PlanningCover :game="game" :color="entry.color" />
|
|
</component>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Light sweeping down the whole slot -->
|
|
<div class="planning-multi-sheen pointer-events-none absolute inset-0"></div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
@property --planning-angle {
|
|
syntax: "<angle>";
|
|
initial-value: 0deg;
|
|
inherits: false;
|
|
}
|
|
|
|
/* Rotating gradient border + glow in the slot color */
|
|
.planning-multi {
|
|
background: conic-gradient(from var(--planning-angle),
|
|
var(--slot-color), #ffd76a, var(--slot-color), #ffffff, var(--slot-color));
|
|
box-shadow: 0 0 24px -4px var(--slot-color);
|
|
animation: planning-spin 6s linear infinite, planning-glow 3s ease-in-out infinite alternate;
|
|
}
|
|
|
|
.planning-multi-inner {
|
|
background: linear-gradient(180deg,
|
|
color-mix(in srgb, var(--slot-color) 35%, var(--color-base-200)),
|
|
var(--color-base-200) 40%,
|
|
color-mix(in srgb, var(--slot-color) 20%, var(--color-base-200)));
|
|
}
|
|
|
|
.planning-multi-title {
|
|
background: linear-gradient(90deg, #fff 20%, #ffd76a 40%, #fff 60%);
|
|
background-size: 200% 100%;
|
|
background-clip: text;
|
|
color: transparent;
|
|
animation: planning-shimmer 3s linear infinite;
|
|
}
|
|
|
|
.planning-multi-sheen {
|
|
background: linear-gradient(180deg, transparent 0%, rgb(255 255 255 / 0.12) 50%, transparent 100%);
|
|
background-size: 100% 600px;
|
|
background-repeat: no-repeat;
|
|
animation: planning-sheen 7s ease-in-out infinite;
|
|
}
|
|
|
|
@keyframes planning-spin {
|
|
to {
|
|
--planning-angle: 360deg;
|
|
}
|
|
}
|
|
|
|
@keyframes planning-glow {
|
|
to {
|
|
box-shadow: 0 0 40px 0 var(--slot-color);
|
|
}
|
|
}
|
|
|
|
@keyframes planning-shimmer {
|
|
from {
|
|
background-position: 100% 0;
|
|
}
|
|
|
|
to {
|
|
background-position: -100% 0;
|
|
}
|
|
}
|
|
|
|
@keyframes planning-sheen {
|
|
from {
|
|
background-position: 0 -600px;
|
|
}
|
|
|
|
to {
|
|
background-position: 0 calc(100% + 600px);
|
|
}
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
|
|
.planning-multi,
|
|
.planning-multi-title,
|
|
.planning-multi-sheen {
|
|
animation: none;
|
|
}
|
|
}
|
|
</style>
|