@@ -1,49 +1,90 @@
< script setup >
import { computed } from 'vue'
import { useI18n } from 'vue-i18n'
import { versions } from '../data/versions.js'
import { versions , tracks } from '../data/versions.js'
const { locale } = useI18n ( )
const { locale } = useI18n ( { useScope : 'global' } )
const L = ( obj ) => ( locale . value === 'zh-CN' ? obj . zh : obj . en )
// 按演进线分组;同一线内按 order 排序;线之间按各自最早 order 排序
const grouped = computed ( ( ) => {
return tracks
. map ( ( t ) => {
const items = versions
. filter ( ( v ) => v . track === t . id )
. sort ( ( a , b ) => a . order - b . order )
return { track : t , items }
} )
. filter ( ( g ) => g . items . length > 0 )
. sort ( ( a , b ) => Math . min ( ... a . items . map ( ( i ) => i . order ) ) - Math . min ( ... b . items . map ( ( i ) => i . order ) ) )
} )
// 标记 3D 转折: 3D 线之前的最后一条 2D 线结尾,提示形态转变
const is3D = ( track ) => track . form === '3D'
< / script >
< template >
< div class = "timeline" >
< div
v-for = "(v , i) in versions "
:key = "v .id"
class = "timeline__item "
v-for = "(g , g i) in grouped "
:key = "g.track .id"
class = "timeline__track "
: class = "{ 'timeline__track--3d': is3D(g.track) }"
>
< div class = "timeline__marker" >
< span class = "timeline__dot" > < / span >
< span v-if = "i < versions.length - 1" class="timeline__line" > < / span >
<!-- 线标题 -- >
< div class = "timeline__track-head" >
< span class = "timeline__form" : class = "{ 'is-3d': is3D(g.track) }" > { { g . track . form } } < / span >
< span class = "timeline__track-name" > { { L ( g . track . name ) } } < / span >
< / div >
< p class = "timeline__track-desc" > { { L ( g . track . desc ) } } < / p >
<!-- 该线下的版本 -- >
< div class = "timeline__items" >
< div
v-for = "(v, i) in g.items"
:key = "v.id"
class = "timeline__item"
>
< div class = "timeline__marker" >
< span class = "timeline__dot" > < / span >
< span v-if = "i < g.items.length - 1" class="timeline__line" > < / span >
< / div >
< div class = "timeline__body" >
< div class = "timeline__head" >
< span class = "timeline__version" > v { { v . version } } < / span >
< span class = "timeline__product" > { { v . product } } < / span >
< / div >
< div class = "timeline__row" >
< span class = "timeline__label" > { { locale === 'zh-CN' ? '操作' : 'Controls' } } < / span >
< span > { { L ( v . control ) } } < / span >
< / div >
< div class = "timeline__row" >
< span class = "timeline__label" > { { locale === 'zh-CN' ? '说明' : 'Note' } } < / span >
< span > { { L ( v . note ) } } < / span >
< / div >
< div class = "timeline__row" >
< span class = "timeline__label" > Unity < / span >
< span class = "timeline__mono" > { { v . unity } } < / span >
< / div >
< div class = "timeline__links" >
< a v-if = "v.webgl" :href="v.webgl" target="_blank" rel="noopener" >
{{ locale = = = ' zh -CN ' ? ' WebGL 试玩 ' : ' Play WebGL ' }} ↗
< / a >
< a v-if = "v.apk" :href="v.apk" target="_blank" rel="noopener" >
{{ locale = = = ' zh -CN ' ? ' 下载 APK ' : ' Download APK ' }} ↗
< / a >
< / div >
< / div >
< / div >
< / div >
<div class = "timeline__body" >
< div class = "timeline__head" >
< span class = "timeline__series" > { { v . series } } < / span >
< span class = "timeline__version" > v { { v . version } } < / span >
< / div >
< div class = "timeline__product" > { { v . product } } < / div >
< div class = "timeline__row" >
< span class = "timeline__label" > { { locale === 'zh-CN' ? '操作' : 'Controls' } } < / span >
< span > { { L ( v . control ) } } < / span >
< / div >
< div class = "timeline__row" >
< span class = "timeline__label" > { { locale === 'zh-CN' ? '说明' : 'Note' } } < / span >
< span > { { L ( v . note ) } } < / span >
< / div >
< div class = "timeline__row" >
< span class = "timeline__label" > Unity < / span >
< span class = "timeline__mono" > { { v . unity } } < / span >
< / div >
< div class = "timeline__links" >
< a v-if = "v.webgl" :href="v.webgl" target="_blank" rel="noopener" >
{{ locale = = = ' zh -CN ' ? ' WebGL 试玩 ' : ' Play WebGL ' }} ↗
< / a >
< a v-if = "v.apk" :href="v.apk" target="_blank" rel="noopener" >
{{ locale = = = ' zh -CN ' ? ' 下载 APK ' : ' Download APK ' }} ↗
< / a >
< / div >
<! - - 2D - > 3 D 转折标记 : 仅出现在第一条 3 D 线之前 -- >
< div v-if = "is3D(g.track) && gi > 0" class= "timeline__pivot" >
< span class = "timeline__pivot-line" > < / span >
< span class = "timeline__pivot-text" >
{ { locale === 'zh-CN' ? '形态转变: 2D → 3D 滚球' : 'Form shift: 2D → 3D rolling ball' } }
< / span >
< span class = "timeline__pivot-line" > < / span >
< / div >
< / div >
< / div >
@@ -51,42 +92,87 @@ const L = (obj) => (locale.value === 'zh-CN' ? obj.zh : obj.en)
< style scoped >
. timeline {
position : relative ;
display : flex ;
flex - direction : column ;
gap : var ( -- space - xl ) ;
}
. timeline _ _track -- 3 d {
padding - top : var ( -- space - sm ) ;
}
. timeline _ _track - head {
display : flex ;
align - items : center ;
gap : var ( -- space - sm ) ;
margin - bottom : var ( -- space - xs ) ;
}
. timeline _ _form {
display : inline - flex ;
align - items : center ;
padding : 2 px 10 px ;
font - size : 0.74 rem ;
font - weight : 600 ;
border - radius : 999 px ;
background : var ( -- surface - 2 ) ;
color : var ( -- text - secondary ) ;
letter - spacing : 0.04 em ;
}
. timeline _ _form . is - 3 d {
background : var ( -- accent - weak ) ;
color : var ( -- accent ) ;
}
. timeline _ _track - name {
font - weight : 600 ;
font - size : 1.05 rem ;
color : var ( -- text - primary ) ;
}
. timeline _ _track - desc {
font - size : 0.9 rem ;
color : var ( -- text - secondary ) ;
margin : 0 0 var ( -- space - md ) ;
max - width : 640 px ;
}
. timeline _ _items {
padding - left : 4 px ;
}
. timeline _ _item {
display : flex ;
gap : var ( -- space - md ) ;
padding - bottom : var ( -- space - lg ) ;
padding - bottom : var ( -- space - md ) ;
}
. timeline _ _marker {
position : relative ;
flex : 0 0 16 px ;
flex : 0 0 14 px ;
display : flex ;
flex - direction : column ;
align - items : center ;
padding - top : 4 px ;
padding - top : 6 px ;
}
. timeline _ _dot {
width : 12 px ;
height : 12 px ;
width : 10 px ;
height : 10 px ;
border - radius : 50 % ;
background : var ( -- bg ) ;
border : 2 px solid var ( -- accent ) ;
flex : 0 0 12 px ;
flex : 0 0 10 px ;
}
. timeline _ _track -- 3 d . timeline _ _dot {
background : var ( -- accent ) ;
}
. timeline _ _line {
flex : 1 ;
width : 2 px ;
background : var ( -- border ) ;
margin - top : 4 px ;
min - height : 24 px ;
min - height : 20 px ;
}
. timeline _ _body {
flex : 1 ;
padding : var ( -- space - md ) var ( -- space - lg ) ;
padding : var ( -- space - s m) var ( -- space - md ) ;
background : var ( -- bg ) ;
border : 1 px solid var ( -- border ) ;
border - radius : var ( -- radius ) ;
border - radius : var ( -- radius - sm ) ;
box - shadow : var ( -- shadow - sm ) ;
transition : box - shadow var ( -- transition ) ;
}
@@ -97,37 +183,32 @@ const L = (obj) => (locale.value === 'zh-CN' ? obj.zh : obj.en)
display : flex ;
align - items : baseline ;
gap : var ( -- space - sm ) ;
margin - bottom : 2 px ;
}
. timeline _ _series {
font - weight : 600 ;
color : var ( -- text - primary ) ;
font - size : 1.02 rem ;
margin - bottom : var ( -- space - xs ) ;
}
. timeline _ _version {
font - family : var ( -- font - mono ) ;
font - size : 0.85 rem ;
font - size : 0.82 rem ;
color : var ( -- accent ) ;
background : var ( -- accent - weak ) ;
padding : 1 px 8 px ;
border - radius : 6 px ;
font - weight : 600 ;
}
. timeline _ _product {
font - size : 0.85 rem ;
font - size : 0.82 rem ;
color : var ( -- text - tertiary ) ;
margin - bottom : var ( -- space - sm ) ;
}
. timeline _ _row {
display : flex ;
gap : var ( -- space - sm ) ;
font - size : 0.9 rem ;
font - size : 0.88 rem ;
color : var ( -- text - secondary ) ;
padding : 2 px 0 ;
padding : 1 px 0 ;
}
. timeline _ _label {
flex : 0 0 48 px ;
flex : 0 0 52 px ;
color : var ( -- text - tertiary ) ;
font - size : 0.82 rem ;
font - size : 0.8 rem ;
}
. timeline _ _mono {
font - family : var ( -- font - mono ) ;
@@ -135,7 +216,27 @@ const L = (obj) => (locale.value === 'zh-CN' ? obj.zh : obj.en)
. timeline _ _links {
display : flex ;
gap : var ( -- space - md ) ;
margin - top : var ( -- space - sm ) ;
font - size : 0.88 rem ;
margin - top : var ( -- space - x s) ;
font - size : 0.85 rem ;
}
/* 2D -> 3D 转折标记 */
. timeline _ _pivot {
display : flex ;
align - items : center ;
gap : var ( -- space - md ) ;
margin : var ( -- space - md ) 0 0 ;
}
. timeline _ _pivot - line {
flex : 1 ;
height : 1 px ;
background : linear - gradient ( 90 deg , transparent , var ( -- border - strong ) , transparent ) ;
}
. timeline _ _pivot - text {
font - size : 0.8 rem ;
font - weight : 600 ;
color : var ( -- accent ) ;
white - space : nowrap ;
letter - spacing : 0.02 em ;
}
< / style >