add in initial broken LL images

This commit is contained in:
trmazi
2026-09-09 10:07:21 -04:00
parent 8b08433e83
commit 90d3d58409
19 changed files with 193 additions and 114 deletions

View File

@@ -1,6 +1,6 @@
<script setup>
import AccordionItem from "@/components/AccordionItem.vue";
import BaseImage from "@/components/BaseImage.vue";
import ReactiveImage from "@/components/ReactiveImage.vue";
const ASSET_PATH = import.meta.env.VITE_ASSET_PATH;
defineProps({
@@ -25,7 +25,7 @@ defineProps({
class="grid"
>
<p v-for="p of item.body">{{ p }}</p>
<BaseImage
<ReactiveImage
:url="`${ASSET_PATH}/${imagePath}/${item.image}.webp`"
class="mt-2"
/>

View File

@@ -1,7 +1,7 @@
<script setup>
import { ref } from "vue";
import { ref, onMounted, onBeforeUnmount } from "vue";
defineProps({
const props = defineProps({
url: {
type: String,
required: true,
@@ -10,86 +10,49 @@ defineProps({
type: Number,
default: 200,
},
alt: {
type: String,
default: "",
},
});
const cardRef = ref(null);
const shineRef = ref(null);
const imgRef = ref(null);
const loaded = ref(false);
var observer;
function handleMouseMove(event) {
const card = cardRef.value;
const shine = shineRef.value;
if (!card || !shine) return;
onMounted(() => {
observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting) {
imgRef.value.src = props.url;
loaded.value = true;
const rect = card.getBoundingClientRect();
observer.disconnect();
}
},
{
rootMargin: "200px",
threshold: 0.01,
},
);
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
observer.observe(imgRef.value);
});
const centerX = rect.width / 2;
const centerY = rect.height / 2;
const dx = (x - centerX) / centerX;
const dy = (y - centerY) / centerY;
const aspect = rect.width / rect.height;
const maxTilt = 10;
const rotateX = dy * -maxTilt * Math.min(1, aspect);
const rotateY = dx * maxTilt * Math.min(1, 1 / aspect);
card.style.transform = `
rotateX(${rotateX}deg)
rotateY(${rotateY}deg)
scale(1.05)
`;
shine.style.opacity = "1";
shine.style.background = `
radial-gradient(
circle at ${x}px ${y}px,
rgba(255,255,255,0.6),
rgba(255,255,255,0.2),
transparent 65%
)
`;
}
function resetTransform() {
if (cardRef.value) {
cardRef.value.style.transform = "rotateX(0deg) rotateY(0deg) scale(1)";
}
if (shineRef.value) {
shineRef.value.style.opacity = "0";
}
}
onBeforeUnmount(() => {
observer?.disconnect();
});
</script>
<template>
<div
class="relative inline-block perspective-[900px]"
@mousemove="handleMouseMove"
@mouseleave="resetTransform"
>
<a :href="url" target="_blank" rel="noopener noreferrer">
<div
ref="cardRef"
class="relative will-change-transform transition-transform duration-200 ease-out transform-3d"
>
<div
ref="shineRef"
class="pointer-events-none absolute inset-0 opacity-0 transition-opacity duration-200 mix-blend-overlay rounded-lg"
/>
<img
:src="url"
class="block rounded-lg shadow-xl"
:style="{
width: `${size}px`,
height: 'auto',
}"
draggable="false"
/>
</div>
</a>
</div>
<figure class="image__wrapper">
<img
ref="imgRef"
class="image__item"
:alt="alt"
:width="size"
draggable="false"
:class="{ loaded }"
/>
</figure>
</template>

View File

@@ -5,6 +5,7 @@ import { dashCode } from "@/constants/userData";
import { GameConstants, getGameInfo, VersionConstants } from "@/constants";
import UserEmblem from "@/components/UserEmblem.vue";
import UserQpro from "@/components/UserQpro.vue";
import BaseImage from "@/components/BaseImage.vue";
import { getGitadoraColor, getJubilityColor } from "@/constants/skillColor.js";
import { getFlareLevel } from "@/helpers/flare";
const GAME_ASSET_PATH = import.meta.env.VITE_GAME_ASSET_PATH;
@@ -112,10 +113,9 @@ onMounted(async () => {
"
class="place-self-center w-20"
>
<img
:src="`${GAME_ASSET_PATH}/chara/popn/${profile.chara}.png`"
width="80"
height="80"
<BaseImage
:url="`${GAME_ASSET_PATH}/chara/popn/${profile.chara}.png`"
:size="80"
/>
</div>
<div class="drop-shadow-2xl">

View File

@@ -1,5 +1,6 @@
<script setup>
import { computed } from "vue";
import BaseImage from "@/components/BaseImage.vue";
const props = defineProps({
path: {
@@ -23,7 +24,7 @@ const spanClass = computed(
<template>
<span :class="spanClass">
<img :src="path" class="rounded-full" :class="`${w}`" />
<BaseImage :url="path" class="rounded-full" :class="`${w}`" />
<slot />
</span>
</template>

View File

@@ -1,6 +1,7 @@
<script setup>
import { PhDownload, PhCaretLeft, PhCaretRight } from "@phosphor-icons/vue";
import BaseButton from "@/components/BaseButton.vue";
import BaseImage from "@/components/BaseImage.vue";
import UserAvatar from "@/components/UserAvatar.vue";
import { useRoute } from "vue-router";
import { exportJSON } from "@/helpers/contentExport";
@@ -55,10 +56,10 @@ function downloadJSON() {
>
<template #loading>
<div class="text-center align-center grid">
<img
<BaseImage
class="rounded-full place-self-center"
src="/icon.gif"
width="60"
url="/icon.gif"
:size="60"
/>
<h1 class="text-lg text-gray-100">Loading</h1>
</div>
@@ -117,9 +118,9 @@ function downloadJSON() {
<template #item-chara="{ chara }">
<div class="chara-wrapper w-6.25">
<img
:src="`${GAME_ASSET_PATH}/chara/popn/${chara ? chara : 1}.png`"
width="80"
<BaseImage
:url="`${GAME_ASSET_PATH}/chara/popn/${chara ? chara : 1}.png`"
:size="80"
/>
</div>
</template>

View File

@@ -4,6 +4,7 @@ import { computed } from "vue";
import { useStyleStore } from "@/stores/style.js";
import AsideMenuList from "@/components/Menus/AsideMenuList.vue";
import AsideMenuItem from "@/components/Menus/AsideMenuItem.vue";
import BaseImage from "@/components/BaseImage.vue";
defineProps({
menu: {
@@ -50,7 +51,7 @@ const asideLgCloseClick = (event) => {
>
<div class="dark:bg-slate-900 flex flex-col gap-4 my-2 pb-4 border-b-2">
<div class="flex items-center gap-3 mx-2">
<img class="rounded-full shadow-xl" src="/favicon.png" />
<BaseImage class="rounded-full shadow-xl" url="/favicon.png" />
<h2 class="text-md">
<samp class="tracking-tighter">PhaseII</samp> eAmusement Network
</h2>

View File

@@ -4,6 +4,7 @@ import CardBox from "@/components/CardBox.vue";
import BaseIcon from "@/components/BaseIcon.vue";
import BaseButton from "@/components/BaseButton.vue";
import BaseButtons from "@/components/BaseButtons.vue";
import BaseImage from "@/components/BaseImage.vue";
import { useMainStore } from "@/stores/main";
import { PhCloudCheck, PhCloudX, PhShrimp } from "@phosphor-icons/vue";
@@ -56,10 +57,10 @@ function closeModal() {
class="grid text-center justify-center grid-cols-1 gap-3"
>
<template v-if="!mainStore.userCustomize.shrimpLinks">
<img
<BaseImage
class="rounded-full place-self-center"
src="/icon.gif"
width="70"
url="/icon.gif"
:size="70"
/>
</template>
<template v-else>

View File

@@ -0,0 +1,96 @@
<script setup>
import { ref } from "vue";
import BaseImage from "@/components/BaseImage.vue";
defineProps({
url: {
type: String,
required: true,
},
size: {
type: Number,
default: 200,
},
});
const cardRef = ref(null);
const shineRef = ref(null);
function handleMouseMove(event) {
const card = cardRef.value;
const shine = shineRef.value;
if (!card || !shine) return;
const rect = card.getBoundingClientRect();
const x = event.clientX - rect.left;
const y = event.clientY - rect.top;
const centerX = rect.width / 2;
const centerY = rect.height / 2;
const dx = (x - centerX) / centerX;
const dy = (y - centerY) / centerY;
const aspect = rect.width / rect.height;
const maxTilt = 10;
const rotateX = dy * -maxTilt * Math.min(1, aspect);
const rotateY = dx * maxTilt * Math.min(1, 1 / aspect);
card.style.transform = `
rotateX(${rotateX}deg)
rotateY(${rotateY}deg)
scale(1.05)
`;
shine.style.opacity = "1";
shine.style.background = `
radial-gradient(
circle at ${x}px ${y}px,
rgba(255,255,255,0.6),
rgba(255,255,255,0.2),
transparent 65%
)
`;
}
function resetTransform() {
if (cardRef.value) {
cardRef.value.style.transform = "rotateX(0deg) rotateY(0deg) scale(1)";
}
if (shineRef.value) {
shineRef.value.style.opacity = "0";
}
}
</script>
<template>
<div
class="relative inline-block perspective-[900px]"
@mousemove="handleMouseMove"
@mouseleave="resetTransform"
>
<a :href="url" target="_blank" rel="noopener noreferrer">
<div
ref="cardRef"
class="relative will-change-transform transition-transform duration-200 ease-out transform-3d"
>
<div
ref="shineRef"
class="pointer-events-none absolute inset-0 opacity-0 transition-opacity duration-200 mix-blend-overlay rounded-lg"
/>
<BaseImage
:url="url"
class="block rounded-lg shadow-xl"
:style="{
width: `${size}px`,
height: 'auto',
}"
draggable="false"
/>
</div>
</a>
</div>
</template>

View File

@@ -1,5 +1,6 @@
<script setup>
import { computed } from "vue";
import BaseImage from "@/components/BaseImage.vue";
const ASSET_PATH = import.meta.env.VITE_ASSET_PATH;
@@ -28,16 +29,15 @@ const username = computed(() => props.username);
<template>
<div>
<div class="relative inline-block">
<img
:src="avatar"
<BaseImage
:url="avatar"
:alt="username"
width="200"
height="200"
:size="200"
class="w-full h-auto bg-gray-100 dark:bg-slate-800 rounded-full overflow-hidden drop-shadow-lg"
/>
<img
<BaseImage
v-if="border"
:src="`${ASSET_PATH}/border/${border}.webp`"
:url="`${ASSET_PATH}/border/${border}.webp`"
alt="border"
class="absolute inset-0 w-full h-full overflow-hidden scale-[1.02]"
style="object-fit: fill"

View File

@@ -1,5 +1,6 @@
<script setup>
import { computed, watch, ref } from "vue";
import BaseImage from "@/components/BaseImage.vue";
const props = defineProps({
profile: {
@@ -73,8 +74,8 @@ function formatEmblem(emblem) {
:key="index"
style="position: absolute"
>
<img
:src="src"
<BaseImage
:url="src"
:style="`width: ${size}px; height: ${size}px`"
class="drop-shadow-lg"
/>

View File

@@ -4,6 +4,7 @@ import { useRoute } from "vue-router";
import CardBox from "@/components/CardBox.vue";
import BaseButton from "@/components/BaseButton.vue";
import LayoutGuest from "@/layouts/LayoutGuest.vue";
import BaseImage from "@/components/BaseImage.vue";
import { APIGetContactless, APIPostContactless } from "@/stores/api/account.js";
const route = useRoute();
@@ -48,7 +49,10 @@ async function submit() {
>
<div class="flex flex-col items-center text-wrap h-full md:mt-5">
<div class="flex flex-col items-center text-wrap">
<img src="/favicon.png" class="rounded-full shadow-lg mb-2" />
<BaseImage
url="/favicon.png"
class="rounded-full shadow-lg mb-2"
/>
<h1 class="text-xl"><samp>PhaseII</samp></h1>
<button class="text-sm text-gray-700 dark:text-white/75">
Spinnin' since 2021

View File

@@ -7,6 +7,7 @@ import LayoutGuest from "@/layouts/LayoutGuest.vue";
import { APIGetAuthClient, APIPostAuthClient } from "@/stores/api/account.js";
import { getIntentById } from "@/constants/developer";
import { InternalApps } from "@/constants/developer/apps.js";
import BaseImage from "@/components/BaseImage.vue";
const CDN_URL = import.meta.env.VITE_CDN_URL;
const route = useRoute();
@@ -66,7 +67,10 @@ const submit = async () => {
>
<div class="flex flex-col items-center text-wrap h-full md:mt-5">
<div class="flex flex-col items-center text-wrap">
<img src="/favicon.png" class="rounded-full shadow-lg mb-2" />
<BaseImage
url="/favicon.png"
class="rounded-full shadow-lg mb-2"
/>
<h1 class="text-xl"><samp>PhaseII</samp></h1>
<button class="text-sm text-gray-700 dark:text-white/75">
Spinnin' since 2021
@@ -90,8 +94,8 @@ const submit = async () => {
<span class="font-bold">{{ clientData?.name }}</span>
</h1>
</template>
<img
:src="clientData.image ?? `${CDN_URL}/${clientData?.data?.img}`"
<BaseImage
:url="clientData.image ?? `${CDN_URL}/${clientData?.data?.img}`"
width="75"
class="rounded-full shadow-lg mb-2"
/>

View File

@@ -8,6 +8,7 @@ import FormCheckRadio from "@/components/FormCheckRadio.vue";
import FormField from "@/components/FormField.vue";
import FormControl from "@/components/FormControl.vue";
import BaseButton from "@/components/BaseButton.vue";
import BaseImage from "@/components/BaseImage.vue";
import LayoutGuest from "@/layouts/LayoutGuest.vue";
const router = useRouter();
@@ -53,7 +54,7 @@ const submit = async () => {
class="p-4 flex flex-col md:flex-row w-full space-y-2 md:space-y-0 md:space-x-4"
>
<div class="flex flex-col items-center text-wrap h-full md:mt-5">
<img src="/favicon.png" class="rounded-full shadow-lg mb-2" />
<BaseImage url="/favicon.png" class="rounded-full shadow-lg mb-2" />
<h1 class="text-xl"><samp>PhaseII</samp></h1>
<button
class="text-sm text-gray-700 dark:text-white/75 hover:cursor-pointer"

View File

@@ -15,6 +15,7 @@ import FormControl from "@/components/FormControl.vue";
import BaseButton from "@/components/BaseButton.vue";
import BaseIcon from "@/components/BaseIcon.vue";
import LayoutGuest from "@/layouts/LayoutGuest.vue";
import BaseImage from "@/components/BaseImage.vue";
import { APIRegisterUser } from "@/stores/api/account";
@@ -74,7 +75,7 @@ async function registerProfile() {
class="p-4 flex flex-col md:flex-row w-full space-y-2 md:space-y-0 md:space-x-4"
>
<div class="flex flex-col items-center text-wrap h-full md:mt-5">
<img src="/favicon.png" class="rounded-full shadow-lg mb-2" />
<BaseImage url="/favicon.png" class="rounded-full shadow-lg mb-2" />
<h1 class="text-xl"><samp>PhaseII</samp></h1>
<p class="text-sm text-gray-700 dark:text-white/75">
Spinnin' since 2021

View File

@@ -8,6 +8,7 @@ import FormControl from "@/components/FormControl.vue";
import BaseButton from "@/components/BaseButton.vue";
import BaseIcon from "@/components/BaseIcon.vue";
import LayoutGuest from "@/layouts/LayoutGuest.vue";
import BaseImage from "@/components/BaseImage.vue";
import {
APIEmailAuth,
@@ -78,7 +79,7 @@ async function resetPassword() {
class="p-4 flex flex-col md:flex-row w-full space-y-2 md:space-y-0 md:space-x-4"
>
<div class="flex flex-col items-center text-wrap h-full md:mt-5">
<img src="/favicon.png" class="rounded-full shadow-lg mb-2" />
<BaseImage url="/favicon.png" class="rounded-full shadow-lg mb-2" />
<h1 class="text-xl"><samp>PhaseII</samp></h1>
<p class="text-sm text-gray-700 dark:text-white/75">
Spinnin' since 2021

View File

@@ -1,6 +1,7 @@
<script setup>
import CardBox from "@/components/CardBox.vue";
import BaseButton from "@/components/BaseButton.vue";
import BaseImage from "@/components/BaseImage.vue";
import LayoutGuest from "@/layouts/LayoutGuest.vue";
</script>
@@ -14,7 +15,7 @@ import LayoutGuest from "@/layouts/LayoutGuest.vue";
>
<div class="p-4 flex flex-col w-full space-y-2">
<div class="flex flex-col items-center text-wrap h-full">
<img src="/favicon.png" class="rounded-full shadow-lg mb-2" />
<BaseImage url="/favicon.png" class="rounded-full shadow-lg mb-2" />
<h1 class="text-xl"><samp>PhaseII</samp></h1>
<p class="text-sm text-gray-700 dark:text-white/75">
Broken since 2021

View File

@@ -7,6 +7,7 @@ import LayoutAuthenticated from "@/layouts/LayoutAuthenticated.vue";
import CardBox from "@/components/CardBox.vue";
import BaseButtons from "@/components/BaseButtons.vue";
import BaseButton from "@/components/BaseButton.vue";
import BaseImage from "@/components/BaseImage.vue";
import { APIGetNewsPost } from "@/stores/api/news";
import { APIUserReadNews } from "@/stores/api/account";
@@ -44,8 +45,8 @@ function goBack() {
<transition>
<CardBox v-if="thisNews != {}" has-table>
<div class="flex flex-col items-center">
<img
:src="thisNews.data.img"
<BaseImage
:url="thisNews.data.img"
class="rounded-t-2xl md:rounded-2xl md:w-1/2 md:m-4"
/>
</div>

View File

@@ -16,6 +16,7 @@ import FormField from "@/components/FormField.vue";
import FormControl from "@/components/FormControl.vue";
import LayoutAuthenticated from "@/layouts/LayoutAuthenticated.vue";
import SectionTitleLine from "@/components/SectionTitleLine.vue";
import BaseImage from "@/components/BaseImage.vue";
const ASSET_PATH = import.meta.env.VITE_ASSET_PATH;
import { APIGetCards, APIPutCard, APIDeleteCard } from "@/stores/api/account";
@@ -139,8 +140,8 @@ const copyToClipboard = (text) => {
class="grid dark:bg-slate-900/70 rounded-2xl p-5"
>
<div class="flex justify-between md:grid md:gap-2 mb-4 items-center">
<img
:src="`${ASSET_PATH}/passes/generic.webp`"
<BaseImage
:url="`${ASSET_PATH}/passes/generic.webp`"
class="w-24 md:w-auto"
/>
<h1 class="text-xl text-center font-mono">

View File

@@ -7,6 +7,7 @@ import GameIcon from "@/components/GameIcon.vue";
import UserCard from "@/components/UserCard.vue";
import LayoutAuthenticated from "@/layouts/LayoutAuthenticated.vue";
import SectionTitleLine from "@/components/SectionTitleLine.vue";
import BaseImage from "@/components/BaseImage.vue";
import { getGameInfo } from "@/constants";
import { formatSortableDate } from "@/constants/date";
@@ -168,9 +169,9 @@ onUnmounted(() => {
);
"
/>
<img
<BaseImage
class="w-50 shadow-xl"
:src="`https://cdn.phaseii.network/file/PhaseII/${image.b2_path}`"
:url="`https://cdn.phaseii.network/file/PhaseII/${image.b2_path}`"
/>
</a>
</div>