Featured image of post 博客魔改美化(通用)

博客魔改美化(通用)

昨天写过一篇 Stack 主题魔改,那篇仅适用于 Stack 主题,今天的这篇适用于所有 Hugo 博客(部分适用于所有网页),以后所有的添加也会在这篇文章里面

博客运行时间

<span id="timeDate">载入天数...</span><span id="times">载入时分秒...</span>

<script language="javascript"> 
    var now = new Date();
    function createtime(){
        // 当前时间
        now.setTime(now.getTime()+250);
        var grt= new Date("20xx/xx/xx 00:00:00"); //网站诞生时间
        days = (now - grt ) / 1000 / 60 / 60 / 24;
        dnum = Math.floor(days);
        hours = (now - grt ) / 1000 / 60 / 60 - (24 * dnum);
        hnum = Math.floor(hours);
        if(String(hnum).length ==1 ){hnum = "0" + hnum;}
        minutes = (now - grt ) / 1000 /60 - (24 * 60 * dnum) - (60 * hnum);
        mnum = Math.floor(minutes);
        if(String(mnum).length ==1 ){mnum = "0" + mnum;}
        seconds = (now - grt ) / 1000 - (24 * 60 * 60 * dnum) - (60 * 60 * hnum) - (60 * mnum);
        snum = Math.round(seconds);
        if(String(snum).length ==1 ){snum = "0" + snum;}

        document.getElementById("timeDate").innerHTML = "本站已稳定运行"+dnum+" 天 ";
        document.getElementById("times").innerHTML = hnum + " 小时 " + mnum + " 分 " + snum + " 秒"
    }
    setInterval("createtime()",250); 
</script> 

旅行者 1 号离地球距离

<span id="voyager">载入旅行者一号离地球距离信息...</span>

<script language="javascript"> 
    var now = new Date();
    function createtime(){
        // 当前时间
        now.setTime(now.getTime()+250);
        var start = new Date("08/01/2022 00:00:00"); // 旅行者1号开始计算的时间
        var dis = Math.trunc(23400000000 + ((now - start) / 1000) * 17); // 距离=秒数*速度 记住转换毫秒
        var unit = (dis / 149600000).toFixed(6);  // 天文单位

        document.getElementById("voyager").innerHTML = "旅行者 1 号当前距离地球 "+ dis +" 千米,约为 "+ unit +" 个天文单位 🚀"
    }
    setInterval("createtime()",250); 
</script> 

已写完了哪本书?

来自:已写完了哪本书? :: 木木木木木

将以下代码添加到 footer.html:

{{$scratch := newScratch}}
{{ range (where .Site.Pages "Kind" "page" )}}
    {{$scratch.Add "total" .WordCount}}
{{ end }}
{{$totalWord := $scratch.Get "total" }}
{{ $wowWord := div $totalWord 10000}}
{{ $wowBook := "还在努力更新中..</br>加油!加油啦!"}}
{{ if ge $wowWord 40 }}{{$wowBook = "写完一本我国著名的</br>四大名著了!"}}
{{ else if ge $wowWord 35 }}{{$wowBook = "写完一本 东野圭吾 的</br>《白夜行》了!"}}
{{ else if ge $wowWord 34 }}{{$wowBook = "写完一本 雨果 的</br>《巴黎圣母院》了!"}}
{{ else if ge $wowWord 32 }}{{$wowBook = "写完一本 艾米莉·勃朗特 的</br>《呼啸山庄》了!"}}
{{ else if ge $wowWord 31 }}{{$wowBook = "写完一本 阿来 的</br>《尘埃落定》了!"}}
{{ else if ge $wowWord 30 }}{{$wowBook = "写完一本 茅盾 的</br>《子夜》了!"}}
{{ else if ge $wowWord 28 }}{{$wowBook = "写完一本 张炜 的</br>《古船》了!"}}
{{ else if ge $wowWord 25 }}{{$wowBook = "写完一本 钱钟书 的</br>《围城》了!"}}
{{ else if ge $wowWord 23 }}{{$wowBook = "写完一本 简·奥斯汀 的</br>《傲慢与偏见》了!"}}
{{ else if ge $wowWord 22 }}{{$wowBook = "写完一本 莫泊桑 的</br>《一生》了!"}}
{{ else if ge $wowWord 21 }}{{$wowBook = "写完一本 东野圭吾 的</br>《解忧杂货店》了!"}}
{{ else if ge $wowWord 20 }}{{$wowBook = "写完一本 巴金 的</br>《寒夜》了!"}}
{{ else if ge $wowWord 19 }}{{$wowBook = "写完一本 亚米契斯 的</br>《爱的教育》了!"}}
{{ else if ge $wowWord 18 }}{{$wowBook = "写完一本 沈从文 的</br>《边城》了!"}}
{{ else if ge $wowWord 17 }}{{$wowBook = "写完一本 马克·吐温 的</br>《汤姆·索亚历险记》了!"}}
{{ else if ge $wowWord 16 }}{{$wowBook = "写完一本 曹禺 的</br>《日出》了!"}}
{{ else if ge $wowWord 15 }}{{$wowBook = "写完一本 伯内特 的</br>《秘密花园》了!"}}
{{ else if ge $wowWord 14 }}{{$wowBook = "写完一本 史铁生 的</br>《宿命的写作》了!"}}
{{ else if ge $wowWord 13 }}{{$wowBook = "写完一本 曹禺 的</br>《雷雨》了!"}}
{{ else if ge $wowWord 12 }}{{$wowBook = "写完一本 余华 的</br>《活着》了!"}}
{{ else if ge $wowWord 11 }}{{$wowBook = "写完一本 鲁迅 的</br>《彷徨》了!"}}
{{ else if ge $wowWord 10 }}{{$wowBook = "写完一本 马克·吐温 的</br>《王子与乞丐》了!"}}
{{ else if ge $wowWord 9 }}{{$wowBook = "写完一本 林海音 的</br>《城南旧事》了!"}}
{{ else if ge $wowWord 7 }}{{$wowBook = "写完一本 鲁迅 的</br>《呐喊》了!"}}
{{ else if ge $wowWord 5 }}{{$wowBook = "写完一本 埃克苏佩里 的</br>《小王子》了!"}}
{{ end }}
 {{$totalWord }}  <br> {{$wowBook | safeHTML }}

