点击查看更新记录

更新记录

2022-04-07:公开第一版方案

  1. 学会了用新技术来实现多边形剪裁。旧版的伪类三角看着辣眼睛。
  2. 陈旧的技术留着也没啥意思,但又不想白写。毕竟是学习经验。所以直接公开。
  3. 新版方案待定,预计用clip-path实现多边形剪裁。

2022-03-31:内测版v0.08

  1. 应洪哥意见调整了配色。
  2. 虚化图片边界。
  3. 调整a标签作用域,点击标题和描述版块均可跳转文章。
  4. 禁用手机端描述版块点击动作,避免来不及触发快门动画就跳转文章。

2022-03-31:内测版v0.07

  1. 发布预览截图
  2. 编写开发思路
  3. 吐槽贰猹
点击查看参考教程
参考方向教程原贴
本帖的卡片原设为贰猹提供贰猹の小窝
Flex布局参数解释Flex 布局教程:语法篇 - 阮一峰的网络日志
Transition属性实现平滑过渡动画CSS3实现伪类hover离开时平滑过渡效果示例
CSS伪类实现三角形绘制纯CSS 实现绘制各种三角形(各种角度) - saucxs - 博客园
使用clip-path实现多边形剪裁。不可思议的CSS之clip-path

思路讲解

点击查看店长的碎碎念

在很久很久以前,到底有多久呢,我查了下聊天记录,
贰猹提出设计稿
贰猹和我交流了下他对主题首页卡片的设计想法,效果很不错。原本呢,贰猹是打算等自己学好了前端就把这个卡片写出来的。然后,就这么咕咕咕了一个寒假(其实是被木木抓起来关进小黑屋里写友链朋友圈后端API,写不好不给饭吃)。冰老师在鸽之灵,看到后继有鸽,也会很欣慰吧。
然后我就忍不住了啊,这么优秀的设计,等贰猹写出来我要等到啥时候。二话不说,征求了贰猹的授权以后,咱就开始动手进行试做。
贰猹的原设
可以看到贰猹的设计里,有用到三角形的结构。然而在div,span这些传统的html元素中,是没有三角形的。最多通过调整border-radius做出圆形。所以这里就要用到border的特性了。
一个传统的盒子模型
在传统的盒子布局中,border是占用盒子的位置内部位置的。所以,通过调整盒子四条边的border,同时把内部元素的长宽都设为0,就可以拼出三角形。缺点则是border没法设置background-image,没法直接把拼出的三角形当成相框来用。所以就需要用到z-index属性控制卡片层级,通过叠加拼接遮盖的方式,来实现形状的组合。
又因为relative和absolute定位下,元素的长宽属性的所谓100%对应的是不同等级的父级元素。(一个父级,一个祖父级?大概是这样子),总之我硬是用calc方法强行把伪类三角计算到了合适的位置。

编写手机端样式时,在F12界面调试伪类三角平移量的时候,突发奇想,可以用动画写个快门效果,所以最后的作品,手机端摒弃了贰猹原设里的倒三角描述卡片,转为类平行四边形的边框。同时加了悬停显示快门效果的遮罩动画。

我是打算先独享一个月再发布具体教程的,谁知道贰猹咕咕咕信誓旦旦的说给他一个月,早就把我的样式抠出来了。
贰猹的槽点
那就让我们拭目以待吧。反正等他抠出来我再发布教程貌似也没啥问题。
避免忘记,设置个日程


2022年4月7日更新。

在尝试找到界面曲面化来实现3D效果方案时,看到了SAO血条的2D实现方案,让我了解到一个新的CSS属性,clip-path,它可以通过polygon直接剪裁出多边形。非常的好用!而且还支持animation。比如旧版方案里手机端的那个快门效果。
如果我用伪类实现,代码量是这样的:

然后若我用clip-path去实现,代码量是这样的:

差距啊!
我现在只觉得好多好多旧版方案都显得辣眼睛。要大改!侧栏的SAO卡片效果要改,直接画出好看的边框,首页的文章卡片要改,反正不能再搞不靠谱的三角形伪类拼接。还有以前写的SAO血条,啊,那个项目我是打算废弃了。换成新版友链方案怎么样,左边是一排血条,点击再右边显示好看的角色属性卡片这样子。用clip-path的话,血条那个形状已经完全不成问题了的说。哦,对了,还有twikoo评论的美化,聊天框这下子好整多了。调整好margin直接剪裁。flex定位的order属性也可以用上去。这样自适应一定会好看许多。
好多好多项目要改,这就是技术负债啊。流下了没有技术的泪水。

魔改步骤

手机端预览
电脑端预览

刚公开就沦为旧版的方案
  1. 修改[Blogroot]\themes\butterfly\layout\includes\mixins\post-ui.pug,将整个文件的内容替换为以下代码:
    mixin postUI(posts)
    each article , index in page.posts.data
    .recent-post-item
    -
    let link = article.link || article.path
    let title = article.title || _p('no_title')
    const position = theme.cover.position
    let leftOrRight = position === 'both'
    ? index%2 == 0 ? 'left' : 'right'
    : position === 'left' ? 'left' : 'right'
    let post_cover = article.cover
    let no_cover = article.cover === false || !theme.cover.index_enable ? 'no-cover' : ''
    -
    .recent-post-content(class=leftOrRight)
    a.article-content(href=url_for(link) title=subtitle)
    //- Display the article introduction on homepage
    case theme.index_post_content.method
    when false
    - break
    when 1
    .article-content-text!= article.description
    when 2
    if article.description
    .article-content-text!= article.description
    else
    - const content = strip_html(article.content)
    - let expert = content.substring(0, theme.index_post_content.length)
    - content.length > theme.index_post_content.length ? expert += ' ...' : ''
    .article-content-text!= expert
    default
    - const content = strip_html(article.content)
    - let expert = content.substring(0, theme.index_post_content.length)
    - content.length > theme.index_post_content.length ? expert += ' ...' : ''
    .article-content-text!= expert
    .recent-post-info
    a.article-title(href=url_for(link) title=subtitle)
    .article-title-link= title
    .recent-post-meta
    .article-meta-wrap
    if (is_home() && (article.top || article.sticky > 0))
    span.article-meta
    i.fas.fa-thumbtack.sticky
    span.sticky= _p('sticky')
    span.article-meta-separator |
    if (theme.post_meta.page.date_type)
    span.post-meta-date
    if (theme.post_meta.page.date_type === 'both')
    i.far.fa-calendar-alt
    span.article-meta-label=_p('post.created')
    time.post-meta-date-created(datetime=date_xml(article.date) title=_p('post.created') + ' ' + full_date(article.date))=date(article.date, config.date_format)
    span.article-meta-separator |
    i.fas.fa-history
    span.article-meta-label=_p('post.updated')
    time.post-meta-date-updated(datetime=date_xml(article.updated) title=_p('post.updated') + ' ' + full_date(article.updated))=date(article.updated, config.date_format)
    else
    - let data_type_updated = theme.post_meta.page.date_type === 'updated'
    - let date_type = data_type_updated ? 'updated' : 'date'
    - let date_icon = data_type_updated ? 'fas fa-history' :'far fa-calendar-alt'
    - let date_title = data_type_updated ? _p('post.updated') : _p('post.created')
    i(class=date_icon)
    span.article-meta-label=date_title
    time(datetime=date_xml(article[date_type]) title=date_title + ' ' + full_date(article[date_type]))=date(article[date_type], config.date_format)
    if (theme.post_meta.page.categories && article.categories.data.length > 0)
    span.article-meta
    span.article-meta-separator |
    i.fas.fa-inbox
    each item, index in article.categories.data
    a(href=url_for(item.path)).article-meta__categories #[=item.name]
    if (index < article.categories.data.length - 1)
    i.fas.fa-angle-right.article-meta-link
    if (theme.post_meta.page.tags && article.tags.data.length > 0)
    span.article-meta.tags
    span.article-meta-separator |
    i.fas.fa-tag
    each item, index in article.tags.data
    a(href=url_for(item.path)).article-meta__tags #[=item.name]
    if (index < article.tags.data.length - 1)
    span.article-meta-link #[='•']

    mixin countBlockInIndex
    - needLoadCountJs = true
    span.article-meta
    span.article-meta-separator |
    i.fas.fa-comments
    if block
    block
    span.article-meta-label= ' ' + _p('card_post_count')

    if theme.comments.card_post_count
    case theme.comments.use[0]
    when 'Disqus'
    when 'Disqusjs'
    +countBlockInIndex
    a(href=full_url_for(link) + '#disqus_thread')
    when 'Valine'
    +countBlockInIndex
    a(href=url_for(link) + '#post-comment' itemprop="discussionUrl")
    span.valine-comment-count(data-xid=url_for(link) itemprop="commentCount")
    when 'Waline'
    +countBlockInIndex
    a(href=url_for(link) + '#post-comment')
    span.waline-comment-count(id=url_for(link))
    when 'Twikoo'
    +countBlockInIndex
    a.twikoo-count(href=url_for(link) + '#post-comment')
    when 'Facebook Comments'
    +countBlockInIndex
    a(href=url_for(link) + '#post-comment')
    span.fb-comments-count(data-href=urlNoIndex(article.permalink))
    .recent-post-cover
    img.article-cover(src=url_for(post_cover) onerror=`this.onerror=null;this.src='`+ url_for(theme.error_img.post_page) + `'` alt=subtitle)

    if theme.ad && theme.ad.index
    if (index + 1) % 3 == 0
    .recent-post-item.ads-wrap!=theme.ad.index
  2. 修改[Blogroot]\themes\butterfly\source\css\_page\homepage.styl,将整个文件的内容替换为以下代码:
    //default color:
    :root
    --recent-post-bgcolor: rgba(255, 255, 255, 0.9)
    --article-content-bgcolor: #49b1f5
    --recent-post-triangle: #fff
    --recent-post-cover-shadow: #ffffff
    [data-theme="dark"]
    --recent-post-bgcolor: rgba(35,35,35,0.5)
    --article-content-bgcolor: #99999a
    --recent-post-triangle: #37e2dd
    --recent-post-cover-shadow: #232323
    .recent-posts
    padding 0 15px 0 15px

    .recent-post-item
    margin-bottom 15px
    width 100%
    background var(--recent-post-bgcolor)
    overflow hidden
    border-radius 15px
    .recent-post-info
    .article-title-link
    display -webkit-box
    -webkit-box-orient vertical
    -webkit-line-clamp 2
    overflow hidden
    .article-content
    background var(--article-content-bgcolor)
    position relative
    display flex
    align-items: center;
    justify-content: center;
    .article-content-text
    transition: all .8s cubic-bezier(0.59, 0.01, 0.48, 1.17)
    display -webkit-box
    -webkit-box-orient vertical
    -webkit-line-clamp 4
    text-overflow: ellipsis
    overflow hidden
    color #fff
    text-shadow: 1px 2px 3px #000;
    .recent-post-cover
    position relative
    background transparent
    img
    &.article-cover
    height 100%
    width 100%
    object-fit cover

    .recent-post-info
    align-items center
    flex-direction column
    position relative
    background var(--recent-post-bgcolor)
    display flex
    color #000000
    .article-title
    height 50%
    font-size 24px
    display: flex
    align-items: center
    justify-content: flex-end
    flex-direction: column
    .article-title-link
    color: var(--text-highlight-color)
    transition: all .2s ease-in-out
    &:hover
    color: $text-hover
    .recent-post-meta
    height 50%
    display: flex
    align-items: center
    justify-content: flex-start
    flex-direction: column
    .article-meta-wrap
    font-size 12px
    color #969797
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    overflow: hidden;
    a
    color: var(--text-highlight-color)
    transition: all .2s ease-in-out
    color #969797
    &:hover
    color: $text-hover
    &.ads-wrap
    display: block !important
    height: auto !important
    @media screen and (min-width:600px)
    .recent-post-item
    &:hover
    .recent-post-content
    &.both,
    &.right
    transform translateX(21%)
    transition: all .8s cubic-bezier(0.59, 0.01, 0.48, 1.17)
    &::before
    transition: all .8s cubic-bezier(0.59, 0.01, 0.48, 1.17)
    left: 50px;
    .article-content-text
    margin 20px 20px 20px 60px
    &.left
    transform translateX(-21%)
    transition: all .8s cubic-bezier(0.59, 0.01, 0.48, 1.17)
    &::before
    transition: all .8s cubic-bezier(0.59, 0.01, 0.48, 1.17)
    right: 50px;
    .article-content-text
    margin 20px 60px 20px 20px


    .recent-post-content
    background var(--recent-post-bgcolor)
    position relative
    height 200px
    width 130%
    z-index 0
    display flex
    overflow hidden
    border 0px solid
    &::before
    content: "";
    width: 0;
    height: 0;
    background: transparent;
    position: absolute;
    z-index: 3;
    top: calc(50% - 10px);
    border-top: 10px solid transparent;
    border-bottom: 10px solid transparent;
    transition: all .5s cubic-bezier(0.59, 0.01, 0.48, 1.17)
    &.both,
    &.right
    flex-direction: row;
    left calc(-23.07% - 41px)
    transition: all .5s cubic-bezier(0.59, 0.01, 0.48, 1.17)
    &::before
    left: calc(23.07% + 40px);
    border-left: 6px solid var(--recent-post-triangle);
    .recent-post-info
    &::before
    background linear-gradient(to right, var(--recent-post-cover-shadow), transparent)
    left calc(100% - 1px)
    .article-content
    &::before
    right -59px
    border-left 60px solid var(--article-content-bgcolor)
    .article-content-text
    margin 20px 20px 20px 0px
    .article-title
    padding 0px 30px 0px 70px
    .recent-post-meta
    padding 0px 20px 0px 70px
    &.left
    flex-direction: row-reverse;
    right 9px
    transition: all .5s cubic-bezier(0.59, 0.01, 0.48, 1.17)
    &::before
    right: calc(23.07% + 40px);
    border-right: 6px solid var(--recent-post-triangle);
    .recent-post-info
    &::before
    background linear-gradient(to left, var(--recent-post-cover-shadow), transparent)
    right calc(100% - 1px)
    .article-content
    &::before
    left -59px
    border-right 60px solid var(--article-content-bgcolor)
    .article-content-text
    margin 20px 0px 20px 20px
    .article-title
    padding 0px 70px 0px 30px
    .recent-post-meta
    padding 0px 70px 0px 20px

    .article-content
    width 30%
    height 200px
    left 0
    align-items center
    &::before
    content ""
    width 0
    height 0
    background transparent
    position absolute
    z-index 2
    top 0
    border-top 100px solid transparent
    border-bottom 100px solid transparent
    .recent-post-info
    width 60%
    height 200px
    &::before
    content ""
    width 200px
    height 200px
    position absolute
    z-index 1
    top 0
    .recent-post-meta
    & > .article-meta-wrap
    margin: 6px 0
    color: $theme-meta-color
    font-size: 90%

    & > .post-meta-date
    cursor: default

    .sticky
    color: $sticky-color

    i
    margin: 0 4px 0 0

    .article-meta-label
    if hexo-config('post_meta.page.label')
    padding-right: 4px
    else
    display: none

    .article-meta-separator
    margin: 0 6px

    .article-meta-link
    margin: 0 4px

    if hexo-config('post_meta.page.date_format') == 'relative'
    time
    display: none

    a
    color: $theme-meta-color

    &:hover
    color: $text-hover
    text-decoration: underline
    .recent-post-cover
    width 40%
    height 200px
    @media screen and (max-width:600px)
    .recent-post-item
    height 400px
    .recent-post-content
    display flex
    flex-direction: column
    height 400px
    .article-content
    pointer-events none
    order: 1;
    height: 200px;
    position: absolute;
    width: calc(100% - 40px);
    z-index: 3;
    background: rgba(22,22,22,0.5);
    border-top-left-radius: 15px;
    border-top-right-radius: 15px;
    display: none
    opacity: 0
    .article-content-text
    height 120px
    color: white;
    width: 80%

    .recent-post-cover
    order: 2
    height 200px
    transition: all .5s
    .recent-post-info
    order: 3
    height 200px
    &::before
    content: '';
    width: 0;
    height: 0;
    position: absolute;
    z-index: 3;
    bottom: calc(100% - 4px);
    left: 0;
    border-bottom: 50px solid var(--recent-post-bgcolor);
    border-right: 300px solid transparent;
    &::after
    content: '';
    width: 0;
    height: 0;
    position: absolute;
    z-index: 3;
    bottom: calc(100% + 150px);
    right: 0;
    border-top: 50px solid var(--recent-post-bgcolor);
    border-left: 300px solid transparent;
    .article-title
    padding: 0px 35px 0px 35px
    .recent-post-meta
    padding: 0px 30px 0px 30px
    &:hover
    .article-content
    display: flex !important;
    animation: shutter-effect-content 0.5s 2 forwards linear
    .recent-post-info
    &::before
    animation: shutter-effect-left 0.5s 1 ease-in-out
    &::after
    animation: shutter-effect-right 0.5s 1 ease-in-out
    .recent-post-cover
    filter blur(2px)
    @keyframes shutter-effect-right {
    0%{
    bottom: calc(100% + 150px);
    border-top: 50px solid var(--recent-post-bgcolor);
    border-left: 300px solid transparent;
    }
    50%{
    bottom: 100%;
    border-top: 200px solid var(--recent-post-bgcolor);
    border-left: 600px solid transparent;
    }
    100%{
    bottom: calc(100% + 150px);
    border-top: 50px solid var(--recent-post-bgcolor);
    border-left: 300px solid transparent;
    }
    }
    @keyframes shutter-effect-left {
    0%{
    bottom: calc(100% - 4px);
    border-bottom: 50px solid var(--recent-post-bgcolor);
    border-right: 300px solid transparent;
    }
    50%{
    bottom: calc(100% - 4px);
    border-bottom: 200px solid var(--recent-post-bgcolor);
    border-right: 600px solid transparent;
    }
    100%{
    bottom: calc(100% - 4px);
    border-bottom: 50px solid var(--recent-post-bgcolor);
    border-right: 300px solid transparent;
    }
    }
    @keyframes shutter-effect-content {
    from {
    opacity: 0
    }
    to {
    opacity: 1
    }
    }

    注意事项

    样式配色因为采用了大量伪类,所以如果底色采用了半透明配色,可能因为卡片叠加加深导致接合边界非常明显的暴露出来。所以在配色上,我是不建议加半透明的。
    因为部分伪类的偏移量是靠计算得出的,为了尽量满足自适应效果,部分位置保留了5%左右的容差。所以在一些极端屏宽比下,还是会出现一些样式不完美问题。(处女座不能用可真是遗憾呢

新版方案

施工中。。。。。。等我重写整个架构

TO DO

博客实装卡片

等4月底前被贰猹抠走(已经不重要了)

4月底发布教程(旧版无所谓啦)