有没有更好的方法来实现模拟有限字符显示的水平滚动文本效果?

Posted

技术标签:

【中文标题】有没有更好的方法来实现模拟有限字符显示的水平滚动文本效果?【英文标题】:Is there a better way of achieving horizontal scrolling text effect imitating a limited character display? 【发布时间】:2020-04-03 18:13:49 【问题描述】:

我正在尝试在命令行上模拟 16 个字符的显示,该显示循环无限类似于证券交易所代码的长字符串。

现在,我首先打印 ASCII 字符串的前 16 字节切片,然后一次移动超过 1 个字节:

package main

import (
    "fmt"
    "time"
)

const (
    chars = 16
    text  = "There are many variations of passages of Lorem Ipsum available!!!"
)

func main() 
    fmt.Print("\033[2J") // clear screen
    buf := []byte(text)

    i := chars
    for 
        fmt.Print("\033[H") // move cursor back to first position
        fmt.Printf(string(buf[i-chars : i]))
        i++

        if i == len(buf)+1 
            i = chars
        

        time.Sleep(time.Second / 4)

        // visualization of what's happening:
        // fmt.Printf("\t\t Character:%d of Length:%d | Slice: %d:%d \n", i, len(buf), i-chars, i)
    


当我到达文本末尾时,我重置循环内的计数器并从第一片开始再次打印。我不想这样做,而是想要获得“翻转”效果,其中切片的头部无缝连接到切片的尾部。

问题是,我不能使用空缓冲区并将头部附加到循环内的尾部,因为它会无限增长。

所以我决定不这样做,而是在循环之前将字符串的前 16 个字节附加到它的尾部,并立即缩小切片 -16 个字节。但由于后备数组中仍然存在 -16 个字节,我可以从循环中扩展/收缩:

func main() 
    fmt.Print("\033[2J") // clear screen
    buf := []byte(text)
    buf = append(buf, buf[:chars]...)
    buf = buf[:len(buf)-chars]

    var expanded bool
    i := chars
    for 
        fmt.Print("\033[H") // move cursor back to first position
        fmt.Printf(string(buf[i-chars : i]))
        i++

        if i+1 == len(buf)-chars && !expanded 
            buf = buf[:len(buf)+chars]
            expanded = true
        

        if i+1 == len(buf) 
            i = chars
            buf = buf[:len(buf)-chars]
            expanded = false
        

        time.Sleep(time.Second / 2)

        // visualization of what's happening:
        //fmt.Printf("\t\t Character:%d of Length:%d | Slice: %d:%d | Cap: %d\n", i, len(buf), i-chars, i, cap(buf))
    

这让我到达了我想要的地方,但我对 Go 还很陌生,所以我想知道是否有更好的方法来实现相同的结果?

【问题讨论】:

【参考方案1】:

首先我不会更改缓冲区。将前 16 个字符附加到其末尾以轻松获得“翻转”效果是个好主意,但是当您到达其末尾时将位置重置为 0 会更容易且更便宜。

接下来,您不需要对字节切片进行操作。只需在string 上操作即可。字符串可以被索引和切片,就像切片一样,切片 string 甚至不需要复制(不必),它返回一个新的字符串(标题),它共享字符串数据的支持数组。不要忘记索引和切片strings 使用字节索引(不是rune 索引),这对于 ASCII 文本很好(它们的字符在 UTF-8 中一对一映射到字节),但不会起作用带有多字节特殊字符。您的示例文本很好。

也不要使用fmt.Printf() 打印文本,该文本需要格式string(将其第一个参数视为格式字符串)。而是使用fmt.Print()

总而言之,您的解决方案可以简化为性能更佳的解决方案,并且更简洁:

func main() 
    fmt.Print("\033[2J") // clear screen
    s := text + text[:chars]

    for i := 0; ; i = (i + 1) % len(text) 
        fmt.Print("\033[H") // move cursor back to first position
        fmt.Print(s[i : i+chars])
        time.Sleep(time.Second / 2)
    

还要注意,当位置达到len(text)时,我们将其重置为0,因此之前的文本以text的最后一个字符开头,并从头开始使用chars-1。所以追加chars-1而不是chars也足够了:

s := text + text[:chars-1]

【讨论】:

以上是关于有没有更好的方法来实现模拟有限字符显示的水平滚动文本效果?的主要内容,如果未能解决你的问题,请参考以下文章

C#的textbox怎么没有显示水平滚动条?

隐藏html水平但不垂直滚动条

剑道网格的水平滚动

具有垂直和水平滚动的 Ant Design Table

如何在 SpriteKit 中制作水平滚动字符选择器

jsp中多个文本区域的通用滚动条