用Gemini写了一个单页 TVL.COM.CN

/ 邓工 / 55阅读 / 0评论 / 分类: 网络

用Gemini写了一个单页 TVL.COM.CN,非常不错!

数据获取慢和失败的根本原因是因为我们使用了**“公共代理服务”**(如 allorigins 或 rss2json)来绕过跨域限制。这些公共服务是免费的,全球几万人共用,当然会拥堵、超时。

要实现秒开、稳定、实时,唯一的办法是建立直连

这需要您配合做一个极小的服务器修改(只需改动一行代码)。只要解除了您博客的“锁”(CORS限制),您的网页就可以直接读取数据,速度就是您打开博客的速度。


第一步:解锁您的博客服务器(必做)

请通过宝塔面板或 FTP,在您博客网站的根目录下找到 rss.php 文件(注意是 rss.php,不是 index.php,这个文件更独立,修改更安全)。

在文件最顶端<?php 的紧下面)加入这三行代码:

PHP
<?php
// --- 复制这三行开始 ---
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET");
header("Access-Control-Allow-Headers: *");
// --- 复制这三行结束 ---

/**
 * RSS输出
 * @copyright (c) Emlog All Rights Reserved
 */
// ... 下面是原有的代码,不要动

原理: 这几行代码的作用是告诉浏览器:“我是 hzpm.com,我允许 TV Labs 这个网页直接读取我的 RSS 数据,不要拦截它。”


第二步:使用直连版 HTML 代码

修改完服务器文件并保存后,使用下面这个版本的 HTML。

这个版本移除了所有代理,直连您的服务器,速度最快,且永不封号。

