魔改笔记四:友链页重构及友链朋友圈适配

碎碎念

4月08日,店长久违的更新了方舟系列的友链卡片,作为店长铁粉,当然要第一时间买店长的火柴啦,于是我将店长所有的友链卡片稍微整合了一下,结合安知鱼的教程,最终实现了“五世同堂”友链页面,大家可以在下面链接卡片或者友链页面查看教程,五种友链可以随便混合搭配,大家按照自己的butterfly主题风格稍作修改即可,并且针对鱼的友链美化后无法适配友链朋友圈,这里提供了一个相对可行解决方案,我也试验过,可以正常自行部署以及vercel无服务器部署,于是在此分享给大家!

功能对比

-教程-

本站教程

店长教程

安知鱼教程

原生友链页

功能解释

卡片类型

𝟱

𝟱

𝟮

𝟭

友链卡片的不同外观效果数目

随机顺序

相同组别中友链卡片顺序随机打乱

卡片组合

友链页面不同组别设置不同效果卡片

适配友圈

友链朋友圈无法抓取的解决方案

适配夜间

针对于byer卡片对于夜间模式的简单适配

写在最前

本教程涉及到魔改,需要修改主题源文件,需要一定的技术水平和操作风险。在本次教程中,我也会详细讲解具体做法和相关修改内容的原因,以便于能够让更多人学会魔改。在进行任何修改之前,请务必备份好所有原始文件,以防意外发生。备份的方法可以是将整个项目压缩成一个压缩文件,确保您在出问题后可以随时通过解压方式回滚到之前的状态。谨慎操作,祝您顺利魔改成功!如果有任何疑问或需要帮助,请随时评论区或者邮件联系我。

由于风格原因,原用于演示的友链页面中的ark及byer改为butterfly及另外两种特效,前两者具体样式请看截图。

卡片魔改

注意先去备份所有源文件,防止改错了回不去的尴尬局面(别跟我一样呜呜呜)

修改模板文件

打开:"[root]\themes\butterfly\layout\includes\page\flink.pug"文件,覆盖式写入下面的内容:

代码语言:javascript
复制
#article-container
  if top_img === false
    h1.page-title= page.title
  .flink
    if site.data.link
      each i in site.data.link
        if i.class_name
          h2!= i.class_name
        if i.class_desc
          .flink-desc!=i.class_desc
        if i.flink_style === 'butterfly'
          .butterfly-flink-list
            - let randomList = i.link_list.slice()
            if i.random
              - randomList.sort(() => Math.random() - 0.5)
            each item in randomList
              .flink-list-item
                a(href=url_for(item.link)  title=item.name target="_blank")
                  .flink-item-icon
                    if theme.lazyload.enable
                      img.nolazyload(data-lazy-src=url_for(item.avatar) onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.flink) + `'` alt=item.name )
                    else
                      img.nolazyload(src=url_for(item.avatar) onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.flink) + `'` alt=item.name )
                  .flink-item-info
                    .flink-item-name= item.name
                    .flink-item-desc(title=item.descr)= item.descr
        else if i.flink_style === 'flexcard'
          .flexcard-flink-list
            - let randomList = i.link_list.slice()
            if i.random
              - randomList.sort(() => Math.random() - 0.5)
            each item in randomList
              a.flink-list-card(href=url_for(item.link) target='_blank' data-title=item.descr)
                .wrapper.cover
                  - var siteshot = item.siteshot ? url_for(item.siteshot) : 'https://s0.wp.com/mshots/v1/' + item.link + '?w=400&h=300'
                  if theme.lazyload.enable
                    img.cover.fadeIn.nolazyload(data-lazy-src=siteshot onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.post_page) + `'` alt='' )
                    img.cover.fadeIn.nolazyload(data-lazy-src=avatar onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.post_page) + `'` alt='' )
                  else
                    img.cover.fadeIn.nolazyload(src=siteshot onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.post_page) + `'` alt='' )
                    img.cover.fadeIn.nolazyload(data-lazy-src=avatar onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.post_page) + `'` alt='' )
                .info
                  if theme.lazyload.enable
                    img.flink-avatar.nolazyload(data-lazy-src=url_for(item.avatar) onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.flink) + `'` alt='' )
                  else
                    img.nolazyload(src=url_for(item.avatar) onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.flink) + `'` alt='' )
                  span.flink-sitename= item.name
        else if i.flink_style === 'volantis'
          .volantis-flink-list
            - let randomList = i.link_list.slice()
            if i.random
              - randomList.sort(() => Math.random() - 0.5)
            each item in randomList
              a.site-card(target='_blank' rel='noopener' href=url_for(item.link))
                .img
                  - var siteshot = item.siteshot ? url_for(item.siteshot) : 'https://s0.wp.com/mshots/v1/' + item.link + '?w=400&h=300'
                  img.nolazyload.no-lightbox(src=siteshot onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.post_page) + `'` alt='' )
                .info
                  img.nolazyload.no-lightbox(src=url_for(item.avatar) onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.flink) + `'` alt='' )
                  span.title= item.name
                  span.desc(title=item.descr)= item.descr
        else if i.flink_style === 'byer'
          .byer-flink-list
            - let randomList = i.link_list.slice()
            if i.random
              - randomList.sort(() => Math.random() - 0.5)
            each item in randomList
              .flink-list-item
                a(href=url_for(item.link)  title=item.name target="_blank")
                  .flink-item-bar
                    sapn.flink-item-bar-yellow 
                    sapn.flink-item-bar-green 
                    sapn.flink-item-bar-red
                    sapn.flink-item-bar-x +
                  .flink-item-content
                    .flink-item-text
                      .flink-item-name= item.name 
                      .flink-item-desc(title=item.descr)= item.descr
                    .flink-item-icon
                      img.no-lightbox(src=url_for(item.avatar) onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.flink) + `'` alt=item.name )
        else if i.flink_style === 'ark'
          .ark-flink-list
            - let randomList = i.link_list.slice()
            if i.random
              - randomList.sort(() => Math.random() - 0.5)
            each item in randomList
              a.ark-flink-list-card(href=url_for(item.link) target='_blank' title=item.descr)
                .ark-flink-progress-bar-A
                .ark-flink-progress-bar-B
                .ark-flink-progress-bar-C
                .ark-flink-content
                  .ark-flink-name
                    .flink-sitename= item.name
                    .flink-block
                  .ark-flink-avatar
                    img.no-lightbox(src=url_for(item.avatar) onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.flink) + `'` alt='' )
                  .ark-flink-mask
                    .ark-flink-mask-left 
                    .ark-flink-mask-right
                  .ark-flink-descr
                    .ark-flink-descr-text=item.descr
                  .ark-flink-siteshot
                    - var siteshot = item.siteshot ? url_for(item.siteshot) : 'https://s0.wp.com/mshots/v1/' + item.link + '?w=400&h=300'
                    img.no-lightbox(src=siteshot onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.post_page) + `'` alt='' )
