feat: integrate animate.css for animations, enhance layout, and improve localization files

This commit is contained in:
yuanhau 2025-05-06 19:31:53 +08:00
parent 95661e908a
commit 2f20d49091
21 changed files with 252 additions and 172 deletions

View file

@ -1,11 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import "bootstrap-icons/font/bootstrap-icons.css"; import "bootstrap-icons/font/bootstrap-icons.css";
import "animate.css";
const { t } = useI18n(); const { t } = useI18n();
</script> </script>
<template> <template>
<!--A yhw_tw project--> <!--A yhw_tw project-->
<NuxtLayout> <NuxtLayout>
<NuxtPage/> <NuxtPage />
</NuxtLayout> </NuxtLayout>
</template> </template>

View file

@ -12,6 +12,7 @@
"@nuxtjs/tailwindcss": "6.14.0", "@nuxtjs/tailwindcss": "6.14.0",
"@tailwindcss/vite": "^4.1.5", "@tailwindcss/vite": "^4.1.5",
"@uploadthing/nuxt": "^7.1.7", "@uploadthing/nuxt": "^7.1.7",
"animate.css": "^4.1.1",
"bootstrap-icons": "^1.12.1", "bootstrap-icons": "^1.12.1",
"gsap": "^3.13.0", "gsap": "^3.13.0",
"nuxt": "^3.17.2", "nuxt": "^3.17.2",
@ -605,6 +606,8 @@
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="], "ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
"animate.css": ["animate.css@4.1.1", "", {}, "sha512-+mRmCTv6SbCmtYJCN4faJMNFVNN5EuCTTprDTAo7YzIGji2KADmakjVA3+8mVDkZ2Bf09vayB35lSQIex2+QaQ=="],
"ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="], "ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
"ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="], "ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],

View file

@ -1,3 +1,9 @@
<template> <template>
<div
class="flex flex-row justify-center align-center text-center absolute inset-x-0"
>
<span class="text-xs m-4"
>{{ new Date().getFullYear() }} &copy; 吳元皓</span
>
</div>
</template> </template>

View file

@ -1,62 +1,91 @@
<script setup lang="ts"> <script setup lang="ts">
import { gsap } from 'gsap'; import { gsap } from "gsap";
import { ScrambleTextPlugin } from 'gsap/dist/ScrambleTextPlugin'; import { ScrambleTextPlugin } from "gsap/dist/ScrambleTextPlugin";
gsap.registerPlugin(ScrambleTextPlugin) gsap.registerPlugin(ScrambleTextPlugin);
const { t, locale, locales } = useI18n(); const { t, locale, locales } = useI18n();
const switchLocalePath = useSwitchLocalePath() const switchLocalePath = useSwitchLocalePath();
const dropdownOpen = ref(false); const dropdownOpen = ref(false);
const title = ref(null); const title = ref(null);
const availableLocales = computed(() => { const availableLocales = computed(() => {
return locales.value.filter(i => i.code !== locale.value) return locales.value.filter((i) => i.code !== locale.value);
}) });
const toggleDropdown = () => { const toggleDropdown = () => {
dropdownOpen.value = !dropdownOpen.value; dropdownOpen.value = !dropdownOpen.value;
} };
const runAnimation = () => { const runAnimation = () => {
gsap.to(title.value, { gsap.to(title.value, {
duration: 1, duration: 1,
scrambleText: "BlindSpec", scrambleText: "BlindSpec",
}); });
} };
</script> </script>
<template> <template>
<!--Spent too much time trying to set a Navbar....--> <!--Spent too much time trying to set a Navbar....-->
<div class="fixed top-0 inset-x-0 bg-[#81611a]/70 backdrop-blur-sm h-[55px] flex align-center flex-row text-white pl-4 pt-2 gap-x-5 justify-between z-50 rounded-3xl m-2"> <div
class="fixed top-0 inset-x-0 bg-[#81611a]/70 backdrop-blur-sm h-[55px] flex align-center flex-row text-white pl-4 pt-2 gap-x-5 justify-between z-50 rounded-3xl m-2"
>
<div class="text-3xl text-bold"> <div class="text-3xl text-bold">
<!--Use mouseenter instead of hover--> <!--Use mouseenter instead of hover-->
<a href="/" @mouseenter="runAnimation" ref="title"> <a href="/" @mouseenter="runAnimation" ref="title"> BlindSpec </a>
BlindSpec
</a>
</div> </div>
<div class="text-[0.9em] text-center align-center"> <div class="text-[0.9em] text-center align-center">
<a href="/" class="hover:text-blue-500 cursor-pointer transiton-all duration-100">{{ t("nav.home") }}</a> <a
href="/"
class="hover:text-blue-500 cursor-pointer transiton-all duration-100"
>{{ t("nav.home") }}</a
>
&nbsp; &nbsp;
<a href="/dailybriefing" class="hover:text-blue-500 cursor-pointer transiton-all duration-100">{{ t("nav.dailybriefing") }}</a> <a
href="/dailybriefing"
class="hover:text-blue-500 cursor-pointer transiton-all duration-100"
>{{ t("nav.dailybriefing") }}</a
>
</div> </div>
<div class="relative"> <div class="relative">
<button @click="toggleDropdown" <button
class="flex items-center space-x-2 px-4 py-2 rounded hover:bg-gray-900 transition-all duration-100"> @click="toggleDropdown"
class="flex items-center space-x-2 px-4 py-2 rounded hover:bg-gray-900 transition-all duration-100"
>
<span>{{ locale }}</span> <span>{{ locale }}</span>
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <svg
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="w-4 h-4"
d="M19 9l-7 7-7-7"/> fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 9l-7 7-7-7"
/>
</svg> </svg>
</button> </button>
<div v-if="dropdownOpen" <Transition
class="absolute top-full left-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1"> enter-active-class="animate__animated animate__fadeInDown animate__faster"
<a v-for="loc in availableLocales" leave-active-class="animate__animated animate__fadeOutUp animate__faster"
>
<div
v-if="dropdownOpen"
class="absolute top-full right-0 mt-2 w-48 bg-white rounded-md shadow-lg py-1"
>
<a
v-for="loc in availableLocales"
:key="loc.code" :key="loc.code"
:href="switchLocalePath(loc.code)" :href="switchLocalePath(loc.code)"
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"> v-on:click="dropdownOpen = false"
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 transition-all duration-100"
>
{{ loc.name || loc.code }} {{ loc.name || loc.code }}
</a> </a>
</div> </div>
</Transition>
</div> </div>
</div> </div>
</template> </template>

