odometer.go (1904B)
1 package odometer 2 3 import ( 4 "dkforest/pkg/utils" 5 "fmt" 6 ) 7 8 type Odometer struct { 9 s string 10 } 11 12 func New(s string) *Odometer { 13 return &Odometer{s: s} 14 } 15 16 func (o *Odometer) Html() string { 17 out := `<div class="odometer">` 18 for i := 0; i < len(o.s); i++ { 19 out += fmt.Sprintf(`<div class="wrapper"><div class="outer"><span class="below odometer%d"></span><span class="top inner">%c</span></div></div>`, i, o.s[i]) 20 } 21 out += "</div>" 22 return out 23 } 24 25 func (o *Odometer) Css() string { 26 out := ` 27 .odometer { display: table; font-family: monospace; font-size: 40px; } 28 .odometer .wrapper { display: table-cell; } 29 .odometer .outer { display: grid; grid-template: 1fr / 1fr; place-items: center; } 30 .odometer .outer > * { grid-column: 1 / 1; grid-row: 1 / 1; } 31 .odometer .outer .below { z-index: 1; } 32 .odometer .outer .top { z-index: 2; } 33 .odometer .outer .inner { visibility: visible; animation-name: inner_frames; animation-duration: 3s; } 34 @keyframes inner_frames { 0% { visibility: hidden; } 99% { visibility: hidden; } 100% { visibility: visible; } }` 35 l := len(o.s) 36 for i := 0; i < l; i++ { 37 out += fmt.Sprintf(` 38 .odometer .odometer%d:before { visibility: hidden; content: "%c"; animation-name: odometer%d_frames; animation-duration: 3s; } 39 @keyframes odometer%d_frames {`, i, o.s[i], i, i) 40 out += ` 0% { visibility: visible; }` 41 n := 20 42 step := 100.0 / float64(n) 43 pct := 0.0 44 var prev int 45 for j := 0; j <= n; j++ { 46 if j == n-(l-i) { 47 break 48 } 49 if pct >= 95 { 50 break 51 } 52 if j == 0 { 53 pct += utils.RandFloat(0, 3) 54 } 55 var num int 56 for { 57 num = utils.RandInt(0, 9) 58 if j > 0 && num == prev { 59 continue 60 } 61 break 62 } 63 prev = num 64 out += fmt.Sprintf(`%.2f%% { content: "%d"; }`, pct, num) 65 pct += step 66 } 67 out += ` 99% { visibility: visible; }` 68 out += ` 100% { visibility: hidden; }` 69 out += `}` 70 } 71 return out 72 }