图片懒加载

注:Stack 主题默认有懒加载,不需要自行添加

原理:使用 Hugo (v0.62+) 的渲染钩子对 markdown 文件内的链接和图像自定义渲染

实现:/layouts/_default/_markup 中新建 render-image.html 然后丢入代码:

<p class="md_image">
  <img loading='lazy' src="{{ .Destination | safeURL }}" alt="{{ .Text }}" {{ with .Title}} title="{{ . }}"{{ end }} />
</p>

博客产量

来自:如何给 Hugo 博客添加热力图 - 椒盐豆豉

任意位置加入以下代码:

<div id="heatmap" style="
  max-width: 600px;
  height: 180px;
  padding: 2px;
  text-align: center;
  margin: auto"
></div>
<script src="https://npm.elemecdn.com/echarts@5.3.0/dist/echarts.min.js"></script>
<script type="text/javascript">
  var chartDom = document.getElementById('heatmap');
  var myChart = echarts.init(chartDom);
  window.onresize = function() {
      myChart.resize();
  };
  var option;

  // echart heatmap data seems to only support two elements tuple
  // it doesn't render when each item has 3 value
  // it also only pass first 2 elements when reading event param
  // so here we build a map to store additional metadata like link and title
  // map format {date: [{wordcount, link, title}]}
  // for more information see https://blog.douchi.space/hugo-blog-heatmap
  var dataMap = new Map();
  {{ range ((where .Site.RegularPages "Type" "post")) }}
    var key = {{ .Date.Format "2006-01-02" }};
    var value = dataMap.get(key);
    var wordCount = {{ .WordCount }};
    var link = {{ .RelPermalink}};
    var title = {{ .Title }};
    
    // multiple posts in same day
    if (value == null) {
      dataMap.set(key, [{wordCount, link, title}]);
    } else {
      value.push({wordCount, link, title});
    }
  {{- end -}}

  var data = [];
  // sum up the word count
  for (const [key, value] of dataMap.entries()) {
    var sum = 0;
    for (const v of value) {
      sum += v.wordCount;
    }
    data.push([key, (sum / 1000).toFixed(1)]);
  }
  
  var startDate = new Date();
  var year_Mill = startDate.setFullYear((startDate.getFullYear() - 1));
  var startDate = +new Date(year_Mill);
  var endDate = +new Date();

  var dayTime = 3600 * 24 * 1000;
  startDate = echarts.format.formatTime('yyyy-MM-dd', startDate);
  endDate = echarts.format.formatTime('yyyy-MM-dd', endDate);

  // change date range according to months we want to render
  function heatmap_width(months){             
    var startDate = new Date();
    var mill = startDate.setMonth((startDate.getMonth() - months));
    var endDate = +new Date();
    startDate = +new Date(mill);

    endDate = echarts.format.formatTime('yyyy-MM-dd', endDate);
    startDate = echarts.format.formatTime('yyyy-MM-dd', startDate);

    var showmonth = [];
    showmonth.push([
        startDate,
        endDate
    ]);
    return showmonth
  };

  function getRangeArr() {
    const windowWidth = window.innerWidth;
    if (windowWidth >= 600) {
      return heatmap_width(12);
    } else if (windowWidth >= 400) {
      return heatmap_width(9);
    } else {
      return heatmap_width(6);
    }
  }

  option = {
    title: {
        top: 0,
        left: 'center',
        text: '博客废话产量'
    },
    tooltip: {
      hideDelay: 1000,
      enterable: true,
      formatter: function (p) {
        const date = p.data[0];
        const posts = dataMap.get(date);
        var content = `${date}`;
        for (const [i, post] of posts.entries()) {
            content += "<br>";
            var link = post.link;
            var title = post.title;
            var wordCount = (post.wordCount / 1000).toFixed(1);
            content += `<a href="${link}" target="_blank">${title} | ${wordCount} 千字</a>`
        }
        return content;
      }
    },
    visualMap: {
        min: 0,
        max: 10,
        type: 'piecewise',
        orient: 'horizontal',
        left: 'center',
        top: 30,
        
        inRange: {   
          //  [floor color, ceiling color]
          color: ['#7aa8744c', '#7AA874' ] 
        },
        splitNumber: 4,
        text: ['千字', ''],
        showLabel: true,
        itemGap: 20,
    },
    calendar: {
        top: 80,
        left: 20,
        right: 4,
        cellSize: ['auto', 12],
        range: getRangeArr(),
        itemStyle: {
            color: '#F1F1F1',
            borderWidth: 2.5,
            borderColor: '#fff',
        },
        yearLabel: { show: false },
        // the splitline between months. set to transparent for now.
        splitLine: {
          lineStyle: {
            color: 'rgba(0, 0, 0, 0.0)',
            // shadowColor: 'rgba(0, 0, 0, 0.5)',
            // shadowBlur: 5,
            // width: 0.5,
            // type: 'dashed',
          }
        }
    },
    series: {
        type: 'heatmap',
        coordinateSystem: 'calendar',
        data: data,
    }
  };
  myChart.setOption(option);
  myChart.on('click', function(params) {
    if (params.componentType === 'series') {
      // open the first post on the day
      const post = dataMap.get(params.data[0])[0];
      const link = window.location.origin + post.link;
      window.open(link, '_blank').focus();
    }
});
</script> 