4
i18n/i18n.config.ts Normal file
View file

@ -0,0 +1,4 @@
export default defineI18nConfig(() => ({
legacy: false,
locale: "en",
}));

View file

@ -3,7 +3,8 @@
"sitename": "BlindSpec" "sitename": "BlindSpec"
}, },
"nav": { "nav": {
"home": "Home" "home": "Home",
"dailybriefing": "Daily Briefing"
}, },
"dailybriefing": "Daily Briefing", "dailybriefing": "Daily Briefing",
"Welcome": "Welcome", "Welcome": "Welcome",

View file

@ -1,4 +0,0 @@
export default defineI18nConfig(() => ({
legacy: false,
locale: 'en',
}))

View file

@ -3,7 +3,8 @@
"sitename": "新聞盲點平台" "sitename": "新聞盲點平台"
}, },
"nav": { "nav": {
"home": "首頁" "home": "首頁",
"dailybriefing": "今日報導"
}, },
"dailybriefing": "今日報導", "dailybriefing": "今日報導",
"Welcome": "歡迎", "Welcome": "歡迎",

View file

@ -1,15 +1,15 @@
<script setup lang="ts"> <script setup lang="ts">
import Navigation from '~/components/navigation.vue'; import Navigation from "~/components/navigation.vue";
import Footer from "~/components/footer.vue"; import Footer from "~/components/footer.vue";
</script> </script>
<template> <template>
<nav> <nav>
<Navigation/> <Navigation />
</nav> </nav>
<main class="pt-[60px]"> <main class="pt-[60px]">
<slot/> <slot />
</main> </main>
<footer> <footer>
<Footer/> <Footer />
</footer> </footer>
</template> </template>

View file

@ -1,15 +1,15 @@
<script setup lang="ts"> <script setup lang="ts">
import Navigation from '~/components/navigation.vue'; import Navigation from "~/components/navigation.vue";
import Footer from "~/components/footer.vue"; import Footer from "~/components/footer.vue";
</script> </script>
<template> <template>
<nav> <nav>
<Navigation/> <Navigation />
</nav> </nav>
<main class="pt-[60px]"> <main class="pt-[60px]">
<slot/> <slot />
</main> </main>
<footer> <footer>
<Footer/> <Footer />
</footer> </footer>
</template> </template>

