前言

handsome 主题是一款精心设计的 typecho 主题,它在保持功能丰富的同时又不失简洁优雅。本文将介绍一些实用的美化技巧,帮助你打造一个独具特色的博客。

美化教程

右侧导航栏

打开后台-更改外观-设置外观-开发者设置-复制代码粘贴至自定义CSS即可

/*右侧导航栏*/
.sidebar-icon svg.feather.feather-thumbs-up {
  color: #ff0000;
}
.sidebar-icon svg.feather.feather-message-square {
  color: #495dc3;
}
.sidebar-icon svg.feather.feather-gift {
  color: #52de97;
}

主题标题居中

打开后台-更改外观-设置外观-开发者设置-复制代码粘贴至自定义CSS即可

/*主题标题居中*/
header.bg-light.lter.wrapper-md {
  text-align: center;
}

页面两侧框架留白间距
打开后台-更改外观-设置外观-开发者设置-复制代码粘贴至自定义CSS即可

/*两侧框架留白间距*/
@media (min-width: 1700px) {
  .app.container {
    width: 95%;
  }

  .app.container .app-aside,
  .app.container .app-header {
    max-width: 95%;
  }
}

@media (min-width: 2048px) {
  .app.container {
    width: 93%;
  }

  .app.container .app-aside,
  .app.container .app-header {
    max-width: 93%;
  }
}

@media (min-width: 1200px) {
  .app.container {
    width: 90%;
  }

  .app.container .app-aside,
  .app.container .app-header {
    max-width: 90%;
  }

  .app.container .app-footer-fixed {
    max-width: 970px;
  }

  .app.container.app-aside-folded .app-footer-fixed {
    max-width: 1110px;
  }

  .app.container.app-aside-dock .app-footer-fixed {
    max-width: 90%;
  }
}

handsome 原生入站提示

打开后台-更改外观-设置外观-开发者设置-复制代码粘贴至自定义输出body尾部代码即可

<script>
function kaygb_referrer(){
var kaygb_referrer = document.referrer;
if  (kaygb_referrer != ""){
return "感谢您的访问! 您来自:<br>" + document.referrer;
}else{
return "";
}}
$.message({
    message: "为了网站的正常运行,请不要使用广告屏蔽插件,谢谢!<br >" + kaygb_referrer(),
    title: "网站加载完成",
    type: "success",
    autoHide: !1,
    time: "3000"
})
</script>

评论一键赞、踩、打卡

打开后台-更改外观-设置外观-开发者设置-复制代码粘贴至自定义JavaScript即可
<!--评论 打卡、赞、踩 功能-->
window.SIMPALED = {}; // 创建全局对象 SIMPALED,用于存放编辑器功能

// 定义一个函数 a,用于在文本框中插入文字
function a(a, b, c) {
  if (document.selection) {
    a.focus();
    sel = document.selection.createRange();
    c ? (sel.text = b + sel.text + c) : (sel.text = b);
    a.focus();
  } else if (a.selectionStart || "0" == a.selectionStart) {
    var l = a.selectionStart;
    var m = a.selectionEnd;
    var n = m;
    c
      ? (a.value =
          a.value.substring(0, l) + b + a.value.substring(l, m) + c + a.value.substring(m, a.value.length))
      : (a.value = a.value.substring(0, l) + b + a.value.substring(m, a.value.length));
    c ? (n += b.length + c.length) : (n += b.length - m + l);
    l == m && c && (n -= c.length);
    a.focus();
    a.selectionStart = n;
    a.selectionEnd = n;
  } else {
    a.value += b + c;
    a.focus();
  }
}

