给hugo-theme-learn增加timeline功能

前言

现在使用hugo-theme-learn主题,但这个主题没有提供时间线功能,为了给首页增加时间线功能,花了两天研究了一下怎么添加时间线功能。

为了方便增加时间线的事件,采用自定义shortcode的方式增加时间线,代码大部分参考引用链接里面的,魔改了一下以适应自己的主题,增加了过去多少天的显示。

代码实现

layout/shortcodes新增文件:event.htmltimeline.html,内容分别如下

event.html
{{$duration := ""}}
{{$to := now }}
{{ if ne (.Get "to") ""}}
{{$to = time (.Get "to") }}
{{end}}
{{$enabledTime := ne (.Get "from") ""}}
{{if $enabledTime }}
{{$from := time (.Get "from") }}
{{ $diff := $to.Sub $from }}
{{ $days := div $diff.Hours 24 | math.Round }}

{{$tmonths:=mul ($to.Sub $from).Hours 0.00136986301 }}
{{$months := mod $tmonths 12 }}
{{$years := math.Floor (div $tmonths 12)}}
{{$yearStr := "years"}}
{{if lt $years 2 }}
{{$yearStr = "year"}}
{{end}}
{{$monthStr := "months"}}
{{if lt $months 2 }}
{{$monthStr = "month"}}
{{end}}
{{$daysStr := "days"}}

{{ $idays :=int $days }}

{{$duration = ""}}
{{if gt $years 0 }}
{{$duration = printf "%s %.0f %s" $duration $years $yearStr}}
{{$idays = sub $idays (mul 365 $years)}}
{{end}}
{{if gt $months 0 }}
{{$duration = printf "%s %d %s" $duration $months $monthStr}}
{{$idays = sub $idays (mul 30 $months)}}
{{end}}
{{$idays = int $idays}}
{{if gt $idays 0}}
{{if lt $idays 2}}
{{$daysStr = "day"}}
{{end}}
{{$duration = printf "%s %d %s" $duration $idays $daysStr}}
{{end}}
{{end}}

{{ $from := .Get "from" | time }}
{{ $to := now }}
{{ $diff := $to.Sub $from }}
{{ $ago := div $diff.Hours 24 | math.Round }}
{{ $measure := cond (eq 1 $ago) "day" "days" }}

{{ $seed := "foo" }}
{{ $random := delimit (shuffle (split (md5 $seed) "" )) "" }}

<div class="container">
    <div class="content">
        <div class="title">{{.Get "title"}}</div>
        {{if $enabledTime }}
        <div class="moment" {{ if eq .Ordinal 0 }} id="moment" {{ end }} {{ if ne .Ordinal 0 }}id="moment-{{ substr $random 0 16}}"{{end}}>
            {{ if ne .Ordinal 0 }} {{$duration}} {{ end }} | {{ $ago }} {{ $measure}} ago
        </div>
        {{ end }}
        <div class="body">

            {{.Inner}}
        </div>

    </div>
    <div class="date">{{$to.Year}}</div>
</div>
{{ if and (eq (.Ordinal) 0) $enabledTime }}
<script>
    function non0plural(number, name) {
        if (number == 0) {
            return ""
        }
        if (number > 1) {
            return number + " " + name + "s"
        }
        return number + " " + name
    }
    function refresh() {
        start = dayjs({{.Get "from"}})
    
        now = dayjs()
        total_days = now.diff(start,"d",true)
        total_months = now.diff(start, "M", true)
        months = total_months % 12
        years = Math.floor((total_months) / 12)
    // for (var i = 0, els = document.querySelectorAll(`[id^="moment"]`); i < els.length; i++) {
    //     els[i].innerHTML = non0plural(years, "year") + " " + non0plural(months.toFixed(8), "month")
    // }
        // 如果月份大于1则
        if(years>=1){
            total_days-=365*years
        }
        if(months>=1){
            total_days-=30*months
        }
        el = document.querySelector("#moment");
        el.innerHTML = years>1? non0plural(years, "year"):"" + " " + months>1? non0plural(months.toFixed(4), "month"):"" + total_days>=1?non0plural(total_days.toFixed(2),"day"):"" 
        el.innerHTML = el.innerHTML + " ago"
    }
    window.setInterval(refresh, 100);
