Add priority and lazy loading to some images

This commit is contained in:
marzban-dev
2025-09-09 10:45:27 +03:30
parent 8a498147e1
commit 4a21072e54
28 changed files with 192 additions and 132 deletions
+12 -7
View File
@@ -22,15 +22,16 @@ const emit = defineEmits<Emits>();
<template>
<button
@click="emit('select', null)"
:class="
isSelected
? 'ring-2 ring-offset-2 ring-blue-500 border-blue-500'
: 'border-slate-200'
"
:class="isSelected ? 'ring-2 ring-offset-2 ring-blue-500 border-blue-500' : 'border-slate-200'"
class="w-full p-5 border rounded-xl flex flex-col gap-4 transition-all cursor-pointer relative overflow-hidden"
>
<div class="aspect-square flex-center">
<NuxtImg :src="gateway.picture" class="object-cover" />
<NuxtImg
:src="gateway.picture"
loading="lazy"
fetch-priority="low"
class="object-cover"
/>
</div>
<span class="typo-label-sm text-right text-black">
{{ gateway.title }}
@@ -43,7 +44,11 @@ const emit = defineEmits<Emits>();
v-if="isSelected"
class="bg-blue-500 rounded-md p-0.5 text-center bottom-4 left-4 text-slate-200 text-[10px] lg:text-xs absolute"
>
<Icon name="bi:check" size="20" class="**:fill-white" />
<Icon
name="bi:check"
size="20"
class="**:fill-white"
/>
</span>
</Transition>
</button>
@@ -30,15 +30,11 @@ const visible = computed({
class="fixed inset-0 w-full h-svh z-9999 flex-center"
v-if="visible"
>
<div
class="overflow-y-auto max-h-svh absolute left-[50%] py-10 w-fit max-w-[50rem] translate-x-[-50%]"
>
<div class="overflow-y-auto max-h-svh absolute left-[50%] py-10 w-fit max-w-[50rem] translate-x-[-50%]">
<DialogContent
class="min-w-[30vw] max-w-[50vw] data-[state=open]:animate-content-show text-black font-iran-yekan-x focus:outline-none z-[100]"
>
<div
class="w-full h-[350px] shrink-0 rounded-2xl relative overflow-hidden flex-center"
>
<div class="w-full h-[350px] shrink-0 rounded-2xl relative overflow-hidden flex-center">
<div
class="bg-custom-conic size-[200%] absolute -top-1/2 -left-1/2 animate-spin [animation-duration:3s] z-[1]"
></div>
@@ -48,6 +44,8 @@ const visible = computed({
<NuxtImg
class="aspect-square w-[300px]"
src="/img/heymlz/heymlz-payment-progress.gif"
loading="lazy"
fetch-priority="low"
/>
<div class="-translate-y-28 flex-center gap-3">
@@ -55,9 +53,7 @@ const visible = computed({
name="svg-spinners:3-dots-bounce"
size="20"
/>
<span class="text-lg">
در حال انتقال به درگاه پرداخت
</span>
<span class="text-lg"> در حال انتقال به درگاه پرداخت </span>
</div>
</div>
</div>
@@ -72,15 +68,7 @@ const visible = computed({
.bg-custom-conic {
background-size: 100% 100%;
background-position: 0px 0px, 0px 0px;
background-image: radial-gradient(
142% 91% at -6% 90%,
#ff000000 1%,
#ff000000 99%
),
conic-gradient(
from 0deg at 50% 50%,
var(--color-blue-500) 0%,
#ff000000 34%
);
background-image: radial-gradient(142% 91% at -6% 90%, #ff000000 1%, #ff000000 99%),
conic-gradient(from 0deg at 50% 50%, var(--color-blue-500) 0%, #ff000000 34%);
}
</style>
@@ -27,15 +27,19 @@ const { isLoading: cartImageIsLoading } = useImage({
v-if="!cartImageIsLoading"
class="size-[3.5rem] shrink-0 rounded-100 border border-gray-300 overflow-hidden"
>
<NuxtImg :src="image" alt="product" class="object-conver" />
<NuxtImg
:src="image"
alt="product"
loading="lazy"
fetch-priority="low"
class="object-conver"
/>
</div>
<Skeleton
v-else
class="!size-[3.5rem] aspect-square shrink-0 !rounded-100 border border-slate-200"
/>
<span
class="text-xs font-semibold lg:text-sm text-gray-800 line-clamp-2"
>
<span class="text-xs font-semibold lg:text-sm text-gray-800 line-clamp-2">
{{ title }}
</span>
</div>
@@ -123,6 +123,8 @@ watch(
>
<NuxtImg
:src="data.product.image"
loading="lazy"
fetch-priority="low"
class="object-cover size-full"
alt="product"
/>
@@ -38,6 +38,8 @@ const remaining = computed(() => items.value.length - max.value);
>
<NuxtImg
:src="item"
loading="lazy"
fetch-priority="low"
alt="avatar"
class="rounded-full object-cover w-full h-full"
/>
+4
View File
@@ -51,6 +51,8 @@ const createdAt = usePersianTimeAgo(new Date(date.value));
<NuxtImg
:src="image"
loading="lazy"
fetch-priority="low"
class="group-hover:scale-105 transition-transform duration-200 absolute size-full object-cover z-10"
alt=""
/>
@@ -98,6 +100,8 @@ const createdAt = usePersianTimeAgo(new Date(date.value));
<NuxtImg
v-if="variant === 'lg'"
loading="lazy"
fetch-priority="low"
:src="image"
class="group-hover:scale-105 transition-transform duration-200 absolute size-full object-cover z-10"
alt=""
+4 -20
View File
@@ -41,6 +41,8 @@ const brands = ref([
HEYMLZ
</div>
<NuxtImg
loading="lazy"
fetch-priority="low"
src="/img/heymlz/heymlz-logo.png"
class="h-[25px] sm:h-[45px] invert"
/>
@@ -62,6 +64,8 @@ const brands = ref([
class="flex items-center px-6 sm:px-10 h-[90px] sm:h-[140px]"
>
<NuxtImg
loading="lazy"
fetch-priority="low"
:src="brand"
class="h-[25px] sm:h-[45px] opacity-25"
/>
@@ -70,23 +74,3 @@ const brands = ref([
</div>
</div>
</template>
<!-- <NuxtImg
src="/img/brands/brand-2.png"
class="h-[25px] sm:h-[45px]"
/>
<NuxtImg
src="/img/brands/brand-3.png"
class="h-[25px] sm:h-[45px]"
/>
<NuxtImg
src="/img/brands/brand-4.png"
class="h-[25px] sm:h-[45px]"
/>
<NuxtImg
src="/img/brands/brand-5.png"
class="h-[25px] sm:h-[45px]"
/>
<NuxtImg
src="/img/brands/brand-6.png"
class="h-[25px] sm:h-[45px]"
/> -->
@@ -44,6 +44,8 @@ const { colorObject } = useImageColor(`#category-image-${id.value}`);
:id="`category-image-${id}`"
class="group-hover:scale-105 transition-transform duration-200 absolute object-contain size-full"
:src="picture"
loading="lazy"
fetch-priority="low"
alt=""
/>
</Transition>
+4
View File
@@ -3,6 +3,8 @@
<NuxtImg
src="/img/footer-bg.jpg"
alt=""
loading="lazy"
fetch-priority="low"
class="absolute z-10 object-cover opacity-45"
:style="{
mask: 'linear-gradient(to bottom, black 0%, rgba(0,0,0,0) 100%',
@@ -13,6 +15,8 @@
<div class="flex items-center flex-col gap-8 pb-[10px] pt-[80px] lg:pt-[100px] lg:pb-[50px] justify-center">
<img
src="/img/heymlz/heymlz-small-idle.gif"
loading="lazy"
fetch-priority="low"
class="size-[150px] lg:size-[220px] rounded-full drop-shadow-2xl"
/>
<span class="font-bold text-2xl lg:text-5xl text-gradient bg-gradient-to-l from-blue-500 to-blue-700">
+3
View File
@@ -124,6 +124,9 @@ const isHomePage = computed(() => route.path === "/");
<div class="header-navbar-item flex items-center justify-end h-full">
<NuxtImg
src="/img/heymlz/heymlz-logomotion.gif"
preload
loading="eager"
fetch-priority="high"
class="h-2/3"
/>
</div>
@@ -35,24 +35,26 @@ const highlights = ref<Highlight[]>([
<template>
<section class="w-full border-t-[0.5px] border-slate-200">
<div
class="w-full py-[3rem] lg:py-[5rem] gap-8 sm:gap-12 xl:gap-0 container grid grid-cols-2 lg:grid-cols-4"
>
<template v-for="(highlight, index) in highlights" :key="index">
<div class="w-full py-[3rem] lg:py-[5rem] gap-8 sm:gap-12 xl:gap-0 container grid grid-cols-2 lg:grid-cols-4">
<template
v-for="(highlight, index) in highlights"
:key="index"
>
<div class="flex flex-col-center gap-[.75rem] px-5">
<div class="size-[70px] md:size-[100px] flex-center">
<NuxtImg :src="highlight.icon" class="w-full" />
<NuxtImg
:src="highlight.icon"
loading="lazy"
fetch-priority="low"
class="w-full"
/>
</div>
<div class="w-full flex-col-center gap-[.25rem]">
<span
class="typo-sub-h-sm lg:typo-sub-h-md text-black text-center"
>
<span class="typo-sub-h-sm lg:typo-sub-h-md text-black text-center">
{{ highlight.title }}
</span>
<p
class="text-slate-500 typo-p-xs lg:typo-p-sm mt-1 text-center"
>
<p class="text-slate-500 typo-p-xs lg:typo-p-sm mt-1 text-center">
{{ highlight.description }}
</p>
</div>
@@ -90,6 +90,8 @@ const changeSlide = (id: number) => {
<NuxtImg
class="absolute object-cover size-full"
:src="slide.image"
loading="lazy"
fetch-priority="low"
:alt="String(slide.id)"
/>
</div>
@@ -67,6 +67,8 @@ const parallaxStyle = computed(() => {
<NuxtImg
:id="`product-image-${id}`"
:src="picture"
loading="lazy"
fetch-priority="low"
class="group-hover:scale-105 transition-transform duration-200 size-full object-contain absolute inset-0"
alt="product-background"
/>
+2
View File
@@ -42,6 +42,8 @@ const onSlideChange = (swiper: SwiperClass) => {
:style="{
filter: 'drop-shadow(0px 0px 20px rgba(0, 0, 0, 0.4))',
}"
loading="lazy"
fetch-priority="low"
src="/img/heymlz/heymlz-category-seat.gif"
/>
<Swiper
+5
View File
@@ -214,6 +214,9 @@ onUnmounted(() => {
<NuxtImg
v-else
preload
loading="eager"
fetch-priority="high"
class="absolute inset-0 size-full object-cover"
:src="slide.image!"
:alt="slide.title"
@@ -265,6 +268,8 @@ onUnmounted(() => {
filter: 'drop-shadow(0px 0px 20px rgba(0, 0, 0, 0.4))',
}"
src="/img/heymlz/heymlz-seat.gif"
loading="lazy"
fetch-priority="low"
/>
<Button
+16 -15
View File
@@ -1,5 +1,4 @@
<script setup lang="ts">
// import
import { Swiper, SwiperSlide } from "swiper/vue";
@@ -7,7 +6,7 @@ import type { SwiperClass } from "swiper/react";
// types
type Props = {}
type Props = {};
// props
@@ -23,7 +22,6 @@ const swiper_instance = ref<SwiperClass | null>(null);
const onSwiper = (swiper: SwiperClass) => {
swiper_instance.value = swiper;
};
</script>
<template>
@@ -31,6 +29,8 @@ const onSwiper = (swiper: SwiperClass) => {
<NuxtImg
class="absolute size-full object-cover"
src="/img/hero-bg.jpg"
loading="lazy"
fetch-priority="low"
alt=""
/>
<div class="absolute bg-black/60 size-full z-10" />
@@ -50,15 +50,18 @@ const onSwiper = (swiper: SwiperClass) => {
>
<div class="flex justify-center items-center">
<div class="max-w-[900px] px-4 text-white flex flex-col items-center gap-4">
<Icon name="ci:instagram" size="28" class="**:stroke-white" />
<p class="text-base xs:text-lg sm:typo-h-6 lg:typo-h-5 !font-normal !leading-[150%] lg:leading-[175%] max-sm:px-4 sm:max-w-[600px] lg:max-w-[800px] text-center">
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با
استفاده از طراحان گرافیک است. چاپگرها و متون بلکه روزنامه و مجله
در ستون و سطرآنچنان که لازم.
<Icon
name="ci:instagram"
size="28"
class="**:stroke-white"
/>
<p
class="text-base xs:text-lg sm:typo-h-6 lg:typo-h-5 !font-normal !leading-[150%] lg:leading-[175%] max-sm:px-4 sm:max-w-[600px] lg:max-w-[800px] text-center"
>
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک
است. چاپگرها و متون بلکه روزنامه و مجله در ستون و سطرآنچنان که لازم.
</p>
<span class="typo-p-sm md:typo-p-xl text-center">
- منصور مرزبان
</span>
<span class="typo-p-sm md:typo-p-xl text-center"> - منصور مرزبان </span>
</div>
</div>
</SwiperSlide>
@@ -69,10 +72,8 @@ const onSwiper = (swiper: SwiperClass) => {
v-for="(i, index) in 6"
:class="swiper_instance?.realIndex === index ? 'bg-white' : 'bg-transparent'"
class="border border-white size-1.5 md:size-2 rounded-full transition-all duration-200"
>
</div>
></div>
</div>
</div>
</div>
</template>
</template>
+6 -6
View File
@@ -88,6 +88,8 @@ watch(
:src="homeData!.difreance_section.image1"
class="select-none absolute size-full object-cover transition-[filter] duration-250 brightness-[95%]"
:alt="homeData!.difreance_section.title1"
loading="lazy"
fetch-priority="low"
/>
<video
v-else
@@ -110,6 +112,8 @@ watch(
:src="homeData!.difreance_section.image2"
class="overlay-image select-none absolute object-cover size-full transition-[filter] duration-250 brightness-[95%]"
:alt="homeData!.difreance_section.title2"
loading="lazy"
fetch-priority="low"
/>
<video
v-else
@@ -126,9 +130,7 @@ watch(
:style="{
left: `${clipPathPercent}%`,
}"
:class="[
activeSlideVideo !== 'none' ? 'opacity-10' : '',
]"
:class="[activeSlideVideo !== 'none' ? 'opacity-10' : '']"
class="select-none w-[5px] sm:w-2 bg-black h-full absolute left-0 flex items-center justify-center transition-opacity duration-250"
>
<div
@@ -143,9 +145,7 @@ watch(
</div>
</div>
<div
class="absolute bottom-0 p-6 md:p-10 w-full flex justify-between items-end transition-opacity"
>
<div class="absolute bottom-0 p-6 md:p-10 w-full flex justify-between items-end transition-opacity">
<div
class="flex flex-col gap-2 text-black transition-opacity"
:class="activeSlideVideo === 'right' ? 'opacity-0' : ''"
@@ -106,6 +106,8 @@ const childImageVariants = {
<NuxtImg
:src="slide.background_image"
class="absolute size-full object-cover"
loading="lazy"
fetch-priority="low"
/>
</motion.div>
</template>
@@ -133,6 +135,8 @@ const childImageVariants = {
<NuxtImg
class="w-[130px] sm:w-[180px] lg:w-[250px] xl:w-[300px] z-20 mt-40"
:src="slide.image3"
loading="lazy"
fetch-priority="low"
alt=""
/>
</motion.div>
@@ -144,6 +148,8 @@ const childImageVariants = {
<NuxtImg
class="w-[130px] sm:w-[180px] lg:w-[250px] xl:w-[300px] z-20"
:src="slide.image2"
loading="lazy"
fetch-priority="low"
alt=""
/>
</motion.div>
@@ -155,6 +161,8 @@ const childImageVariants = {
<NuxtImg
class="w-[130px] sm:w-[180px] lg:w-[250px] xl:w-[300px] z-20 mt-40"
:src="slide.image1"
loading="lazy"
fetch-priority="low"
alt=""
/>
</motion.div>
@@ -222,6 +230,8 @@ const childImageVariants = {
>
<NuxtImg
src="/img/heymlz/heymlz-falling.gif"
loading="lazy"
fetch-priority="low"
class="absolute top-[101px] sm:top-[100px] lg:top-[117px] left-1/2 -translate-1/2 w-[200px] lg:w-[250px] drop-shadow-md"
/>
<Button
@@ -187,6 +187,8 @@ whenever(
<NuxtImg
class="size-[225px] sm:size-[250px] drop-shadow-2xl"
src="/img/heymlz/heymlz-small-idle.gif"
loading="lazy"
fetch-priority="low"
alt=""
/>
</div>
@@ -199,6 +201,8 @@ whenever(
<NuxtImg
class="size-[225px] sm:size-[250px] drop-shadow-2xl"
src="/img/heymlz/heymlz-small-idle.gif"
loading="lazy"
fetch-priority="low"
alt=""
/>
<div class="flex flex-col gap-4 items-center">
@@ -1,5 +1,5 @@
<script setup lang="ts">
import useGetAccount from '~/composables/api/account/useGetAccount';
import useGetAccount from "~/composables/api/account/useGetAccount";
// types
@@ -85,31 +85,34 @@ onMounted(() => {
src="/img/heymlz/heymlz-full-body.jpg"
class="size-full object-cover absolute"
alt="profile"
loading="lazy"
fetch-priority="low"
/>
<NuxtImg
v-else
:src="account?.profile_photo ?? ''"
class="size-full object-cover absolute"
loading="lazy"
fetch-priority="low"
alt="profile"
/>
</div>
<div
class="rounded-150 px-4 py-3 whitespace-pre-wrap overflow-hidden"
:class="
reverse
? 'bg-slate-100 text-slate-600'
: 'bg-black text-white'
"
:class="reverse ? 'bg-slate-100 text-slate-600' : 'bg-black text-white'"
>
<div
v-if="!loadingContent"
:id="`chat-message-content-${id}`"
class="typo-p-sm font-normal whitespace-pre-wrap"
v-html="content"
>
</div>
></div>
<Icon v-else name="svg-spinners:3-dots-bounce" size="20" />
<Icon
v-else
name="svg-spinners:3-dots-bounce"
size="20"
/>
</div>
</div>
</div>
@@ -127,6 +127,8 @@ const limitedComments = computed(() => {
>
<NuxtImg
src="/img/heymlz/heymlz-contact-us.gif"
loading="lazy"
fetch-priority="low"
class="w-[200px] lg:w-[300px] translate-y-[-25px]"
/>
<span class="text-xl text-black font-semibold translate-y-[-25px]"> هیچ نظری ثبت نشده است </span>
+7 -13
View File
@@ -1,5 +1,4 @@
<script lang="ts" setup>
// import
import type { ProductVariantProvideType } from "~/pages/product/[id].vue";
@@ -7,7 +6,6 @@ import type { ProductVariantProvideType } from "~/pages/product/[id].vue";
// provide / inject
const { selectedVariant } = inject("productVariant") as ProductVariantProvideType;
</script>
<template>
@@ -20,30 +18,26 @@ const { selectedVariant } = inject("productVariant") as ProductVariantProvideTyp
<Accordion />
</div>
<div class="w-full lg:w-[450px] xl:w-[600px]">
<div
class="w-full bg-slate-50 rounded-xl flex-col-center px-5 py-16 sm:p-[5rem] gap-[1.5rem]"
>
<div class="w-full bg-slate-50 rounded-xl flex-col-center px-5 py-16 sm:p-[5rem] gap-[1.5rem]">
<span class="typo-h-6 mb-8">داخل جعبه چیه؟</span>
<div
class="w-full grid grid-cols-2 gap-y-[1.5rem] sm:gap-x-[3rem]"
>
<div class="w-full grid grid-cols-2 gap-y-[1.5rem] sm:gap-x-[3rem]">
<div
v-for="inPackItem in selectedVariant!.in_pack_items"
class="w-full flex-col-center gap-[.75rem]"
>
<div
class="size-[6.25rem] rounded-full border-slate-200 bg-white flex-center"
>
<div class="size-[6.25rem] rounded-full border-slate-200 bg-white flex-center">
<div class="size-11 relative">
<NuxtImg
class="size-full absolute object-cover"
:src="inPackItem.cover"
:alt="inPackItem.item_title"
loading="lazy"
fetch-priority="low"
/>
</div>
</div>
<span class="text-black typo-p-md">
{{ inPackItem.item_title}}
{{ inPackItem.item_title }}
</span>
</div>
</div>
@@ -51,4 +45,4 @@ const { selectedVariant } = inject("productVariant") as ProductVariantProvideTyp
</div>
</div>
</section>
</template>
</template>
@@ -38,9 +38,7 @@ const profile = computed(() => {
});
const username = computed(() => {
return is_user.value
? `${account.value?.first_name} ${account.value?.last_name}`
: "ادمین پشتیبانی هی ملز";
return is_user.value ? `${account.value?.first_name} ${account.value?.last_name}` : "ادمین پشتیبانی هی ملز";
});
</script>
@@ -54,7 +52,12 @@ const username = computed(() => {
:class="is_user ? 'rounded-br-none' : 'rounded-bl-none'"
>
<div class="w-2/12 flex items-start justify-start">
<NuxtImg :src="profile" class="size-16 rounded-full" />
<NuxtImg
:src="profile"
loading="lazy"
fetch-priority="low"
class="size-16 rounded-full"
/>
</div>
<div class="w-10/12 flex flex-col items-start pt-2">
@@ -65,9 +68,7 @@ const username = computed(() => {
>
{{ timeAgo }}
</p>
<p
class="text-xs font-semibold text-dynamic-secondary line-clamp-1"
>
<p class="text-xs font-semibold text-dynamic-secondary line-clamp-1">
{{ username }}
</p>
</div>