!= page.content</code></pre></div></div><p>注意缩进问题,整个文件全部覆盖,可以看见这个文件中,下面是这段代码的解释:</p><ol class="ol-level-0"><li><code>#article-container</code>: 这是一个带有ID为<code>article-container</code>的HTML元素。</li><li><code>if top_img === false</code>: 如果<code>top_img</code>的值为<code>false</code>,则不需要顶部<code>banner</code>图片。</li><li><code>h1.page-title= page.title</code>: 在<code>.flink</code>类内部,如果条件满足,则创建一个<code>h1</code>标题元素,标题内容为<code>page.title</code>。</li><li><code>.flink</code>: 这是一个类为<code>flink</code>的HTML元素。</li><li><code>if site.data.link</code>: 如果<code>site.data.link</code>存在,则执行以下内容。</li><li><code>each i in site.data.link</code>: 遍历每个组别,也就是不同<code>site.data.link</code>中的每个元素,将当前元素赋值给<code>i</code>。</li><li><code>if i.class_name</code>: 如果<code>i</code>中存在<code>class_name</code>属性,则创建一个<code>h2</code>元素显示<code>i.class_name</code>。</li><li><code>if i.class_desc</code>: 如果<code>i</code>中存在<code>class_desc</code>属性,则创建一个<code>.flink-desc</code>元素显示<code>i.class_desc</code>。