效果见首页

自定义字体

使用谷歌字体

  1. 修改 /layouts/partials/footer/components/custom-font.html 为以下代码:
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="这里填入字体 CSS" rel="stylesheet">
  1. Google Fonts 找一个你心仪的字体然后把字体 css 填入 填入字体 CSS

  2.  在以下代码中填入 font-family 后填入 /assets/scss/custom.scss 中即可

body, .article-content {
    font-family: ;
}

其他字体

霞骛文楷

/layouts/partials/head/custom.html 中加入以下代码(选一个即可):

<!-- LXGW WenKai GB Screen -->
<link rel="stylesheet" href="https://npm.elemecdn.com/lxgw-wenkai-screen-webfont@1.7.0/lxgwwenkaigbscreen.css" />
<!-- LXGW WenKai GB Screen R -->
<link rel="stylesheet" href="https://npm.elemecdn.com/lxgw-wenkai-screen-webfont@1.7.0/lxgwwenkaigbscreenr.css" />
<!-- LXGW WenKai Screen -->
<link rel="stylesheet" href="https://npm.elemecdn.com/lxgw-wenkai-screen-webfont@1.7.0/lxgwwenkaiscreen.css" />
<!-- LXGW WenKai Screen R -->
<link rel="stylesheet" href="https://npm.elemecdn.com/npm/lxgw-wenkai-screen-webfont@1.7.0/lxgwwenkaiscreenr.css" />

再在 /assets/scss/custom.scss 中加入以下代码:

// 字体
body, .article-content {
    font-family: "LXGW WenKai Screen", sans-serif;
}

上面选择了 lxgwwenkaiscreenr.csslxgwwenkaigbscreenr.css 需使用以下代码:

// 字体
body, .article-content {
    font-family: "LXGW WenKai Screen R", sans-serif;
}

HarmonyOS Sans

/layouts/partials/head/custom.html 中加入以下代码(选一个即可):