View file

@ -1,19 +1,25 @@
export default defineNuxtConfig({ export default defineNuxtConfig({
compatibilityDate: '2025-05-06', compatibilityDate: "2025-05-06",
devtools: { enabled: true }, devtools: { enabled: true },
routeRules: { routeRules: {
"/": { redirect: "/home"}, "/": { redirect: "/home" },
"/zh_tw": { redirect: "/zh_tw/home"}, "/zh_tw": { redirect: "/zh_tw/home" },
} , },
css: ['~/styles/main.css'], css: ["~/styles/main.css"],
modules: ['@nuxt/image', '@nuxtjs/robots', '@nuxtjs/seo', '@nuxtjs/i18n', "@nuxtjs/tailwindcss"], modules: [
"@nuxt/image",
"@nuxtjs/robots",
"@nuxtjs/seo",
"@nuxtjs/i18n",
"@nuxtjs/tailwindcss",
],
i18n: { i18n: {
defaultLocale: 'en', defaultLocale: "en",
vueI18n: './i18n.config.ts', vueI18n: "./i18n.config.ts",
locales: [ locales: [
{ code: 'en', name: 'English', file: 'en.json' }, { code: "en", name: "English", file: "en.json" },
{ code: 'zh_tw', name: 'Chinese Tradional', file: 'zh-tw.json' }, { code: "zh_tw", name: "Chinese Tradional", file: "zh-tw.json" },
] ],
}, },
site: { site: {
url: "https://news.yuanhau.com", url: "https://news.yuanhau.com",
@ -28,14 +34,17 @@ export default defineNuxtConfig({
{ charset: "utf-8" }, { charset: "utf-8" },
{ name: "viewport", content: "width=device-width, initial-scale=1" }, { name: "viewport", content: "width=device-width, initial-scale=1" },
{ name: "og:author", content: "@hpware on GitHub" }, { name: "og:author", content: "@hpware on GitHub" },
{ name: "og:author:email", content: "public+newscompareauthor@yuanhau.com" }, {
] name: "og:author:email",
} content: "public+newscompareauthor@yuanhau.com",
},
],
},
}, },
postcss: { postcss: {
plugins: { plugins: {
tailwindcss: {}, tailwindcss: {},
autoprefixer: {}, autoprefixer: {},
},// Add your content paths here }, // Add your content paths here
}, },
}) });

View file

@ -19,6 +19,7 @@
"@nuxtjs/tailwindcss": "6.14.0", "@nuxtjs/tailwindcss": "6.14.0",
"@tailwindcss/vite": "^4.1.5", "@tailwindcss/vite": "^4.1.5",
"@uploadthing/nuxt": "^7.1.7", "@uploadthing/nuxt": "^7.1.7",
"animate.css": "^4.1.1",
"bootstrap-icons": "^1.12.1", "bootstrap-icons": "^1.12.1",
"gsap": "^3.13.0", "gsap": "^3.13.0",
"nuxt": "^3.17.2", "nuxt": "^3.17.2",

View file

@ -1,7 +1,5 @@
<template> <template>
<div> <div>
<h1 class="text-3xl font-bold text-center"> <h1 class="text-3xl font-bold text-center"></h1>
</h1>
</div> </div>
</template> </template>