然后根据不同的主题风格去使用不同类名创建元素:

  1. if i.flink_style === 'butterfly': 如果i中的flink_style属性的值为butterfly,则执行以下内容,渲染一个蝴蝶风格的链接列表。
  2. else if i.flink_style === 'flexcard': 如果i中的flink_style属性的值为flexcard,则执行以下内容,渲染一个flexcard风格的链接列表。
  3. else if i.flink_style === 'volantis': 如果i中的flink_style属性的值为volantis,则执行以下内容,渲染一个volantis风格的链接列表。
  4. else if i.flink_style === 'byer': 如果i中的flink_style属性的值为byer,则执行以下内容,渲染一个byer风格的链接列表。
  5. else if i.flink_style === 'ark': 如果i中的flink_style属性的值为ark,则执行以下内容,渲染一个ark风格的链接列表。
  • != page.content: 最后,将page.content的内容原样输出。
  • 实际上理论不难,就是给不同样式的卡片添加不同的类名,后面根据不同类名进行不同的CSS渲染。

    修改样式文件

    打开:"[root]\themes\butterfly\source\css\_page\flink.styl"文件,覆盖以下内容:

    代码语言:javascript
    复制
    .flink-desc
      margin: .2rem 0 .5rem
    //bf原生
    .butterfly-flink-list
      overflow: auto
      padding: 10px 10px 0
      text-align: center
    

    & > .flink-list-item
    position: relative
    float: left
    overflow: hidden
    line-height: 17px
    -webkit-transform: translateZ(0)
    height: 100px;
    padding: 10px;
    width: calc(100% / 5 - 0.5rem)
    margin: 0.5rem 0.25rem;
    border-radius: 12px;
    border: var(--style-border);
    -webkit-transition: all .3s ease-in-out;
    -moz-transition: all .3s ease-in-out;
    -o-transition: all .3s ease-in-out;
    -ms-transition: all .3s ease-in-out;
    transition: all .3s ease-in-out;

    +maxWidth1200()
      width: calc(50% - 15px) !important
    
    +maxWidth600()
      width: calc(100% - 15px) !important
    
    &amp;:hover
      border-color: #101010 !important;
      background-color: #eeeeee !important;
      box-shadow: #cccccc !important;
    
      .flink-item-icon
        width: 0;
        height: 0;
        margin-left: -10px;
      
    
    &amp;:hover:before,
    &amp;:focus:before,
    &amp;:active:before
      transform: scale(1)
    
    a
      color: var(--font-color)
      text-decoration: none
    
      .flink-item-icon
        float: left
        overflow: hidden
        margin: 15px 10px
        width: 60px
        height: 60px
        border-radius: 35px
        transition: all .3s ease-out
        margin: 8px 0 8px 0;
        border-radius: 50%;
        overflow: hidden;
    
        img
          width: 100%
          height: 100%
          transition: filter 375ms ease-in .2s, transform .3s
          object-fit: cover
    
      .img-alt
        display: none
    

    .flink-item-info
    display: flex;
    flex-wrap: wrap;
    padding-left: 10px;
    text-align: left;
    flex-direction: column;

    .flink-item-name
    @extend .limit-one-line
    padding: 12px 0 16px 0;
    height: auto;
    font-weight: bold
    font-size: 1.2em

    .flink-item-desc
    @extend .limit-one-line
    padding: 0
    height: 35px
    font-size: .93em
    opacity: .7;
    word-break: break-all;
    white-space: break-spaces;
    display: -webkit-box;
    overflow: hidden;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;

    .flink-name
    margin-bottom: 5px
    font-weight: bold
    font-size: 1.5em
    //flexcard卡片
    #article-container img
    margin-bottom: 0.5rem;
    object-fit: cover;
    max-height: 900px;
    .flexcard-flink-list
    overflow hidden
    .flink-list-card
    .wrapper img
    transition: transform .5s ease-out !important;

    & > a
    width: calc(100% / 5 - 0.5rem);
    height 150px
    position relative
    display block
    margin: 0.5rem 0.25rem;
    float left
    overflow hidden
    padding: 0;
    border-radius: 8px;
    transition all .3s ease 0s, transform .6s cubic-bezier(.6, .2, .1, 1) 0s
    box-shadow none
    border: var(--style-border)!important;
    &:hover
    .info
    transform translateY(-100%)
    .wrapper
    img
    transform scale(1.2)
    &::before
    position: fixed
    width:inherit
    margin:auto
    left:0
    right:0
    top:10%
    border-radius: 10px
    text-align: center
    z-index: 100
    content: attr(data-title)
    font-size: 20px
    color: #fff
    padding: 10px
    background-color: rgba($theme-color,0.8)

    .cover
      width 100%
      transition transform .5s ease-out
    .wrapper
      position relative
      .fadeIn
        animation coverIn .8s ease-out forwards
      img
        height 150px
        pointer-events none
    .info
      display flex
      flex-direction column
      justify-content center
      align-items center
      width 100%
      height 100%
      overflow hidden
      border-radius 3px
      background-color hsla(0, 0%, 100%, .7)
      transition transform .5s cubic-bezier(.6, .2, .1, 1) 0s
      img
        position relative
        top 45px
        width 80px
        height 80px
        border-radius 50%
        box-shadow 0 0 10px rgba(0, 0, 0, .3)
        z-index 1
        text-align center
        pointer-events none
      span
        padding 20px 10% 60px 10%
        font-size 16px
        width 100%
        text-align center
        box-shadow 0 0 10px rgba(0, 0, 0, .3)
        background-color hsla(0, 0%, 100%, .7)
        color var(--font-color)
        white-space nowrap
        overflow hidden
        text-overflow ellipsis
    

    .flexcard-flink-list>a .info,
    .flexcard-flink-list>a .wrapper .cover
    position absolute
    top 0
    left 0

    @media screen and (max-width:1024px)
    .flexcard-flink-list
    & > a
    width calc(33.33333% - 15px)

    @media screen and (max-width:600px)
    .flexcard-flink-list
    & > a
    width calc(50% - 15px)

    [data-theme=dark]
    .flexcard-flink-list a .info,
    .flexcard-flink-list a .info span
    background-color rgba(0, 0, 0, .6)
    .flexcard-flink-list
    & > a
    &:hover
    &:before
    background-color: rgba(#121212,0.8);
    .justified-gallery > div > img,
    .justified-gallery > figure > img,
    .justified-gallery > a > a > img,
    .justified-gallery > div > a > img,
    .justified-gallery > figure > a > img,
    .justified-gallery > a > svg,
    .justified-gallery > div > svg,
    .justified-gallery > figure > svg,
    .justified-gallery > a > a > svg,
    .justified-gallery > div > a > svg,
    .justified-gallery > figure > a > svg
    position static!important

    trans($time = 0.28s)
    transition: all $time ease
    -moz-transition: all $time ease
    -webkit-transition: all $time ease
    -o-transition: all $time ease

    //volantis卡片,我的最爱
    .volantis-flink-list
    display: flex
    flex-wrap: wrap
    justify-content: flex-start
    margin: -0.5 * 16px
    align-items: stretch
    .site-card
    margin: 16px * 0.5
    width: "calc(100% / 4 - %s)" % 16px
    @media screen and (min-width: 2048px)
    width: "calc(100% / 5 - %s)" % 16px
    @media screen and (max-width: 768px)
    width: "calc(100% / 3 - %s)" % 16px
    @media screen and (max-width: 500px)
    width: "calc(100% / 2 - %s)" % 16px
    display: block
    line-height: 1.4
    height 100%
    .img
    width: 100%
    height 150px
    @media screen and (max-width: 500px)
    height 100px
    overflow: hidden
    border-radius: 12px * 0.5
    box-shadow: 0 1px 2px 0px rgba(0, 0, 0, 0.2)
    background: #f6f6f6
    img
    width: 100%
    height 100%
    pointer-events:none;
    // trans(.75s)
    transition: transform 2s ease
    object-fit: cover

    .info
    margin-top: 16px * 0.5
    img
    width: 32px
    height: 32px
    pointer-events:none;
    border-radius: 16px
    float: left
    margin-right: 8px
    margin-top: 2px
    span
    display: block
    .title
    font-weight: 600
    font-size: var(--global-font-size)
    color: #444
    display: -webkit-box
    -webkit-box-orient: vertical
    overflow: hidden
    -webkit-line-clamp: 1
    trans()
    .desc
    font-size: var(--global-font-size)
    word-wrap: break-word;
    line-height: 1.2
    color: #888
    display: -webkit-box
    -webkit-box-orient: vertical
    overflow: hidden
    -webkit-line-clamp: 2
    .img
    trans()
    &:hover
    .img
    box-shadow: 0 4px 8px 0px rgba(0, 0, 0, 0.1), 0 2px 4px 0px rgba(0, 0, 0, 0.1), 0 4px 8px 0px rgba(0, 0, 0, 0.1), 0 8px 16px 0px rgba(0, 0, 0, 0.1)
    .info .title
    color: #ff5722
    //byer卡片
    #article-container
    .flink
    margin-bottom: 20px

    .byer-flink-list
      overflow: auto
      padding: 10px 10px 0
      text-align: center
    
      &amp; &gt; .flink-list-item
        position: relative
        background: #ffffff
        float: left
        overflow: hidden
        margin: 15px 7px
        width: calc(100% / 3 - 15px)
        height: 120px
        border-radius: 2px
        line-height: 17px
        -webkit-transform: translateZ(0)
        border: 1px solid
        box-shadow: 3px 3px 1px 1px #fee34c;
    
        +maxWidth1024()
          width: calc(50% - 15px) !important
    
        +maxWidth600()
          width: calc(100% - 15px) !important
    
        a
          color: var(--font-color)
          text-decoration: none
          .flink-item-bar
            height: 15px
            border-width: 0 0 1px 0
            border-style: none none solid none
            background: #fde135
            display: flex;
            align-items: center;
            flex-direction: row;
            flex-wrap: nowrap;
            padding: 0 3px 0 3px
            sapn
              width: 10px;
              height: 10px;
              margin: 0 1px 0 1px
              border-radius: 50%;
              display: block;
              border: 1px solid;
              display: flex;
              align-items: center;
              justify-content: flex-start;
              &amp;.flink-item-bar-yellow
                background: #fde135
              &amp;.flink-item-bar-green
                background: #249a33
              &amp;.flink-item-bar-red
                background: #f13b06
              &amp;.flink-item-bar-x
                background: transparent
                border: 0px
                margin-left: auto
                transform: rotate(45deg);
                font-size: 23px;
                padding: 0px 0px 6px 0px;
          .flink-item-content
            display: flex;
            height: 105px
            flex-direction: row;
            align-items: center;
            justify-content: space-between;
            padding: 0 5px 0 5px;
            .flink-item-text
              width: 60%;
              display: flex;
              flex-direction: column;
              align-items: center;
              .flink-item-name
                @extend .limit-one-line
                max-width: 100%;
                padding: 0px 5px 0px 5px;
                margin: 0px 0 6px 0;
                height: 50%;
                font-weight: bold;
                font-size: 1.43em;
                border-width: 0 0 7px 0;
                border-style: solid;
                border-color: #fbf19f;
              .flink-item-desc
                @extend .limit-one-line
                max-width: 100%;
                height: 50%;
                padding: 5px 5px 5px 5px;
                font-size: 0.93em;
                position: relative
                &amp;:before
                  content: &#34;&#34;;
                  background: transparent;
                  display: block;
                  height: calc(100% - 4px);
                  width: calc(100% - 4px);
                  position: absolute;
                  left: 0;
                  top: 0;
                  border-radius: 2px;
                  border: 1px solid;
                  clip-path: polygon(0 0, 100% 0, 100% 100%, 95% 100%, 95% 50%, 90% 50%, 90% 100%, 0 100%);
    
    
            .flink-item-icon
              overflow: hidden;
              margin: 0px 5px;
              width: 70px;
              height: 70px;
              border: 1px solid;
              border-radius: 2px;
              transition: width .3s ease-out
              box-shadow: 2px 2px 1px 1px #fee34c;
              img
                width: 50px;
                height: 50px;
                margin: 9px 9px;
                transition: filter 375ms ease-in .2s, transform .3s
                object-fit: cover
    
              .img-alt
                display: none
    

    //byer卡片暗夜模式改造
    [data-theme=dark]
    #article-container
    .flink
    .byer-flink-list
    & > .flink-list-item
    background: rgb(40,40,40)
    box-shadow: 3px 3px 1px 1px #1B5A70;
    a
    .flink-item-bar
    background: #1B5A70;
    .flink-item-content
    .flink-item-text
    .flink-item-name
    border-color: #5EBAD9;
    .flink-item-icon
    box-shadow: 2px 2px 1px 1px #1B5A70;

    //下面是aki及其自定义配色:
    :root
    --ark-flink-default-color: rgba(153, 54, 44,0.8) /主色调/
    --ark-flink-mask: #818181 /遮罩层配色/
    --ark-flink-progress-default: rgba(227, 236, 238, 0.8) /能量条默认配色/
    --ark-flink-progress-charge: #d97f17 /能量条充能配色/
    --flink-name-border-color: #d97f17 /ID边框配色/

    [data-theme="dark"]
    --ark-flink-default-color: rgba(55, 112, 143,0.8)
    --ark-flink-mask: #37708f
    --ark-flink-progress-default: rgba(46, 160, 221, 0.8)
    --ark-flink-progress-charge: rgba(227, 236, 238, 0.8)
    --flink-name-border-color: rgba(227, 236, 238, 0.8)

    //适配ark方舟友链卡片
    #article-container
    .flink
    margin-bottom: 20px

    .ark-flink-list
      overflow: auto
      padding: 10px 10px 0
      text-align: center
    
      &amp; &gt; .ark-flink-list-card
        position: relative
        display: block
        color: var(--font-color)
        text-decoration: none
        float: left
        overflow: hidden
        margin: 15px 7px
        width: calc(100% / 3 - 15px)
        height: 220px
        border-radius: 2px
        line-height: 17px
        -webkit-transform: translateZ(0)
    
        +maxWidth1024()
          width: calc(50% - 15px) !important
    
        +maxWidth600()
          width: calc(100% - 15px) !important
        
    
      a.ark-flink-list-card
        *
          transition: all 0.3s cubic-bezier(.6, 0, .5, 1)
        &amp;:hover
          *
            transition: all 0.3s cubic-bezier(.6, 0, .5, 1)
          .ark-flink-progress-bar-A,
          .ark-flink-progress-bar-B,
          .ark-flink-progress-bar-C
            background: var(--ark-flink-progress-charge)
          .ark-flink-content
            .ark-flink-name
              bottom: 0px;
            .ark-flink-avatar
              transform: rotateX(90deg)
            .ark-flink-mask
              .ark-flink-mask-left
                transition-delay: 0.3s;
                left: -35%;
              .ark-flink-mask-right
                transition-delay: 0.3s;
                right: -55%;
            .ark-flink-descr
              .ark-flink-descr-text
                transition-delay: 0.3s;
                opacity: 1
                animation: ark-flink-type 1.5s steps(20, end) 0.3s,ark-flink-blink .75s step-end infinite; /* 定义光标的闪烁动画 */  
        .ark-flink-progress-bar-A,
        .ark-flink-progress-bar-B,
        .ark-flink-progress-bar-C
          display: block
          position: absolute;
          background: var(--ark-flink-progress-default)
          z-index 6
        .ark-flink-progress-bar-A
          height: 8px;
          width: 100px;
          top: 3px;
          left: 6px;
          clip-path: polygon(0% 100%, 8% 0%, 28% 0%, 20% 100%, 23% 100%, 31% 0%, 46% 0%, 38% 100%, 41% 100%, 49% 0%, 64% 0%, 56% 100%, 59% 100%, 67% 0%, 82% 0%, 74% 100%, 77% 100%, 85% 0%, 100% 0%, 90% 100%);
        .ark-flink-progress-bar-B
          height: 8px;
          width: 35px;
          bottom: 35px;
          left: 0;
          clip-path: polygon(0% 0%, 40% 0%, 15% 100%, 25% 100%, 50% 0%, 85% 0%, 60% 100%, 70% 100%, 90% 0%, 100% 100%, 15% 100%);
        .ark-flink-progress-bar-C
          height: 100px
          width: 8px
          bottom: 50px
          right: 0
          clip-path: polygon( 0% 0%, 100% 8%, 100% 28%, 0% 20%, 0% 23%, 100% 31%,100% 46% ,0% 38% ,0% 41% ,100% 49% ,100% 64% ,0% 56% ,0% 59% ,100% 67% ,100% 82% ,0% 74% , 0% 77%,100% 85% , 100% 100%,0% 90% );
        .ark-flink-content
          display: block
          position: absolute
          background: radial-gradient(var(--ark-flink-default-color),transparent)
          width: calc(100% - 10px)
          height: 100%
          top: 0
          left: 0
          clip-path: polygon(0 15px, 100px 15px, 115px 0, calc(100% - 45px) 0, calc(100% - 15px) 45px, 100% 45px, 100% calc(100% - 25px), calc(100% - 30px) calc(100% - 25px), calc(100% - 55px) calc(100% - 10px), calc(100% - 90px) calc(100% - 10px), calc(100% - 100px) 100%, 100px 100%, 90px calc(100% - 10px), 55px calc(100% - 10px), 35px calc(100% - 45px), 0% calc(100% - 45px));
          .ark-flink-avatar,
          .ark-flink-mask,
          .ark-flink-descr,
          .ark-flink-siteshot
            position: absolute
            width: 100%
            height: 100%
            top: 0
            left: 0
          .ark-flink-name
            display: block;
            position: absolute;
            z-index: 5;
            bottom: 10px;
            left: 20%;
            color: white;
            text-shadow: 1px 1px 5px black;
            background: transparent;
            height: 40px;
            width: 60%;
            border-style: double;
            border-width: 5px 5px 0 5px;
            border-color: var(--flink-name-border-color);
            transform: perspective(0.5em) rotateX(3deg);
            .flink-sitename
              transform: perspective(0.5em) rotateX(-3deg);
              font-size: 15px;
              margin: 5px 0 0 0;
            .flink-block
              transform: perspective(0.5em) rotateX(-10deg);
              display: block;
              width: 60%;
              height: 13px;
              background: var(--flink-name-border-color);
              position: absolute;
              bottom: 0px;
              left: 20%;
          .ark-flink-avatar
            z-index 4
            display: flex
            align-items: center;
            justify-content: center;
            transform: rotateX(0deg)
            img
              width: 100px;
              height: 100px;
              margin: 0 auto 20px;
              object-fit: cover
              clip-path: polygon(30% 0%, 70% 0%, 100% 30%, 100% 70%, 70% 100%, 30% 100%, 0% 70%, 0% 30%);
            .img-alt
              display: none
          .ark-flink-mask
            z-index 3
            .ark-flink-mask-left
              width: 100%;
              height: 100%;
              background: repeating-linear-gradient(0deg, var(--ark-flink-mask),transparent 1px);
              clip-path: polygon(50% 0, 50% calc(50% - 60px), calc(50% - 20px) calc(50% - 60px), calc(50% - 50px) calc(50% - 30px), calc(50% - 50px) calc(50% + 10px), calc(50% - 20px) calc(50% + 40px), 50% calc(50% + 40px), 50% 100%, 0% 100%, 0% 0%);
              left: 0%;
              position: absolute;
            .ark-flink-mask-right
              background: repeating-linear-gradient(0deg, var(--ark-flink-mask),transparent 1px);
              width: 100%;
              height: 100%;
              clip-path: polygon(100% 0%, 50% 0%, 50% calc(50% - 60px), calc(50% - 20px) calc(50% - 60px), calc(50% - 50px) calc(50% - 30px), calc(50% - 50px) calc(50% + 10px), calc(50% - 20px) calc(50% + 40px), 50% calc(50% + 40px), 50% 100%, 100% 100%);
              right: 0%;
              position: absolute;
          
          .ark-flink-descr
            z-index 2
            display: flex
            align-items: center;
            justify-content: center;
            .ark-flink-descr-text
              color: white;
              text-shadow: 1px 1px 5px black;
              font-size: 1.5em;
              height: 1.5em;
              line-height: 1.5em;
              overflow: hidden; /* 隐藏超出容器的文本 */
              border-right: .15em solid orange; /* 打字效果的光标动画 */
              white-space: nowrap; /* 确保文本在一行内显示,不换行 */
              margin: 20px;
              opacity: 0
          .ark-flink-siteshot
            z-index 1
            display: flex
            align-items: center;
            justify-content: center;
            img
              height: 75%;
              width: 100%;
              margin: 0 auto 20px;
              object-fit: cover;
    

    /* 定义打字机动画 */
    @keyframes ark-flink-type
    from
    width: 0;
    to
    width: 100%;

    /* 定义光标闪烁动画 */
    @keyframes ark-flink-blink
    from,
    to
    border-color: transparent;
    50%
    border-color: orange

    这里大家可以看到内容很多,我也在上面标注了,因为这个是五种友链的分别适配,如果你觉得太多了可以尝试删掉你不需要的部分,上面备注写的也比较详尽,但是建议在修改后将备注删除,防止因为备注导致编译失败,这个就不需要过多解释了,如果想要二次修改的,按照styl的格式,类似于css进行修改即可。

    修改友链数据文件

    打开:"[root]\source_data\link.yml"按照下方格式写入文件:

    代码语言:javascript
    复制
    - class_name: 亦师亦友 · 给予帮助的人最可爱
    flink_style: volantis
    random: true
    link_list:
    - name: 清羽飞扬
    link: https://blog.qyliu.top/
    avatar: https://cdn.qyliu.top/i/2024/03/21/65fc56832e37d.png
    descr: 柳影曳曳,清酒孤灯,扬笔撒墨,心境如霜
    siteshot: https://cdn.qyliu.top/i/2024/03/21/65fc57d6b8615.png
    - name: Akilarの糖果屋
    link: https://akilar.top/
    siteshot: https://cdn.qyliu.top/i/2024/03/21/65fc582187f19.png
    avatar: https://cdn.qyliu.top/i/2024/04/06/661170950f7a2.png
    descr: 欢迎光临糖果屋

    下面是相关选项的内容及可选项:

    一级

    二级

    butterfly

    volantis

    flexcard

    byer

    ark

    解释

    class_name

    (✅)

    (✅)

    (✅)

    (✅)

    (✅)

    每类提示词

    flink_style

    卡片样式

    random

    *

    (✅)

    (✅)

    (✅)

    (✅)

    (✅)

    链接顺序随机

    name

    卡片名称

    link

    卡片链接

    link_list

    avatar

    卡片头像

    descr

    卡片描述

    siteshot

    (✅)

    (✅)

    (✅)

    网站截图

    • ❌:不支持选项
    • (✅):可选选项,可以省略,比如siteshot,可以根据网站信息采用api截图生成,api如下:
      'https://s0.wp.com/mshots/v1/' + [link] + '?w=400&h=300'
    • ✅:必选项,不可省略

    注意,上面link.yml文件给的代码仅供格式参考,由于本站图床已开启防盗链,你们需要修改为自己的信息才能正常显示,谢谢!参考样式也可以看我的友链链接。

    butterflyvolantisflexcardbyerark

    butterfly预览
    vloantis预览
    flexcard预览
    byer预览
    ark预览

    卡片魔改到此结束,下面我们进行友链朋友圈适配。

    朋友圈适配

    选择方法

    为什么之前的不行了呢?究其原因还是因为修改了我们的类名,导致原本针对于原生bf的爬虫无法正常获取到对应类名导致无法正常获取,而本教程由于需要对于不同卡片进行不同渲染,所以也需要保证类名不一致,所以我们只能选择曲线方案,利用其它方法来适配朋友圈。可以看见朋友圈中有一种通用方式,如下:

    所以在这里我决定使用这个通用教程来尝试适配我们的友链朋友圈。

    获取json文件

    首先我们需要从一个位置获得所有的友链信息,这里我们选择从link.yml文件中获取,毕竟这里是存所有友链信息的位置,在博客根目录下创建文件[root]\link.js,写入以下内容:

    代码语言:javascript
    复制
    const YML = require('yamljs')
    const fs = require('fs')

    const blacklist = ['name1', 'name2']; // 由于某种原因,不想订阅的友链的名称

    let friends = [],
    data_f = YML.parse(fs.readFileSync('source/_data/link.yml').toString().replace(/(?<=rss:)\s*\n/g, ' ""\n'));

    data_f.forEach((entry, index) => {
    let lastIndex = data_f.length - 4; // 这里需要按照你们的修改,这里的4表示我后面四项是不需要的,我只需要爬取后四项之前的所有类别即可
    if (index < lastIndex) {
    const filteredLinkList = entry.link_list.filter(linkItem => !blacklist.includes(linkItem.name));
    friends = friends.concat(filteredLinkList);
    }
    });

    // 根据规定的格式构建 JSON 数据
    const friendData = {
    friends: friends.map(item => {
    return [item.name, item.link, item.avatar];
    })
    };

    // 将 JSON 对象转换为字符串
    const friendJSON = JSON.stringify(friendData, null, 2);

    // 写入 friend.json 文件
    fs.writeFileSync('./source/friend.json', friendJSON);

    console.log('friend.json 文件已生成。');

    注意这个代码,需要确保friend.json文件名称没有被使用,如果被使用了自己换一个,除此之外,大家需要修改的内容为:

    • blacklist:友链朋友圈黑名单,可以在这里填入不想订阅的友链名称,和上面的name标签对应
    • lastIndex:可以直接修改为你想要订阅的前几个组别,比如我的,就只需要订阅前两个组别即可,后面不需要,也可以修改后面的数字,比如后四项无效。

    正确修改后,在根目录运行:

    代码语言:javascript
    复制
    node link.js

    即可在source文件夹中获得一个friend.json文件,格式如下:

    代码语言:javascript
    复制

    符合友链朋友圈的要求,实际上这个文件,如果你的友链数目比较少,你也可以自行按照格式创建。

    修改朋友圈配置项

    下面在友链朋友圈的配置文件/hexo_circle_of_friends/fc_settings.yaml中,屏蔽掉上面butterfly友链抓取装置,选择下面的通用选项:

    代码语言:javascript
    复制
    # 友链页地址

    参数说明:

    link:必填,在这里填写你的友链页面地址

    theme:必填,友链页的获取策略。需要指定该页面的主题,可选参数如下(这些是目前支持的主题):

    - common1: 通用主题1,请参考:https://fcircle-doc.yyyzyyyz.cn/#/developmentdoc?id=友链页适配

    - common2: 通用主题2,请参考:https://fcircle-doc.yyyzyyyz.cn/#/developmentdoc?id=友链页适配

    - butterfly:butterfly主题

    - fluid:fluid主题

    - matery:matery主题

    - nexmoe:nexmoe主题

    - stun:stun主题

    - sakura: sakura主题

    - volantis:volantis主题

    - Yun:Yun主题

    - stellar:stellar主题

    支持配置多个友链页面并指定不同主题策略,每个用{}分隔,它们会被同时爬取,数据保存在一起。

    LINK: [

    , # 友链页地址1,修改为你的友链页地址

    , # 友链页地址2

    , # 友链页地址3

    ...

    ]

    配置项友链

    enable:# 是否启用配置项友链 true/false(针对还未适配主题或者有定制需求的用户)

    json_api:通过json格式配置友链,详见:https://fcircle-doc.yyyzyyyz.cn/#/developmentdoc?id=配置项json友链

    list字段填写格式:["name", "link", "avatar","suffix"],其中:

    name:必填,友链的名字

    link:必填,友链主页地址

    avatar:必填,头像地址

    suffix:选填,自定义订阅后缀,主要针对不规范的网站订阅后缀,见示例2

    SETTINGS_FRIENDS_LINKS: {
    enable: true,
    json_api: "https://blog.qyliu.top/friend.json" # 你的地址,需要生成文件部署之后才能使用网络文件的方式访问,这个道理应该都懂
    list: [
    # 示例1:
    # ["贰猹の小窝", "https://noionion.top/", "https://pub-noionion.oss-cn-hangzhou.aliyuncs.com/head.jpg"],
    # ["Akilarの糖果屋", "https://akilar.top/", "https://akilar.top/images/headimage.png"],
    # # 示例2:使用suffix的配置如下
    # # 程序目前自动获取 "atom.xml", "feed/atom", "rss.xml", "rss2.xml", "feed", "index.xml" 这些后缀
    # # 如果目标地址的后缀不在其中,就可以在这里添加
    # # 比如 https://elizen.me/hello.xml ,填写:
    # ["elizen", "https://elizen.me/", "https://akilar.top/images/headimage.png", "hello.xml"]
    ]
    }

    get links from gitee

    从gitee issue中获取友链

    GITEE_FRIENDS_LINKS: {
    enable: false, # true 开启gitee issue兼容
    type: "normal", # volantis/stellar用户请在这里填写volantis
    owner: "ccknbc", # 填写你的gitee用户名
    repo: "blogroll", # 填写你的gitee仓库名
    state: "open", # 填写抓取的issue状态(open/closed)
    }

    get links from github

    从github issue中获取友链

    GITHUB_FRIENDS_LINKS: {
    enable: false, # true 开启github issue兼容
    type: "normal", # volantis/stellar用户请在这里填写volantis
    owner: "ccknbc", # 填写你的github用户名
    repo: "ccknbc-actions", # 填写你的github仓库名
    state: "open", # 填写抓取的issue状态(open/closed)
    label: "active", # 填写抓取的issue标签
    }

    block site list

    添加屏蔽站点,支持正则表达式

    BLOCK_SITE: [

    https://example.com/, # 屏蔽 https://example.com/

    .*.com, # 含有.com的全部屏蔽

    ^http://, # http开头的全部屏蔽

    ]

    每个友链最多获取几篇文章,此值越大,则抓取的文章上限越多,相应地运行速度也会降低,反之亦然

    请设置一个正整数

    MAX_POSTS_NUM: 4

    启用HTTP代理,此项设为true,并且需要添加一个环境变量,名称为PROXY,值为[IP]:[端口],比如:192.168.1.106:8080

    HTTP_PROXY: false

    过期文章清除(天)

    OUTDATE_CLEAN: 30

    5.x以后默认为sqlite,同时不建议使用leancloud

    存储方式,可选项:leancloud,mysql,sqlite,mongodb;默认为sqlite

    DATABASE: "sqlite"

    部署方式,可选项:github,server,docker;默认为github

    DEPLOY_TYPE: "github"

    修改如下:

    • 屏蔽其中的link中的所有链接:
    • 设置SETTINGS_FRIENDS_LINKSenable选项为true,然后在下面链接填写你的文件网络地址,注意是网络地址,好像还没有适配本地地址来着……,网络地址需要生成friend.json后上传并部署一次,然后才能通过网络路径[你的网站根地址]/friend.json访问。
    • (可选)下面的选项包括屏蔽站点(是我的功能多余了呵呵),每个友链获取文章数目等等选项,文档里写的很详细,这里就不教怎么部署了,如果有问题可以评论区提出来。

    然后按照相关教程部署后,添加前端到对应页面即可。本站的预览页面:

    好了本次教程结束啦!欢迎参考原教程进行更多的学习,链接在下面,如果有什么不懂的可以到评论区问我,我们下篇文章见!