</script>
{{ end }}
timeline.html
<style type="text/css">
    .timeline {
        position: relative;
        margin: 0 auto;
    }

    /* The actual timeline (the vertical ruler) */
    .timeline::after {
        content: "";
        position: absolute;
        width: 6px;
        background-color: #444;
        top: 0;
        bottom: 0;
        left: 10%;
        margin-left: -3px;
    }

    /* Container around content */
    .timeline .container {
        padding: 10px 10px 10px 40px;
        margin-top: 10px;
        position: relative;
        /* background-color: gray; */
        width: 90%;
        left: 10%;
    }

    /* The circles on the timeline */
    .timeline .container::after {
        content: "";
        position: absolute;
        width: 25px;
        height: 25px;
        left: -12px;
        background-color: rgb(106, 215, 229);
        border: 4px solid #444;
        top: 0px;
        border-radius: 50%;
        z-index: 1;
    }

    /* date display */
    .timeline .container .date {
        position: absolute;
        top: 0px;
        z-index: 1;
        left: -15%;
        font-size: large;
    }

    /* Add arrows to the right container (pointing left) */
    .timeline .container::before {
        content: " ";
        height: 0;
        position: absolute;
        top: 30px;
        width: 0;
        z-index: 1;
        left: 26px;
        border: medium solid #6ad7e5;
        border-width: 13px 13px 13px 0px;
        border-color: #6ad7e5 #6ad7e5 transparent transparent;
    }

    /* The actual content */
    .timeline .content {
        box-shadow: 0 0 3px 3px #6ad7e5;
        background-color: white;
        position: relative;
        border-radius: 6px;
        transition: box-shadow 0.3s;
    }

    /* small shadow change on hover*/
    .timeline .content:hover {
        box-shadow: 0 0 3px 4px #6ad7e5;
    }

    /* card title format */
    .timeline .content .title {
        padding: 5px 30px;
        font-weight: bold;
        display: inline-block;
    }

    /* time moment format*/
    .timeline .content .moment {
        color: #c41a16;
        text-align: right;
        position: absolute;
        top: 0;
        right: 0;
        padding: 5px;
    }

    /* body size */
    .timeline .content .body {
        padding: 5px 30px;
        word-wrap: break-word;
        /* height: 73px; */
        /* max-height: 120px; */
        text-overflow: ellipsis;

        overflow: hidden;

    }

    /* responsive for small devices*/
    @media screen and (max-width: 600px) {
        .timeline .container {
            padding: 10px 10px 0px 40px;
            left: 5%;
            width: 95%;
        }

        .timeline .container .date {
            font-size: small;
            transform: rotate(-90deg);
            left: -5%;
            top: 30px;
        }

        .timeline .container::after {
            left: 3px;
        }

        .timeline .content .body {
            padding: 5px 5px;
        }

        .timeline .content .moment {
            position: relative;
        }
    }
</style>
<div class="timeline">
    {{ .Inner }}
</div>

由于event.html里面引用了dayjs,这个js不在当前主题内,可以在config.toml新增custom_js = ["js/dayjs.min.js"],然后把dayjs.min.js放在theme\hugo-theme-learn\static\js目录内,即可解决导入dayjs的问题。

此时可以在需要添加时间线的地方使用timeline的shortcode添加,以下是我博客首页的时间线代码:

{{< timeline >}}
    {{% event title="09-03至09-04" from="2022-09-03" to="2022-09-04" %}}
这两天,给hugo主题增加了timeline的shortcode,见[增加timeline功能](others/add-timeline-shortcode)
    {{% /event %}}
    {{% event title="08-29至09-02" from="2022-08-29" to="2022-09-02" %}}
周内审计了代码
    {{% /event %}}
{{< /timeline >}}

除了这个还顺便给博客新增了基于Github issue的评论功能,参考 utteranc

参考

https://metalblueberry.github.io/post/howto/2021-02-28_hugo_timeline_shortcode/

创建于:Monday, September 5,2022
最后修改于: Thursday, May 4,2023