View file

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { gsap } from 'gsap'; import { gsap } from "gsap";
import { ScrambleTextPlugin } from 'gsap/dist/ScrambleTextPlugin'; import { ScrambleTextPlugin } from "gsap/dist/ScrambleTextPlugin";
gsap.registerPlugin(ScrambleTextPlugin) gsap.registerPlugin(ScrambleTextPlugin);
const loading = ref(true); const loading = ref(true);
const route = useRoute(); const route = useRoute();
const slug = route.params.slug; const slug = route.params.slug;
@ -11,7 +11,11 @@ definePageMeta({
layout: "newsorg", layout: "newsorg",
}); });
const { data: fetchNewsOrgInfo, pending, error } = useFetch("/api/getData/fetchNewsOrgInfo", { const {
data: fetchNewsOrgInfo,
pending,
error,
} = useFetch("/api/getData/fetchNewsOrgInfo", {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
@ -35,7 +39,7 @@ onMounted(() => {
gsap.to(orgNameAnimation.value, { gsap.to(orgNameAnimation.value, {
duration: 1, duration: 1,
scrambleText: fetchNewsOrgInfo.value?.title, scrambleText: fetchNewsOrgInfo.value?.title,
}); });
}); });
// Import Icons // Import Icons
@ -44,16 +48,32 @@ import { GlobeAltIcon } from "@heroicons/vue/24/outline";
<template> <template>
<div> <div>
<div class="text-center align-center justify-center"> <div class="text-center align-center justify-center">
<div class="flex flex-row bg-[#AAACAAFF] rounded-3xl p-3 gap-3 m-3"> <div
<NuxtImg :src="fetchNewsOrgInfo?.logoUrl" class="w-48 h-48 rounded-[10px]"/> class="flex flex-row bg-[#AAACAAFF] rounded-3xl p-3 gap-3 m-3 scale-5"
>
<NuxtImg
:src="fetchNewsOrgInfo?.logoUrl"
class="w-48 h-48 rounded-[10px]"
/>
<div class="flex flex-col gap-3 text-left"> <div class="flex flex-col gap-3 text-left">
<h1 class="text-4xl font-bold m-3 text-left">{{ fetchNewsOrgInfo?.title }}</h1> <h1 class="text-4xl font-bold m-3 text-left">
<span class="text-ms m-1 mt-5 text-left text-wrap">{{ fetchNewsOrgInfo?.description }}</span> {{ fetchNewsOrgInfo?.title }}
</h1>
<span class="text-ms m-1 mt-5 text-left text-wrap">{{
fetchNewsOrgInfo?.description
}}</span>
</div> </div>
</div> </div>
<div class="gap-[3px] flex flex-row text-center align-center justify-center"> <div
<a :href="fetchNewsOrgInfo?.website" target="_blank" class="text-blue-200 hover:text-blue-300 transiton-all duration-100 flex flex-row"><GlobeAltIcon class="w-6 h-6" />網站</a> class="gap-[3px] flex flex-row text-center align-center justify-center"
>
<a
:href="fetchNewsOrgInfo?.website"
target="_blank"
class="text-blue-200 hover:text-blue-300 transiton-all duration-100 flex flex-row"
><GlobeAltIcon class="w-6 h-6" />網站</a
>
</div> </div>
</div> </div>
</div> </div>

View file

@ -1,4 +1,4 @@
export default defineEventHandler(async(event) => { export default defineEventHandler(async (event) => {
const body = await readBody(event); const body = await readBody(event);
return { return {
body: body, body: body,
@ -7,8 +7,7 @@ export default defineEventHandler(async(event) => {
website: "https://www.taisounds.com.tw", website: "https://www.taisounds.com.tw",
description: "wah wah wah wah wah wah I dont fucking care", description: "wah wah wah wah wah wah I dont fucking care",
facebook: "https://www.facebook.com/taisounds", facebook: "https://www.facebook.com/taisounds",
logoUrl: "https://cdn.discordapp.com/avatars/918723093646684180/4eecc27ac05ee8a701fa167808610c7a.jpg", logoUrl:
} "https://cdn.discordapp.com/avatars/918723093646684180/4eecc27ac05ee8a701fa167808610c7a.jpg",
}) };
});

View file

@ -2,5 +2,9 @@ import s3 from "~/server/components/s3";
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
const slug = getRouterParam(event, "slug"); const slug = getRouterParam(event, "slug");
return sendRedirect(event, `${process.env.S3_ENDPOINT}/${process.env.S3_BUCKETNAME}/${slug}`, 302); return sendRedirect(
}) event,
`${process.env.S3_ENDPOINT}/${process.env.S3_BUCKETNAME}/${slug}`,
302,
);
});

View file

@ -1,3 +1 @@
export default defineEventHandler(async () => { export default defineEventHandler(async () => {});
});

View file

@ -9,4 +9,4 @@
body { body {
@apply bg-black m-0 p-0 min-h-screen text-white; @apply bg-black m-0 p-0 min-h-screen text-white;
} }
} }

View file

@ -1,4 +1,15 @@
/** @type {import('tailwindcss').Config} */ /** @type {import('tailwindcss').Config} */
module.exports = { module.exports = {
content: [],
} theme: {
extend: {
backgroundColor: {
page: "#000000",
},
},
},
plugins: [],
corePlugins: {
preflight: true,
},
};