简介

自从iOS 26 的液态玻璃设计火了之后,就一直想把手底下的网站改一改样式,思来想去只有网盘最合适,刚好用的就是openlist。于是就修改为玻璃拟态主题,并加入了视频背景和自定义底部内容。

特点

  • 玻璃拟态效果:所有 UI 元素采用高级玻璃质感设计
  • 动态视频背景:支持响应式视频背景(桌面/移动端自适应)
  • SVG 滤镜特效:使用高级 SVG 滤镜实现液体玻璃效果
  • 暗色模式支持:完美适配 Hope UI 暗色模式
  • 响应式设计:全面适配桌面和移动设备
  • 优雅动画:平滑过渡和悬停效果

详细步骤

  1. 登录 OpenList/AList 管理后台
  2. 进入「设置」→「全局」→「自定义头部」将以下内容添加到对应位置

<style type="text/css">
body {
  /* 手机端背景图 */
  --mobile-background-image: url(https://csblog.chario.cn/usr/uploads/2026/03/哈妮克孜05-scaled-1.jpg;
  /* 电脑端背景图 */
  --desktop-background-image: url(https://csblog.chario.cn/usr/uploads/2026/03/哈妮克孜10-scaled-1.jpg;
}
.hope-ui-dark .markdown-body a{color:var(--hope-colors-loContrast) !important}html ::selection{background-color:var(--hope-colors-accent8);color:var(--hope-colors-loContrast)}body{background-image:var(--desktop-background-image) !important;background-repeat:no-repeat !important;background-size:cover !important;background-attachment:fixed !important;background-position-x:center !important}@media screen and (max-width: 960px){body{background-image:var(--mobile-background-image) !important}}.video-container{position:fixed;top:0;left:0;width:100%;height:100%;z-index:-1;overflow:hidden}.video-container video{min-width:100%;min-height:100%;width:auto;height:auto;position:absolute;top:50%;left:50%;transform:translate(-50%, -50%);object-fit:cover}.hope-c-PJLV-idaeksS-css,.hope-c-PJLV-ikaMhsQ-css{background:none !important}.hope-c-PJLV-idaeksS-css{border-radius:var(--hope-radii-full);overflow:hidden;padding:var(--hope-space-1) var(--hope-space-2)}.hope-c-khZXrc{display:inline-block !important}.hope-c-cPYwgm{text-overflow:ellipsis;display:-webkit-box !important;-webkit-line-clamp:1;-webkit-box-orient:vertical;white-space:normal;word-break:break-all}.hope-c-iIOWzi{padding:0 var(--hope-space-1) !important}.hope-c-PJLV-igScBhH-css,.hope-c-hOYTCS,.hope-c-PJLV-idaeksS-css,.hope-c-PJLV-ieTGfmR-css,.header-right.hope-stack>div,.hope-c-PJLV-ijgzmFG-css,.hope-c-PJLV-ikJQsXT-css,.hope-c-zbPwS,.hope-c-XJURY,.hope-c-ivMHWx-fbcPgu-cv,.hope-c-ivMHWx-dvmlqS-cv,.hope-c-ivMHWx-dMllzy-cv,.hope-c-cFbQhW,.hope-c-ivMHWx-knrFJ-cv,.hope-c-ivMHWx-gHYUvy-cv,.hope-c-PJLV-iigjoxS-css,.solid-contextmenu,.hope-c-PJLV-idusLCn-css,.hope-c-ivMHWx-eHkSxq-cv,.hope-c-kvTTWD-hYRNAb-variant-filled,.hope-c-ivMHWx:disabled,.hope-c-mHASU-byiOue-variant-filled,.hope-c-mHASU-kukbfD-variant-outline,.hope-c-PJLV-ihVEsOa-css,.hope-c-gIqhpo,.hope-c-PJLV-idMNcPR-css,.hope-c-PJLV-ieGWMbI-css,.hope-c-PJLV-iZZmce-css,.hope-c-PJLV-ibcBsNO-css .hope-c-PJLV-iubUra-css,.hope-c-PJLV-ibcBsNO-css .hope-c-PJLV-ifjOQLV-css,.hope-c-cIFneQ,.hope-c-PJLV-ikAHMBe-css{background-color:transparent !important;box-shadow:inset 1px 1px 0px 0px rgba(255,255,255,0.5),inset -1px -1px 0px 0px rgba(255,255,255,0.6) !important;cursor:grab;backdrop-filter:url(#liquid-glass-filter) blur(20px) saturate(1.4) !important;pointer-events:auto;border:none !important}.header-right.hope-stack>div{height:var(--hope-sizes-8);border:none !important;padding:var(--hope-space-1) var(--hope-space-2) !important}.hope-c-PJLV-ijSQbqe-css{background-color:var(--hope-colors-blackAlpha3) !important;border-color:rgba(234,234,234,0.5) !important}.hope-c-PJLV-iciJSBF-css{opacity:0.5}.markdown-body .highlight pre,.markdown-body pre{background-color:var(--hope-colors-blackAlpha3) !important;backdrop-filter:blur(var(--hope-space-3))}.hope-c-gUeVCo[data-active],.hope-c-kRwRnM-gSazcJ-colorScheme-neutral[data-active]{background-color:var(--hope-colors-blackAlpha3) !important}.hope-c-ctSAUo,.hope-c-bICGYT-dWksIc-scrollBehavior-outside{background-color:var(--hope-colors-blackAlpha6) !important;backdrop-filter:blur(8px)}.footer{opacity:0;transition:opacity .3s ease-in-out}.copyright{font-size:14px;text-align:center}.copyright .copyright-links{display:flex;align-items:center;justify-content:center;gap:var(--hope-space-2)}.copyright .copyright-desc{line-height:2}.copyright a{transition:color .3s ease-in-out}.copyright a:hover{color:var(--hope-colors-accent8)}.hope-c-PJLV-ihWgyFw-css,.hope-c-PJLV-ibiABng-css{display:none !important}
</style>

  1. 进入「设置」→「全局」→「自定义内容」将以下内容添加到对应位置

<!-- 视频背景 -->
<div class="video-container"><video id="bg-video" autoplay muted loop playsinline><source type="video/mp4"></video></div>
<!-- SVG -->
<svg style="display:none"><filter id="liquid-glass-filter" x="0%" y="0%" width="100%" height="100%" filterUnits="objectBoundingBox"><feTurbulence type="fractalNoise" baseFrequency="0.01 0.01" numOctaves="1" seed="5" result="turbulence"></feTurbulence><feComponentTransfer in="turbulence" result="mapped"><feFuncR type="gamma" amplitude="1" exponent="10" offset="0.5"></feFuncR><feFuncG type="gamma" amplitude="0" exponent="1" offset="0"></feFuncG><feFuncB type="gamma" amplitude="0" exponent="1" offset="0.5"></feFuncB></feComponentTransfer><feGaussianBlur in="turbulence" stdDeviation="3" result="softMap"></feGaussianBlur><feSpecularLighting in="softMap" surfaceScale="5" specularConstant="1" specularExponent="100" lighting-color="white" result="specLight"><fePointLight x="-200" y="-200" z="300"></fePointLight></feSpecularLighting><feComposite in="specLight" operator="arithmetic" k1="0" k2="1" k3="1" k4="0" result="litImage"></feComposite><feDisplacementMap in="SourceGraphic" in2="softMap" scale="150" xChannelSelector="R" yChannelSelector="G"></feDisplacementMap></filter></svg>
<!-- JS -->
<script type="text/javascript">
// 配置项
const config = Object.freeze({
  video: Object.freeze({
    mobileSource: 'https://csblog.chario.cn/usr/uploads/2026/03/small.mp4',  // 手机端视频源地址
    desktopSource: 'https://csblog.chario.cn/usr/uploads/2026/03/large.mp4',  // 桌面端视频源地址
    elementId: 'bg-video', // 视频元素ID
    breakpoint: 768 // 响应式断点
  }),
  footer: Object.freeze({
    selector: '.footer', // 页脚选择器
    maxAttempts: 10, // 最大重试次数(默认10次)
    retryInterval: 1000, // 重试间隔(默认1000ms)
    blog: 'https://blog.chario.cn/',  // 博客地址,不填则不显示
    ziyuan: 'https://www.chario.cn/'  // 新增资源站地址
  })
});
const DeviceType=Object.freeze({MOBILE:"mobile",DESKTOP:"desktop"}),FILE_EXTENSION_REGEX=/\/[^/]+\.[a-zA-Z0-9]{1,10}(?:$|\/)/;function hasFileExtension(url){try{return FILE_EXTENSION_REGEX.test(new URL(url).pathname)}catch(error){return console.error("[URL] URL解析错误:",error),!1}}function debounce(func,wait=250){let timeout;return function(...args){clearTimeout(timeout),timeout=setTimeout(()=>func.apply(this,args),wait)}}class BackgroundVideoManager{constructor(){this.videoElement=document.getElementById(config.video.elementId),this.sourceElement=this.videoElement?.querySelector("source"),this.lastSource="",this.lastDeviceType=null,this.videoElement&&this.sourceElement?this.videoElement.addEventListener("loadeddata",this.handleVideoLoad.bind(this)):console.warn("[VideoManager] 视频元素未找到,视频功能将禁用")}init(){if(!this.videoElement)return;this.switchVideoSource();const resizeHandler=debounce(()=>this.switchVideoSource());window.addEventListener("resize",resizeHandler),document.addEventListener("visibilitychange",this.handleVisibilityChange.bind(this))}handleVideoLoad(){hasFileExtension(window.location.href)||this.playVideo().catch(e=>console.debug("[VideoManager] 视频自动播放失败:",e))}handleVisibilityChange(){"visible"!==document.visibilityState||hasFileExtension(window.location.href)||this.playVideo().catch(e=>console.debug("[VideoManager] 可见性恢复播放失败:",e))}getDeviceType(){return window.innerWidth<config.video.breakpoint||window.matchMedia("(pointer: coarse)").matches?DeviceType.MOBILE:DeviceType.DESKTOP}switchVideoSource(){const deviceType=this.getDeviceType();if(deviceType===this.lastDeviceType)return;this.lastDeviceType=deviceType;const newSource=deviceType===DeviceType.MOBILE?config.video.mobileSource:config.video.desktopSource;newSource!==this.lastSource&&(this.sourceElement.src=newSource,this.lastSource=newSource,this.videoElement.load())}playVideo(){return this.videoElement.play().catch(e=>{console.warn("[VideoManager] 视频播放被阻止:",e)})}pauseVideo(){this.videoElement.pause()}}class FooterManager{constructor(){this.currentYear=(new Date).getFullYear(),this.attemptCount=0,this.intervalId=null,this.extractedLinks=null}init(){this.tryRenderFooter()||(this.intervalId=setInterval(()=>{if(this.attemptCount++>=config.footer.maxAttempts)return this.cleanup(),void console.warn(`[FooterManager] 页脚元素未找到,已尝试 ${config.footer.maxAttempts} 次,停止尝试`);this.tryRenderFooter()&&this.cleanup()},config.footer.retryInterval))}tryRenderFooter(){const footerElement=document.querySelector(config.footer.selector);return!!footerElement&&(this.extractedLinks||(this.extractedLinks=this.extractLinksFromFooter(footerElement)),footerElement.innerHTML=this.getFooterHtml(),footerElement.style.opacity="1",!0)}extractLinksFromFooter(footerElement){try{const links=footerElement.querySelectorAll("a[href]");let loginUrl="",manageUrl="",isLoggedIn=!1;return links.forEach(link=>{const href=link.getAttribute("href"),text=link.textContent.trim().toLowerCase();text.includes("登录")||href?.includes("login")?loginUrl=href:(text.includes("管理")||href?.includes("manage"))&&(manageUrl=href)}),manageUrl?isLoggedIn=!0:loginUrl&&(isLoggedIn=!1),loginUrl||manageUrl?{loginUrl:loginUrl,manageUrl:manageUrl,isLoggedIn:isLoggedIn}:(console.warn("[FooterManager] 未从现有footer中提取到登录或管理链接"),null)}catch(error){return console.warn("[FooterManager] 提取footer链接失败:",error),null}}cleanup(){this.intervalId&&(clearInterval(this.intervalId),this.intervalId=null)}getFooterHtml(){const links=this.extractedLinks||{loginUrl:"",manageUrl:"",isLoggedIn:!1},linkElements=[];config.footer.ziyuan&&config.footer.ziyuan.trim()&&linkElements.push(`<a href="${config.footer.ziyuan}" target="_blank">资源站</a>`),config.footer.blog&&config.footer.blog.trim()&&linkElements.push(`<a href="${config.footer.blog}" target="_blank">博客</a>`),links.isLoggedIn&&links.manageUrl?linkElements.push(`<a href="${links.manageUrl}">管理</a>`):!links.isLoggedIn&&links.loginUrl&&linkElements.push(`<a href="${links.loginUrl}">登录</a>`);const linksHtml=linkElements.length>0?linkElements.join(" | "):"";return`\n        <div class="copyright">\n          ${linksHtml?`<div class="copyright-links">${linksHtml}</div>`:""}\n          <div class="copyright-desc">\n            <p>免责声明:本站为个人网盘,网盘所发布的一切影视、源代码、注册信息及软件等资源仅限用于学习和研究目的</p>\n            <p>Copyright ©2024 - ${this.currentYear} All rights Reserved. | 由OpenList驱动</p>\n          </div>\n        </div>\n      `}}function createUrlChangeHandler(videoManager){return debounce(()=>{const hasExtension=hasFileExtension(window.location.href);document.body.classList.toggle("has-file-extension",hasExtension),videoManager&&videoManager.videoElement&&(hasExtension?videoManager.pauseVideo():videoManager.playVideo())},100)}function init(){const videoManager=new BackgroundVideoManager;videoManager.init();(new FooterManager).init();const urlChangeHandler=createUrlChangeHandler(videoManager);urlChangeHandler();["hashchange","popstate","pushState","replaceState"].forEach(event=>{if("pushState"===event||"replaceState"===event){const original=history[event];history[event]=function(){const result=original.apply(this,arguments);return urlChangeHandler(),result}}else window.addEventListener(event,urlChangeHandler)})}"loading"!==document.readyState?init():document.addEventListener("DOMContentLoaded",init);
</script>

  1. 保存设置并刷新页面查看效果

自定义配置

您可以根据需要修改以下配置:

  1. 视频源:
    • 修改 config.video.mobileSource 和 config.video.desktopSource 替换为您的视频链接

  2. 背景图片(防止视频失效):
    • 在 CSS 中修改 --mobile-background-image 和 --desktop-background-image 变量值

  3. 页脚信息:
    • 在 config.footer.blog 中修改博客链接
    • 在 getFooterHtml() 方法中自定义页脚 HTML

效果展示

  • 移动设备(小屏):
  • 桌面设备(大屏):

前言

typecho版本 : 1.2

主题 : handsome

我在使用handsome主题的时光机功能的时候,发现使用 img 标签显示的图片,全都无法显示。

cbe91770642110.png

de821770642135.png

我琢磨了半天,最后,在handsome主题文档中找到了解决办法。

解决方案

这个问题产生的原因是 Typecho默认 用户不能在评论中插入图片或html代码

需要在 '网站后台→网站设置→评论→允许使用的HTML标签和属性' 中添加以下标签

'''<img src=""><audio class="" src="" preload><video src=""></video>'''

在添加代码之后,评论(时光机)就可以显示图片了

typecho 默认的数据库不支持emoji,所以需要通过一下方式修改数据库的编码。

如果你的数据库不支持emoji,但是在文章编辑中或者主题设置中使用了emoji会导致你的内容丢失!

alter table typecho_comments convert to character set utf8mb4 collate utf8mb4_unicode_ci;
alter table typecho_contents convert to character set utf8mb4 collate utf8mb4_unicode_ci;
alter table typecho_fields convert to character set utf8mb4 collate utf8mb4_unicode_ci;
alter table typecho_metas convert to character set utf8mb4 collate utf8mb4_unicode_ci;
alter table typecho_options convert to character set utf8mb4 collate utf8mb4_unicode_ci;
alter table typecho_relationships convert to character set utf8mb4 collate utf8mb4_unicode_ci;
alter table typecho_users convert to character set utf8mb4 collate utf8mb4_unicode_ci;

执行上述sql语句来修改表的编码,如果用的是宝塔面板和MySQL数据库,一般有可视化的phpmyadmin界面,可以在phpmyadmin的控制台里面执行上面的语句,如果是服务器可以在服务器上连接mysql后执行。

最后将 Typecho 目录下的config.inc.php 配置文件中数据库定义参数中的 charset 为 utf8mb4

$db->addServer(array (
      'host'      =>  localhost, 
      'user'      =>  'root',
      'password'  =>  'root',
      'charset'   =>  'utf8mb4', //修改这一行
      'port'      =>  3306,
      'database'  =>  ''
  ), Typecho_Db::READ | Typecho_Db::WRITE);

如果还是不行,进入数据库管理软件phpmyadmin,选中要改的数据库,点击上方的操作,下滑找到排序规则,将选项改为utf8mb4_unicode_ci,记得选中下方选项框

效果预览

添加此功能后,您可以在博客侧边栏或页脚等位置实时显示当前网站的在线访客数量,如下图所示:

实现步骤

步骤一:添加统计函数代码

  • 默认主题:/usr/themes/default/functions.php
  • Handsome主题:/usr/themes/handsome/functions.php

在文件末尾添加以下代码:

/**
 * 在线人数统计函数
 */
function online_users() {
    // 基本配置参数
    $filename = 'online.txt';        // 数据文件路径,默认在网站根目录
    $cookiename = 'Typecho_Online';  // Cookie名称,用于标识访客
    $onlinetime = 30;                // 在线有效时间(秒),超过此时间未活动则视为离线
  
    // 读取在线用户数据文件
    $online = file_exists($filename) ? file($filename) : array();
    $nowtime = $_SERVER['REQUEST_TIME']; 
    $nowonline = array(); 
  
    // 清理过期的在线记录
    foreach($online as $line) { 
        $row = explode('|', $line); 
        $sesstime = trim($row[1]); 
        // 仅保留有效期内的记录
        if(($nowtime - $sesstime) <= $onlinetime) {
            $nowonline[$row[0]] = $sesstime;
        } 
    } 
  
    // 处理当前访客的Cookie
    if(isset($_COOKIE[$cookiename])) {
        // 已有Cookie,获取访客ID
        $uid = $_COOKIE[$cookiename]; 
    } else {
        // 无Cookie,生成新的唯一访客ID
        $vid = 0;
        do {
            $vid++; 
            $uid = 'U'.$vid; 
        } while(array_key_exists($uid, $nowonline)); 
  
        // 设置Cookie,默认会话期有效
        setcookie($cookiename, $uid); 
    } 
  
    // 更新当前访客的在线时间
    $nowonline[$uid] = $nowtime;
  
    // 计算在线总人数
    $total_online = count($nowonline); 
  
    // 将更新后的数据写回文件
    if($fp = @fopen($filename, 'w')) { 
        if(flock($fp, LOCK_EX)) { // 获取独占锁
            rewind($fp); 
            foreach($nowonline as $fuid => $ftime) { 
                $fline = $fuid.'|'.$ftime."\n"; 
                @fputs($fp, $fline); 
            } 
            flock($fp, LOCK_UN); // 释放锁
        } 
        fclose($fp);
    } 
  
    return $total_online; 
}

步骤二:在模板中显示在线人数

通用方法(适用于任何位置)

在需要显示在线人数的模板文件中,添加以下代码:

<div class="online-users">
    当前在线: <?php echo online_users(); ?> 人
</div>

Handsome主题侧边栏添加方法

  1. 编辑文件:/usr/themes/handsome/component/sidebar.php
  2. 在适当位置(如"统计信息"部分)添加:

<li class="list-group-item"><div class="text-second">
<span class="blog-info-icons"> 
<i data-feather="user"></i></span>
<span class="badge pull-right"><?php echo online_users() ?>
</span><?php _me("在线人数") ?></li>

页脚添加方法

编辑主题的footer.php文件,在合适位置添加:

<div class="site-info">
    <!-- 其他页脚信息 -->
    <span class="online-count">当前在线: <?php echo online_users(); ?> 人</span>
</div>

转载自Xuan's blog

文章标题:Typecho添加实时在线人数统计功能 本文地址:https://blog.ybyq.wang/archives/331.html