2
2
{{ .Scratch.Set "scope" "single" }}
3
3
4
4
< style >
5
- .waterfall {
6
- list-style : none;
7
- column-gap : 0 ;
8
- padding : 0 ;
5
+ # waterfall {
9
6
column-count : 4 ;
7
+ column-gap : 10px ;
8
+ position : relative;
10
9
}
11
- .waterfall .item {
12
- width : 100% ;
13
- padding : 2px ;
10
+
11
+ @media only screen and (max-width : 640px ) {
12
+ # waterfall {
13
+ column-count : 2 ;
14
+ }
15
+ }
16
+
17
+ # waterfall figure a {
14
18
margin : 0 ;
19
+ display : grid;
20
+ grid-template-rows : 1fr auto;
21
+ margin-bottom : 10px ;
15
22
break-inside : avoid;
23
+ position : relative;
24
+ }
25
+
26
+ # waterfall figure a > img {
27
+ grid-row : 1 / -1 ;
28
+ grid-column : 1 ;
29
+ margin-top : 0 ;
30
+ margin-bottom : 0 ;
31
+ border-radius : 0.5rem ;
32
+ transition : transform 0.3s ease, filter 0.3s ease;
33
+ }
34
+
35
+ # waterfall figure : hover img {
36
+ transform : scale (1.2 );
37
+ z-index : 1 ;
38
+ border-bottom-left-radius : 0 ;
39
+ border-bottom-right-radius : 0 ;
40
+ }
41
+
42
+ .blur {
43
+ filter : blur (5px );
16
44
}
17
45
18
- .waterfall .item img {
19
- width : 100% ;
46
+ # waterfall figure {
47
+ position : relative;
48
+ display : inline-block;
20
49
margin-top : 0 ;
21
50
margin-bottom : 0 ;
22
51
}
23
52
24
- .view-image-tools {opacity : 1 ;animation : 1s 1.5s forwards fadeOut}
25
- .view-image-tools : hover {opacity : 1 ;animation : none;transition : opacity .2s }
26
- @keyframes fadeOut{to {opacity : 0 }}
53
+ # waterfall figure figcaption {
54
+ visibility : hidden;
55
+ opacity : 0 ;
56
+ position : absolute;
57
+ left : 0 ;
58
+ right : 0 ;
59
+ background : rgba (0 , 0 , 0 , 0.7 );
60
+ color : white;
61
+ padding : 10px ;
62
+ padding-bottom : 0 ;
63
+ margin : 0 ;
64
+ text-align : center;
65
+ transform : scale (1 );
66
+ z-index : 2 ;
67
+ border-bottom-left-radius : 0.5rem ;
68
+ border-bottom-right-radius : 0.5rem ;
69
+ transition : transform 0.3s ease, opacity 0.3s ease;
70
+ }
71
+
72
+ # waterfall figure figcaption p {
73
+ margin-top : 0 ;
74
+ margin-bottom : 0 ;
75
+ font-size : 12px ;
76
+ text-align : left;
77
+ }
78
+
79
+ # waterfall figure figcaption p a {
80
+ color : white;
81
+ }
82
+
83
+ # waterfall figure : hover > figcaption {
84
+ visibility : visible;
85
+ opacity : 1 ;
86
+ transform : scale (1.2 );
87
+ }
88
+
89
+ /* Loading spinner */
90
+ # waterfall > .loading-spinner {
91
+ position : absolute;
92
+ width : 3rem ;
93
+ height : 3rem ;
94
+ margin : auto;
95
+ top : calc (50% - 0.5rem );
96
+ right : calc (50% - 1.5rem );
97
+ }
98
+
99
+ .loading-spinner {
100
+ background-image : url ("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 128 128'%3E%3Cg%3E%3CanimateTransform attributeName='transform' type='rotate' from='0 64 64' to='360 64 64' dur='1000ms' repeatCount='indefinite'/%3E%3Cpath d='M64 6.69a57.3 57.3 0 1 1 0 114.61A57.3 57.3 0 0 1 6.69 64' fill='none' stroke='%23404040' stroke-width='12'/%3E%3C/g%3E%3C/svg%3E" );
101
+ background-repeat : no-repeat;
102
+ background-position : center center;
103
+ background-color : transparent;
104
+ background-size : min (2.5rem , calc (100% - 0.5rem ));
105
+ }
106
+
107
+ /* view image custom */
108
+ .view-image-tools {
109
+ opacity : 1 ;
110
+ animation : 1s 1.5s forwards fadeOut
111
+ }
112
+
113
+ .view-image-tools : hover {
114
+ opacity : 1 ;
115
+ animation : none;
116
+ transition : opacity .2s
117
+ }
118
+
119
+ @keyframes fadeOut {
120
+ to {
121
+ opacity : 0
122
+ }
123
+ }
27
124
</ style >
28
125
29
126
< article >
@@ -58,20 +155,26 @@ <h1 class="mt-0 text-4xl font-extrabold text-neutral-900 dark:text-neutral">
58
155
59
156
< section class ="flex flex-col max-w-full mt-0 prose dark:prose-invert lg:flex-row ">
60
157
61
- < div class ="min-w-0 min-h-0 max-w-fit ">
158
+ < div class ="min-w-0 min-h-0 max-w-fit ">
62
159
63
- < div class ="article-content max-w-full mb-20 ">
64
- {{ .Content }}
160
+ < div class ="article-content max-w-full mb-20 ">
161
+ {{ .Content }}
65
162
66
- < ul class ="waterfall " id ="waterfall " view-image >
67
- <!-- 图片将动态插入到这里 -->
68
- </ ul >
69
-
70
- < button id ="load-more " type ="button " class ="text-neutral-50 bg-[#4285F4] hover:bg-[#4285F4]/90 focus:ring-4 focus:outline-none focus:ring-[#4285F4]/50 font-medium rounded-lg text-sm px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-[#4285F4]/55 me-2 mb-2 "> Load More</ button >
163
+ < div id ="waterfall " class ="waterfall ">
164
+ < div class ="loading-spinner "> </ div >
165
+ < div id ="list-1 "> </ div >
166
+ < div id ="list-2 "> </ div >
167
+ < div id ="list-3 "> </ div >
168
+ < div id ="list-4 "> </ div >
71
169
</ div >
72
170
171
+ < div class ="flex justify-center ">
172
+ < button id ="loadMore " type ="button " class ="text-neutral-50 bg-[#4285F4] hover:bg-[#4285F4]/90 focus:ring-4 focus:outline-none focus:ring-[#4285F4]/50 font-medium rounded-lg text-sm px-5 py-2.5 text-center inline-flex items-center dark:focus:ring-[#4285F4]/55 me-2 mt-20 mb-2 "> Load More</ button >
173
+ </ div >
73
174
</ div >
74
175
176
+ </ div >
177
+
75
178
</ section >
76
179
77
180
</ article >
@@ -93,58 +196,148 @@ <h1 class="mt-0 text-4xl font-extrabold text-neutral-900 dark:text-neutral">
93
196
</ script >
94
197
95
198
< script >
96
- const waterfall_container = document . getElementById ( 'waterfall' ) ;
97
- const loadMoreButton = document . getElementById ( 'load-more' ) ;
98
- let lastMaxId = localStorage . getItem ( 'lastMaxId' ) || '' ;
99
-
100
- function fetchImages ( maxId = '' ) {
101
- fetch ( `https://e5n.cc/api/v1/accounts/111136231674527355/statuses?only_media=true&limit=10&exclude_replies=true&exclude_reblogs=true&max_id=${ maxId } ` )
102
- . then ( response => response . json ( ) )
103
- . then ( data => {
104
- if ( data . length > 0 ) {
105
- const newLastMaxId = data [ data . length - 1 ] . id ;
106
- localStorage . setItem ( 'lastMaxId' , lastMaxId ) ;
107
- data . forEach ( status => {
108
- status . media_attachments . forEach ( media => {
109
- const imgUrl = media . preview_url ;
110
- const hrefUrl = media . url ;
111
- const existingItem = Array . from ( waterfall_container . children ) . find ( item => item . querySelector ( 'img' ) . src === imgUrl ) ;
112
-
113
- if ( ! existingItem ) {
114
- const item = document . createElement ( 'li' ) ;
115
- item . className = 'item' ;
116
- const original = document . createElement ( 'a' ) ;
117
- original . href = hrefUrl
118
- const img = document . createElement ( 'img' ) ;
119
- img . src = imgUrl ;
120
- original . appendChild ( img ) ;
121
- item . appendChild ( original ) ;
122
- waterfall_container . appendChild ( item ) ;
123
- img . style . aspectRatio = media . meta . small . aspect ; // 设置图片的宽高比
124
- } else {
125
- // 删除重复内容
126
- const newItem = document . createElement ( 'li' ) ;
127
- newItem . className = 'item' ;
128
- const newImg = document . createElement ( 'img' ) ;
129
- newImg . src = imgUrl ;
130
- newItem . appendChild ( newImg ) ;
131
- waterfall_container . replaceChild ( newItem , existingItem ) ;
132
- }
133
- } ) ;
134
- } ) ;
135
- }
136
- } )
137
- . catch ( error => console . error ( 'Error fetching data:' , error ) ) ;
199
+ const apiUrl = 'https://e5n.cc/api/v1/accounts/111136231674527355/statuses' ;
200
+ let maxId = null ;
201
+
202
+ async function fetchImages ( limit , maxId = null ) {
203
+ const url = new URL ( apiUrl ) ;
204
+ url . searchParams . append ( 'tagged' , 'ealbum' ) ;
205
+ url . searchParams . append ( 'only_media' , 'true' ) ;
206
+ url . searchParams . append ( 'limit' , limit ) ;
207
+ url . searchParams . append ( 'exclude_replies' , 'true' ) ;
208
+ url . searchParams . append ( 'exclude_reblogs' , 'true' ) ;
209
+ if ( maxId ) {
210
+ url . searchParams . append ( 'max_id' , maxId ) ;
138
211
}
139
212
140
- loadMoreButton . addEventListener ( 'click' , ( ) => {
141
- fetchImages ( lastMaxId ) ;
213
+ const response = await fetch ( url ) ;
214
+ const data = await response . json ( ) ;
215
+ return data ;
216
+ }
217
+
218
+ function getShortestList ( ) {
219
+ const lists = [
220
+ document . getElementById ( 'list-1' ) ,
221
+ document . getElementById ( 'list-2' ) ,
222
+ document . getElementById ( 'list-3' ) ,
223
+ document . getElementById ( 'list-4' )
224
+ ] ;
225
+ let shortestList = lists [ 0 ] ;
226
+ lists . forEach ( list => {
227
+ if ( list . offsetHeight < shortestList . offsetHeight ) {
228
+ shortestList = list ;
229
+ }
230
+ } ) ;
231
+ return shortestList ;
232
+ }
233
+
234
+ function extractTextFromHTML ( htmlString ) {
235
+ // 使用正则表达式去掉 <a> 标签及其内容
236
+ const strippedString = htmlString . replace ( / < a [ ^ > ] * > ( .* ?) < \/ a > / g, '$1' ) ;
237
+
238
+ // 去掉其他 HTML 标签
239
+ const textOnly = strippedString . replace ( / < [ ^ > ] + > / g, '' ) ;
240
+
241
+ return textOnly ;
242
+ }
243
+ function renderImages ( data ) {
244
+ const waterfall = document . getElementById ( 'waterfall' ) ;
245
+ data . forEach ( ( item , index ) => {
246
+ item . media_attachments . forEach ( media => {
247
+ const listId = `list-${ ( index % 4 ) + 1 } ` ;
248
+ const list = document . getElementById ( listId ) ;
249
+
250
+ const figure = document . createElement ( 'figure' )
251
+
252
+ const a = document . createElement ( 'a' ) ;
253
+ a . href = media . url ;
254
+
255
+ const img = document . createElement ( 'img' ) ;
256
+ img . src = media . preview_url ;
257
+ img . style . width = '100%' ;
258
+ img . style . height = 'auto' ;
259
+ img . style . objectFit = 'cover' ;
260
+
261
+ const figcaption = document . createElement ( 'figcaption' ) ;
262
+ figcaption . innerHTML = '<p><a href="' + item . url + '" target="_blank" no-view>' + extractTextFromHTML ( item . content ) + '</a></p>' ;
263
+
264
+ const loading = document . querySelector ( '.loading-spinner' ) ;
265
+ loading . style . display = 'none' ;
266
+
267
+ a . appendChild ( img ) ;
268
+ figure . append ( a , figcaption ) ;
269
+ list . appendChild ( figure ) ;
270
+ } ) ;
142
271
} ) ;
272
+ }
273
+
274
+ async function loadInitialImages ( ) {
275
+ const data = await fetchImages ( 20 ) ;
276
+ renderImages ( data ) ;
277
+ if ( data . length > 0 ) {
278
+ maxId = data [ data . length - 1 ] . id ;
279
+ }
280
+ addHoverEffects ( ) ;
281
+ }
282
+
283
+ async function loadMoreImages ( ) {
284
+ const data = await fetchImages ( 12 , maxId ) ;
285
+ if ( data . length > 0 ) {
286
+ // Insert the first item into the shortest list
287
+ const shortestList = getShortestList ( ) ;
288
+ const firstItem = data [ 0 ] ;
289
+ const firstItemMedia = data [ 0 ] . media_attachments [ 0 ] ;
290
+
291
+ const figure = document . createElement ( 'figure' )
292
+
293
+ const a = document . createElement ( 'a' ) ;
294
+ a . href = firstItemMedia . url ;
295
+
296
+ const img = document . createElement ( 'img' ) ;
297
+ img . src = firstItemMedia . preview_url ;
298
+ img . style . width = '100%' ;
299
+ img . style . height = 'auto' ;
300
+ img . style . objectFit = 'cover' ;
301
+
302
+ const figcaption = document . createElement ( 'figcaption' )
303
+ figcaption . innerHTML = '<p><a href="' + firstItem . url + '" target="_blank" no-view>' + extractTextFromHTML ( firstItem . content ) + '</a></p>' ;
304
+
305
+ a . appendChild ( img ) ;
306
+ figure . append ( a , figcaption ) ;
307
+ shortestList . appendChild ( figure ) ;
308
+
309
+ // Render the rest of the items
310
+ renderImages ( data . slice ( 1 ) ) ;
311
+
312
+ maxId = data [ data . length - 1 ] . id ;
313
+ addHoverEffects ( ) ;
314
+ }
315
+ }
316
+
317
+ function addHoverEffects ( ) {
318
+ const images = document . querySelectorAll ( '#waterfall figure' ) ;
319
+ images . forEach ( img => {
320
+ img . addEventListener ( 'mouseenter' , ( ) => {
321
+ images . forEach ( otherImg => {
322
+ if ( otherImg !== img ) {
323
+ otherImg . classList . add ( 'blur' ) ;
324
+ }
325
+ } ) ;
326
+ } ) ;
327
+ img . addEventListener ( 'mouseleave' , ( ) => {
328
+ images . forEach ( otherImg => {
329
+ otherImg . classList . remove ( 'blur' ) ;
330
+ } ) ;
331
+ } ) ;
332
+ } ) ;
333
+ }
334
+
335
+ document . getElementById ( 'loadMore' ) . addEventListener ( 'click' , loadMoreImages ) ;
143
336
144
- window . ViewImage && ViewImage . init ( '.waterfall a' ) ;
337
+ window . ViewImage && ViewImage . init ( 'figure a' ) ;
145
338
146
- // 初始加载
147
- fetchImages ( ) ;
339
+ // Load initial images when the page loads
340
+ window . onload = loadInitialImages ;
148
341
</ script >
149
342
150
343
{{ end }}
0 commit comments