window.SIMPALED.Editor = {
  daka: function() {
    var b = new Date().toLocaleTimeString();
    var c = document.getElementById("comment") || 0;
    a(c, "滴!学生卡!打卡时间:" + b, ",请上车的乘客系好安全带~");

    // 将光标移到文本最后
    if (c.setSelectionRange) {
      var len = c.value.length;
      c.setSelectionRange(len, len);
      c.focus();
    } else if (c.createTextRange) {
      var range = c.createTextRange();
      range.collapse(false);
      range.select();
      c.focus();
    }
  },

  zan: function() {
    var c = document.getElementById("comment") || 0;
    var yuluResponses = [
      " 这篇文章展现了作者深邃的思想和独特的观点,令人赞叹不已。 ",
      " 文章中的优美语言和精彩描写让人感受到一种心灵的震撼和触动,实在值得赞美。 ",
      " 作者以敏锐的洞察力和真挚的情感,将文字融为一体,这种写作技巧令人赞赏。 ",
      " 这篇文章展现了作者的才华和学识,无不让人对其赞美有加。 ",
      " 文章中的感人故事和深刻的寓意令人深深地感动和赞叹。  ",
      " 作者的文字流畅而富有节奏感,令人为之倾倒,实在是一篇令人赞美的佳作。 ",
      " 文章中的深邃思考和清晰逻辑令人对作者的才华赞赏不已。 ",
      " 这篇文章的独到见解和新颖观点,让人对作者的创意赞美有加。 ",
      " 作者运用生动的比喻和细腻的描写,让读者仿佛身临其境,实在是一种令人赞美的写作艺术。 ",
      " 这篇文章的深情笔触和真挚感受让人深深感叹,实在是一篇值得赞美的美文。 ",
    ];

    var randomIndex = Math.floor(Math.random() * yuluResponses.length);
    var randomResponse = yuluResponses[randomIndex];
    a(c, randomResponse);
  },

  cai: function() {
    var c = document.getElementById("comment") || 0;
    var yuluResponses = [
      " 我是来替大家批评博主的,我是来批评的,这篇文章的结构有点混乱,需要更好地组织和展开内容。博主多多像我学习啊! ",
      " 我是来替大家批评博主的,作者的表达能力有待提高,有些地方表述不够清晰,读者可能会感到困惑。博主多多像我学习啊! ",
      " 我是来替大家批评博主的,文章的语法错误较多,建议仔细检查并进行修改。博主多多像我学习啊! ",
      " 我是来替大家批评博主的,内容有些空洞,需要更多具体的例子或细节来支撑观点。博主多多像我学习啊! ",
      " 我是来替大家批评博主的,文章缺乏逻辑,有些段落之间的衔接不够自然。博主多多像我学习啊! ",
      " 我是来替大家批评博主的,作者的观点没有充分论证,需要更多的证据和理由来支持主张。博主多多像我学习啊! ",
      " 我是来替大家批评博主的,文章中出现了一些拼写错误和用词不当的情况,建议进行仔细校对。博主多多像我学习啊! ",
      " 我是来替大家批评博主的,语言表达过于简单,可以尝试使用更丰富的词汇和句式来提升文章质量。博主多多像我学习啊! ",
      " 我是来替大家批评博主的,这篇文章的主题不够突出,需要更好地明确中心思想。博主多多像我学习啊! ",
      " 我是来替大家批评博主的,文章缺乏吸引人的开头,建议引入更有趣的故事或引语来吸引读者注意。博主多多像我学习啊! ",
    ];

    var randomIndex = Math.floor(Math.random() * yuluResponses.length);
    var randomResponse = yuluResponses[randomIndex];
    a(c, randomResponse);
  },

  yulu: function() {
    var c = document.getElementById("comment") || 0;
    var yuluResponses = [
      " 有你在的日子才是我的日常。 ",
      " 夹在我女友与前女友与青梅竹马间的果然是修罗场! ",
      " 既然如此,就再努力一次吧。别在这里愁眉不展,也不要再自欺欺人,重新来过! ",
      " 比自己,比梦想更重要的东西永远都存在着... ",
      " 嘛,那又怎么样呢? ",
      " 自身不先改变的话,一切都不会改变。 ",
      " 比起有一百个朋友,不如有个比一百人还要重要的真心朋友。 ",
      " 我有在反省,但我不后悔。 ",
      " 要超越过去与悲伤,用坚强和笑容去开拓明天。 ",
      " 男人许下的诺言就一定要遵守。 ",
      " 没有回忆就去创造回忆,没有道路就去开辟道路。 ",
      " 我敬你是条汉子! ",
      " 不相信自己的人,连努力的价值都没有。 ",
      " 微风摇曳着窗帘,夕阳斜射入教室,在那里鼓起勇气告白的少年。即使现在也能清晰地回想起她的声音「当朋友,不行吗?」 ",
      " 就算是沉落地面的太阳,只要夜晚过了一定会再度升起,不管有什么痛苦或难过的事,跟今天截然不同的明天也一定会到来。 ",
      " 我的腿让我停下,可是心却不允许我那么做。 ",
      " 生活就像超级女生,走到最后的都是纯爷们。 ",
      " 我要拼,装上假牙也要拼! ",
      " 想要成为无论多么悲伤的时候,也能够漂亮微笑的人吧。 ",
      " 人们只是用好人来称呼对自己有用的人而以,不存在对所有人都有用的人。 ",
      " 烈焰中舞动的火花,将赐予邪恶异性交往以天罚。 ",
      " 无论乌云有多浓厚,星星也一定还在,只是暂时看不到了而已。 ",
      " 不相信人咬不到肚脐的,咬破肚脐去死如何? ",
      " 人一生会遇到约万人,两个人相爱的概率是.。所以你不爱我,我不怪你。 ",
      " 不管看到什么样的过去,都请不要迷失自己,不管你变成什么样子,我都是你的同伴。 ",
      " 心,可是很重的。 ",
      " 我爱上的人,称我为怪叔叔 ",
      " 慕君之心,至死方休。 ",
      " 虚伪的眼泪,会伤害别人,虚伪的笑容,会伤害自己。 ",
      " 若隐若现才是艺术! ",
      " 生我何用?不能欢笑。灭我何用?不减狂骄。 ",
      " 就是因为你不好,才要留在你身边,给你幸福。 ",
      " 呐,我们好像是,被宇宙和地球拆散的恋人似的。 ",
      " 你会梦游,我会磨牙,我们晚上一起去吓人吧! ",
      " 或许只需一滴露水,便能守护这绽放的花朵。 ",
      " 自己永远是孤单的,但你可以让其他人变得不孤单。 ",
      " 和哥哥的便当比起来夜空的薯片就像大便一样!只会给我大便的大便夜空是笨蛋~笨蛋~ ",
      " 我手中的魔法,是守护挚爱的力量,是坚定这个信念所必须的力量,我一定会拯救你的,无论在何时、何地。 ",
      " 要改变别人的心真是件很难办的事,不过改变自己要容易一点。 ",
      " 风筝的线你随时可以放开,只是别盼望我会回来。 ",
      " 与你的生命等价的东西,这个世界上根本没有。 ",
      " 君子可寓意于物,但不可留意于物。 ",
      " 最好的感觉,是有人懂你的欲言又止。 ",
      " 看似美好的东西,往往藏着陷阱。 ",
      " 爱,其实很简单,困难的是去接受它。 ",
      " 喜欢大胸只是本能,喜欢贫乳才是审美。 ",
      " 二次元什么的我本来是不感冒的,直到我的膝盖中了一箭。 ",
      " 你才是⑨!你全家都是⑨! ",
      " 努力是不会背叛自己的,虽然梦想有时会背叛自己。 ",
      " 面对就好,去经历就好。 ",
      " 我从小就害怕虫子 ",
      " 做不到的话,不过就是一死 。但是,赢了就能活下去,要是不战斗就赢不了。 ",
      " 既然认准这条路,何必去打听要走多久。 ",
      " 研表究明,汉字的序顺并不定一能影阅响读,比如当你看完这句话后,才发这现里的字全是都乱的。 ",
      " 少年心意,一如明月松间的青石流水,那些年里看到了,却不懂。 ",
      " 明明只是活着,哀伤却无处不在⋯⋯ ",
      " 少罗嗦,你还不如虫子呢! ",
      " 天空是连着的,如果我们也能各自发光的话,无论距离有多远,都能看到彼此努力的身影。 ",
      " 别人恋爱不成功,你连暗恋都不成功! ",
    ];

    var randomIndex = Math.floor(Math.random() * yuluResponses.length);
    var randomResponse = yuluResponses[randomIndex];
    a(c, randomResponse);
  },
};