HTML
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
    <title>TV Labs | 视觉科学研究所</title>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Manrope:wght@300;400;500&family=Playfair+Display:ital,wght@0,400;0,600;1,400&display=swap" rel="stylesheet">

    <style>
        :root {
            --bg-color: #fcfcfc;
            --text-primary: #111111;
            --text-secondary: #666666;
            --menu-text-active: #0044cc;
        }

        * { margin: 0; padding: 0; box-sizing: border-box; -webkit-font-smoothing: antialiased; -webkit-tap-highlight-color: transparent; }

        body {
            background-color: var(--bg-color); color: var(--text-primary); font-family: 'Manrope', sans-serif;
            overflow: hidden; width: 100%; height: 100dvh; position: relative;
        }

        /* --- 1. 氛围质感 --- */
        .noise-overlay {
            position: fixed; top: 0; left: 0; width: 100%; height: 100%; pointer-events: none; z-index: 90; opacity: 0.05;
            background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E");
        }
        .optical-bg { position: fixed; top: 0; left: 0; width: 100vw; height: 100%; z-index: 1; background: #fff; overflow: hidden; }
        .orb { position: absolute; border-radius: 50%; filter: blur(80px); opacity: 0.5; animation: float 20s infinite ease-in-out; }
        .orb-1 { width: 80vw; height: 80vw; background: linear-gradient(120deg, #e0c3fc 0%, #8ec5fc 100%); top: -30%; left: -20%; }
        .orb-2 { width: 70vw; height: 70vw; background: linear-gradient(120deg, #f093fb 0%, #f5576c 100%); bottom: -30%; right: -20%; opacity: 0.3; animation-delay: -10s; }

        /* --- 2. 导航与Logo --- */
        nav {
            position: fixed; top: 0; left: 0; width: 100%; padding: 40px 50px;
            display: flex; justify-content: space-between; align-items: center;
            z-index: 999; mix-blend-mode: difference; color: #fff;
        }
        .logo-container { display: flex; align-items: center; gap: 12px; text-decoration: none; color: inherit; }
        .logo-mark { width: 24px; height: 24px; position: relative; }
        .logo-mark::before, .logo-mark::after {
            content: ''; position: absolute; width: 18px; height: 18px; border-radius: 50%; mix-blend-mode: exclusion;
        }
        .logo-mark::before { background: #fff; left: 0; top: 50%; transform: translateY(-50%); }
        .logo-mark::after { background: #fff; right: 0; top: 50%; transform: translateY(-50%); opacity: 0.7; }
        .logo-text { display: flex; flex-direction: column; line-height: 1; }
        .logo-text .tv { font-family: 'Playfair Display', serif; font-size: 1.4rem; font-weight: 700; font-style: italic; letter-spacing: -0.5px; }
        .logo-text .labs { font-family: 'Manrope', sans-serif; font-size: 0.5rem; font-weight: 600; letter-spacing: 3px; text-transform: uppercase; margin-left: 2px; opacity: 0.8; }
        .menu-btn { font-family: 'Manrope', sans-serif; font-size: 0.85rem; font-weight: 600; letter-spacing: 2px; text-transform: uppercase; cursor: pointer; color: inherit; position: relative; padding-bottom: 2px; }
        .menu-btn::after { content: ''; position: absolute; bottom: 0; left: 0; width: 0; height: 1px; background: currentColor; transition: width 0.3s; }
        .menu-btn:hover::after { width: 100%; }

        /* --- 3. 菜单遮罩 --- */
        .menu-overlay {
            position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(255, 255, 255, 0.75);
            backdrop-filter: blur(25px); -webkit-backdrop-filter: blur(25px); z-index: 500;
            display: flex; flex-direction: column; justify-content: center; align-items: center;
            opacity: 0; visibility: hidden; transition: all 0.5s cubic-bezier(0.16, 1, 0.3, 1);
        }
        .menu-overlay.active { opacity: 1; visibility: visible; }
        .menu-list { list-style: none; text-align: center; padding: 0; }
        .menu-list li { margin: 15px 0; overflow: hidden; }
        .menu-list a {
            display: block; font-family: 'Playfair Display', serif; font-size: 3.5rem; color: #111; text-decoration: none;
            transform: translateY(120%); transition: transform 0.6s cubic-bezier(0.16, 1, 0.3, 1), color 0.3s; line-height: 1.1;
        }
        .menu-list a span {
            display: block; font-family: 'Manrope', sans-serif; font-size: 0.8rem; letter-spacing: 3px;
            text-transform: uppercase; color: var(--text-secondary); margin-top: -5px; opacity: 0; transform: translateY(10px); transition: all 0.3s;
        }
        .menu-list a:hover { color: var(--menu-text-active); font-style: italic; }
        .menu-list a:hover span { opacity: 1; transform: translateY(0); }
        .menu-overlay.active .menu-list a { transform: translateY(0); }
        .menu-overlay.active li:nth-child(1) a { transition-delay: 0.1s; }
        .menu-overlay.active li:nth-child(2) a { transition-delay: 0.15s; }
        .menu-overlay.active li:nth-child(3) a { transition-delay: 0.2s; }
        .menu-overlay.active li:nth-child(4) a { transition-delay: 0.25s; }

        /* --- 4. 主视觉 --- */
        .hero {
            position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 10;
            display: flex; flex-direction: column; justify-content: center; align-items: center;
            text-align: center; padding: 0 20px; transition: filter 0.5s, transform 0.5s;
        }
        body.menu-open .hero { filter: blur(8px); transform: scale(0.95); }

        .hero-label { font-size: 0.8rem; letter-spacing: 4px; text-transform: uppercase; color: var(--text-secondary); margin-bottom: 3vh; opacity: 0; animation: fadeIn 2s ease 0.5s forwards; }
        .hero h1 { font-family: 'Playfair Display', serif; font-size: clamp(3rem, 10vw, 7.5rem); font-weight: 500; line-height: 1.1; margin-bottom: 2vh; color: #111; opacity: 0; animation: fadeInUp 1.5s ease 0.8s forwards; }
        .hero h1 i { font-family: 'Playfair Display', serif; font-weight: 400; color: var(--text-secondary); }
        .hero p { max-width: 500px; font-size: 1.05rem; line-height: 1.8; color: var(--text-secondary); opacity: 0; animation: fadeInUp 1.5s ease 1.1s forwards; }

        /* --- 5. 滚动文字 (光学聚焦) --- */
        .marquee {
            position: absolute; bottom: 30px; left: 0; width: 100%; overflow: hidden; z-index: 20; padding-bottom: env(safe-area-inset-bottom);
            opacity: 0; animation: fadeIn 1s ease 2s forwards; pointer-events: none;
        }
        .track { display: flex; width: max-content; animation: scroll 40s linear infinite; }
        .marquee:hover .track { animation-play-state: paused; }
        .content { display: flex; align-items: center; flex-shrink: 0; gap: 0; }
        .content a {
            display: inline-block; padding: 0 1.5vw; 
            font-family: 'Manrope', sans-serif; font-size: 0.8rem; text-transform: uppercase; letter-spacing: 2px; 
            color: #999; white-space: nowrap; text-decoration: none; pointer-events: auto; position: relative;
            transition: all 0.4s cubic-bezier(0.16, 1, 0.3, 1); 
        }
        .content a::before {
            content: ''; position: absolute; bottom: -2px; left: 0; width: 100%; height: 1px; 
            background-color: var(--menu-text-active); transform: scaleX(0); transform-origin: center;
            transition: transform 0.4s cubic-bezier(0.16, 1, 0.3, 1); opacity: 0.8;
        }
        .track:hover .content a { opacity: 0.4; filter: blur(0.8px); }
        .track:hover .content a:hover { color: var(--menu-text-active); opacity: 1; filter: blur(0); }
        .content a:hover::before { transform: scaleX(1); }
        .content a::after { content: "•"; color: #ddd; margin-left: 1.5vw; display: inline-block; text-decoration: none; pointer-events: none; opacity: 0.5; }

        @keyframes scroll { 0% { transform: translateX(0); } 100% { transform: translateX(-50%); } }
        @keyframes float { 0%, 100% { transform: translate(0, 0); } 50% { transform: translate(5%, 8%); } }
        @keyframes fadeInUp { from { opacity: 0; transform: translateY(30px); } to { opacity: 1; transform: translateY(0); } }
        @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } }

        @media (max-width: 768px) {
            nav { padding: 25px 20px; }
            .menu-list a { font-size: 2.2rem; }
            .hero { padding-bottom: 60px; }
            .hero h1 { margin-bottom: 15px; }
            .marquee { bottom: 0; padding: 15px 0 calc(15px + env(safe-area-inset-bottom)) 0; background: linear-gradient(to top, rgba(252,252,252,0.9) 0%, rgba(252,252,252,0) 100%); }
            .content a { font-size: 0.7rem; padding: 0 12px; }
            .content a::after { margin-left: 12px; }
            .logo-text .tv { font-size: 1.2rem; }
            .logo-mark { width: 20px; height: 20px; }
            .logo-mark::before, .logo-mark::after { width: 15px; height: 15px; }
        }
    </style>
</head>
<body>

    <div class="noise-overlay"></div>
    <div class="optical-bg">
        <div class="orb orb-1"></div>
        <div class="orb orb-2"></div>
    </div>

    <nav>
        <a href="#" class="logo-container">
            <div class="logo-mark"></div>
            <div class="logo-text">
                <span class="tv">TV</span>
                <span class="labs">LABS</span>
            </div>
        </a>
        <div class="menu-btn" onclick="toggleMenu()">Menu +</div>
    </nav>

    <div class="menu-overlay" id="menuOverlay">
        <ul class="menu-list">
            <li><a href="#">关于我们 <span>About</span></a></li>
            <li><a href="#">研究领域 <span>Research</span></a></li>
            <li><a href="#">实验室 <span>Labs</span></a></li>
            <li><a href="#">联系 <span>Contact</span></a></li>
        </ul>
    </div>

    <section class="hero">
        <div class="hero-label">The Institute of Vision Science</div>
        <h1>
            重塑视觉<br>
            <i>Reshaping Reality.</i>
        </h1>
        <p>
            探索光子、神经与数字像素的数学边界。<br>
            致力于下一代显示技术的底层物理研究。
        </p>

        <div class="marquee">
            <div class="track" id="marqueeTrack">
                <div class="content" id="contentA">
                    <a href="#">正在同步数据...</a>
                </div>
                <div class="content" id="contentB">
                    <a href="#">正在同步数据...</a>
                </div>
            </div>
        </div>
    </section>

    <script>
        function toggleMenu() {
            const overlay = document.getElementById('menuOverlay');
            const btn = document.querySelector('.menu-btn');
            const body = document.body;
            overlay.classList.toggle('active');
            body.classList.toggle('menu-open');
            btn.textContent = overlay.classList.contains('active') ? 'Close -' : 'Menu +';
        }

        // --- 极速直连模式 ---
        // 直接请求您的 RSS 文件,不经过任何中间商
        const DIRECT_RSS_URL = 'https://www.hzpm.com/rss.php';

        let lastHash = "";

        async function fetchBlogTitles() {
            try {
                // 1. 发起直连请求
                // 注意:前提是您已经修改了 rss.php 添加了 Header,否则这里会报错 CORS
                const response = await fetch(DIRECT_RSS_URL + '?t=' + new Date().getTime());

                if (!response.ok) throw new Error("Connection failed");

                const str = await response.text();

                // 2. 浏览器直接解析 XML
                const parser = new DOMParser();
                const xmlDoc = parser.parseFromString(str, "text/xml");
                const items = xmlDoc.querySelectorAll("item");

                let articles = [];

                if (items.length > 0) {
                    items.forEach((item) => {
                        const title = item.querySelector("title")?.textContent;
                        const link = item.querySelector("link")?.textContent;
                        if (title && link) articles.push({ title, link });
                    });
                } else {
                    throw new Error("No articles");
                }

                // 3. 智能比对更新
                const currentHash = JSON.stringify(articles);
                if (currentHash !== lastHash) {
                    updateMarquee(articles);
                    lastHash = currentHash;
                    console.log("Feed Updated via Direct Connection");
                }

            } catch (error) {
                console.error("Direct Fetch Error:", error);

                // 如果直连失败(通常是因为还没改 PHP 文件),显示提示
                if (lastHash === "") {
                    updateMarquee([
                        { title: "请修改服务器 rss.php 添加跨域Header", link: "#" },
                        { title: "Connection Blocked by CORS", link: "#" }
                    ]);
                }
            }
        }

        function updateMarquee(articles) {
            const contentA = document.getElementById('contentA');
            const contentB = document.getElementById('contentB');
            const displayArticles = articles.slice(0, 15);

            const htmlString = displayArticles.map(item => 
                `<a href="${item.link}" target="_blank">${item.title}</a>`
            ).join('');

            if (contentA.innerHTML !== htmlString) {
                contentA.innerHTML = htmlString;
                contentB.innerHTML = htmlString;
            }
        }

        document.addEventListener('DOMContentLoaded', fetchBlogTitles);
        setInterval(fetchBlogTitles, 30000); // 30秒极速轮询

    </script>
</body>
</html>

总结:

  1. 先做:去服务器修改 rss.php(加入那 3 行 PHP 代码)。

  2. 后做:使用上面这个新的 HTML。

  3. 结果:数据将直接从您的服务器飞到用户浏览器,没有任何中间环节,速度最快,且只要您发文章,这里 30 秒内就会自动同步。


扫描二维码,在手机上阅读
发表评论