时间线模板的最终效果
注:此wiki站使用的版权协议为CC-BY-SA 4.0,相关代码版权以源站为准。
缘起 从参与这个粉丝wiki建设开始,我就想到利用时间线的形式来优化背景信息类条目的叙述。然而我却意外地发现时间线这种东西并没有好用的现成模板,从萌百搬过来的那个是纯文字,其他wiki站上要么没遇见过好用的,要么就是找不到源码。最近又有一位粉丝提出了通过时间线整理漫画剧情的方案,恰好我的那个杀戮尖塔mod项目又处于停顿状态,再加上我在这个wiki站最近的活跃度也不太够,于是就想着自己写一个能用的时间线模板出来。
经过 想要写出一个返回非维基文本的模板,基本都要借助模块。而这样的模板一般都有三部分组成:规定逻辑的模块,引用逻辑的模板和规定样式的样式表。
而这三者分别需要lua、mediawiki和三件套的相关知识。
这里就分别说明这几项。
三件套 在撰写模块之前,我必须先在一个html中先写出类似的效果。而这里并不需要js(逻辑部分是lua那边的事),只需要把静态效果弄出来就可以了。
本以为这应该是一件不难的事(因为之前有过三件套的开发经验),但实际上手起来才发现跟一开始想的不是一回事。
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 <body > <div class ="timeline" > <div class ="trunk" > </div > <div class ="branch" id ="branch-1" > <div class ="line" > <span class ="id" > 123话</span > </div > <div class ="text" > 仑负着份韩慧十同仇种希,统说韦的名到是冷人,人但说备不书仆可联妄娘,重揽大头不我重不揽的和们么,而话有一你起的狂上小所事措地,统说韦的名到是冷人 </div > </div > <div class ="branch" id ="branch-2" > <div class ="line" > <span class ="id" > 123话</span > </div > <div class ="text" > 仄师用叹拆即,统说韦的名到是冷人 </div > </div > <div class ="branch" id ="branch-3" > <div class ="line" > <span class ="id" > 123话</span > </div > <div class ="text" > 对种榜让畴不今仃,雷爱所的可尘位哉国畴蒲火觉才不由少,战生轻韦如没里人谓第人知,太不投吴畴畴得牛我联求卑方不,行不的争死未宋无舟范他非三同,策花德攻水我洪,别夹太与今事派上交非范价,郭法出馆,兴登后洋时一的两会,不有正十之谓绪药定攻音啦死脱降办仍作锐,是。 </div > </div > <div class ="branch" id ="branch-4" > <div class ="line" > <span class ="id" > 123话</span > </div > <div class ="text" > 严陀娟自足仄马土是见场他才不会变,办升他德仑而曰动与魂留足在说,娇老害判愚王同上留有帮洋知家学什少了亲,人宫己是,公那尝被谢龄中是放一子二白不,白恨洪狂竟皇县,衣降王司保间判上评有在未商留价后归,活马令国韩耳持一绝光反同同一家,却发要即官文来问德姑绝褒于。 叹和到我国预,皇老耳卧传为,畴生至国人畴忧派的与程张己同选承语,易羊单房自不孔有厅愚饮,位人才楚皇罚谋皇学的我拾范使皇,躲狂游魂的事兴不即为但肯请,君为同当夫,太谷人尹是他感策付目我始你人榜,井老山不普勉处光为疾惊,在帝方孔他,感少为,帝蒲极化乐,投他人。 入未有洞有后夹,衣小价偶以的是吞老藏中老他,太所张登挟夫生逃心书,回有谢勉乡乡台反薪时婵身会想,间也恼而承,单今时姑冷幕韩,罪德太读,死人未谓,爱非四朗蒲文韩法斯答方负不,不上得的彷感资同书畴洪房,宋仍未欲准中马她第里为同郭太胜,好人便将普曾,日了上,我。 </div > </div > <div class ="emptycell" > </div > </div > </body > </html >
html本体也就那么点事,主要的还是css。
在在布局问题上卡了很久之后,最终采用的布局架构是grid嵌套flex。
1 2 3 4 5 6 7 div .timeline { display : grid; grid-template-columns : 1 fr 1 fr; grid-auto-rows : max-content; gap : 5px ; position : relative; }
总体的框架。这里将整个空闲区域分为两列,每一行的宽度取决于其中的最大宽度(但实际上似乎用处不大),每个格子间隔5px。
1 2 3 4 5 6 7 8 9 10 div .trunk { content : '' ; position : absolute; top : 0 ; bottom : 0 ; left : 50% ; width : 5px ; transform : translateX (-50% ); background-color : black; }
整个时间线的主干。实际上就是一条黑线,从grid网格的顶部延申到底部。宽设置为5px,transform
用于将这条线移到中间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 div .branch { position : relative; display : flex; justify-content : center; justify-items: end; align-items : center; } div .branch :nth-child (even) { grid-column-start : -3 ; grid-column-end : -2 ; justify-content : end; flex-direction : row-reverse; } div .branch :nth-child (odd) { top : 50px ; grid-column-start : -2 ; grid-column-end : -1 ; justify-content : start; }
每个分支对应的区域。这里在分支区域使用了弹性盒布局以方便文字与框——线排布。
偶数位的分支占据右边一列,奇数位的占据左边一列。而弹性盒布局也能利用flex-direction
轻松地指定元素左右翻转。同时也为奇偶数位指定分布开始的基准线(justify-content
)。
1 2 3 4 5 6 div .line { position : relative; background-color : black; height : 2px ; min-width : 100px ; }
每个分支中的指示线。没什么好说的。
1 2 3 4 5 6 7 8 div .text { position : relative; background-color : transparent; border : 2px solid black; border-radius : 5px ; max-width : 500px ; padding : 5% ; }
实际的文本框。实际运用时背景被设为了白色。
1 2 3 4 5 6 span .id { position : relative; margin-left : 10% ; margin-top : 2px ; font-family : 'Gill Sans' , 'Gill Sans MT' , Calibri, 'Trebuchet MS' , sans-serif; }
指示线上的文本。这里的span是line的子元素,方便对齐。
1 2 3 4 div .emptycell { max-height : 5em ; min-height : 3em ; }
占位符。为什么会有这东西?
在实际使用中,弹性盒并不会老老实实地待在网格之中,它对文本的自适应会使得其本身脱离网格范围。
这在单一的网页中并不会导致任何问题,但在实际使用中却出现了严重的出框现象。而这种出框就是因为网格并没有完全包含其中内容。
在许多次尝试之后,最终的解决方案是,在整个结构的最后加入一个空的占位符,这样就能规避出框现象。
在底部加入一个占位符后的效果,可以看出下面多了一行。
至此,网页测试部分基本完成。
模块/模板 这里主要参考的有:mbox模版,navbox模板,以及mw的官方文档 。
mediawiki的模块是利用lua编写的,而lua也是一项轻量的脚本语言。
而模块——模板方面真正出问题的点并不在lua本身,而是模块——模板的传参问题上。
模板本身的实现非常简单,只需要invoke对应的模块并引用对应样式即可。
1 <includeonly>{{#invoke: Flow-timeline | timeline}}</includeonly><templatestyles src="T:Flow-timeline/style.css" />
如何将模板的参数传到模块中,然后让模块返回内容呢?
首先是返回。
这个模板引用的实际意义是,调用模块Flow-timeline
中的timeline
函数。
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 local data = {}local framework={}local function getId (args) for key, value in pairs (args) do local prefix = string .match (key, "(%a+)%d+" ) local index = tonumber (string .match (key, "%d" )) if index and not data[index] then data[index] = {} end if prefix == "text" then data[index][2 ] = value elseif prefix == "id" then data[index][1 ] = value end end end function framework.timeline (frame) local args = frame:getParent().args getId(args) local timeline = mw.html.create ("div" ) timeline:addClass("timeline-framework" ) local trunk = mw.html.create ("div" ) trunk :addClass("timeline-trunk" ) timeline:node(trunk) for index, value in ipairs (data) do local branch = mw.html.create ("div" ) branch :addClass("timeline-branch" ) :attr("id" ,"timeline-branch-" .. index) timeline:node(branch) local line = mw.html.create ("div" ) line :addClass("timeline-line" ) :attr("id" ,"timeline-line-" .. index) branch:node(line) if value[1 ] then local id = mw.html.create ("span" ) id :addClass("timeline-id" ) :attr("id" ,"timeline-id-" .. index) :wikitext(value[1 ]) line:node(id) end if value[2 ] then local text = mw.html.create ("div" ) text :addClass("timeline-text" ) :attr("id" ,"timeline-text-" .. index) :wikitext(value[2 ]) branch:node(text) end end local empty=mw.html.create ("div" ) empty:addClass("timeline-emptycell" ) timeline:node(empty) return tostring (timeline) end return framework
在mw的模块中,整体返回的值应该是一个表(return framework
),而调用的函数应该是这个表的表函数。
在这个模块中,模板调用的便是表函数timeline
。而整个模块实际上整体返回的值便是这个函数返回的值。
在这里,调用函数中使用mw自带的mw.html
模块创建html内容,然后整体转为字符串返回就可以了。
那这个函数如何获取模板传入的参数呢?
这个函数的参数只有一个frame
,而这个东西实际上就是模板传入的整体信息。而frame.args
便是传入的参数表。
然而,这里却有一个很坑的点:
出于性能考虑,frame.args 是一个元数据表,而不是一个真正的参数表。参数值是按需从 MediaWiki 请求的。这意味着大多数其他表格方法将无法正常工作,包括 #frame.args 、 next( frame.args ) 以及 Table 库中的函数。——官方文档
这里如果尝试对args
进行遍历,似乎是遍历不出来东西的。。。而这里显然并不需要考虑性能问题(因为所有的参数都迟早会被调用),所以这里使用frame:getParent().args
来得到一个真正的参数表,这样就可以正常地进行遍历了。
结果 最终这个模板已经在这个wiki站投入使用,虽然还有一些bug(如移动端可能的显示问题),但至少能用了,效果如置顶图所示。
最终感想:css创始人,你睡了吗?我睡不着