打开Handsome主题目录下的 component/comments.php文件,找到并删除如下内容:

修改成如下代码:

<textarea id="comment" class="textarea form-control OwO-textarea" name="text" rows="5" placeholder="<?php _me("说点什么吧……") ?>" οnkeydοwn="if(event.ctrlKey&&event.keyCode==13){document.getElementById('submit').click();return false};"><?php $this->remember('text'); ?></textarea>
<div class="OwO" style="display: inline;"></div>
<div class="OwO" title="打卡" style="display: inline;" onclick="javascript:SIMPALED.Editor.daka();"><div class="OwO-logo"><span class="smile-icons"><i class="iconfont icon-daohanglan-01"></i></span><span class="OwOlogotext">打卡</span></div></div>
<div class="OwO" title="语录" style="display: inline;" onclick="javascript:SIMPALED.Editor.yulu();"><div class="OwO-logo"><i class="fontello-pencil"></i><span class="OwOlogotext">语录</span></div></div>
<div class="OwO" title="赞" style="display: inline;" onclick="javascript:SIMPALED.Editor.zan();"><div class="OwO-logo"><i class="glyphicon glyphicon-thumbs-up"></i><span class="OwOlogotext"></span></div></div>
<div class="OwO" title="踩" style="display: inline;" onclick="javascript:SIMPALED.Editor.cai();"><div class="OwO-logo"><i class="glyphicon glyphicon-thumbs-down"></i><span class="OwOlogotext"></span></div></div>

时光机头像圆形

打开后台-更改外观-设置外观-开发者设置-复制代码粘贴至自定义CSS即可
/* 时光机圆形头像 */
.img-square {border-radius: 50%;}
.list-group-item .thumb-sm .img-square {border-radius: 5px;}

logo 扫光

打开后台-更改外观-设置外观-开发者设置-复制代码粘贴至自定义CSS即可
/* logo 扫光开始 */
.navbar-brand{position:relative;overflow:hidden;margin: 0px 0 0 0px;}.navbar-brand:before{content:""; position: absolute; left: -665px; top: -460px; width: 200px; height: 15px; background-color: rgba(255,255,255,.5); -webkit-transform: rotate(-45deg); -moz-transform: rotate(-45deg); -ms-transform: rotate(-45deg); -o-transform: rotate(-45deg); transform: rotate(-45deg); -webkit-animation: searchLights 6s ease-in 0s infinite; -o-animation: searchLights 6s ease-in 0s infinite; animation: searchLights 6s ease-in 0s infinite;}@-moz-keyframes searchLights{50%{left: -100px; top: 0;} 65%{left: 120px; top: 100px;}}@keyframes searchLights{40%{left: -100px; top: 0;} 60%{left: 120px; top: 100px;} 80%{left: -100px; top: 0px;}}
/*logo 扫光结束*/

赞赏按钮跳动

打开后台-更改外观-设置外观-开发者设置-复制代码粘贴至自定义CSS即可
/*赞赏按钮跳动开始*/
.btn-pay {
animation: star 0.5s ease-in-out infinite alternate;
}
@keyframes star {
from {
transform: scale(1);
}
to {
transform: scale(1.1);
}
}
/*赞赏按钮跳动结束*/

鼠标点击特效

打开后台-更改外观-设置外观-开发者设置-复制代码粘贴至自定义JavaScript即可
/* 鼠标点击特效 */
var a_idx = 0;
jQuery(document).ready(function ($) {
    $("body").click(function (e) {
        var a = [
            "富强", "民主", "文明", "和谐", "自由", "平等", "公正", "法治",
            "爱国", "敬业", "诚信", "友善"
        ];
        var $i = $("<span/>").text(a[a_idx]);
        a_idx = (a_idx + 1) % a.length;
        var x = e.pageX,
            y = e.pageY;
        $i.css({
            "z-index": 9999,
            "top": y - 20,
            "left": x,
            "position": "absolute",
            "font-weight": "bold",
            "color": "#FA7298"
        });
        $("body").append($i);
        $i.animate({
            "top": y - 180,
            "opacity": 0
        }, 1500, function () {
            $i.remove();
        });
    });
});

