Files
xiaomai 067f9d4828 feat: Update index.html and add media files for photo wall project
- Modified index.html to include favicon, title, and linked assets for Vite app.
- Added three new media files: LaguBangsaJohor.mp4, LaguNegaraku.mp4, and LaguTeoChew.mp4.
- Created nameList.json containing the names of the first founders with their status.
- Introduced demo/photoWall/v0/index.html for a dynamic carousel with background video and marquee text.
- Added demo/photoWall/v1/index.html for a photo wall layout with responsive design.
- Created demo/photoWall/v3/images.json and nameList.json for image and name data.
- Implemented demo/photoWall/v3/index.html with Vue.js for an interactive photo wall experience.
2025-11-09 23:38:01 +08:00

378 lines
12 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<title>动态翻页轮播图</title>
<style>
.carousel-container {
perspective: 1000px;
}
.carousel-item {
transition: transform 0.6s ease-in-out, opacity 0.6s ease-in-out;
transform-style: preserve-3d;
}
/* 响应式调整 */
@media (max-width: 768px) {
.carousel-container {
width: 100% !important;
height: 60vh !important;
}
.carousel-image {
width: 90% !important;
height: auto !important;
}
}
.marquee-text {
overflow: clip;
}
.marquee-text-track {
display: flex;
padding-left: 4.8rem;
gap: 4.8rem;
width: max-content;
animation: marquee-move-text 120s linear infinite;
}
@keyframes marquee-move-text {
to {
transform: translateX(-50%);
}
}
</style>
</head>
<body class="bg-red-500 min-h-screen flex items-center justify-center">
<div id="app">
<!-- 背景视频(在最底层)打开页面自动播放 -->
<video
src="assets/麦卉 - 前人种树后人凉《潮州劲歌金曲》.mp4"
class="absolute w-full h-full object-cover blur-2xl"
autoplay
loop
controls
></video>
<div
class="carousel-container w-screen h-screen flex items-center justify-center overflow-hidden"
>
<div class="relative w-full h-full flex items-center justify-center">
<!-- 轮播图片容器 -->
<div class="relative w-full h-full flex items-center justify-center">
<!-- 第一张图片 (prev) -->
<div
class="absolute carousel-item carousel-image h-172 w-7xl object-cover rounded-2xl shadow-2xl z-10"
:class="getImageClass(0)"
>
<img
:src="getImage(displayImages[0])"
:alt="`图片 ${displayImages[0] + 1}`"
class="w-full h-full object-cover rounded-2xl"
/>
</div>
<!-- 第二张图片 (current) -->
<div
class="absolute carousel-item carousel-image h-172 w-7xl object-cover rounded-2xl shadow-2xl z-20"
:class="getImageClass(1)"
>
<img
:src="getImage(displayImages[1])"
:alt="`图片 ${displayImages[1] + 1}`"
class="w-full h-full object-cover rounded-2xl"
/>
</div>
<!-- 第三张图片 (next) -->
<div
class="absolute carousel-item carousel-image h-172 w-7xl object-cover rounded-2xl shadow-2xl z-10"
:class="getImageClass(2)"
>
<img
:src="getImage(displayImages[2])"
:alt="`图片 ${displayImages[2] + 1}`"
class="w-full h-full object-cover rounded-2xl"
/>
</div>
<!-- 第四张图片 (helper) -->
<div
class="absolute carousel-item carousel-image h-172 w-7xl object-cover rounded-2xl shadow-2xl z-0"
:class="getImageClass(3)"
>
<img
:src="getImage(displayImages[3])"
:alt="`图片 ${displayImages[3] + 1}`"
class="w-full h-full object-cover rounded-2xl"
/>
</div>
</div>
<!-- 控制按钮 -->
<button
@click="prevSlide"
class="absolute left-4 z-30 bg-white/20 hover:bg-white/30 text-white p-3 rounded-full transition-all"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 19l-7-7 7-7"
/>
</svg>
</button>
<button
@click="nextSlide"
class="absolute right-4 z-30 bg-white/20 hover:bg-white/30 text-white p-3 rounded-full transition-all"
>
<svg
xmlns="http://www.w3.org/2000/svg"
class="h-6 w-6"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5l7 7-7 7"
/>
</svg>
</button>
<!-- 指示器 -->
<!-- <div class="absolute bottom-4 z-30 flex space-x-2">
<button
v-for="i in images.length"
:key="i"
@click="goToSlide(i-1)"
class="w-3 h-3 rounded-full transition-all"
:class="currentIndex === i-1 ? 'bg-white' : 'bg-white/50'"
></button>
</div> -->
</div>
</div>
<!-- Breaking News 滚动条 -->
<div
class="fixed top-0 left-0 w-full bg-red-700 text-yellow-400 text-2xl py-3 overflow-hidden z-50 breaking-news-container"
>
<div
class="absolute left-0 top-0 bg-red-900 text-yellow-100 px-4 py-3 font-bold z-10 breaking-news-label"
>
2005年第一届创办人名表
</div>
<div class="marquee-text">
<div class="marquee-text-track">
<span>|</span>
<span>楊吉陽(已故)</span>
<span>唐華(已故)</span>
<span>許仁菘(已故)</span>
<span>許汶信(已故)</span>
<span>劉炎松(已故)</span>
<span>楊淑清(已故)</span>
<span>黃芝芳(已故)</span>
<span>|</span>
<span>劉吉棟</span>
<span>許任隆</span>
<span>楊順發</span>
<span>吳祥森</span>
<span>林庭芝</span>
<span>林炳華</span>
<span>林庭珠</span>
<span>李玉媚</span>
<span>林應財</span>
<span>許斯杰</span>
<span>許敏捷</span>
<span>許智興</span>
<span>劉德祥</span>
<span>李豫梅</span>
<span>黄潮明</span>
<span>楊信陞</span>
<span>蔡立義</span>
<span>林炳龍</span>
<span>劉振昌</span>
<span>劉迪發</span>
<span>楊美雄</span>
<span>彭三媚</span>
<span>楊光豐</span>
<span>楊秀娥</span>
<span>莊秀清</span>
<span>李玉嬌</span>
<span>趙惜嬌</span>
<span>陳秀珠</span>
<span>張彩雁</span>
<span>劉暐康</span>
<span>王貴興</span>
<span>劉林順</span>
<span>劉益華</span>
<span>紀有平</span>
</div>
</div>
</div>
</div>
<script>
const { createApp, ref, computed, onMounted, onUnmounted } = Vue;
createApp({
setup() {
// 图片数组
const images = Array.from(
{ length: 25 },
(_, i) => `assets/image (${i + 1}).png`
);
// 当前索引
const currentIndex = ref(0);
// 显示图片的索引数组
const displayImages = ref([]);
// 初始化显示图片
const initializeDisplayImages = () => {
const len = images.length;
displayImages.value = [
(currentIndex.value - 1 + len) % len,
currentIndex.value,
(currentIndex.value + 1) % len,
(currentIndex.value + 2) % len,
];
};
// 获取图片URL
const getImage = (index) => images[index];
// 获取图片类名
const getImageClass = (position) => {
switch (position) {
case 0: // 上一张
return "scale-90 -translate-x-3/4 opacity-80";
case 1: // 当前
return "scale-100 translate-x-0 opacity-100";
case 2: // 下一张
return "scale-90 translate-x-3/4 opacity-80";
case 3: // 隐藏的搬运工
return "scale-90 translate-x-full opacity-0";
default:
return "";
}
};
// 轮播控制
const nextSlide = () => {
// 将第一个元素移到末尾
displayImages.value.push(displayImages.value.shift());
// 更新当前索引
currentIndex.value = (currentIndex.value + 1) % images.length;
// 更新最后一个元素(搬运工角色)
displayImages.value[3] = (currentIndex.value + 2) % images.length;
};
const prevSlide = () => {
// 将最后一个元素移到开头
displayImages.value.unshift(displayImages.value.pop());
// 更新当前索引
currentIndex.value =
(currentIndex.value - 1 + images.length) % images.length;
// 更新第一个元素(搬运工角色)
displayImages.value[0] =
(currentIndex.value - 1 + images.length) % images.length;
};
const goToSlide = (index) => {
const diff = index - currentIndex.value;
if (diff > 0) {
for (let i = 0; i < diff; i++) {
nextSlide();
}
} else if (diff < 0) {
for (let i = 0; i < Math.abs(diff); i++) {
prevSlide();
}
}
};
// 自动轮播
let autoPlayInterval = null;
const startAutoPlay = () => {
autoPlayInterval = setInterval(nextSlide, 3000);
};
const stopAutoPlay = () => {
if (autoPlayInterval) {
clearInterval(autoPlayInterval);
autoPlayInterval = null;
}
};
// 生命周期
onMounted(() => {
initializeDisplayImages();
startAutoPlay();
});
onUnmounted(() => {
stopAutoPlay();
});
return {
images,
currentIndex,
displayImages,
getImage,
getImageClass,
nextSlide,
prevSlide,
goToSlide,
startAutoPlay,
stopAutoPlay,
};
},
}).mount("#app");
// 监听用户的第一次交互(点击或触摸)
document.addEventListener("click", initVideo, { once: true });
document.addEventListener("touchstart", initVideo, { once: true });
function initVideo() {
const video = document.querySelector("video");
if (video) {
video.muted = false; // 允许声音播放
video.play().catch((err) => {
console.warn("视频无法自动播放:", err);
});
}
}
// Duplicate the marquee text track children node
// and make it aria-hidden to create a seamless loop
const marqueeTextTrack = document.querySelector(".marquee-text-track");
const childCount = marqueeTextTrack.children.length; // ✅ 固定长度
for (let i = 0; i < childCount; i++) {
const child = marqueeTextTrack.children[i];
const clone = child.cloneNode(true);
clone.setAttribute("aria-hidden", "true");
marqueeTextTrack.appendChild(clone);
}
</script>
</body>
</html>