Skip to content

Commit

Permalink
Loading animations
Browse files Browse the repository at this point in the history
Animated odometer loader
Added fade-in / fade-out animation to loader

Added fade-in animation to Navigation.vue and Gallery.vue
  • Loading branch information
ethanfox committed Jul 18, 2024
1 parent 1d63d22 commit e5fecf1
Show file tree
Hide file tree
Showing 6 changed files with 223 additions and 138 deletions.
Binary file modified bun.lockb
Binary file not shown.
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@
"format": "prettier --write src/"
},
"dependencies": {
"animate.css": "^4.1.1",
"axios": "^1.7.2",
"odometer": "^0.4.8",
"pinia": "^2.1.7",
"vue": "^3.4.29",
"vue-router": "^4.3.3"
"vue-router": "^4.3.3",
"vue3-odometer": "^0.1.3"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.8.0",
Expand Down
160 changes: 33 additions & 127 deletions src/components/Gallery.vue
Original file line number Diff line number Diff line change
@@ -1,148 +1,41 @@
<script setup>
import Navigation from "./Navigation.vue";
</script>
<template>
<div class="masonry-container p-8 relative">
<div v-for="artwork in artworks" :key="artwork.id" class="masonry-item">
<img
:src="artwork.image_url"
:alt="artwork.title"
v-if="artwork.image_url"
/>
<h2>{{ artwork.title }}</h2>
<p>Artist: {{ artwork.artist_title }}</p>
</div>

<div ref="bottom" class="bottom-detector"></div>
</div>
</template>

<script>
import axios from "axios";
export default {
data() {
return {
artworks: [],
loading: true,
error: null,
page: 1,
observer: null,
initialLoad: true,
};
},
props: ["artworks"],
mounted() {
this.fetchArtworks();
this.setupInfiniteScroll();
},
methods: {
async fetchArtworks() {
try {
const response = await axios.get(
"https://api.artic.edu/api/v1/artworks",
{
params: {
limit: 20,
page: this.page,
fields: "id,title,image_id,artist_title",
},
}
);
const newArtworks = response.data.data.map((artwork) => ({
...artwork,
image_url: `https://www.artic.edu/iiif/2/${artwork.image_id}/full/843,/0/default.jpg`,
size: this.getRandomSize(),
}));
this.artworks = [...this.artworks, ...newArtworks];
this.page++;
} catch (error) {
this.error = "An error occurred while fetching artworks";
console.error("Error fetching artworks:", error);
} finally {
setTimeout(() => {
this.loading = false;
this.initialLoad = false;
}, 3000); // 3000ms to allow for fade-out of loader
}
},
getRandomSize() {
const sizes = ["small", "medium", "large"];
return sizes[Math.floor(Math.random() * sizes.length)];
},
setupInfiniteScroll() {
this.observer = new IntersectionObserver(
const observer = new IntersectionObserver(
([entry]) => {
if (entry.isIntersecting && !this.loading) {
this.fetchArtworks();
if (entry.isIntersecting) {
this.$emit("load-more");
}
},
{ rootMargin: "200px" }
);
this.$nextTick(() => {
this.observer.observe(this.$refs.bottom);
});
observer.observe(this.$refs.bottom);
},
},
beforeUnmount() {
if (this.observer) {
this.observer.disconnect();
}
},
};
</script>

<template>
<div class="w-screen h-svh overflow-y-scroll relative bg-neutral-100">
<transition name="fade">
<div
v-if="loading && initialLoad"
key="loading"
class="w-screen h-screen flex bg-[#b50938]"
>
<div
class="my-auto mx-auto text-center justify-center w-full gap-2 flex"
>
<h1 class="text-8xl my-auto text-white font-medium">
Art Institute Chicago
</h1>
<h1
class="text-[16rem] tracking-tighter my-auto font-display text-white"
>
97%
</h1>
</div>
</div>
</transition>

<transition name="fade-second">
<div v-if="!loading && !initialLoad">
<div v-if="error">{{ error }}</div>
<div v-else>
<Navigation />
<div class="masonry-container p-8 relative">
<div v-for="artwork in artworks" :key="artwork.id">
<div class="masonry-item">
<img
:src="artwork.image_url"
:alt="artwork.title"
v-if="artwork.image_url"
/>
<h2>{{ artwork.title }}</h2>
<p>Artist: {{ artwork.artist_title }}</p>
</div>
</div>
</div>
</div>
<div ref="bottom" class="bottom-detector"></div>
<div v-if="loading && artworks.length > 0">Loading more...</div>
</div>
</transition>
</div>
</template>

<style scoped>
.fade-enter-active,
.fade-leave-active,
.fade-second-enter-active,
.fade-second-leave-active {
transition: opacity 1.5s;
}
.fade-enter-from,
.fade-leave-to,
.fade-second-enter-from,
.fade-second-leave-to {
opacity: 0;
}
.masonry-container {
column-count: 3; /* Adjust based on your preferred layout */
column-gap: 3rem;
Expand Down Expand Up @@ -186,6 +79,19 @@ export default {
@media (max-width: 800px) {
.masonry-item {
padding: 2rem;
animation: fadeSlideUp 1.5s forwards;
animation-delay: 6s;
opacity: 0; /* Ensure initial state is set correctly */
}
}
@keyframes fadeSlideUp {
0% {
opacity: 0;
transform: translateY(20px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
Expand Down
7 changes: 5 additions & 2 deletions src/components/Navigation.vue
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,12 @@
>
<a
class="flex-p-8 my-auto mx-auto font-semibold text-sm lg:text-lg hover:bg-neutral-200 px-6 object-center w-full h-full text-center flex content-center transition-all"
href="/visit"
><span class="mx-auto my-auto">Visit</span></a
href="https://www.artic.edu/visit"
target="_blank"
rel="noopener noreferrer"
>
<span class="mx-auto my-auto">Visit</span>
</a>
<a
class="flex-p-8 my-auto mx-auto font-semibold text-sm lg:text-lg px-4 object-center w-full h-full text-center flex content-center"
href="/about"
Expand Down
1 change: 1 addition & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import "./assets/style.css";

import { createApp } from "vue";
import { createPinia } from "pinia";
import "animate.css";

import App from "./App.vue";
import router from "./router";
Expand Down
Loading

0 comments on commit e5fecf1

Please sign in to comment.