复制版权提示

打开后台-更改外观-设置外观-开发者设置-复制代码粘贴至自定义JavaScript即可
/* 复制成功提示代码开始 */
kaygb_copy();
function kaygb_copy() {
    $(document).ready(function () {
        $("body").bind('copy', function (e) {
            hellolayer();
        });
    });
    var sitesurl = window.location.href;
    function hellolayer() {
        $.message({
            message: "尊重原创,转载请注明出处!<br> 本文作者:XXX<br>原文链接:" + sitesurl,
            title: "复制成功",
            type: "warning",
            autoHide: false,
            time: "3000"
        });
    }
}
/* 复制成功提示代码结束 */

彩色标签云及右栏数字

打开后台-更改外观-设置外观-开发者设置-复制代码粘贴至自定义JavaScript即可
/*彩色标签云代码开始*/
let tags = document.querySelectorAll("#tag_cloud-2 a");
let infos = document.querySelectorAll(".badge");
let colorArr = ["#428BCA", "#AEDCAE", "#ECA9A7", "#DA99FF", "#FFB380", "#D9B999"];
tags.forEach(tag => {
tagsColor = colorArr[Math.floor(Math.random() * colorArr.length)];
tag.style.backgroundColor = tagsColor;
});
infos.forEach(info => {
infosColor = colorArr[Math.floor(Math.random() * colorArr.length)];
info.style.backgroundColor = infosColor;
});
/*彩色标签云代码结束*/

自定义右键