<!-- B 站 -->
<link
  rel="stylesheet"
  href="https://s1.hdslb.com/bfs/static/jinkela/long/font/regular.css"
/>

<!-- 我的 GitHub 仓库 -->
<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/gh/Xalaok/HarmonyOS-Sans-Webfont@main/regular.min.css"
/>

再在 /assets/scss/custom.scss 中加入以下代码:

// 字体
body, .article-content {
    font-family: "'HarmonyOS_Regular", sans-serif;
}

MiSans

/layouts/partials/head/custom.html 中加入以下代码:

<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/misans@3.1.1/lib/misans-400-regular.min.css"
/>

再在 /assets/scss/custom.scss 中加入以下代码:

// 字体
body, .article-content {
    font-family: "MiSans", sans-serif;
}

鼠标样式

来自:打造一款乖巧的鼠标指针特效 - Xecades

Cursor.css

#cursor {
    position: fixed;
    width: 16px;
    height: 16px;
    background: #000;
    border-radius: 8px;
    opacity: 0.25;
    z-index: 10086;
    pointer-events: none;
    transition: 0.2s ease-in-out;
    transition-property: background, opacity, transform;
}

#cursor.hidden {
    opacity: 0;
}

#cursor.hover {
    opacity: 0.1;
    transform: scale(2.5);
}

#cursor.active {
    opacity: 0.5;
    transform: scale(0.5);
}

Cursor.js

var CURSOR;

Math.lerp = (a, b, n) => (1 - n) * a + n * b;

const getStyle = (el, attr) => {
    try {
        return window.getComputedStyle
            ? window.getComputedStyle(el)[attr]
            : el.currentStyle[attr];
    } catch (e) {}
    return "";
};

class Cursor {
    constructor() {
        this.pos = {curr: null, prev: null};
        this.pt = [];
        this.create();
        this.init();
        this.render();
    }

    move(left, top) {
        this.cursor.style["left"] = `${left}px`;
        this.cursor.style["top"] = `${top}px`;
    }

    create() {
        if (!this.cursor) {
            this.cursor = document.createElement("div");
            this.cursor.id = "cursor";
            this.cursor.classList.add("hidden");
            document.body.append(this.cursor);
        }

        var el = document.getElementsByTagName('*');
        for (let i = 0; i < el.length; i++)
            if (getStyle(el[i], "cursor") == "pointer")
                this.pt.push(el[i].outerHTML);

        document.body.appendChild((this.scr = document.createElement("style")));
        this.scr.innerHTML = `* {cursor: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8' width='8px' height='8px'><circle cx='4' cy='4' r='4' opacity='.5'/></svg>") 4 4, auto}`;
    }
    // 换颜色在opacity='.5'后面加fill='rgb(xxx, xxx, xxx)'

    refresh() {
        this.scr.remove();
        this.cursor.classList.remove("hover");
        this.cursor.classList.remove("active");
        this.pos = {curr: null, prev: null};
        this.pt = [];

        this.create();
        this.init();
        this.render();
    }

    init() {
        document.onmouseover  = e => this.pt.includes(e.target.outerHTML) && this.cursor.classList.add("hover");
        document.onmouseout   = e => this.pt.includes(e.target.outerHTML) && this.cursor.classList.remove("hover");
        document.onmousemove  = e => {(this.pos.curr == null) && this.move(e.clientX - 8, e.clientY - 8); this.pos.curr = {x: e.clientX - 8, y: e.clientY - 8}; this.cursor.classList.remove("hidden");};
        document.onmouseenter = e => this.cursor.classList.remove("hidden");
        document.onmouseleave = e => this.cursor.classList.add("hidden");
        document.onmousedown  = e => this.cursor.classList.add("active");
        document.onmouseup    = e => this.cursor.classList.remove("active");
    }

    render() {
        if (this.pos.prev) {
            this.pos.prev.x = Math.lerp(this.pos.prev.x, this.pos.curr.x, 0.15);
            this.pos.prev.y = Math.lerp(this.pos.prev.y, this.pos.curr.y, 0.15);
            this.move(this.pos.prev.x, this.pos.prev.y);
        } else {
            this.pos.prev = this.pos.curr;
        }
        requestAnimationFrame(() => this.render());
    }
}

(() => {
    CURSOR = new Cursor();
    // 需要重新获取列表时,使用 CURSOR.refresh()
})();

效果见 Xecades 主页

阅读量: 0
Licensed under CC BY-NC-SA 4.0 转载请在留言板告知