@@ -0,0 +1,664 @@
<!doctype html>
< html lang = "zh-CN" >
< head >
< meta charset = "utf-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1" >
< title > 移动端 HUD 草图 · 暗黑 ARPG< / title >
< link rel = "stylesheet" href = "../themes/default.css" >
< style >
* { box-sizing : border-box ; }
: root { color-scheme : dark ; }
html , body { margin : 0 ; min-height : 100 % ; background : radial-gradient ( circle at 50 % 0 % , #1c3048 0 , #0b1018 42 rem ) ; color : var ( - - color - text ) ; font-family : var ( - - font - sans ) ; }
body { padding : 34 px 20 px 60 px ; }
button , input { font : inherit ; }
button { color : inherit ; cursor : pointer ; }
. page-head { max-width : 1040 px ; margin : 0 auto 24 px ; }
. eyebrow { color : var ( - - color - primary ) ; font-size : var ( - - text - xs ) ; letter-spacing : .18 em ; text-transform : uppercase ; }
h1 { margin : 5 px 0 ; font-size : clamp ( 1.45 rem , 3 vw , 2.2 rem ) ; }
. page-head p { margin : 6 px 0 0 ; max-width : 720 px ; color : var ( - - color - text - muted ) ; line-height : 1.55 ; }
. variant { max-width : 1040 px ; margin : auto ; }
. variant-intro { display : flex ; align-items : baseline ; justify-content : space-between ; gap : 16 px ; margin : 0 auto 14 px ; max-width : 920 px ; }
. variant-intro h2 { margin : 0 ; font-size : var ( - - text - lg ) ; }
. variant-intro p { margin : 0 ; color : var ( - - color - text - muted ) ; font-size : var ( - - text - sm ) ; }
. device-wrap { display : flex ; justify-content : center ; }
. device { position : relative ; width : min ( 844 px , calc ( 100 vw - 40 px ) ) ; aspect-ratio : 844 / 390 ; overflow : hidden ; border : 1 px solid rgba ( 221 , 238 , 255 , .45 ) ; border-radius : 30 px ; background : #0a1018 ; box-shadow : 0 28 px 80 px rgba ( 0 , 0 , 0 , .55 ) , 0 0 0 7 px rgba ( 255 , 255 , 255 , .035 ) ; transition : width .25 s ease , aspect-ratio .25 s ease , border-radius .25 s ease ; }
. preview-tablet . device { width : min ( 1280 px , calc ( 100 vw - 40 px ) ) ; aspect-ratio : 16 / 9 ; border-radius : 24 px ; }
. preview-desktop . device { width : min ( 1440 px , calc ( 100 vw - 40 px ) ) ; aspect-ratio : 16 / 9 ; border-radius : 18 px ; }
. scene { position : absolute ; inset : 0 ; overflow : hidden ; background : linear-gradient ( 145 deg , rgba ( 16 , 39 , 54 , .85 ) , rgba ( 8 , 17 , 25 , .88 ) 48 % , rgba ( 32 , 22 , 42 , .94 ) ) ; }
. scene :: before { content : "" ; position : absolute ; inset : -12 % ; opacity : .55 ; background : radial-gradient ( ellipse at 25 % 37 % , rgba ( 95 , 181 , 187 , .28 ) , transparent 24 % ) , radial-gradient ( ellipse at 78 % 23 % , rgba ( 144 , 97 , 221 , .28 ) , transparent 22 % ) , linear-gradient ( 145 deg , transparent 35 % , rgba ( 255 , 211 , 108 , .08 ) 36 % , transparent 37 % 58 % , rgba ( 116 , 185 , 255 , .08 ) 59 % , transparent 60 % ) ; transform : rotate ( -7 deg ) ; }
. scene :: after { content : "" ; position : absolute ; inset : 0 ; background : linear-gradient ( transparent 57 % , rgba ( 4 , 8 , 13 , .42 ) 82 % , rgba ( 4 , 8 , 13 , .78 ) ) ; pointer-events : none ; }
. terrain { position : absolute ; inset : 32 % 4 % 16 % ; opacity : .4 ; background : repeating-linear-gradient ( 164 deg , transparent 0 19 px , rgba ( 173 , 214 , 213 , .16 ) 20 px 21 px ) , repeating-linear-gradient ( 22 deg , transparent 0 36 px , rgba ( 46 , 70 , 82 , .42 ) 37 px 39 px ) ; clip-path : polygon ( 0 25 % , 25 % 4 % , 53 % 24 % , 75 % 0 , 100 % 21 % , 100 % 100 % , 0 100 % ) ; }
. hero { position : absolute ; left : 48 % ; top : 40 % ; width : 48 px ; height : 82 px ; transform : translate ( -50 % , -50 % ) ; border-radius : 45 % 45 % 28 % 28 % ; background : linear-gradient ( 90 deg , #152d44 , #b7d7e8 45 % , #315375 58 % , #101d2d ) ; box-shadow : 0 0 18 px rgba ( 116 , 185 , 255 , .45 ) ; }
. hero :: before { content : "" ; position : absolute ; left : 9 px ; top : -26 px ; width : 30 px ; height : 32 px ; border-radius : 50 % ; background : radial-gradient ( circle at 40 % 42 % , #ffe9c5 0 18 % , #6f4c3e 19 % 54 % , #1a2432 55 % ) ; }
. hero :: after { content : "✦" ; position : absolute ; right : -29 px ; top : 6 px ; color : var ( - - color - accent ) ; font-size : 28 px ; text-shadow : 0 0 14 px #ffb638 ; }
. mob { position : absolute ; left : 68 % ; top : 44 % ; width : 58 px ; height : 42 px ; border-radius : 48 % 52 % 35 % 35 % ; background : linear-gradient ( 140 deg , #2b4155 , #8c6672 45 % , #201824 ) ; box-shadow : 0 0 18 px rgba ( 255 , 111 , 120 , .2 ) ; }
. mob :: before { content : "" ; position : absolute ; left : 13 px ; top : -17 px ; width : 34 px ; height : 22 px ; border-radius : 55 % 45 % 35 % 35 % ; background : #5f3442 ; }
. mob :: after { content : "· ·" ; position : absolute ; left : 9 px ; top : -13 px ; color : #ffb1a9 ; letter-spacing : 6 px ; font-size : 13 px ; }
. top-status { position : absolute ; z-index : 2 ; top : 14 px ; left : 14 px ; right : 14 px ; display : flex ; align-items : center ; gap : 9 px ; }
. avatar { width : 44 px ; height : 44 px ; flex : 0 0 auto ; display : grid ; place-items : center ; border : 1 px solid rgba ( 255 , 211 , 108 , .78 ) ; border-radius : 50 % ; background : radial-gradient ( circle , #658fb6 , #182637 62 % , #0b121c ) ; box-shadow : 0 0 0 3 px rgba ( 12 , 20 , 30 , .58 ) ; font-size : 20 px ; }
. status-copy { min-width : 0 ; flex : 0 0 148 px ; width : 148 px ; }
. status-line { display : flex ; align-items : center ; justify-content : space-between ; gap : 7 px ; font-size : var ( - - text - xs ) ; }
. status-name { overflow : hidden ; text-overflow : ellipsis ; white-space : nowrap ; }
. level { color : var ( - - color - accent ) ; font-family : var ( - - font - mono ) ; }
. meter { height : 7 px ; margin-top : 4 px ; overflow : hidden ; border : 1 px solid rgba ( 255 , 255 , 255 , .18 ) ; border-radius : 8 px ; background : rgba ( 3 , 8 , 13 , .72 ) ; }
. meter i { display : block ; height : 100 % ; border-radius : inherit ; }
. meter . hp i { width : 78 % ; background : linear-gradient ( 90 deg , #f05767 , #ff9d82 ) ; }
. meter . mp i { width : 62 % ; background : linear-gradient ( 90 deg , #4f91f8 , #8dc8ff ) ; }
. meter . resource { height : 5 px ; margin-top : 3 px ; }
. meter . resource i { width : 68 % ; background : linear-gradient ( 90 deg , #39217d , #8c55ed 70 % , #caa0ff ) ; }
. mini-map { position : relative ; width : 74 px ; height : 74 px ; flex : 0 0 auto ; overflow : hidden ; border : 1 px solid rgba ( 213 , 235 , 255 , .52 ) ; border-radius : 50 % ; background : radial-gradient ( circle at 44 % 58 % , rgba ( 101 , 178 , 161 , .5 ) 0 12 % , transparent 13 % ) , linear-gradient ( 35 deg , #17313a 0 38 % , #284b58 39 % 44 % , #17313a 45 % 63 % , #4c385d 64 % ) ; box-shadow : inset 0 0 0 4 px rgba ( 8 , 14 , 22 , .55 ) , 0 6 px 14 px rgba ( 0 , 0 , 0 , .3 ) ; }
. mini-map :: before { content : "" ; position : absolute ; left : 47 % ; top : 43 % ; width : 7 px ; height : 7 px ; border-radius : 50 % ; background : var ( - - color - accent ) ; box-shadow : 0 0 0 4 px rgba ( 255 , 211 , 108 , .18 ) , 16 px -20 px 0 -2 px var ( - - color - danger ) ; }
. target-card { position : absolute ; z-index : 2 ; top : 94 px ; left : 50 % ; width : 190 px ; transform : translateX ( -50 % ) ; padding : 8 px 11 px ; opacity : .92 ; border : 1 px solid rgba ( 255 , 111 , 120 , .36 ) ; border-radius : 12 px ; background : rgba ( 13 , 19 , 29 , .62 ) ; backdrop-filter : blur ( 12 px ) ; box-shadow : var ( - - shadow - sm ) ; }
. target-card strong { display : block ; font-size : var ( - - text - sm ) ; }
. target-card small { color : #ffabb0 ; font-size : var ( - - text - xs ) ; }
. target-card . meter { height : 5 px ; margin-top : 6 px ; }
. target-card . meter i { width : 43 % ; background : linear-gradient ( 90 deg , #e65060 , #ffab87 ) ; }
. quest-pill { position : absolute ; z-index : 2 ; top : 102 px ; right : 14 px ; display : flex ; align-items : center ; gap : 6 px ; border : 1 px solid rgba ( 255 , 211 , 108 , .36 ) ; border-radius : 12 px ; padding : 8 px 10 px ; background : rgba ( 13 , 19 , 29 , .54 ) ; color : #ffe9ab ; font-size : var ( - - text - xs ) ; backdrop-filter : blur ( 12 px ) ; }
. chat-bubble { position : absolute ; z-index : 2 ; left : 50 % ; top : 57 % ; transform : translateX ( -50 % ) ; max-width : 168 px ; padding : 7 px 10 px ; border : 1 px solid rgba ( 255 , 255 , 255 , .23 ) ; border-radius : 12 px 12 px 12 px 3 px ; background : rgba ( 10 , 16 , 24 , .72 ) ; color : #e3efff ; font-size : var ( - - text - xs ) ; backdrop-filter : blur ( 10 px ) ; }
. chat-bubble :: after { content : "" ; position : absolute ; left : 11 px ; bottom : -7 px ; border : 6 px solid transparent ; border-top-color : rgba ( 10 , 16 , 24 , .72 ) ; border-left : 0 ; }
. joystick { position : absolute ; z-index : 4 ; left : 22 px ; bottom : 116 px ; width : 110 px ; height : 110 px ; border : 1 px solid rgba ( 213 , 235 , 255 , .24 ) ; border-radius : 50 % ; background : radial-gradient ( circle , rgba ( 116 , 185 , 255 , .2 ) 0 28 % , rgba ( 116 , 185 , 255 , .07 ) 29 % 62 % , rgba ( 10 , 16 , 24 , .15 ) 63 % ) ; box-shadow : inset 0 0 0 12 px rgba ( 9 , 17 , 26 , .22 ) ; touch-action : none ; }
. joystick :: before { content : "" ; position : absolute ; inset : 18 px ; border : 1 px dashed rgba ( 213 , 235 , 255 , .24 ) ; border-radius : 50 % ; }
. stick { position : absolute ; left : 50 % ; top : 50 % ; width : 54 px ; height : 54 px ; transform : translate ( -50 % , -50 % ) ; border : 1 px solid rgba ( 213 , 235 , 255 , .64 ) ; border-radius : 50 % ; background : radial-gradient ( circle at 35 % 28 % , rgba ( 255 , 255 , 255 , .32 ) , rgba ( 73 , 134 , 193 , .66 ) 38 % , rgba ( 19 , 49 , 75 , .88 ) ) ; box-shadow : 0 5 px 12 px rgba ( 0 , 0 , 0 , .36 ) ; transition : transform .12 s ease ; }
. joystick . is-active . stick { box-shadow : 0 0 0 6 px rgba ( 116 , 185 , 255 , .14 ) , 0 5 px 12 px rgba ( 0 , 0 , 0 , .36 ) ; }
. combat-cluster { position : absolute ; z-index : 4 ; right : 16 px ; bottom : 116 px ; width : 156 px ; height : 156 px ; }
. skill { position : absolute ; display : grid ; place-items : center ; width : 52 px ; height : 52 px ; border : 1 px solid rgba ( 213 , 235 , 255 , .54 ) ; border-radius : 50 % ; background : radial-gradient ( circle at 36 % 30 % , rgba ( 255 , 255 , 255 , .33 ) , rgba ( 55 , 88 , 129 , .8 ) 45 % , rgba ( 10 , 19 , 31 , .92 ) ) ; box-shadow : 0 6 px 14 px rgba ( 0 , 0 , 0 , .36 ) ; font-size : 20 px ; transition : transform .15 s ease , filter .15 s ease ; }
. skill : hover , . skill : focus-visible { transform : scale ( 1.08 ) ; filter : brightness ( 1.25 ) ; }
. skill . main { right : 0 ; bottom : 14 px ; width : 78 px ; height : 78 px ; border-color : rgba ( 255 , 211 , 108 , .74 ) ; background : radial-gradient ( circle at 35 % 25 % , #ffdf9a , #ad5b4d 42 % , #2b1b2e 78 % ) ; font-size : 29 px ; }
. skill . s1 { left : 12 px ; top : 5 px ; }
. skill . s2 { left : 52 px ; top : 52 px ; }
. skill . s3 { right : 4 px ; top : 3 px ; }
. skill . s4 { left : 0 ; bottom : 10 px ; }
. skill em { position : absolute ; right : -2 px ; bottom : -2 px ; display : none ; min-width : 18 px ; padding : 2 px 4 px ; border-radius : 8 px ; background : #0a111c ; color : #f9fcff ; font-size : 10 px ; font-style : normal ; }
. skill . cooling { filter : grayscale ( .7 ) brightness ( .58 ) ; }
. skill . cooling em { display : block ; }
. context-action { position : absolute ; z-index : 4 ; right : 23 px ; bottom : 279 px ; display : flex ; align-items : center ; gap : 6 px ; border : 1 px solid rgba ( 101 , 225 , 178 , .45 ) ; border-radius : 18 px ; padding : 7 px 10 px ; background : rgba ( 14 , 44 , 46 , .72 ) ; color : #adffe1 ; font-size : var ( - - text - xs ) ; backdrop-filter : blur ( 10 px ) ; }
. utility-btn { min-width : 41 px ; height : 38 px ; display : grid ; place-items : center ; flex : 1 ; border : 1 px solid rgba ( 213 , 235 , 255 , .12 ) ; border-radius : 12 px ; background : rgba ( 116 , 185 , 255 , .08 ) ; font-size : 17 px ; transition : all .15 s ease ; }
. utility-btn : hover , . utility-btn . active { border-color : rgba ( 116 , 185 , 255 , .5 ) ; background : rgba ( 116 , 185 , 255 , .21 ) ; transform : translateY ( -2 px ) ; }
. utility-btn span { display : block ; margin-top : 1 px ; color : var ( - - color - text - muted ) ; font-size : 9 px ; }
. floating-cooldown { position : absolute ; z-index : 5 ; left : 50 % ; top : 69 % ; transform : translate ( -50 % , -50 % ) ; padding : 9 px 13 px ; border : 1 px solid rgba ( 255 , 211 , 108 , .42 ) ; border-radius : 16 px ; background : rgba ( 22 , 18 , 19 , .74 ) ; color : #ffe4a0 ; font-size : var ( - - text - xs ) ; opacity : 0 ; transition : opacity .2 s ease ; }
. floating-cooldown . show { opacity : 1 ; }
. modal-layer { position : absolute ; z-index : 8 ; inset : 0 ; display : none ; align-items : flex-end ; background : rgba ( 2 , 6 , 10 , .48 ) ; backdrop-filter : blur ( 6 px ) ; }
. modal-layer . open { display : flex ; }
. sheet { width : 100 % ; max-height : 74 % ; overflow : auto ; padding : 17 px ; border-top : 1 px solid rgba ( 213 , 235 , 255 , .32 ) ; border-radius : 24 px 24 px 0 0 ; background : rgba ( 12 , 21 , 33 , .95 ) ; box-shadow : 0 -16 px 42 px rgba ( 0 , 0 , 0 , .4 ) ; transform : translateY ( 20 px ) ; animation : rise .18 s ease forwards ; }
@ keyframes rise { to { transform : translateY ( 0 ) ; } }
. sheet-head { display : flex ; align-items : center ; justify-content : space-between ; margin-bottom : 12 px ; }
. sheet-head h3 { margin : 0 ; font-size : var ( - - text - lg ) ; }
. sheet-title-row { display : flex ; align-items : center ; gap : 8 px ; }
. close-btn { width : 32 px ; height : 32 px ; border : 1 px solid var ( - - color - border ) ; border-radius : 50 % ; background : var ( - - color - surface - soft ) ; }
. back-btn { width : 32 px ; height : 32 px ; border : 1 px solid var ( - - color - border ) ; border-radius : 50 % ; background : var ( - - color - surface - soft ) ; }
. action-grid { display : grid ; grid-template-columns : repeat ( 4 , 1 fr ) ; gap : 8 px ; }
. action-item { min-height : 58 px ; border : 1 px solid rgba ( 213 , 235 , 255 , .13 ) ; border-radius : 14 px ; background : rgba ( 116 , 185 , 255 , .07 ) ; color : var ( - - color - text ) ; transition : all .15 s ease ; }
. action-item : hover { border-color : rgba ( 116 , 185 , 255 , .5 ) ; background : rgba ( 116 , 185 , 255 , .16 ) ; transform : translateY ( -2 px ) ; }
. action-item b { display : block ; font-size : 20 px ; }
. action-item span { color : var ( - - color - text - muted ) ; font-size : 10 px ; }
. menu-grid { display : grid ; grid-template-columns : repeat ( 3 , minmax ( 0 , 1 fr ) ) ; gap : 10 px ; }
. menu-item { min-height : 88 px ; padding : 10 px 7 px ; border : 1 px solid rgba ( 213 , 235 , 255 , .16 ) ; border-radius : 15 px ; background : linear-gradient ( 145 deg , rgba ( 116 , 185 , 255 , .13 ) , rgba ( 10 , 16 , 24 , .35 ) ) ; color : var ( - - color - text ) ; text-align : center ; transition : transform .15 s ease , border-color .15 s ease , background .15 s ease ; }
. menu-item : hover , . menu-item : focus-visible { transform : translateY ( -2 px ) ; border-color : rgba ( 255 , 211 , 108 , .72 ) ; background : rgba ( 116 , 185 , 255 , .23 ) ; outline : none ; }
. menu-item b { display : block ; margin-bottom : 5 px ; color : var ( - - color - accent ) ; font-size : 25 px ; line-height : 1 ; }
. menu-item span { display : block ; font-size : var ( - - text - sm ) ; }
. menu-item small { display : block ; margin-top : 4 px ; color : var ( - - color - text - muted ) ; font-size : 9 px ; line-height : 1.3 ; }
. sheet-note { margin : 10 px 0 0 ; color : var ( - - color - text - muted ) ; font-size : var ( - - text - xs ) ; line-height : 1.4 ; }
. chat-list { display : grid ; gap : 8 px ; margin-bottom : 10 px ; }
. chat-line { padding : 8 px 10 px ; border-radius : 12 px ; background : rgba ( 255 , 255 , 255 , .055 ) ; font-size : var ( - - text - sm ) ; }
. chat-line strong { color : var ( - - color - primary ) ; }
. chat-compose { display : flex ; gap : 8 px ; }
. chat-compose input { min-width : 0 ; flex : 1 ; padding : 10 px ; border : 1 px solid var ( - - color - border ) ; border-radius : 12 px ; background : rgba ( 0 , 0 , 0 , .25 ) ; color : var ( - - color - text ) ; outline : none ; }
. send-btn { padding : 0 13 px ; border : 1 px solid rgba ( 101 , 225 , 178 , .4 ) ; border-radius : 12 px ; background : rgba ( 101 , 225 , 178 , .14 ) ; color : #adffe1 ; }
. annotation-label { position : fixed ; z-index : 50 ; display : none ; padding : 5 px 7 px ; pointer-events : none ; border : 1 px solid rgba ( 255 , 211 , 108 , .45 ) ; border-radius : 7 px ; background : #101a27 ; color : #ffe4a0 ; font : 11 px var ( - - font - mono ) ; box-shadow : var ( - - shadow - sm ) ; }
body . annotate [ data-label ] : hover { outline : 1 px dashed rgba ( 255 , 211 , 108 , .7 ) ; outline-offset : 3 px ; }
. toolbar { position : fixed ; z-index : 30 ; right : 16 px ; bottom : 16 px ; display : flex ; align-items : center ; gap : 7 px ; padding : 8 px 10 px ; border : 1 px solid rgba ( 255 , 255 , 255 , .16 ) ; border-radius : 12 px ; background : rgba ( 5 , 9 , 14 , .78 ) ; color : #fff ; font-size : 11 px ; opacity : .44 ; transition : opacity .2 s ease ; backdrop-filter : blur ( 12 px ) ; }
. toolbar : hover { opacity : 1 ; }
. toolbar button , . toolbar select { border : 1 px solid rgba ( 255 , 255 , 255 , .19 ) ; border-radius : 7 px ; padding : 5 px 7 px ; background : rgba ( 255 , 255 , 255 , .08 ) ; color : #fff ; }
. toolbar button : hover , . toolbar select : hover { background : rgba ( 116 , 185 , 255 , .22 ) ; }
. toast { position : fixed ; z-index : 60 ; left : 50 % ; bottom : 72 px ; transform : translate ( -50 % , 12 px ) ; padding : 9 px 14 px ; opacity : 0 ; border : 1 px solid rgba ( 213 , 235 , 255 , .28 ) ; border-radius : 12 px ; background : rgba ( 9 , 16 , 25 , .93 ) ; color : var ( - - color - text ) ; box-shadow : var ( - - shadow - md ) ; transition : all .18 s ease ; pointer-events : none ; font-size : var ( - - text - sm ) ; }
. toast . show { opacity : 1 ; transform : translate ( -50 % , 0 ) ; }
. interaction-section { max-width : 1040 px ; margin : 54 px auto 0 ; }
. interaction-head { display : flex ; align-items : flex-end ; justify-content : space-between ; gap : 24 px ; margin-bottom : 18 px ; }
. interaction-head h2 { margin : 5 px 0 6 px ; font-size : clamp ( 1.35 rem , 2.6 vw , 2 rem ) ; }
. interaction-head p { max-width : 720 px ; margin : 0 ; color : #c6b399 ; line-height : 1.55 ; }
. interaction-legend { display : flex ; flex-wrap : wrap ; justify-content : flex-end ; gap : 6 px ; max-width : 330 px ; }
. legend-chip { display : inline-flex ; align-items : center ; gap : 5 px ; padding : 6 px 9 px ; border : 1 px solid rgba ( 224 , 181 , 105 , .26 ) ; border-radius : 999 px ; background : rgba ( 93 , 53 , 25 , .2 ) ; color : #d8bd91 ; font-size : 10 px ; white-space : nowrap ; }
. legend-chip i { width : 7 px ; height : 7 px ; border-radius : 50 % ; background : #e0b15e ; box-shadow : 0 0 8 px rgba ( 224 , 177 , 91 , .7 ) ; }
. legend-chip . menu i { background : #8f6be8 ; box-shadow : 0 0 8 px rgba ( 143 , 107 , 232 , .7 ) ; }
. legend-chip . full i { background : #68baa0 ; box-shadow : 0 0 8 px rgba ( 104 , 186 , 160 , .7 ) ; }
. legend-chip . modal i { background : #e26c5c ; box-shadow : 0 0 8 px rgba ( 226 , 108 , 92 , .7 ) ; }
. flow-filter { display : flex ; flex-wrap : wrap ; gap : 7 px ; margin : 0 0 14 px ; }
. flow-filter button { padding : 7 px 12 px ; border : 1 px solid rgba ( 224 , 181 , 105 , .24 ) ; border-radius : 999 px ; background : rgba ( 44 , 24 , 14 , .62 ) ; color : #c8b18e ; font-size : 11 px ; }
. flow-filter button . active , . flow-filter button : hover { border-color : rgba ( 239 , 195 , 113 , .72 ) ; background : rgba ( 163 , 100 , 44 , .28 ) ; color : #f5d998 ; }
. flow-grid { display : grid ; grid-template-columns : repeat ( 2 , minmax ( 0 , 1 fr ) ) ; gap : 12 px ; }
. flow-card { min-width : 0 ; padding : 14 px ; border : 1 px solid rgba ( 224 , 181 , 105 , .25 ) ; border-radius : 16 px ; background : linear-gradient ( 145 deg , rgba ( 64 , 35 , 20 , .72 ) , rgba ( 20 , 11 , 8 , .88 ) ) ; box-shadow : 0 10 px 26 px rgba ( 0 , 0 , 0 , .18 ) ; }
. flow-card . is-hidden { display : none ; }
. flow-card-head { display : flex ; align-items : center ; justify-content : space-between ; gap : 10 px ; margin-bottom : 12 px ; }
. flow-card-head h3 { margin : 0 ; color : #f4e0b5 ; font-size : 14 px ; }
. flow-index { color : #d8a758 ; font : 11 px var ( - - font - mono ) ; letter-spacing : .08 em ; }
. flow-scope { color : #a99478 ; font-size : 10 px ; }
. flow-track { display : flex ; align-items : stretch ; gap : 6 px ; overflow-x : auto ; padding : 2 px 1 px 5 px ; scrollbar-width : thin ; }
. flow-node { position : relative ; min-width : 104 px ; min-height : 67 px ; padding : 9 px 8 px ; border : 1 px solid rgba ( 224 , 181 , 105 , .24 ) ; border-radius : 12 px ; background : rgba ( 16 , 9 , 7 , .8 ) ; color : #f5ead7 ; text-align : left ; transition : transform .15 s ease , border-color .15 s ease , background .15 s ease ; }
. flow-node : hover , . flow-node : focus-visible { transform : translateY ( -2 px ) ; border-color : rgba ( 239 , 195 , 113 , .85 ) ; background : rgba ( 117 , 68 , 32 , .36 ) ; outline : none ; }
. flow-node b { display : block ; margin-bottom : 5 px ; font-size : 12 px ; }
. flow-node small { display : block ; color : #bba586 ; font-size : 10 px ; line-height : 1.35 ; }
. flow-node . menu-node { border-color : rgba ( 143 , 107 , 232 , .4 ) ; }
. flow-node . full-node { border-color : rgba ( 104 , 186 , 160 , .4 ) ; }
. flow-node . modal-node { border-color : rgba ( 226 , 108 , 92 , .42 ) ; }
. flow-arrow { align-self : center ; flex : 0 0 auto ; color : #d8a758 ; font-size : 16 px ; opacity : .78 ; }
. flow-detail { margin-top : 14 px ; min-height : 132 px ; padding : 16 px 18 px ; border : 1 px solid rgba ( 239 , 195 , 113 , .34 ) ; border-radius : 16 px ; background : rgba ( 20 , 10 , 7 , .84 ) ; box-shadow : 0 15 px 32 px rgba ( 0 , 0 , 0 , .22 ) ; }
. flow-detail . empty { display : grid ; place-items : center ; color : #bba586 ; text-align : center ; }
. flow-detail h3 { margin : 4 px 0 8 px ; color : #f4e0b5 ; font-size : 17 px ; }
. detail-kicker { color : #d8a758 ; font : 10 px var ( - - font - mono ) ; letter-spacing : .14 em ; text-transform : uppercase ; }
. detail-grid { display : grid ; grid-template-columns : 1.1 fr 1 fr 1 fr ; gap : 14 px ; }
. detail-block strong { display : block ; margin-bottom : 4 px ; color : #d8bd91 ; font-size : 11 px ; }
. detail-block p { margin : 0 ; color : #c8b18e ; font-size : 11 px ; line-height : 1.5 ; }
. detail-block p + p { margin-top : 4 px ; }
. flow-note { margin : 12 px 0 0 ; padding-top : 10 px ; border-top : 1 px solid rgba ( 224 , 181 , 105 , .14 ) ; color : #9f8b70 ; font-size : 10 px ; line-height : 1.5 ; }
. hidden { display : none !important ; }
@ media ( max-width : 620px ) { body { padding-inline : 10 px ; } . variant-intro { display : block ; padding-inline : 4 px ; } . variant-intro p { margin-top : 4 px ; } . toolbar { right : 8 px ; bottom : 8 px ; } }
/* Diablo-inspired ARPG direction: warm bronze frames, dark glass, circular combat controls. */
body { background : radial-gradient ( circle at 50 % 0 % , #3a2416 0 , #170e0b 35 rem , #090706 70 rem ) ; }
. page-head p { color : #c6b399 ; }
. device { border-color : rgba ( 224 , 181 , 105 , .72 ) ; border-radius : 22 px ; box-shadow : 0 28 px 80 px rgba ( 0 , 0 , 0 , .7 ) , 0 0 0 6 px rgba ( 204 , 147 , 64 , .08 ) , inset 0 0 0 1 px rgba ( 255 , 229 , 175 , .14 ) ; }
. scene { background : linear-gradient ( 145 deg , #725031 , #b98045 40 % , #4b2b1d 78 % , #1b100c ) ; }
. scene :: before { opacity : .7 ; background : radial-gradient ( ellipse at 34 % 40 % , rgba ( 255 , 224 , 152 , .28 ) , transparent 24 % ) , radial-gradient ( ellipse at 70 % 28 % , rgba ( 42 , 16 , 13 , .4 ) , transparent 27 % ) , repeating-linear-gradient ( 151 deg , rgba ( 255 , 220 , 155 , .06 ) 0 2 px , transparent 2 px 38 px ) ; }
. scene :: after { background : linear-gradient ( 180 deg , rgba ( 12 , 7 , 5 , .54 ) , transparent 20 % , transparent 62 % , rgba ( 8 , 5 , 4 , .7 ) ) ; }
. terrain { opacity : .62 ; background : repeating-linear-gradient ( 164 deg , transparent 0 17 px , rgba ( 70 , 35 , 18 , .18 ) 18 px 20 px ) , repeating-linear-gradient ( 22 deg , transparent 0 35 px , rgba ( 255 , 218 , 152 , .16 ) 36 px 38 px ) ; }
. hero { background : linear-gradient ( 90 deg , #1a1210 , #d2a85d 43 % , #6b3d25 59 % , #110b09 ) ; box-shadow : 0 0 20 px rgba ( 255 , 198 , 93 , .48 ) ; }
. hero :: before { background : radial-gradient ( circle at 40 % 42 % , #ffe0b2 0 18 % , #6d3b2b 19 % 54 % , #160e0c 55 % ) ; }
. hero :: after { color : #ffe0a1 ; text-shadow : 0 0 14 px #ef812e ; }
. mob { background : linear-gradient ( 140 deg , #493021 , #963f32 45 % , #1c1110 ) ; box-shadow : 0 0 18 px rgba ( 214 , 69 , 45 , .35 ) ; }
. mob :: before { background : #4f1e1b ; }
. mob :: after { color : #ffc0a0 ; }
. top-status { top : 15 px ; left : 18 px ; right : auto ; }
. player-status { display : flex ; width : 148 px ; flex-direction : column ; align-items : center ; gap : 3 px ; }
. player-name { display : flex ; width : 148 px ; align-items : center ; justify-content : center ; gap : 6 px ; color : #f4e0b5 ; font : 600 11 px var ( - - font - mono ) ; text-shadow : 0 2 px 4 px #000 ; white-space : nowrap ; }
. player-meters { width : 44 px ; }
. party-rail { position : absolute ; z-index : 4 ; top : 112 px ; left : 18 px ; display : grid ; width : 148 px ; gap : 5 px ; }
. party-member { display : grid ; grid-template-columns : 27 px minmax ( 0 , 1 fr ) auto ; align-items : center ; gap : 6 px ; min-height : 34 px ; padding : 4 px 6 px 4 px 4 px ; border : 1 px solid rgba ( 223 , 181 , 111 , .32 ) ; border-radius : 8 px ; background : rgba ( 19 , 10 , 7 , .65 ) ; color : #f4e0b5 ; text-align : left ; box-shadow : 0 4 px 10 px rgba ( 0 , 0 , 0 , .28 ) ; transition : transform .15 s ease , border-color .15 s ease , background .15 s ease ; }
. party-member : hover , . party-member : focus-visible { transform : translateX ( 2 px ) ; border-color : rgba ( 239 , 195 , 113 , .76 ) ; background : rgba ( 87 , 46 , 22 , .78 ) ; outline : none ; }
. party-avatar { display : grid ; width : 25 px ; height : 25 px ; place-items : center ; border : 1 px solid rgba ( 239 , 195 , 112 , .6 ) ; border-radius : 50 % ; background : radial-gradient ( circle , #89684e , #291711 68 % ) ; color : #f7d58f ; font-size : 12 px ; }
. party-copy { min-width : 0 ; }
. party-copy strong { display : block ; overflow : hidden ; color : #e9d5ae ; font-size : 9 px ; line-height : 1.15 ; text-overflow : ellipsis ; white-space : nowrap ; }
. party-hp { height : 4 px ; margin-top : 3 px ; overflow : hidden ; border-radius : 4 px ; background : rgba ( 5 , 3 , 2 , .72 ) ; }
. party-hp i { display : block ; height : 100 % ; border-radius : inherit ; background : linear-gradient ( 90 deg , #8a2525 , #e85a46 ) ; }
. party-distance { color : #bfa77f ; font : 9 px var ( - - font - mono ) ; }
. top-right-controls { position : absolute ; z-index : 4 ; top : 14 px ; right : 14 px ; display : flex ; align-items : flex-start ; gap : 7 px ; }
. top-right-controls . menu-button { min-width : 42 px ; width : 42 px ; height : 42 px ; flex : 0 0 auto ; background : rgba ( 17 , 9 , 7 , .78 ) ; border-color : rgba ( 224 , 177 , 91 , .5 ) ; box-shadow : 0 4 px 10 px rgba ( 0 , 0 , 0 , .42 ) ; }
. avatar { border-color : rgba ( 239 , 195 , 112 , .92 ) ; background : radial-gradient ( circle , #99704c , #2b1a13 62 % , #0d0907 ) ; box-shadow : 0 0 0 3 px rgba ( 13 , 8 , 6 , .7 ) , 0 0 16 px rgba ( 231 , 167 , 68 , .22 ) ; }
. level { color : #f4ca7b ; }
. meter { height : 9 px ; border-color : rgba ( 255 , 227 , 170 , .25 ) ; background : rgba ( 11 , 6 , 5 , .82 ) ; box-shadow : inset 0 1 px 2 px rgba ( 0 , 0 , 0 , .45 ) ; }
. meter . hp i { background : linear-gradient ( 90 deg , #761b1e , #e2432c 72 % , #ff8961 ) ; }
. meter . mp i { background : linear-gradient ( 90 deg , #3c247f , #8454f0 72 % , #c58aff ) ; }
. mini-map { width : 142 px ; height : 86 px ; border-radius : 9 px ; border-color : rgba ( 223 , 187 , 119 , .72 ) ; background : radial-gradient ( circle at 43 % 58 % , rgba ( 226 , 179 , 83 , .42 ) 0 7 % , transparent 8 % ) , linear-gradient ( 32 deg , #21150e 0 31 % , #6a4323 32 % 39 % , #271711 40 % 63 % , #80603b 64 % ) ; box-shadow : inset 0 0 0 4 px rgba ( 8 , 5 , 4 , .68 ) , 0 6 px 14 px rgba ( 0 , 0 , 0 , .48 ) ; }
. mini-map :: before { left : 47 % ; top : 43 % ; background : #f8d17e ; box-shadow : 0 0 0 4 px rgba ( 255 , 205 , 106 , .2 ) , 16 px -20 px 0 -2 px #d65838 ; }
. mini-map :: after { content : "荒漠边境" ; position : absolute ; right : 7 px ; bottom : 5 px ; color : rgba ( 245 , 213 , 157 , .7 ) ; font : 10 px var ( - - font - mono ) ; letter-spacing : .08 em ; }
. target-card { top : 13 px ; width : 260 px ; padding : 4 px 12 px 6 px ; border : 0 ; border-radius : 0 ; background : rgba ( 13 , 8 , 6 , .2 ) ; box-shadow : none ; text-align : center ; }
. target-card strong { color : #f4e0b5 ; font : 600 15 px var ( - - font - mono ) ; letter-spacing : .1 em ; text-shadow : 0 2 px 4 px #000 ; }
. target-card small { color : #d0b285 ; }
. target-card . meter { height : 11 px ; margin-top : 5 px ; border-color : rgba ( 10 , 5 , 4 , .9 ) ; }
. target-card . meter i { background : linear-gradient ( 90 deg , #6d171a , #d43d2e 75 % , #f06d45 ) ; }
. quest-pill { top : 109 px ; right : 168 px ; border-color : rgba ( 224 , 177 , 91 , .38 ) ; background : rgba ( 24 , 13 , 9 , .65 ) ; color : #f1d49a ; box-shadow : 0 5 px 13 px rgba ( 0 , 0 , 0 , .25 ) ; }
. chat-bubble { top : 57 % ; background : rgba ( 17 , 9 , 7 , .72 ) ; border-color : rgba ( 236 , 197 , 133 , .28 ) ; color : #f4e6cb ; }
. context-action { right : 235 px ; bottom : 104 px ; border-color : rgba ( 219 , 173 , 93 , .58 ) ; background : rgba ( 25 , 12 , 8 , .74 ) ; color : #f6d593 ; box-shadow : 0 5 px 15 px rgba ( 0 , 0 , 0 , .32 ) ; }
. right-rail { position : absolute ; z-index : 4 ; top : 111 px ; right : 14 px ; display : grid ; gap : 8 px ; }
. right-rail . utility-btn { min-width : 42 px ; width : 42 px ; height : 42 px ; flex : 0 0 auto ; background : rgba ( 17 , 9 , 7 , .64 ) ; border-color : rgba ( 216 , 171 , 99 , .3 ) ; box-shadow : 0 4 px 10 px rgba ( 0 , 0 , 0 , .3 ) ; }
. right-rail . utility-btn span { color : #d8bd91 ; }
. joystick { left : 25 px ; bottom : 18 px ; width : 120 px ; height : 120 px ; border-color : rgba ( 223 , 181 , 111 , .32 ) ; background : radial-gradient ( circle , rgba ( 185 , 119 , 58 , .26 ) 0 28 % , rgba ( 74 , 39 , 22 , .42 ) 29 % 62 % , rgba ( 12 , 7 , 5 , .26 ) 63 % ) ; box-shadow : inset 0 0 0 12 px rgba ( 27 , 13 , 8 , .28 ) , 0 7 px 18 px rgba ( 0 , 0 , 0 , .35 ) ; }
. joystick :: before { border-color : rgba ( 238 , 202 , 139 , .2 ) ; }
. stick { border-color : rgba ( 236 , 194 , 117 , .58 ) ; background : radial-gradient ( circle at 35 % 28 % , rgba ( 255 , 239 , 196 , .28 ) , rgba ( 114 , 70 , 38 , .82 ) 38 % , rgba ( 30 , 14 , 9 , .94 ) ) ; }
. combat-cluster { right : 20 px ; bottom : 15 px ; width : 190 px ; height : 190 px ; }
. combat-cluster :: before { content : "" ; position : absolute ; inset : 16 px -1 px -2 px 8 px ; border : 1 px solid rgba ( 185 , 132 , 68 , .3 ) ; border-radius : 50 % ; background : radial-gradient ( circle , rgba ( 21 , 10 , 8 , .56 ) , rgba ( 12 , 6 , 5 , .12 ) 68 % , transparent 69 % ) ; box-shadow : 0 0 0 11 px rgba ( 12 , 6 , 5 , .18 ) ; }
. combat-cluster :: after { content : "" ; position : absolute ; inset : 5 px -4 px 12 px 5 px ; border : 2 px solid rgba ( 219 , 169 , 83 , .28 ) ; border-left-color : transparent ; border-bottom-color : transparent ; border-radius : 50 % ; transform : rotate ( 34 deg ) ; pointer-events : none ; }
. skill { z-index : 1 ; width : 58 px ; height : 58 px ; border-color : rgba ( 232 , 191 , 115 , .58 ) ; background : radial-gradient ( circle at 35 % 26 % , rgba ( 247 , 225 , 177 , .24 ) , rgba ( 62 , 28 , 22 , .94 ) 46 % , rgba ( 12 , 6 , 6 , .98 ) ) ; box-shadow : 0 6 px 14 px rgba ( 0 , 0 , 0 , .6 ) , inset 0 0 0 3 px rgba ( 6 , 3 , 3 , .36 ) ; }
. skill : hover , . skill : focus-visible { border-color : #f4d28d ; filter : brightness ( 1.3 ) ; box-shadow : 0 0 0 3 px rgba ( 238 , 187 , 90 , .2 ) , 0 7 px 16 px rgba ( 0 , 0 , 0 , .6 ) ; }
. skill . main { width : 88 px ; height : 88 px ; border-color : rgba ( 234 , 196 , 123 , .86 ) ; background : radial-gradient ( circle at 35 % 25 % , #7e5cf1 , #321d75 48 % , #0e0820 78 % ) ; box-shadow : 0 0 0 4 px rgba ( 15 , 7 , 28 , .75 ) , 0 0 18 px rgba ( 107 , 65 , 220 , .6 ) , 0 7 px 17 px rgba ( 0 , 0 , 0 , .65 ) ; font-size : 31 px ; }
. skill . main : hover , . skill . main : focus-visible { box-shadow : 0 0 0 5 px rgba ( 232 , 190 , 100 , .27 ) , 0 0 24 px rgba ( 138 , 92 , 255 , .78 ) ; }
. skill . s1 { left : 10 px ; top : 14 px ; }
. skill . s2 { left : 62 px ; top : 70 px ; }
. skill . s3 { right : 2 px ; top : 15 px ; }
. skill . s4 { left : 13 px ; bottom : 2 px ; }
# mobile-hud . skill . s4 { left : auto ; right : 16 px ; top : -52 px ; bottom : auto ; border-color : rgba ( 223 , 66 , 44 , .86 ) ; background : radial-gradient ( circle at 35 % 25 % , #f06a40 , #7d1f25 45 % , #1a080b 78 % ) ; }
. skill em { background : #140907 ; color : #f2d491 ; }
. utility-btn { border-color : rgba ( 215 , 171 , 98 , .2 ) ; border-radius : 7 px ; background : rgba ( 124 , 76 , 40 , .16 ) ; }
. utility-btn : hover , . utility-btn . active { border-color : rgba ( 240 , 200 , 123 , .72 ) ; background : rgba ( 173 , 107 , 46 , .28 ) ; }
. utility-btn span { color : #cdb58d ; }
. modal-layer { background : rgba ( 7 , 3 , 2 , .6 ) ; }
. sheet { border-top-color : rgba ( 226 , 181 , 99 , .5 ) ; background : rgba ( 25 , 14 , 10 , .97 ) ; }
. action-item { border-color : rgba ( 220 , 176 , 100 , .24 ) ; background : rgba ( 139 , 82 , 40 , .12 ) ; }
. action-item : hover { border-color : rgba ( 239 , 195 , 113 , .7 ) ; background : rgba ( 163 , 100 , 44 , .24 ) ; }
. action-item span , . sheet-note { color : #c8b18e ; }
. menu-item { border-color : rgba ( 220 , 176 , 100 , .3 ) ; background : linear-gradient ( 145 deg , rgba ( 128 , 73 , 34 , .38 ) , rgba ( 30 , 15 , 9 , .72 ) ) ; box-shadow : inset 0 1 px 0 rgba ( 255 , 224 , 163 , .08 ) , 0 6 px 14 px rgba ( 0 , 0 , 0 , .2 ) ; }
. menu-item : hover , . menu-item : focus-visible { border-color : rgba ( 239 , 195 , 113 , .82 ) ; background : linear-gradient ( 145 deg , rgba ( 164 , 95 , 40 , .52 ) , rgba ( 44 , 20 , 10 , .82 ) ) ; }
. menu-item b { color : #f0c979 ; }
. menu-item small { color : #bda887 ; }
. send-btn { border-color : rgba ( 117 , 186 , 123 , .5 ) ; background : rgba ( 75 , 122 , 73 , .2 ) ; color : #c3e8b9 ; }
. toolbar { background : rgba ( 10 , 6 , 4 , .88 ) ; border-color : rgba ( 228 , 183 , 103 , .32 ) ; }
. toolbar button : hover , . toolbar select : hover { background : rgba ( 171 , 104 , 43 , .28 ) ; }
. toast { background : rgba ( 20 , 10 , 7 , .96 ) ; border-color : rgba ( 224 , 178 , 98 , .42 ) ; }
@ media ( max-height : 460px ) and ( orientation : landscape ) {
body { padding-top : 66 px ; }
. top-status { top : 10 px ; }
. player-status , . player-name { width : 122 px ; }
. player-meters { width : 44 px ; }
. player-name { font-size : 9 px ; }
. party-rail { top : 91 px ; left : 10 px ; width : 122 px ; gap : 4 px ; }
. party-member { min-height : 30 px ; padding-block : 3 px ; }
. party-avatar { width : 22 px ; height : 22 px ; font-size : 10 px ; }
. top-right-controls { top : 9 px ; right : 9 px ; }
. mini-map { width : 108 px ; height : 64 px ; }
. right-rail { top : 82 px ; }
. status-copy { flex-basis : 122 px ; width : 122 px ; }
. quest-pill { top : 84 px ; right : 128 px ; }
. target-card { top : 8 px ; }
. joystick { width : 96 px ; height : 96 px ; left : 18 px ; bottom : 13 px ; }
. combat-cluster { right : 14 px ; bottom : 10 px ; transform : scale ( .82 ) ; transform-origin : bottom right ; }
. context-action { bottom : 88 px ; }
}
@ media ( max-width : 820px ) {
. interaction-head { display : block ; }
. interaction-legend { justify-content : flex-start ; margin-top : 12 px ; }
. flow-grid { grid-template-columns : 1 fr ; }
. detail-grid { grid-template-columns : 1 fr ; gap : 9 px ; }
}
< / style >
< / head >
< body >
< header class = "page-head" >
< div class = "eyebrow" > Mobile ARPG Control HUD / Sketch 001< / div >
< h1 > 暗黑 ARPG 风格 · 横屏 HUD 草图< / h1 >
< p > 按 844×390 横屏手机设计,保持世界画面优先,战斗操作触手可达;底部中央保持留白,低频功能统一收纳到右上角菜单。点击首页控件或下方交互图节点,查看完整的移动端 UI 流程。< / p >
< / header >
< main id = "sketch-canvas" class = "preview-phone" >
< section id = "mobile-hud" class = "variant" >
< div class = "variant-intro" > < h2 > 暗黑 ARPG 横屏 HUD< / h2 > < p > 左上按“名称→头像→HP/MP/资源短条”排列;右上按“背包→小地图→菜单”排列,地图通过点击小地图进入,底部中央保持留白。< / p > < / div >
< div class = "device-wrap" > < div class = "device" data-label = "844 × 390 landscape · 暗黑 ARPG" >
< div class = "scene" >
< div class = "terrain" > < / div > < div class = "hero" data-label = "玩家角色" > < / div > < div class = "mob" > < / div >
< div class = "top-status" data-label = "玩家状态" > < div class = "player-status" > < div class = "player-name" > < span > Azure · 暮光骑士< / span > < span class = "level" > Lv.42< / span > < / div > < button class = "avatar demo-btn" data-panel = "character" data-label = "打开角色状态" > ♜< / button > < div class = "player-meters" > < div class = "meter hp" aria-label = "生命值" > < i > < / i > < / div > < div class = "meter mp" aria-label = "法力值" > < i > < / i > < / div > < div class = "meter resource" aria-label = "职业资源:奥能 68%" > < i > < / i > < / div > < / div > < / div > < / div >
< div class = "party-rail" data-label = "队伍信息:仅组队后显示" > < button class = "party-member demo-btn" data-panel = "party" data-label = "队员 Mira · 点击查看或定位" > < span class = "party-avatar" > M< / span > < span class = "party-copy" > < strong > Mira · 圣骑士< / strong > < span class = "party-hp" > < i style = "width: 83%" > < / i > < / span > < / span > < span class = "party-distance" > 12m< / span > < / button > < button class = "party-member demo-btn" data-panel = "party" data-label = "队员 Ken · 点击查看或定位" > < span class = "party-avatar" > K< / span > < span class = "party-copy" > < strong > Ken · 游侠< / strong > < span class = "party-hp" > < i style = "width: 61%" > < / i > < / span > < / span > < span class = "party-distance" > 34m< / span > < / button > < / div >
< div class = "top-right-controls" data-label = "顶部背包、地图与菜单" > < button class = "utility-btn top-bag demo-btn" data-panel = "inventory" data-label = "打开背包" > ▦< span > 背包< / span > < / button > < button class = "mini-map demo-btn" data-panel = "map" aria-label = "打开地图" data-label = "小地图 / 地图" > < / button > < button class = "utility-btn menu-button demo-btn" data-panel = "more" data-label = "打开菜单" > ☰< span > 菜单< / span > < / button > < / div >
< button class = "target-card demo-btn" data-panel = "target" data-label = "目标板" > < strong > 血狼 · Lv.40< / strong > < small > 敌对目标< / small > < div class = "meter" > < i > < / i > < / div > < / button >
< button class = "quest-pill demo-btn" data-panel = "quest" data-label = "任务追踪" > ◆ 采集狼牙 < b > 2/5< / b > < / button >
< div class = "chat-bubble" data-label = "头顶聊天气泡" > 附近:小心精英怪!< / div >
< button class = "context-action demo-btn" data-toast = "靠近目标后:拾取 / 对话 / 钓鱼 / 进入副本" > ✦ 交互< / button >
< div class = "joystick" data-joystick data-label = "虚拟摇杆" > < div class = "stick" > < / div > < / div >
< div class = "combat-cluster" data-label = "战斗技能区" >
< button class = "skill s1 demo-btn" data-skill = "冰刃" data-toast = "释放技能:冰刃" > ❄< em > 8< / em > < / button >
< button class = "skill s2 demo-btn" data-skill = "冲锋" data-toast = "释放技能:冲锋" > ➤< em > 5< / em > < / button >
< button class = "skill s3 demo-btn" data-skill = "治疗" data-toast = "释放技能:治疗" > ✚< em > 12< / em > < / button >
< button class = "skill s4 demo-btn" data-skill = "药水" data-toast = "使用物品:红色药水" > ♥< em > 3< / em > < / button >
< button class = "skill main demo-btn" data-skill = "普攻" data-toast = "普通攻击" > ⚔< / button >
< / div >
< div class = "floating-cooldown" data-cooldown > 技能冷却中< / div >
< div class = "modal-layer" data-modal > < div class = "sheet" > < div class = "sheet-head" > < div class = "sheet-title-row" > < button class = "back-btn hidden" data-back aria-label = "返回上一级" > ←< / button > < h3 data-sheet-title > 功能< / h3 > < / div > < button class = "close-btn" data-close > × < / button > < / div > < div data-sheet-body > < / div > < / div > < / div >
< / div >
< / div > < / div >
< / section >
< / main >
< section class = "interaction-section" aria-labelledby = "interaction-title" >
< div class = "interaction-head" >
< div >
< div class = "eyebrow" > Interaction map / all screens< / div >
< h2 id = "interaction-title" > 全量 UI 移动端交互图< / h2 >
< p > 每张图表示一条完整操作路径。点击任意节点,查看它的入口位置、触控方式、页面形态和返回路径;任务始终从任务栏进入,组队信息固定显示在左上玩家头像下方。< / p >
< / div >
< div class = "interaction-legend" aria-label = "页面形态图例" >
< span class = "legend-chip" > < i > < / i > 常驻 HUD< / span >
< span class = "legend-chip menu" > < i > < / i > 右上角菜单< / span >
< span class = "legend-chip full" > < i > < / i > 全屏面板< / span >
< span class = "legend-chip modal" > < i > < / i > 场景弹窗< / span >
< / div >
< / div >
< div class = "flow-filter" role = "tablist" aria-label = "交互图筛选" >
< button class = "active" data-flow-filter = "all" role = "tab" aria-selected = "true" > 全部流程< / button >
< button data-flow-filter = "entry" role = "tab" aria-selected = "false" > 进入游戏< / button >
< button data-flow-filter = "combat" role = "tab" aria-selected = "false" > 战斗与任务< / button >
< button data-flow-filter = "character" role = "tab" aria-selected = "false" > 角色与物品< / button >
< button data-flow-filter = "social" role = "tab" aria-selected = "false" > 社交与经济< / button >
< button data-flow-filter = "system" role = "tab" aria-selected = "false" > 系统状态< / button >
< / div >
< div class = "flow-grid" id = "flow-grid" >
< article class = "flow-card" data-flow-group = "entry" >
< div class = "flow-card-head" > < h3 > 进入游戏与角色入口< / h3 > < span class = "flow-index" > 01 · ENTRY< / span > < / div >
< div class = "flow-track" >
< button class = "flow-node full-node" data-detail = "login" > < b > 登录< / b > < small > 账号密码< br > 失败提示< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node full-node" data-detail = "server" > < b > 服务器< / b > < small > 频道选择< br > 连接确认< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node full-node" data-detail = "character_select" > < b > 角色列表< / b > < small > 选择 / 创建< br > 删除 / 改名< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node modal-node" data-detail = "loading" > < b > 加载< / b > < small > 资源加载< br > 进入场景< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node" data-detail = "hud" > < b > 首页 HUD< / b > < small > 开始操作< / small > < / button >
< / div >
< / article >
< article class = "flow-card" data-flow-group = "combat" >
< div class = "flow-card-head" > < h3 > 首页战斗与掉落< / h3 > < span class = "flow-index" > 02 · COMBAT< / span > < / div >
< div class = "flow-track" >
< button class = "flow-node" data-detail = "hud" > < b > 首页 HUD< / b > < small > 左上状态< br > 右上入口< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node" data-detail = "movement" > < b > 移动摇杆< / b > < small > 拖动移动< br > 松手复位< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node" data-detail = "target" > < b > 锁定目标< / b > < small > 点击目标< br > 显示血条< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node" data-detail = "combat_action" > < b > 普攻 / 技能< / b > < small > 点击释放< br > 冷却反馈< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node modal-node" data-detail = "loot" > < b > 掉落 / 交互< / b > < small > 拾取、对话< br > 进入副本< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node" data-detail = "death" > < b > 死亡 / 复活< / b > < small > 战斗失败< br > 回城或复活< / small > < / button >
< / div >
< / article >
< article class = "flow-card" data-flow-group = "combat" >
< div class = "flow-card-head" > < h3 > 任务、地图与副本< / h3 > < span class = "flow-index" > 03 · QUEST< / span > < / div >
< div class = "flow-track" >
< button class = "flow-node" data-detail = "quest" > < b > 任务栏< / b > < small > 首页常驻< br > 直接点击< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node full-node" data-detail = "quest_log" > < b > 任务日志< / b > < small > 列表 / 奖励< br > 任务详情< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node full-node" data-detail = "map" > < b > 小地图 / 大地图< / b > < small > 点击小地图< br > 地图信标< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node" data-detail = "navigation" > < b > 自动寻路< / b > < small > 导航至目标< br > 到达提示< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node modal-node" data-detail = "npc_quest" > < b > NPC 对话< / b > < small > 接取 / 完成< br > 任务奖励< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node modal-node" data-detail = "dungeon_result" > < b > 副本结算< / b > < small > 奖励 / 再次挑战< br > 退出副本< / small > < / button >
< / div >
< / article >
< article class = "flow-card" data-flow-group = "character" >
< div class = "flow-card-head" > < h3 > 角色、装备与技能< / h3 > < span class = "flow-index" > 04 · CHARACTER< / span > < / div >
< div class = "flow-track" >
< button class = "flow-node" data-detail = "character_entry" > < b > 玩家头像< / b > < small > 左上角< br > 唯一入口< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node full-node" data-detail = "character_status" > < b > 角色属性< / b > < small > 等级、属性< br > 成长信息< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node full-node" data-detail = "equipment" > < b > 装备< / b > < small > 穿戴、替换< br > 查看对比< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node full-node" data-detail = "skills" > < b > 技能< / b > < small > 主动、被动< br > 坐骑、表情< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node modal-node" data-detail = "skill_upgrade" > < b > 技能加点< / b > < small > 查看消耗< br > 确认升级< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node modal-node" data-detail = "inspect" > < b > 查看玩家< / b > < small > 点击角色< br > 查看装备< / small > < / button >
< / div >
< / article >
< article class = "flow-card" data-flow-group = "character" >
< div class = "flow-card-head" > < h3 > 背包、物品与仓库< / h3 > < span class = "flow-index" > 05 · ITEMS< / span > < / div >
< div class = "flow-track" >
< button class = "flow-node menu-node" data-detail = "inventory" > < b > 背包< / b > < small > 右上快捷入口< br > 菜单也可进入< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node modal-node" data-detail = "item_tooltip" > < b > 物品详情< / b > < small > 点击选中< br > 长按查看< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node modal-node" data-detail = "item_action" > < b > 使用 / 装备 / 出售< / b > < small > 按钮操作< br > 危险操作确认< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node full-node" data-detail = "safebox" > < b > 仓库< / b > < small > 背包与仓库< br > 左右切换< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node modal-node" data-detail = "select_item" > < b > 任务物品选择< / b > < small > 选择数量< br > 提交确认< / small > < / button >
< / div >
< / article >
< article class = "flow-card" data-flow-group = "social" >
< div class = "flow-card-head" > < h3 > 队伍状态、聊天、好友与公会< / h3 > < span class = "flow-index" > 06 · SOCIAL< / span > < / div >
< div class = "flow-track" >
< button class = "flow-node" data-detail = "party" > < b > 左侧队伍条< / b > < small > 头像下方< br > 成员状态< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node modal-node" data-detail = "party" > < b > 队员信息< / b > < small > 查看 / 定位< br > 离队设置< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node menu-node" data-detail = "social_entry" > < b > 右上菜单< / b > < small > 社交入口< br > 不含队伍< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node full-node" data-detail = "chat" > < b > 聊天< / b > < small > 综合、私聊< br > 队伍、公会< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node full-node" data-detail = "friend" > < b > 好友< / b > < small > 申请、私聊< br > 查看资料< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node full-node" data-detail = "love" > < b > 情侣 / 爱意< / b > < small > 关系信息< br > 互动操作< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node full-node" data-detail = "guild" > < b > 公会< / b > < small > 成员、技能< br > 公告、公会战< / small > < / button >
< / div >
< / article >
< article class = "flow-card" data-flow-group = "social" >
< div class = "flow-card-head" > < h3 > 商店、交易与制作< / h3 > < span class = "flow-index" > 07 · ECONOMY< / span > < / div >
< div class = "flow-track" >
< button class = "flow-node modal-node" data-detail = "commerce_entry" > < b > NPC / 玩家< / b > < small > 靠近后点击< br > 出现交互< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node full-node" data-detail = "shop" > < b > 商店 / 商城< / b > < small > 购买、出售< br > 商城物品< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node full-node" data-detail = "trade" > < b > 玩家交易< / b > < small > 拖入物品< br > 双方确认< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node full-node" data-detail = "private_shop" > < b > 私人商店< / b > < small > 浏览、购买< br > 摆摊管理< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node full-node" data-detail = "cube" > < b > 制作< / b > < small > 放入材料< br > 制作确认< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node modal-node" data-detail = "refine" > < b > 精炼 / 龙魂< / b > < small > 强化结果< br > 失败确认< / small > < / button >
< / div >
< / article >
< article class = "flow-card" data-flow-group = "system" >
< div class = "flow-card-head" > < h3 > 系统设置与异常状态< / h3 > < span class = "flow-index" > 08 · SYSTEM< / span > < / div >
< div class = "flow-track" >
< button class = "flow-node menu-node" data-detail = "system_entry" > < b > 右上菜单< / b > < small > 系统入口< br > 随时可打开< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node full-node" data-detail = "options" > < b > 系统 / 游戏设置< / b > < small > 音量、画面< br > 操作、屏蔽< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node full-node" data-detail = "help" > < b > 帮助< / b > < small > 操作说明< br > 功能说明< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node modal-node" data-detail = "character_switch" > < b > 选择角色< / b > < small > 离开当前角色< br > 二次确认< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node modal-node" data-detail = "logout" > < b > 登出 / 退出< / b > < small > 保存状态< br > 确认离开< / small > < / button > < span class = "flow-arrow" > →< / span >
< button class = "flow-node modal-node" data-detail = "reconnect" > < b > 断线重连< / b > < small > 连接恢复< br > 重新进入场景< / small > < / button >
< / div >
< / article >
< / div >
< aside class = "flow-detail empty" id = "flow-detail" aria-live = "polite" >
< div > < div class = "detail-kicker" > Tap a node< / div > < p > 点击上方任意节点,查看该界面的移动端入口、位置、交互方式和返回路径。< / p > < / div >
< / aside >
< / section >
< div class = "toolbar" id = "sketch-tools" aria-label = "草图工具" >
< span > 横屏预览< / span > < button data-viewport = "phone" > 手机< / button > < button data-viewport = "tablet" > 平板< / button > < button data-viewport = "desktop" > 桌面< / button >
< select id = "theme-switcher" aria-label = "主题" > < option value = "default" > 暗黑铜金< / option > < / select >
< button id = "annotation-toggle" > 标注< / button >
< / div >
< div class = "toast" id = "toast" role = "status" > < / div > < div class = "annotation-label" id = "annotation-label" > < / div >
< script >
const body = document . body ;
const canvas = document . getElementById ( 'sketch-canvas' ) ;
const toast = document . getElementById ( 'toast' ) ;
let toastTimer ;
const featureCatalog = [
[ '♟' , '社交' , 'social' , '聊天 · 好友 · 公会' ] ,
[ '✧' , '成长' , 'growth' , '强化 · 制作 · 龙魂' ] ,
[ '▣' , '活动' , 'adventure' , '副本 · 钓鱼' ] ,
[ '◈' , '商业' , 'market' , '商店 · 交易 · 仓库' ] ,
[ '⚙' , '系统' , 'system' , '设置 · 帮助 · 退出' ]
] ;
const featureCopy = {
inventory : '<p class="sheet-note">装备 / 腰带 / 物品拖放 · 右侧滑出面板,回到战斗时自动收起。</p><div class="action-grid"><button class="action-item demo-btn" data-toast="拖动红药到快捷栏">♥<span>红药 × 12</span></button><button class="action-item demo-btn" data-toast="查看装备">⚔<span>装备</span></button><button class="action-item demo-btn" data-toast="打开腰带">▤<span>腰带</span></button><button class="action-item demo-btn" data-toast="整理背包">✦<span>整理</span></button></div>' ,
character : '<p class="sheet-note">头像入口承载 HP / MP、状态、装备、技能、坐骑和表情动作。</p><div class="action-grid"><button class="action-item demo-btn" data-toast="打开状态页">♥<span>状态</span></button><button class="action-item demo-btn" data-toast="查看装备">⚔<span>装备</span></button><button class="action-item demo-btn" data-panel="skills"><b>✦</b><span>技能</span></button><button class="action-item demo-btn" data-panel="mount"><b>♞</b><span>坐骑</span></button><button class="action-item demo-btn" data-panel="emote"><b>☺</b><span>表情</span></button><button class="action-item demo-btn" data-toast="打开称号页">◆<span>称号</span></button></div>' ,
quest : '<p class="sheet-note">任务追踪使用半透明卡片;点击任务行进入日志,地图信标从卡片右上角进入。</p><div class="action-grid"><button class="action-item demo-btn" data-toast="追踪:采集狼牙">◆<span>追踪</span></button><button class="action-item demo-btn" data-toast="打开任务日志">▤<span>日志</span></button><button class="action-item demo-btn" data-toast="显示地图信标">⌖<span>导航</span></button><button class="action-item demo-btn" data-toast="收起任务条">× <span>隐藏</span></button></div>' ,
map : '<p class="sheet-note">小地图点击后升起全屏地图,保留副本目的地罗盘和任务信标。</p><div class="action-grid"><button class="action-item demo-btn" data-toast="地图缩放 +">+ <span>放大</span></button><button class="action-item demo-btn" data-toast="地图缩放 -">− <span>缩小</span></button><button class="action-item demo-btn" data-toast="查看世界地图">⌖<span>世界地图</span></button><button class="action-item demo-btn" data-toast="回到玩家位置">●<span>定位</span></button></div>' ,
target : '<p class="sheet-note">目标板支持切换目标、查看血量和情境动作;按住目标卡可进入更多交互。</p><div class="action-grid"><button class="action-item demo-btn" data-toast="切换目标">⇄<span>切换</span></button><button class="action-item demo-btn" data-toast="开始自动攻击">⚔<span>攻击</span></button><button class="action-item demo-btn" data-toast="打开目标信息">? <span>信息</span></button><button class="action-item demo-btn" data-toast="取消目标">× <span>清除</span></button></div>' ,
chat : '<div class="chat-list"><div class="chat-line"><strong>[队伍]</strong> Mira:副本入口在北门。</div><div class="chat-line"><strong>[附近]</strong> 你:收到,马上到。</div><div class="chat-line"><strong>[公会]</strong> Ken:今晚 20:00 公会战。</div></div><div class="chat-compose"><input placeholder="输入消息…"><button class="send-btn" data-send>发送</button></div>' ,
party : '<p class="sheet-note">队伍信息从左上玩家头像下方的队员条进入;未组队时该区域自动隐藏。</p><div class="action-grid"><button class="action-item demo-btn" data-toast="定位队员">⌖<span>定位</span></button><button class="action-item demo-btn" data-toast="查看队员资料">♟<span>资料</span></button><button class="action-item demo-btn" data-toast="打开队伍设置">⚙<span>设置</span></button><button class="action-item demo-btn" data-toast="离开队伍确认">× <span>离队</span></button></div>' ,
more : '<p class="sheet-note">菜单只保留五个一级入口;角色相关功能点击左上头像进入,背包、小地图和任务仍从首页直接进入。</p><div class="menu-grid">' + featureCatalog . map ( ( [ icon , name , key , note ] ) => ` <button class="menu-item demo-btn" data-panel=" ${ key } " data-toast="打开 ${ name } "><b> ${ icon } </b><span> ${ name } </span><small> ${ note } </small></button> ` ) . join ( '' ) + '</div>' ,
guild : '<p class="sheet-note">公会信息、成员、会徽、公会战和公告从左侧社交轨进入。</p><div class="action-grid"><button class="action-item demo-btn" data-toast="打开公会成员">♟<span>成员</span></button><button class="action-item demo-btn" data-toast="查看公会技能">✧<span>技能</span></button><button class="action-item demo-btn" data-toast="查看公会会徽">✦<span>会徽</span></button><button class="action-item demo-btn" data-toast="查看公会战">⚔<span>公会战</span></button></div>'
} ;
Object . assign ( featureCopy , {
social : '<p class="sheet-note">社交菜单只保留聊天、好友、公会、情侣和观战;队伍状态固定在左上玩家头像下方。</p><div class="action-grid"><button class="action-item demo-btn" data-panel="chat"><b>☵</b><span>聊天</span></button><button class="action-item demo-btn" data-panel="friend"><b>♧</b><span>好友</span></button><button class="action-item demo-btn" data-panel="guild"><b>♜</b><span>公会</span></button><button class="action-item demo-btn" data-panel="love"><b>♥</b><span>情侣</span></button><button class="action-item demo-btn" data-panel="observer"><b>◉</b><span>观战</span></button></div>' ,
growth : '<p class="sheet-note">成长功能统一处理角色长期养成;技能、坐骑和表情仍归入角色页面,避免重复入口。</p><div class="action-grid"><button class="action-item demo-btn" data-panel="refine"><b>⚒</b><span>精炼</span></button><button class="action-item demo-btn" data-panel="dragon"><b>✧</b><span>龙魂</span></button><button class="action-item demo-btn" data-panel="cube"><b>▣</b><span>制作</span></button></div>' ,
adventure : '<p class="sheet-note">活动通过任务追踪、场景交互或这里进入;地图仍只通过右上小地图打开。</p><div class="action-grid"><button class="action-item demo-btn" data-panel="dungeon"><b>▣</b><span>副本</span></button><button class="action-item demo-btn" data-panel="fishing"><b>♨</b><span>钓鱼</span></button></div>' ,
market : '<p class="sheet-note">涉及货币与物品流转的功能统一归入商业;背包仍在首页右上角单独打开。</p><div class="action-grid"><button class="action-item demo-btn" data-panel="shop"><b>▤</b><span>商店</span></button><button class="action-item demo-btn" data-panel="mall"><b>◈</b><span>商城</span></button><button class="action-item demo-btn" data-panel="trade"><b>⇄</b><span>交易</span></button><button class="action-item demo-btn" data-panel="private_shop"><b>▥</b><span>私人商店</span></button><button class="action-item demo-btn" data-panel="safebox"><b>▤</b><span>仓库</span></button><button class="action-item demo-btn" data-panel="exchange"><b>↔</b><span>兑换</span></button></div>' ,
friend : '<p class="sheet-note">好友与申请使用统一列表;选择好友后可私聊、查看资料或邀请组队。</p><div class="action-grid"><button class="action-item demo-btn" data-toast="查看好友申请">+ <span>申请</span></button><button class="action-item demo-btn" data-toast="打开好友资料">♧<span>资料</span></button><button class="action-item demo-btn" data-toast="发起私聊">☵<span>私聊</span></button><button class="action-item demo-btn" data-toast="邀请组队">♟<span>组队</span></button></div>' ,
love : '<p class="sheet-note">情侣与爱意信息放在社交分组内,避免成为首页固定入口。</p><div class="action-grid"><button class="action-item demo-btn" data-toast="查看关系信息">♥<span>关系</span></button><button class="action-item demo-btn" data-toast="查看爱意等级">✦<span>等级</span></button><button class="action-item demo-btn" data-toast="发送互动">☺<span>互动</span></button><button class="action-item demo-btn" data-toast="打开情侣设置">⚙<span>设置</span></button></div>' ,
skills : '<p class="sheet-note">技能分为主动、被动、坐骑和表情动作;战斗中的技能按钮由此处配置。</p><div class="action-grid"><button class="action-item demo-btn" data-toast="打开主动技能">❄<span>主动</span></button><button class="action-item demo-btn" data-toast="打开被动技能">✧<span>被动</span></button><button class="action-item demo-btn" data-toast="打开坐骑技能">♞<span>坐骑</span></button><button class="action-item demo-btn" data-toast="打开表情动作">☺<span>表情</span></button></div>' ,
shop : '<p class="sheet-note">NPC 商店支持购买和出售;商城使用同一套商品详情与确认交互。</p><div class="action-grid"><button class="action-item demo-btn" data-toast="打开购买列表">+ <span>购买</span></button><button class="action-item demo-btn" data-toast="打开出售列表">− <span>出售</span></button><button class="action-item demo-btn" data-toast="查看商品详情">? <span>详情</span></button><button class="action-item demo-btn" data-toast="确认交易金额">✓<span>确认</span></button></div>' ,
mall : '<p class="sheet-note">商城从菜单进入,购买前查看价格、限购和获得方式,支付动作使用二次确认。</p><div class="action-grid"><button class="action-item demo-btn" data-toast="浏览商城分类">▤<span>分类</span></button><button class="action-item demo-btn" data-toast="查看商品详情">? <span>详情</span></button><button class="action-item demo-btn" data-toast="加入购物车">+ <span>加入</span></button><button class="action-item demo-btn" data-toast="确认购买">✓<span>购买</span></button></div>' ,
safebox : '<p class="sheet-note">仓库采用背包与仓库左右切换;长按物品看详情,拖动完成存入或取出。</p><div class="action-grid"><button class="action-item demo-btn" data-toast="存入仓库">↓<span>存入</span></button><button class="action-item demo-btn" data-toast="取出物品">↑<span>取出</span></button><button class="action-item demo-btn" data-toast="整理仓库">✦<span>整理</span></button><button class="action-item demo-btn" data-toast="切换商城仓库">▤<span>商城仓库</span></button></div>' ,
trade : '<p class="sheet-note">玩家交易通过点击玩家或交互按钮进入;双方放入物品后分别确认,避免误操作。</p><div class="action-grid"><button class="action-item demo-btn" data-toast="拖入交易物品">⇄<span>放入</span></button><button class="action-item demo-btn" data-toast="查看对方物品">? <span>查看</span></button><button class="action-item demo-btn" data-toast="锁定交易内容">▣<span>锁定</span></button><button class="action-item demo-btn" data-toast="等待对方确认">✓<span>确认</span></button></div>' ,
private _shop : '<p class="sheet-note">私人商店通过点击摊位或玩家进入;浏览和摆摊管理共用一个全屏面板。</p><div class="action-grid"><button class="action-item demo-btn" data-toast="浏览私人商店">▤<span>浏览</span></button><button class="action-item demo-btn" data-toast="购买摊位物品">+ <span>购买</span></button><button class="action-item demo-btn" data-toast="打开摆摊管理">⚒<span>管理</span></button><button class="action-item demo-btn" data-toast="关闭私人商店">× <span>关闭</span></button></div>' ,
exchange : '<p class="sheet-note">兑换界面展示兑换材料、产出和数量;滑动数量后点击兑换并确认。</p><div class="action-grid"><button class="action-item demo-btn" data-toast="选择兑换材料">▥<span>材料</span></button><button class="action-item demo-btn" data-toast="调整兑换数量">↔<span>数量</span></button><button class="action-item demo-btn" data-toast="查看兑换产出">✦<span>产出</span></button><button class="action-item demo-btn" data-toast="确认兑换">✓<span>兑换</span></button></div>' ,
cube : '<p class="sheet-note">Cube 制作将材料槽放在中央,底部只保留一个主要制作按钮,制作完成后弹出结果。</p><div class="action-grid"><button class="action-item demo-btn" data-toast="选择制作配方">▤<span>配方</span></button><button class="action-item demo-btn" data-toast="放入制作材料">+ <span>材料</span></button><button class="action-item demo-btn" data-toast="查看制作结果">? <span>预览</span></button><button class="action-item demo-btn" data-toast="确认制作">⚒<span>制作</span></button></div>' ,
refine : '<p class="sheet-note">精炼和龙魂使用同一套强化确认流程:选择装备、查看成功率和消耗,再确认结果。</p><div class="action-grid"><button class="action-item demo-btn" data-toast="选择强化装备">⚔<span>装备</span></button><button class="action-item demo-btn" data-toast="查看强化材料">▥<span>材料</span></button><button class="action-item demo-btn" data-toast="查看成功率">% <span>成功率</span></button><button class="action-item demo-btn" data-toast="确认强化">✧<span>强化</span></button></div>' ,
dragon : '<p class="sheet-note">龙魂属于制作与强化分支,保留独立页面入口但不放在首页常驻 HUD。</p><div class="action-grid"><button class="action-item demo-btn" data-toast="查看龙魂槽位">✧<span>槽位</span></button><button class="action-item demo-btn" data-toast="选择龙魂">◇<span>选择</span></button><button class="action-item demo-btn" data-toast="查看龙魂属性">? <span>属性</span></button><button class="action-item demo-btn" data-toast="确认镶嵌">✓<span>镶嵌</span></button></div>' ,
fishing : '<p class="sheet-note">钓鱼是场景情境交互:靠近钓点后出现交互按钮,完成小游戏后返回场景。</p><div class="action-grid"><button class="action-item demo-btn" data-toast="开始钓鱼">♨<span>开始</span></button><button class="action-item demo-btn" data-toast="收杆">↗<span>收杆</span></button><button class="action-item demo-btn" data-toast="查看鱼获">▥<span>鱼获</span></button><button class="action-item demo-btn" data-toast="退出钓鱼">× <span>退出</span></button></div>' ,
dungeon : '<p class="sheet-note">副本入口通过任务导航或场景交互按钮进入;副本内显示目标罗盘,完成后进入结算。</p><div class="action-grid"><button class="action-item demo-btn" data-toast="查看副本目标">⌖<span>目标</span></button><button class="action-item demo-btn" data-toast="查看队伍状态">♟<span>队伍</span></button><button class="action-item demo-btn" data-toast="退出副本">× <span>退出</span></button><button class="action-item demo-btn" data-toast="开始副本">▣<span>进入</span></button></div>' ,
mount : '<p class="sheet-note">坐骑从技能页面配置,也可在场景中通过情境按钮召唤和收起。</p><div class="action-grid"><button class="action-item demo-btn" data-toast="召唤坐骑">♞<span>召唤</span></button><button class="action-item demo-btn" data-toast="打开坐骑技能">✧<span>技能</span></button><button class="action-item demo-btn" data-toast="更换坐骑">⇄<span>更换</span></button><button class="action-item demo-btn" data-toast="收起坐骑">× <span>收起</span></button></div>' ,
observer : '<p class="sheet-note">观战从社交菜单或队员资料进入,观战时隐藏自身战斗操作,保留退出观战按钮。</p><div class="action-grid"><button class="action-item demo-btn" data-toast="选择观战对象">◉<span>对象</span></button><button class="action-item demo-btn" data-toast="切换观战视角">↔<span>视角</span></button><button class="action-item demo-btn" data-toast="查看战斗信息">? <span>信息</span></button><button class="action-item demo-btn" data-toast="退出观战">× <span>退出</span></button></div>' ,
emote : '<p class="sheet-note">表情动作从技能菜单配置,战斗中不占常驻按钮;选择后在角色头顶显示反馈。</p><div class="action-grid"><button class="action-item demo-btn" data-toast="播放表情:挥手">☺<span>挥手</span></button><button class="action-item demo-btn" data-toast="播放表情:欢呼">✦<span>欢呼</span></button><button class="action-item demo-btn" data-toast="播放动作:坐下">♟<span>动作</span></button><button class="action-item demo-btn" data-toast="设置快捷表情">+ <span>配置</span></button></div>' ,
system : '<p class="sheet-note">系统菜单从右上角进入,采用右侧抽屉;设置和帮助进入全屏面板,离开游戏使用确认弹窗。</p><div class="action-grid"><button class="action-item demo-btn" data-panel="options"><b>⚙</b><span>设置</span></button><button class="action-item demo-btn" data-panel="help"><b>?</b><span>帮助</span></button><button class="action-item demo-btn" data-toast="打开角色选择">♜<span>角色</span></button><button class="action-item demo-btn" data-toast="打开退出确认">× <span>退出</span></button></div>' ,
options : '<p class="sheet-note">设置分为音频、画面、操作和社交屏蔽;滑动条与开关都在全屏面板中完成。</p><div class="action-grid"><button class="action-item demo-btn" data-toast="调整音乐音量">♫<span>音乐</span></button><button class="action-item demo-btn" data-toast="调整音效音量">♪<span>音效</span></button><button class="action-item demo-btn" data-toast="调整镜头距离">◎<span>镜头</span></button><button class="action-item demo-btn" data-toast="保存设置">✓<span>保存</span></button></div>' ,
help : '<p class="sheet-note">帮助按移动、战斗、任务、社交和系统分类,使用短卡片说明,支持上下滚动。</p><div class="action-grid"><button class="action-item demo-btn" data-toast="查看移动说明">↕<span>移动</span></button><button class="action-item demo-btn" data-toast="查看战斗说明">⚔<span>战斗</span></button><button class="action-item demo-btn" data-toast="查看系统说明">⚙<span>系统</span></button><button class="action-item demo-btn" data-toast="查看快捷操作">✦<span>快捷操作</span></button></div>' ,
reconnect : '<p class="sheet-note">断线时覆盖场景显示连接状态;恢复成功后自动回到原位置,失败时提供重试或返回角色列表。</p><div class="action-grid"><button class="action-item demo-btn" data-toast="重新连接中">↻<span>重试</span></button><button class="action-item demo-btn" data-toast="查看网络状态">⌁<span>网络</span></button><button class="action-item demo-btn" data-toast="返回角色列表">♜<span>角色列表</span></button><button class="action-item demo-btn" data-toast="关闭提示">× <span>关闭</span></button></div>'
} ) ;
const titles = { inventory : '背包' , character : '角色' , social : '社交' , growth : '成长' , adventure : '活动' , market : '商业' , skills : '技能' , quest : '任务' , map : '地图' , party : '社交 / 队伍' , friend : '好友' , love : '情侣 / 爱意' , guild : '公会' , target : '目标' , chat : '聊天' , shop : '商店' , mall : '商城' , safebox : '仓库' , trade : '玩家交易' , private _shop : '私人商店' , exchange : '兑换' , cube : 'Cube 制作' , refine : '精炼 / 强化' , dragon : '龙魂' , fishing : '钓鱼' , dungeon : '副本' , mount : '坐骑' , observer : '观战' , emote : '表情动作' , system : '系统' , help : '帮助' , options : '设置' , more : '功能菜单' } ;
const flowDetails = {
login : { kind : '全屏入口' , title : '登录界面' , entry : '启动游戏后全屏显示;账号密码输入区保持在横屏安全区中央。' , touch : '点击输入框唤起键盘,点击登录提交;错误信息在表单下方即时提示。' , back : '登录成功进入服务器选择;失败留在当前页并保留已输入账号。' , note : '不与战斗 HUD 共存。' } ,
server : { kind : '全屏入口' , title : '服务器 / 频道选择' , entry : '登录成功后进入;服务器列表居中,推荐频道置顶。' , touch : '点击频道选中,再点击进入;网络状态和人数使用标签显示。' , back : '返回登录界面,或进入角色列表。' , note : '连接失败直接复用断线重连提示。' } ,
character _select : { kind : '全屏入口' , title : '角色列表与角色管理' , entry : '服务器选择后进入;角色卡横向排列。' , touch : '点击角色选中,点击创建、删除、改名;删除和改名必须二次确认。' , back : '返回服务器选择或进入加载界面。' , note : '阵营、职业和等级放在角色卡内,不进入战斗 HUD。' } ,
loading : { kind : '状态弹窗' , title : '加载界面' , entry : '角色进入场景、切换地图或副本时覆盖全屏。' , touch : '无需操作;加载失败显示重试按钮。' , back : '加载完成进入首页 HUD,异常时转断线重连。' , note : '避免用户在资源未完成时点击世界。' } ,
hud : { kind : '常驻 HUD' , title : '首页 HUD' , entry : '进入场景后的默认页面;世界画面优先。' , touch : '左上头像看角色,右上背包/小地图/菜单,任务栏直接进入任务,底部两侧完成移动和战斗。' , back : '所有功能面板关闭后回到这里。' , note : '名称在头像顶部,HP/MP 条在头像下方且与头像同宽。' } ,
movement : { kind : '常驻 HUD' , title : '移动摇杆' , entry : '首页左下角,仅作为战斗操作控件。' , touch : '按住并拖动控制方向,松手复位;支持边缘拖动防止拇指滑出。' , back : '没有独立页面,松手即返回待机状态。' , note : '底部中央保持空白,不放功能菜单。' } ,
target : { kind : '常驻 HUD' , title : '目标选择与目标板' , entry : '点击敌人、NPC 或目标板触发;目标板位于顶部中央。' , touch : '点击切换目标,长按查看信息;普通攻击和技能自动作用于当前目标。' , back : '点击空白清除目标,或选择新目标。' , note : '目标血条不与玩家血条混淆。' } ,
combat _action : { kind : '常驻 HUD' , title : '普通攻击与技能' , entry : '首页右下角环形战斗盘。' , touch : '点击普攻;点击技能释放;长按可预览需要瞄准的技能;冷却用灰度和数字反馈。' , back : '释放完成回到战斗状态,技能配置从菜单进入。' , note : '药水可作为战斗盘上的物品按钮,不另设底部功能栏。' } ,
loot : { kind : '场景弹窗' , title : '掉落物与情境交互' , entry : '靠近掉落物、NPC、钓点或副本入口时,在角色附近出现交互按钮。' , touch : '点击拾取、对话、钓鱼或进入;掉落物名称显示在世界中并支持点击。' , back : '动作完成回到场景;物品自动进入背包并给出短暂提示。' , note : '不在首页固定显示拾取菜单。' } ,
death : { kind : '场景弹窗' , title : '死亡与复活' , entry : 'HP 归零后覆盖场景中央。' , touch : '点击原地复活、回城或等待;危险选项二次确认。' , back : '复活后回到首页 HUD,回城后进入加载流程。' , note : '死亡时暂时禁用摇杆和技能按钮。' } ,
quest : { kind : '常驻 HUD' , title : '任务追踪栏' , entry : '首页上方常驻显示,不放入右上菜单。' , touch : '点击任务行进入日志;点击目标或导航标记进入地图/自动寻路。' , back : '关闭任务日志后回到首页,任务进度仍保留。' , note : '任务栏是任务系统的唯一首页快捷入口。' } ,
quest _log : { kind : '全屏面板' , title : '任务日志' , entry : '点击任务追踪栏打开;采用列表 + 详情布局。' , touch : '点击任务切换详情,滚动查看奖励;点击追踪更新首页任务栏。' , back : '关闭后返回首页 HUD,不改变当前追踪任务。' , note : '任务地图标记从详情页进入。' } ,
map : { kind : '全屏面板' , title : '小地图与大地图' , entry : '点击右上小地图进入;没有独立地图按钮。' , touch : '双指缩放、拖动地图、点击信标导航,点击定位回到玩家位置。' , back : '点击关闭或系统返回回到首页 HUD。' , note : '背包已替代原地图快捷按钮。' } ,
navigation : { kind : '常驻 + 场景' , title : '自动寻路' , entry : '从任务详情、地图信标或 NPC 目标触发。' , touch : '点击导航开始;到达后显示交互按钮;移动摇杆可随时接管。' , back : '手动移动、点击停止或到达目的地后结束。' , note : '不额外占用常驻 HUD 按钮。' } ,
npc _quest : { kind : '场景弹窗' , title : 'NPC 对话与任务' , entry : '靠近 NPC 后点击角色或情境交互按钮。' , touch : '点击对话选项;接取/完成任务使用明确的主按钮。' , back : '关闭对话回到场景;完成后刷新任务栏和奖励提示。' , note : '商店、仓库和任务可以在同一 NPC 对话入口分流。' } ,
dungeon _result : { kind : '场景弹窗' , title : '副本结算' , entry : '副本完成或失败后自动弹出。' , touch : '滚动查看奖励,点击再次挑战或退出;奖励领取使用主按钮。' , back : '退出回到场景,或再次挑战进入加载界面。' , note : '副本内保留目标罗盘和队伍状态。' } ,
character _entry : { kind : '头像入口' , title : '角色功能入口' , entry : '只点击左上角玩家头像进入。' , touch : '点击头像进入角色页,在其中切换属性、装备、技能、坐骑和外观。' , back : '关闭角色面板回到首页 HUD。' , note : '角色不出现在右上菜单。' } ,
character _status : { kind : '全屏面板' , title : '角色属性' , entry : '左上头像 → 角色属性。' , touch : '左右切换属性分类,滚动查看等级、属性、经验和资源。' , back : '返回角色入口,再返回首页。' , note : '战斗中只展示精简状态条。' } ,
equipment : { kind : '全屏面板' , title : '装备界面' , entry : '左上头像 → 角色 → 装备。' , touch : '点击装备槽查看,长按物品详情,拖动穿戴或替换。' , back : '关闭装备页返回角色或首页。' , note : '物品对比使用上下对照卡片。' } ,
skills : { kind : '全屏面板' , title : '技能与快捷栏配置' , entry : '左上头像 → 角色 → 技能。' , touch : '点击技能查看说明,长按查看数值,拖动到首页技能槽。' , back : '保存配置后回到首页,技能按钮即时更新。' , note : '主动、被动、坐骑、表情分标签显示。' } ,
skill _upgrade : { kind : '场景弹窗' , title : '技能升级 / 加点' , entry : '技能详情页点击升级。' , touch : '点击加点预览消耗,再点击确认;资源不足显示原因。' , back : '确认后刷新技能等级,取消回到技能详情。' , note : '升级不可误触,必须二次确认。' } ,
inspect : { kind : '场景弹窗' , title : '查看其他玩家装备' , entry : '点击场景玩家后选择查看,或从队伍/好友资料进入。' , touch : '点击玩家资料页签查看装备、等级和公会;不可修改对方数据。' , back : '关闭资料卡回到场景或社交列表。' , note : '交易、私聊、组队使用同一上下文操作栏。' } ,
inventory : { kind : '全屏面板' , title : '背包' , entry : '右上背包快捷按钮或菜单 → 背包。' , touch : '点击选中,长按详情,拖动整理/装备/放入快捷栏;使用和出售用明确按钮。' , back : '关闭返回首页,物品操作结果用 toast 提示。' , note : '腰带、装备栏、任务物品作为背包内页。' } ,
item _tooltip : { kind : '场景弹窗' , title : '物品详情提示' , entry : '背包、仓库、商店或掉落物点击/长按。' , touch : '点击物品查看详情;长按保持详情卡,点击操作按钮执行动作。' , back : '点空白或返回关闭详情卡。' , note : '详情卡不遮挡关键操作区域。' } ,
item _action : { kind : '场景弹窗' , title : '使用、装备、出售' , entry : '物品详情卡底部操作区。' , touch : '点击主操作,数量类动作弹出数量选择;出售和稀有物品使用二次确认。' , back : '完成后刷新背包,取消回到详情卡。' , note : '拖放和点击操作同时支持。' } ,
safebox : { kind : '全屏面板' , title : '仓库与扩展物品栏' , entry : '菜单 → 背包 → 仓库,或靠近仓库 NPC 后进入。' , touch : '左右切换背包/仓库/商城仓库;拖动物品存入或取出。' , back : '关闭面板返回 NPC 对话或首页。' , note : '私人商店背包沿用同一物品组件。' } ,
select _item : { kind : '场景弹窗' , title : '任务物品选择' , entry : '任务奖励、提交材料或任务详情中的选择按钮。' , touch : '点击物品,滑动或加减调整数量,提交前显示所需数量。' , back : '确认后回到任务流程,取消不消耗物品。' , note : '数量选择不使用难操作的小型输入框。' } ,
social _entry : { kind : '右上角菜单' , title : '社交功能入口' , entry : '右上菜单 → 社交;队伍不出现在社交菜单。' , touch : '点击聊天、好友、情侣、公会或观战进入对应页面。' , back : '关闭社交页面回到首页 HUD。' , note : '组队信息固定显示在左上玩家头像下方。' } ,
chat : { kind : '全屏面板' , title : '聊天与战斗日志' , entry : '菜单 → 社交 → 聊天;新消息以短暂提示或气泡显示。' , touch : '点击频道切换,点击输入框唤起键盘,长按消息进行复制/私聊。' , back : '关闭回到首页,未读数显示在菜单角标。' , note : '综合、私聊、系统、战斗、队伍、公会统一在频道中。' } ,
party : { kind : '左侧 HUD' , title : '队伍信息' , entry : '组队后显示在左上玩家头像下方;未组队时自动隐藏。' , touch : '点击队员查看资料或定位,长按打开队伍设置;邀请通过好友或玩家交互触发。' , back : '关闭队员卡回到首页,队伍血量和距离继续常驻显示。' , note : '队伍不出现在社交菜单,也不设置独立首页按钮。' } ,
friend : { kind : '全屏面板' , title : '好友与好友申请' , entry : '菜单 → 社交 → 好友,或点击玩家资料。' , touch : '点击申请、同意、私聊、查看资料;列表上下滚动。' , back : '返回社交菜单或首页。' , note : '好友申请通过临时弹窗提醒。' } ,
love : { kind : '全屏面板' , title : '情侣 / 爱意' , entry : '菜单 → 社交 → 情侣 / 爱意。' , touch : '点击关系资料和互动动作;关键关系操作使用确认。' , back : '关闭返回社交或首页。' , note : '低频关系信息不占用首页 HUD。' } ,
guild : { kind : '全屏面板' , title : '公会' , entry : '菜单 → 社交 → 公会。' , touch : '顶部标签切换成员、技能、公告、公会战和会徽;列表支持滚动。' , back : '返回社交菜单或首页。' , note : '公会邀请通过临时确认弹窗进入。' } ,
commerce _entry : { kind : '场景弹窗' , title : 'NPC / 玩家情境入口' , entry : '靠近 NPC、玩家、摊位或制作设施后显示交互按钮。' , touch : '点击交互按钮,从上下文菜单选择对话、商店、交易、仓库或制作。' , back : '关闭上下文菜单返回场景。' , note : '只在附近有可交互对象时出现。' } ,
shop : { kind : '全屏面板' , title : 'NPC 商店与商城' , entry : 'NPC 交互 → 商店,或菜单 → 商店 / 商城。' , touch : '点击商品查看详情,数量选择后购买/出售;支付前二次确认。' , back : '返回 NPC 对话或首页。' , note : '商城使用同一套物品详情和确认组件。' } ,
trade : { kind : '全屏面板' , title : '玩家交易' , entry : '点击玩家 → 交易,或场景交互菜单进入。' , touch : '拖动物品到交易栏,双方锁定后分别确认;内容变化时重新确认。' , back : '取消交易返回场景,已锁定物品自动退回。' , note : '交易金额和物品变化必须明显反馈。' } ,
private _shop : { kind : '全屏面板' , title : '私人商店' , entry : '点击玩家摊位进入浏览;自己的摊位从菜单管理。' , touch : '点击商品购买,管理页拖动物品上架;上架/下架有确认提示。' , back : '关闭返回场景,摊位状态保留。' , note : '使用背包的物品组件,减少学习成本。' } ,
cube : { kind : '全屏面板' , title : 'Cube 制作' , entry : '菜单 → 交易与制作 → Cube,或靠近制作设施。' , touch : '选择配方,拖入材料,查看产出预览,点击制作。' , back : '结果弹窗关闭后回到制作页或首页。' , note : '制作按钮放在面板底部,不占用首页底部。' } ,
refine : { kind : '全屏 + 弹窗' , title : '精炼 / 强化 / 龙魂' , entry : '菜单 → 交易与制作 → 精炼,龙魂作为同组页签。' , touch : '选择装备、查看材料和成功率,确认强化;结果用中央弹窗反馈。' , back : '返回制作菜单或首页,失败也明确显示消耗。' , note : '龙魂当前代码入口需单独开启后再接入正式菜单。' } ,
system _entry : { kind : '右上角菜单' , title : '系统入口' , entry : '右上角菜单最外侧按钮。' , touch : '点击菜单打开右侧抽屉,点击遮罩或关闭按钮收起。' , back : '回到首页 HUD,世界输入恢复。' , note : '菜单打开时暂停战斗点击,防止误操作。' } ,
options : { kind : '全屏面板' , title : '系统设置与游戏设置' , entry : '菜单 → 系统 → 设置。' , touch : '滑动音量、镜头距离等,点击开关控制 PK、名称、伤害数字和屏蔽项。' , back : '即时保存后返回菜单或首页。' , note : '设置项按音频、画面、操作、社交分组。' } ,
help : { kind : '全屏面板' , title : '帮助界面' , entry : '菜单 → 系统 → 帮助。' , touch : '点击分类卡片,上下滚动查看说明;可从快捷入口直接跳转。' , back : '返回系统菜单或首页。' , note : '帮助文案与实际移动端手势保持一致。' } ,
character _switch : { kind : '全屏 + 弹窗' , title : '选择角色' , entry : '菜单 → 系统 → 选择角色。' , touch : '点击后先保存并弹出确认,再回到角色列表。' , back : '取消确认继续当前角色。' , note : '切换角色触发加载流程。' } ,
logout : { kind : '场景弹窗' , title : '登出 / 退出游戏' , entry : '菜单 → 系统 → 登出或退出。' , touch : '点击后弹出确认,显示未保存状态;确认后返回登录或关闭游戏。' , back : '取消后回到系统菜单。' , note : '属于高风险操作,必须二次确认。' } ,
reconnect : { kind : '状态弹窗' , title : '断线重连' , entry : '网络中断时覆盖当前页面,不清除当前 HUD 状态。' , touch : '自动重试;用户可点击重试、查看网络或返回角色列表。' , back : '连接成功恢复原场景,失败则进入角色列表。' , note : '恢复后保留任务追踪、目标和队伍状态。' }
} ;
function showToast ( message ) {
toast . textContent = message ;
toast . classList . add ( 'show' ) ;
clearTimeout ( toastTimer ) ;
toastTimer = setTimeout ( ( ) => toast . classList . remove ( 'show' ) , 1400 ) ;
}
function currentVariantRoot ( el ) { return el . closest ( '.variant' ) ; }
function renderPanel ( root , key , history = [ ] ) {
const modal = root . querySelector ( '[data-modal]' ) ;
const title = root . querySelector ( '[data-sheet-title]' ) ;
const bodyNode = root . querySelector ( '[data-sheet-body]' ) ;
const back = root . querySelector ( '[data-back]' ) ;
title . textContent = titles [ key ] || key ;
bodyNode . innerHTML = featureCopy [ key ] || ` <p class="sheet-note"> ${ titles [ key ] || key } 面板草图。</p> ` ;
modal . dataset . current = key ;
modal . dataset . history = JSON . stringify ( history ) ;
back . classList . toggle ( 'hidden' , history . length === 0 ) ;
bindDynamic ( bodyNode ) ;
}
function openPanel ( trigger , key ) {
const root = currentVariantRoot ( trigger ) ;
const modal = root . querySelector ( '[data-modal]' ) ;
const history = modal . classList . contains ( 'open' ) && modal . dataset . current ? JSON . parse ( modal . dataset . history || '[]' ) : [ ] ;
if ( modal . classList . contains ( 'open' ) && modal . dataset . current && modal . dataset . current !== key ) history . push ( modal . dataset . current ) ;
renderPanel ( root , key , history ) ;
modal . classList . add ( 'open' ) ;
}
function closePanel ( trigger ) {
const modal = trigger . closest ( '[data-modal]' ) ;
modal . classList . remove ( 'open' ) ;
modal . dataset . current = '' ;
modal . dataset . history = '[]' ;
}
function backPanel ( trigger ) {
const modal = trigger . closest ( '[data-modal]' ) ;
const history = JSON . parse ( modal . dataset . history || '[]' ) ;
const key = history . pop ( ) ;
if ( ! key ) return ;
renderPanel ( trigger . closest ( '.variant' ) , key , history ) ;
}
function bindDynamic ( scope = document ) {
scope . querySelectorAll ( '[data-toast]' ) . forEach ( btn => {
if ( btn . dataset . bound ) return ;
btn . dataset . bound = '1' ;
btn . addEventListener ( 'click' , ( ) => showToast ( btn . dataset . toast ) ) ;
} ) ;
const send = scope . querySelector ( '[data-send]' ) ;
if ( send && ! send . dataset . bound ) {
send . dataset . bound = '1' ;
send . addEventListener ( 'click' , ( ) => {
const input = scope . querySelector ( 'input' ) ;
if ( ! input . value . trim ( ) ) { showToast ( '请输入聊天内容' ) ; return ; }
showToast ( '消息已发送' ) ; input . value = '' ;
} ) ;
}
}
const flowDetail = document . getElementById ( 'flow-detail' ) ;
function renderFlowDetail ( key ) {
const detail = flowDetails [ key ] ;
if ( ! detail ) return ;
flowDetail . classList . remove ( 'empty' ) ;
flowDetail . innerHTML = ` <div class="detail-kicker"> ${ detail . kind } </div><h3> ${ detail . title } </h3><div class="detail-grid"><div class="detail-block"><strong>入口 / 所在位置</strong><p> ${ detail . entry } </p></div><div class="detail-block"><strong>移动端触控</strong><p> ${ detail . touch } </p></div><div class="detail-block"><strong>返回路径</strong><p> ${ detail . back } </p></div></div><p class="flow-note">设计备注: ${ detail . note } </p> ` ;
flowDetail . scrollIntoView ( { behavior : 'smooth' , block : 'nearest' } ) ;
}
document . querySelectorAll ( '[data-detail]' ) . forEach ( node => node . addEventListener ( 'click' , ( ) => renderFlowDetail ( node . dataset . detail ) ) ) ;
document . querySelectorAll ( '[data-flow-filter]' ) . forEach ( filter => filter . addEventListener ( 'click' , ( ) => {
const value = filter . dataset . flowFilter ;
document . querySelectorAll ( '[data-flow-filter]' ) . forEach ( item => {
const active = item === filter ;
item . classList . toggle ( 'active' , active ) ;
item . setAttribute ( 'aria-selected' , active ? 'true' : 'false' ) ;
} ) ;
document . querySelectorAll ( '.flow-card' ) . forEach ( card => card . classList . toggle ( 'is-hidden' , value !== 'all' && card . dataset . flowGroup !== value ) ) ;
showToast ( value === 'all' ? '显示全部交互流程' : '已筛选交互流程' ) ;
} ) ) ;
document . addEventListener ( 'click' , event => {
const btn = event . target . closest ( '.demo-btn' ) ;
if ( btn ) {
if ( btn . dataset . panel ) openPanel ( btn , btn . dataset . panel ) ;
if ( btn . dataset . skill ) {
showToast ( btn . dataset . toast || ( '触发:' + btn . dataset . skill ) ) ;
if ( btn . dataset . skill !== '普攻' ) {
btn . classList . add ( 'cooling' ) ;
const badge = btn . querySelector ( 'em' ) ;
if ( badge ) badge . textContent = '3' ;
setTimeout ( ( ) => { btn . classList . remove ( 'cooling' ) ; if ( badge ) badge . textContent = '' ; } , 900 ) ;
}
}
}
const close = event . target . closest ( '[data-close]' ) ;
if ( close ) closePanel ( close ) ;
const back = event . target . closest ( '[data-back]' ) ;
if ( back ) backPanel ( back ) ;
if ( event . target . matches ( '[data-modal]' ) ) closePanel ( event . target . querySelector ( '[data-close]' ) ) ;
} ) ;
document . querySelectorAll ( '[data-viewport]' ) . forEach ( btn => btn . addEventListener ( 'click' , ( ) => {
canvas . className = 'preview-' + btn . dataset . viewport ;
showToast ( '预览尺寸:' + btn . textContent ) ;
} ) ) ;
document . getElementById ( 'annotation-toggle' ) . addEventListener ( 'click' , event => {
body . classList . toggle ( 'annotate' ) ;
event . currentTarget . textContent = body . classList . contains ( 'annotate' ) ? '关闭标注' : '标注' ;
showToast ( body . classList . contains ( 'annotate' ) ? '标注模式已开启' : '标注模式已关闭' ) ;
} ) ;
const annotation = document . getElementById ( 'annotation-label' ) ;
document . addEventListener ( 'mousemove' , event => {
if ( ! body . classList . contains ( 'annotate' ) ) return ;
const el = event . target . closest ( '[data-label]' ) ;
if ( ! el ) { annotation . style . display = 'none' ; return ; }
annotation . textContent = el . dataset . label ;
annotation . style . left = Math . min ( window . innerWidth - 180 , event . clientX + 12 ) + 'px' ;
annotation . style . top = Math . min ( window . innerHeight - 40 , event . clientY + 12 ) + 'px' ;
annotation . style . display = 'block' ;
} ) ;
document . querySelectorAll ( '[data-joystick]' ) . forEach ( joystick => {
let active = false ;
const stick = joystick . querySelector ( '.stick' ) ;
const move = event => {
if ( ! active ) return ;
const rect = joystick . getBoundingClientRect ( ) ;
const x = ( event . clientX ? ? event . touches ? . [ 0 ] ? . clientX ) - ( rect . left + rect . width / 2 ) ;
const y = ( event . clientY ? ? event . touches ? . [ 0 ] ? . clientY ) - ( rect . top + rect . height / 2 ) ;
const limit = 26 , length = Math . max ( 1 , Math . hypot ( x , y ) ) , scale = Math . min ( 1 , limit / length ) ;
stick . style . transform = ` translate(calc(-50% + ${ x * scale } px), calc(-50% + ${ y * scale } px)) ` ;
} ;
const release = ( ) => { active = false ; joystick . classList . remove ( 'is-active' ) ; stick . style . transform = 'translate(-50%, -50%)' ; showToast ( '移动摇杆已复位' ) ; } ;
joystick . addEventListener ( 'pointerdown' , event => { active = true ; joystick . classList . add ( 'is-active' ) ; joystick . setPointerCapture ( event . pointerId ) ; move ( event ) ; } ) ;
joystick . addEventListener ( 'pointermove' , move ) ; joystick . addEventListener ( 'pointerup' , release ) ; joystick . addEventListener ( 'pointercancel' , release ) ;
} ) ;
bindDynamic ( ) ;
< / script >
< / body >
< / html >