打开后台-更改外观-设置外观-开发者设置-复制代码粘贴至自定义输出body尾部的HTML代码即可
<!-- 自定义右键 -->
<style type="text/css">
a {text-decoration: none;}
div.usercm{background-repeat:no-repeat;background-position:center center;background-size:cover;background-color:#fff;font-size:13px!important;width:130px;-moz-box-shadow:1px 1px 3px rgba(0,0,0,.3);box-shadow:0px 0px 15px #333;position:absolute;display:none;z-index:10000;opacity:0.9; border-radius: 8px;}
div.usercm ul{list-style-type:none;list-style-position:outside;margin:0px;padding:0px;display:block}
div.usercm ul li{margin:0px;padding:0px;line-height:35px;}
div.usercm ul li a{color:#666;padding:0 15px;display:block}
div.usercm ul li a:hover{color:#fff;background:rgba(88,130,238,0.88)}
div.usercm ul li a i{margin-right:10px}
a.disabled{color:#c8c8c8!important;cursor:not-allowed}
a.disabled:hover{background-color:rgba(255,11,11,0)!important}
div.usercm{background:#fff !important;}
</style>
<div class="usercm" style="left: 199px; top: 5px; display: none;">
<ul>
<li><a href="https://www.***.cn"><i class="fontello fontello-home"></i><span>首页</span></a></li>
<li><a href="javascript:void(0);" onclick="getSelect();"><i class="fontello fontello-pencil"></i><span>复制</span></a></li>
<li><a href="javascript:void(0);" onclick="baiduSearch();"><i class="fontello fontello-search"></i><span>搜索</span></a></li>
<li><a href="javascript:history.go(1);"><i class="fontello fontello-chevron-right"></i><span>前进</span></a></li>
<li><a href="javascript:history.go(-1);"><i class="fontello fontello-chevron-left"></i><span>后退</span></a></li>
<li style="border-bottom:1px solid gray"><a href="javascript:window.location.reload();"><i class="fontello fontello-refresh"></i><span>重载网页</span></a></li>
<li><a href="https://blog.ybyq.wang/index.php/about.html"><i class="fontello fontello-emo-tongue"></i><span>关于作者</span></a></li>
</ul>
</div>
<script type="text/javascript">
(function(a) {
a.extend({
mouseMoveShow: function(b) {
var d = 0,
c = 0,
h = 0,
k = 0,
e = 0,
f = 0;
a(window).mousemove(function(g) {
d = a(window).width();
c = a(window).height();
h = g.clientX;
k = g.clientY;
e = g.pageX;
f = g.pageY;
h + a(b).width() >= d && (e = e - a(b).width() - 5);
k + a(b).height() >= c && (f = f - a(b).height() - 5);
a("html").on({
contextmenu: function(c) {
3 == c.which && a(b).css({
left: e,
top: f
}).show()
},
click: function() {
a(b).hide()
}
})
},
disabledContextMenu: function() {
window.oncontextmenu = function() {
return !1
}
}
})
})(jQuery);

    function getSelect() {
        "" == (window.getSelection ? window.getSelection() : document.selection.createRange().text) ? layer.msg("请选择需要复制的内容!") : document.execCommand("Copy")
    }
    function baiduSearch() {
        var a = window.getSelection ? window.getSelection() : document.selection.createRange().text;
        "" == a ? layer.msg("请选择需要搜索的内容!") : window.open("https://www.baidu.com/s?wd=" + a)
    }
    $(function() {
        for (var a = navigator.userAgent, b = "Android;iPhone;SymbianOS;Windows Phone;iPad;iPod".split(";"), d = !0, c = 0; c < b.length; c++) if (0 < a.indexOf(b[c])) {
            d = !1;
            break
        }
        d && ($.mouseMoveShow(".usercm"), $.disabledContextMenu())
    });
</script>

响应时间和访客总数

将以下代码放到/usr/themes/handsome/function.php的最下面
/*访问总量代码开始*/
function theAllViews(){
$db = Typecho_Db::get();
    $row = $db->fetchAll('SELECT SUM(VIEWS) FROM `typecho_contents`');
        echo number_format($row[0]['SUM(VIEWS)']);
}
/*访问总量代码结束*/
/*响应时间代码开始*/
function timer_start() {
global $timestart;
$mtime = explode( ' ', microtime() );
$timestart = $mtime[1] + $mtime[0];
return true;
}
timer_start();
function timer_stop( $display = 0, $precision = 3  ) {
global $timestart, $timeend;
$mtime = explode( ' ', microtime() );
$timeend = $mtime[1] + $mtime[0];
$timetotal = number_format( $timeend - $timestart, $precision  );
$r = $timetotal < 1 ? $timetotal * 1000 . " ms" : $timetotal . " s";
if ( $display ) {
echo $r;
}
return $r;
}
/*响应时间代码结束*/
然后将以下代码放到/usr/themes/handsome/component/sidebar.php内,找到博客信息添加即可

<!--访客总数代码开始-->
<li class="list-group-item text-second"> <span class="blog-info-icons"> <i data-feather="users"></i></span>
<span class="badge pull-right"><?php echo theAllViews();?></span><?php _me("访客总数") ?></li>
<!--访客总数代码结束-->

<!--响应耗时代码开始-->
<li class="list-group-item text-second"> <span class="blog-info-icons"> <i data-feather="refresh-ccw"></i></span>
<span class="badge pull-right"><?php echo timer_stop();?></span><?php _me("响应耗时") ?></li>
<!--响应耗时代码结束-->
全站字数统计
将以下代码放到/usr/themes/handsome/component/sidebar.php的开头

<?php
//字数统计
function allOfCharacters() {
$chars = 0;
    $db = Typecho_Db::get();
$select = $db ->select('text')->from('table.contents');
$rows = $db->fetchAll($select);
    foreach ($rows as $row) { $chars += mb_strlen(trim($row['text']), 'UTF-8'); }
    $unit = '';
if($chars >= 10000)     { $chars /= 10000; $unit = '万'; }
    else if($chars >= 1000) { $chars /= 1000;  $unit = '千'; }
$out = sprintf('%.2lf %s',$chars, $unit);
    return $out;
}
?>
然后将以下代码放到/usr/themes/handsome/component/sidebar.php内,加到合适的位置

<!--全站字数开始-->
<li class="list-group-item text-second"><span class="blog-info-icons"><i data-feather="edit-2"></i></span>
<span class="badge pull-right"><?php echo allOfCharacters(); ?></span><?php _me("全站字数") ?></li>
<!--全站字数结束-->

版权提示

在主题文件post.php内,文章内容下方加上下面的代码
<div style="padding: 10px;background: rgba(220, 220, 220, 0.22);font-size: 13px;border-left: 3px solid;text-align: left;">
<span>本文作者:<a href="<?php $this->author->permalink(); ?>" rel="author"> <?php $this->author(); ?></a></span>
<span>文章标题:<a href="<?php $this->permalink() ?>"><?php $this->title() ?></a><br></span>
<span>本文地址:<a href="<?php $this->permalink() ?>"><?php $this->permalink() ?></a><br></span>
<span>版权说明:若无注明,本文皆<a href="<?php $this->options->rootUrl(); ?>" target="_blank" data-original-title="<?php $this->options->title() ?>"><?php $this->options->title() ?></a>原创,转载请保留文章出处。</span>
</div>

时间流逝

将以下代码加到/usr/themes/handsome/component/sidebar.php,在 119 行左右,放在<?php
endif; ?>之后
<!-- 时间倒计时代码开始 -->
<section id="blog_info" class="widget widget_categories wrapper-md clear">
<h5 class="widget-title m-t-none text-md"><?php _me("时间流逝") ?></h5>
<div class="sidebar sidebar-count">
<div class="content">
<div class="item" id="dayProgress">
<div class="title">今日已经过去<span></span>小时</div>
<div class="progress">
<div class="progress-bar">
<div class="progress-inner progress-inner-1"></div>
</div>
<div class="progress-percentage"></div>
</div>
</div>
<div class="item" id="weekProgress">
<div class="title">这周已经过去<span></span>天</div>
<div class="progress">
<div class="progress-bar">
<div class="progress-inner progress-inner-2"></div>
</div>
<div class="progress-percentage"></div>
</div>
</div>
<div class="item" id="monthProgress">
<div class="title">本月已经过去<span></span>天</div>
<div class="progress">
<div class="progress-bar">
<div class="progress-inner progress-inner-3"></div>
</div>
<div class="progress-percentage"></div>
</div>
</div>
<div class="item" id="yearProgress">
<div class="title">今年已经过去<span></span>个月</div>
<div class="progress">
<div class="progress-bar">
<div class="progress-inner progress-inner-4"></div>
</div>
<div class="progress-percentage"></div>
</div>
</div>
</div>
</div>
</section>
<!-- 时间倒计时代码结束 -->
添加完成后,将以下代码放入后台-外观设置-开发者设置-自定义css
/* 时间流逝 */
.sidebar-count .content {
padding: 15px
}

.sidebar-count .content .item {
margin-bottom: 15px
}

.sidebar-count .content .item:last-child {
margin-bottom: 0
}

.sidebar-count .content .item .title {
font-size: 12px;
color: var(--minor);
margin-bottom: 5px;
display: flex;
align-items: center
}

.sidebar-count .content .item .title span {
color: var(--theme);
font-weight: 500;
font-size: 14px;
margin: 0 5px
}

.sidebar-count .content .item .progress {
display: flex;
align-items: center
}

.sidebar-count .content .item .progress .progress-bar {
height: 10px;
border-radius: 5px;
overflow: hidden;
background: var(--classC);
width: 0;
min-width: 0;
flex: 1;
margin-right: 5px
}
@keyframes progress {
0% {
background-position: 0 0
}

    100% {
        background-position: 30px 0
    }

}
.sidebar-count .content .item .progress .progress-bar .progress-inner {
width: 0;
height: 100%;
border-radius: 5px;
transition: width 0.35s;
-webkit-animation: progress 750ms linear infinite;
animation: progress 750ms linear infinite
}

.sidebar-count .content .item .progress .progress-bar .progress-inner-1 {
background: #bde6ff;
background-image: linear-gradient(135deg, #50bfff 25%, transparent 25%, transparent 50%, #50bfff 50%, #50bfff 75%, transparent 75%, transparent 100%);
background-size: 30px 30px
}

.sidebar-count .content .item .progress .progress-bar .progress-inner-2 {
background: #ffd980;
background-image: linear-gradient(135deg, #f7ba2a 25%, transparent 25%, transparent 50%, #f7ba2a 50%, #f7ba2a 75%, transparent 75%, transparent 100%);
background-size: 30px 30px
}

.sidebar-count .content .item .progress .progress-bar .progress-inner-3 {
background: #ffa9a9;
background-image: linear-gradient(135deg, #ff4949 25%, transparent 25%, transparent 50%, #ff4949 50%, #ff4949 75%, transparent 75%, transparent 100%);
background-size: 30px 30px
}

.sidebar-count .content .item .progress .progress-bar .progress-inner-4 {
background: #67c23a;
background-image: linear-gradient(135deg, #4f9e28 25%, transparent 25%, transparent 50%, #4f9e28 50%, #4f9e28 75%, transparent 75%, transparent 100%);
background-size: 30px 30px
}

.sidebar-count .content .item .progress .progress-percentage {
color: var(--info)
}
添加完成后,在到/usr/themes/handsome/assets/js里面创建名为timeinfo.js后把下面代码复制进去保存

function init_life_time() {
function getAsideLifeTime() {
/* 当前时间戳 */
let nowDate = +new Date();
/* 今天开始时间戳 */
let todayStartDate = new Date(new Date().toLocaleDateString()).getTime();
/* 今天已经过去的时间 */
let todayPassHours = (nowDate - todayStartDate) / 1000 / 60 / 60;
/* 今天已经过去的时间比 */
let todayPassHoursPercent = (todayPassHours / 24) * 100;
$('#dayProgress .title span').html(parseInt(todayPassHours));
$('#dayProgress .progress .progress-inner').css('width', parseInt(todayPassHoursPercent) + '%');
$('#dayProgress .progress .progress-percentage').html(parseInt(todayPassHoursPercent) + '%');
/* 当前周几 */
let weeks = {
0: 7,
1: 1,
2: 2,
3: 3,
4: 4,
5: 5,
6: 6
};
let weekDay = weeks[new Date().getDay()];
let weekDayPassPercent = (weekDay / 7) * 100;
$('#weekProgress .title span').html(weekDay);
$('#weekProgress .progress .progress-inner').css('width', parseInt(weekDayPassPercent) + '%');
$('#weekProgress .progress .progress-percentage').html(parseInt(weekDayPassPercent) + '%');
let year = new Date().getFullYear();
let date = new Date().getDate();
let month = new Date().getMonth() + 1;
let monthAll = new Date(year, month, 0).getDate();
let monthPassPercent = (date / monthAll) * 100;
$('#monthProgress .title span').html(date);
$('#monthProgress .progress .progress-inner').css('width', parseInt(monthPassPercent) + '%');
$('#monthProgress .progress .progress-percentage').html(parseInt(monthPassPercent) + '%');
let yearPass = (month / 12) * 100;
$('#yearProgress .title span').html(month);
$('#yearProgress .progress .progress-inner').css('width', parseInt(yearPass) + '%');
$('#yearProgress .progress .progress-percentage').html(parseInt(yearPass) + '%');
}
getAsideLifeTime();
setInterval(() => {
getAsideLifeTime();
}, 1000);
}
init_life_time()
添加完成,将以下代码放入后台-外观设置-开发者设置-自定义输出body尾部
<!-- 时间流逝 -->
<script src="https://你的域名/usr/themes/handsome/assets/js/timeinfo.js"></script>

【已修稿】添加天气小组件(侧边栏)

之前的心知天气版本 已失效 ,本版本由作者开发,灵感来源于Shanshui’s Blog,实现来自于Kimi K2.6大模型
使用OpenMeteo天气API,支持自动定位(浏览器权限),反向查询来自 BigDataCloud 免费 API
本版本无需任何公钥与私钥
将以下代码放入/usr/themes/handsome/component/sidebar.php第 76 行左右
<?php include __DIR__ . '/weather-widget.php'; ?>
随后在 /usr/themes/handsome/component/ 下,创建一个新文件
文件名称:weather-widget.php
文件中放入以下代码
<?php
/**
 * 天气小组件 v4 - 适配 Typecho Handsome 主题原生样式
 * 去除城市切换下拉框,纯自动定位/默认城市
 * 使用 Open-Meteo 免费 API + BigDataCloud 反向地理编码
 * 作者:センク森辞 以及 Kimi
 * 灵感来源于 blog.shanshui.site 
 * 
 * 安装方法:
 * 1. 将本文件保存到 /usr/themes/handsome/component/weather-widget.php
 * 2. 在 /usr/themes/handsome/component/sidebar.php 中合适位置添加:
 *    <?php include __DIR__ . '/weather-widget.php'; ?>
 * 3. 修改下方 $default_city 为你想默认显示的城市
 */

// ==================== 配置区域 ====================
$default_city = '北京';        // 默认城市(当定位失败时显示)
$cache_minutes = 10;           // 缓存时间(分钟)
// ==================================================
?>

<section id="shanshui-weather" class="widget widget_categories wrapper-md clear">
    <h5 class="widget-title m-t-none text-md"><?php _me("天气") ?></h5>

    <div class="panel wrapper-sm">
        <!-- 加载骨架屏 -->
        <div id="sw-loading" class="text-center" style="padding:16px 0;">
            <div class="m-b-sm">
                <span class="inline" style="display:inline-block;width:20px;height:20px;background:rgba(128,128,128,0.15);border-radius:2px;"></span>
            </div>
            <div class="m-b-sm">
                <span class="inline" style="display:inline-block;width:64px;height:14px;background:rgba(128,128,128,0.15);border-radius:2px;"></span>
            </div>
            <div>
                <span class="inline" style="display:inline-block;width:96px;height:10px;background:rgba(128,128,128,0.15);border-radius:2px;"></span>
            </div>
        </div>

        <!-- 天气数据 -->
        <div id="sw-data" class="text-center" style="padding:12px 0;display:none;">
            <div id="sw-icon" style="font-size:28px;line-height:1;margin-bottom:6px;"></div>
            <div style="font-size:20px;font-weight:700;margin-bottom:4px;">
                <span id="sw-temp"></span>°C
            </div>
            <div class="text-muted" style="font-size:12px;margin-bottom:4px;" id="sw-weather"></div>
            <div class="text-muted" style="font-size:11px;" id="sw-detail"></div>
        </div>

        <!-- 错误提示 -->
        <div id="sw-error" class="text-center text-muted" style="padding:16px 0;font-size:11px;display:none;"></div>
    </div>

    <style>
        #shanshui-weather #sw-loading span {
            animation: sw-pulse 1.5s ease-in-out infinite;
        }
        @keyframes sw-pulse {
            0%, 100% { opacity: 1; }
            50% { opacity: 0.4; }
        }
    </style>

    <script>
    (function() {
        const CACHE_KEY = 'shanshui-weather-cache-v1';
        const CACHE_TIME = <?php echo $cache_minutes; ?> * 60 * 1000;
        const DEFAULT_CITY = '<?php echo $default_city; ?>';

        const $ = id => document.getElementById(id);
        const show = id => $(id).style.display = '';
        const hide = id => $(id).style.display = 'none';

        function getCache(key) {
            try {
                const data = JSON.parse(sessionStorage.getItem(CACHE_KEY) || '{}');
                const item = data[key];
                if (item && Date.now() - item.ts < CACHE_TIME) return item.data;
            } catch(e) {}
            return null;
        }

        function setCache(key, data) {
            try {
                const all = JSON.parse(sessionStorage.getItem(CACHE_KEY) || '{}');
                all[key] = { data, ts: Date.now() };
                sessionStorage.setItem(CACHE_KEY, JSON.stringify(all));
            } catch(e) {}
        }

        async function getCityCoords(city) {
            const cache = getCache('coords_' + city);
            if (cache) return cache;

            const res = await fetch(
                'https://geocoding-api.open-meteo.com/v1/search?name=' + 
                encodeURIComponent(city) + '&count=1&language=zh&format=json'
            );
            const data = await res.json();
            if (data.results && data.results[0]) {
                const r = data.results[0];
                const coords = { lat: r.latitude, lon: r.longitude, name: r.name };
                setCache('coords_' + city, coords);
                return coords;
            }
            throw new Error('未找到城市:' + city);
        }

        async function getWeather(lat, lon) {
            const cacheKey = 'weather_' + lat + '_' + lon;
            const cache = getCache(cacheKey);
            if (cache) return cache;

            const res = await fetch(
                'https://api.open-meteo.com/v1/forecast?latitude=' + lat + 
                '&longitude=' + lon + 
                '&current=temperature_2m,relative_humidity_2m,weather_code,wind_speed_10m,wind_direction_10m&timezone=auto'
            );
            const data = await res.json();
            setCache(cacheKey, data);
            return data;
        }

        async function reverseGeocode(lat, lon) {
            const cacheKey = 'geo_' + lat + '_' + lon;
            const cache = getCache(cacheKey);
            if (cache) return cache;

            try {
                const res = await fetch(
                    'https://api.bigdatacloud.net/data/reverse-geocode-client?latitude=' + lat + 
                    '&longitude=' + lon + '&localityLanguage=zh'
                );
                const data = await res.json();
                const city = data.city || data.locality || data.principalSubdivision || '';
                if (city) {
                    setCache(cacheKey, city);
                    return city;
                }
            } catch(e) {}
            return '';
        }

        function getWindDir(deg) {
            const dirs = ['北', '东北', '东', '东南', '南', '西南', '西', '西北'];
            return dirs[Math.round(deg / 45) % 8];
        }

        function getWeatherInfo(code) {
            const map = {
                0: ['晴', '☀️'],
                1: ['多云', '⛅'], 2: ['阴', '☁️'], 3: ['阴', '☁️'],
                45: ['雾', '🌫️'], 48: ['雾凇', '🌫️'],
                51: ['毛毛雨', '🌧️'], 53: ['小雨', '🌧️'], 55: ['中雨', '🌧️'],
                56: ['冻雨', '🌧️'], 57: ['冻雨', '🌧️'],
                61: ['小雨', '🌧️'], 63: ['中雨', '🌧️'], 65: ['大雨', '🌧️'],
                66: ['冻雨', '🌧️'], 67: ['冻雨', '🌧️'],
                71: ['小雪', '🌨️'], 73: ['中雪', '🌨️'], 75: ['大雪', '🌨️'], 77: ['雪粒', '🌨️'],
                80: ['阵雨', '🌧️'], 81: ['阵雨', '🌧️'], 82: ['暴雨', '🌧️'],
                85: ['阵雪', '🌨️'], 86: ['阵雪', '🌨️'],
                95: ['雷暴', '⛈️'], 96: ['雷暴伴冰雹', '⛈️'], 99: ['雷暴伴冰雹', '⛈️']
            };
            return map[code] || ['未知', '🌤️'];
        }

        function getWindLevel(speed) {
            if (speed < 0.3) return 0;
            if (speed < 1.6) return 1;
            if (speed < 3.4) return 2;
            if (speed < 5.5) return 3;
            if (speed < 8.0) return 4;
            if (speed < 10.8) return 5;
            if (speed < 13.9) return 6;
            if (speed < 17.2) return 7;
            if (speed < 20.8) return 8;
            if (speed < 24.5) return 9;
            if (speed < 28.5) return 10;
            if (speed < 32.7) return 11;
            return 12;
        }

        function render(data, cityName) {
            const current = data.current;
            const info = getWeatherInfo(current.weather_code);

            $('sw-icon').textContent = info[1];
            $('sw-temp').textContent = Math.round(current.temperature_2m);
            $('sw-weather').textContent = info[0];
            $('sw-detail').textContent = cityName + ' · ' + 
                getWindDir(current.wind_direction_10m) + '风 ' + 
                getWindLevel(current.wind_speed_10m) + '级 · 湿度' + 
                current.relative_humidity_2m + '%';

            hide('sw-loading');
            hide('sw-error');
            show('sw-data');
        }

        async function showError(msg) {
            console.log('[天气组件]', msg);
            try {
                await loadCity(DEFAULT_CITY);
            } catch(e) {
                $('sw-error').textContent = msg;
                hide('sw-loading');
                hide('sw-data');
                show('sw-error');
            }
        }

        async function loadCity(city) {
            show('sw-loading');
            hide('sw-data');
            hide('sw-error');

            const coords = await getCityCoords(city);
            const weather = await getWeather(coords.lat, coords.lon);
            render(weather, coords.name || city);
        }

        async function init() {
            try {
                const isHttps = window.location.protocol === 'https:';

                if (!isHttps) {
                    console.log('[天气组件] HTTP 站点,使用默认城市');
                    await loadCity(DEFAULT_CITY);
                    return;
                }

                if (navigator.geolocation) {
                    try {
                        const position = await new Promise((resolve, reject) => {
                            navigator.geolocation.getCurrentPosition(resolve, reject, { 
                                timeout: 10000, enableHighAccuracy: false 
                            });
                        });

                        const lat = position.coords.latitude.toFixed(4);
                        const lon = position.coords.longitude.toFixed(4);

                        const [weather, cityName] = await Promise.all([
                            getWeather(lat, lon),
                            reverseGeocode(lat, lon)
                        ]);

                        render(weather, cityName || '本地');
                        return;
                    } catch(e) {
                        console.log('[天气组件] 定位失败:', e.message || e);
                    }
                }

                await loadCity(DEFAULT_CITY);

            } catch(e) {
                console.error('[天气组件] 初始化失败:', e);
                await showError('获取天气失败');
            }
        }

        init();
    })();
    </script>
</section>
如果你后续想换成和风天气或高德天气 API(需要你自己申请 Key),只需要替换 getCityCoordsgetWeather 两个函数即可。

总结

通过以上美化方案,你可以让你的 Handsome 主题博客更加个性化和美观。这些功能都是经过精心挑选的,既实用又不会影响博客的加载速度。你可以根据自己的需求选择性地使用这些功能。
如果你在使用过程中遇到任何问题,欢迎在评论区留言讨论。同时,也欢迎你分享自己的美化心得,让我们一起打造更好的博客!

最后修改:2026 年 08 月 02 日
如果觉得我的文章对你有用,请请我喝杯咖啡,咖啡是我创作的唯一动力
END
本文作者:
文章标题:【优化版】Typecho handsome主题美化
本文地址:https://yuluo.xyz/archives/78.html
许可协议:知识共享署名-非商业性使用 4.0 国际许可协议
版权说明:若无注明,本文皆辞入森川原创,转载请保留文章出处。