点击查看更新记录
更新记录
2022-04-07:公开第一版方案
- 学会了用新技术来实现多边形剪裁。旧版的伪类三角看着辣眼睛。
- 陈旧的技术留着也没啥意思,但又不想白写。毕竟是学习经验。所以直接公开。
- 新版方案待定,预计用clip-path实现多边形剪裁。
2022-03-31:内测版v0.08
- 应洪哥意见调整了配色。
- 虚化图片边界。
- 调整a标签作用域,点击标题和描述版块均可跳转文章。
- 禁用手机端描述版块点击动作,避免来不及触发快门动画就跳转文章。
2022-03-31:内测版v0.07
- 发布预览截图
- 编写开发思路
- 吐槽贰猹
点击查看参考教程
参考方向 | 教程原贴 |
---|---|
本帖的卡片原设为贰猹提供 | 贰猹の小窝 |
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属性也可以用上去。这样自适应一定会好看许多。
好多好多项目要改,这就是技术负债技术负债(英语:Technical debt),又译技术债,也称为设计负债(design debt)、代码负债(code debt),是编程及软件工程中的一个比喻。指开发人员为了加速软件开发,在应该采用最佳方案时进行了妥协,改用了短期内能加速软件开发的方案,从而在未来给自己带来的额的外开发负担,这种技术上的选择,就像一笔债务一样,虽然眼前看起来可以得到好孜处,但必须在未来偿还</span>啊。流下了没有技术的泪水。
魔改步骤
刚公开就沦为旧版的方案
- 修改
[Blogroot]\themes\butterfly\layout\includes\mixins\post-ui.pug
,将整个文件的内容替换为以下代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115mixin 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 - 修改
[Blogroot]\themes\butterfly\source\css\_page\homepage.styl
,将整个文件的内容替换为以下代码:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351//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
height: auto
@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 ;
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月底发布教程(旧版无所谓啦)
Use this card to join the candyhome and participate in a pleasant discussion together .
Welcome to Akilar's candyhome,wish you a nice day .