前端 日志自动滚动效果

一、需求

当某个程序在后端运行的时候,前端经常要显示一个动态的进度条,或者 loading 的动画,或者百分比的数字

当然,更好的效果是展示给用户程序运行的具体细节,比如运行中记录的日志

这就好像电视剧结束后播放的演员表一样

二、实现

一般来说,有两种方法来实现这个需求。

1、scrollIntoView()

scrollIntoView()方法会滚动元素的父容器,使调用scrollIntoView的元素在父元素中可见。

语法如下:

element.scrollIntoView(); // 等同于element.scrollIntoView(true)
element.scrollIntoView(alignToTop); // Boolean型参数
element.scrollIntoView(scrollIntoViewOptions); // Object型参数

参数如下:

参数 类型 默认值 备注
alignToTop boolean true 该元素的顶端是否和其所在滚动区的可视区域的顶端对齐
scrollIntoViewOptions object {behavior: “auto”, block: “start”, inline: “nearest”} behavior:定义滚动动画过度,可选autosmooth
block:定义垂直方向的对齐,可选start, center, end, 或 nearest
inline:定义水平方向的对齐,可选 start, center, end, 或 nearest

使用方法:

<template>
  <div >
    <strong>进程日志</strong>
    <div style="max-height:120px; position:relative">
      <div v-if="logs.length">
        <p
          class="logList-item"
          v-for="(item, index) in logs"
          :key="index"
          :id="(logs.length === index+1)?'scollLog':''"
        >{
    
    {
    
     item }}</p>
      </div>
      <p v-else>暂无数据</p>
    </div>
  </div>
</template>

<script lang='ts'>
import {
    
     Component, Vue, Watch, Prop } from 'vue-property-decorator'
import {
    
     formatTime } from '@/utils'

@Component
export default class extends Vue {
    
    
  private name: string = 'processLog';
  private logs: Array<string> = [];

  @Prop() private LOGS: any;
  @Prop() private scrollT: any;
  // getData 将父组件传递过来的日志转成 `[2021-01-01 12:12:12] 系统正在启动` 这种格式
  private getData () {
    
    
    this.logs = this.LOGS
      ? this.LOGS.map(
        (item: object): string =>
          '[' + formatTime(item.updatedTime) + '] ' + item.content + '\n'
      )
      : []
  }
  
  @Watch('LOGS')
  scrollLogs (newValue: any) {
    
    
    this.getData()
    // 在日志渲染之后,将最底下的日志滚动到其父元素可视区域内
    this.$nextTick(() => {
    
    
      let top = 160
      if (newValue.length !== 0 && this.scrollT < top) {
    
    
        (document.getElementById('scollLog') as HTMLElement).scrollIntoView({
    
     behavior: 'smooth', block: 'nearest' })
      }
    })
  }
  
  mounted () {
    
    
    this.getData()
  }
}
</script>

<style lang="scss" scoped>
.logList-item {
    
    
  padding: 8px 0;
  margin: 0;
}
</style>

总结

这个方法的对 ios safari 和 IE 不是很友好,其他的浏览器没有什么问题

另外,这个方法对布局也没有什么要求,简单方便,易于理解,不管后端返回的是全量数据还是累加数据,只需要针对最后一条渲染的日志调用即可

2、scrollTo

这个方法非常常用,语法和参数就略过了,需要注意的是这个方法对布局有要求,布局不对,这个方法是实现不了滚动的

使用方法

<template>
  <div class="console-wraper">
      <div class="console_window" ref="consoleWindow">
        <div id="console_output" ref="consoleOutput">
			<-- 这里写日志信息,注意,一定要在父元素外再套一层-->
		</div>
      </div>
  </div>
</template>
<script>
/* eslint-disable camelcase */
import {
    
     postRest } from '@/utils/http'
export default {
    
    
  data() {
    
    
    return {
    
    
      sessionType: '',
      sessionId: 0,
      command: '',
    }
  },
  methods: {
    
    
    async getData() {
    
    
	  const res = await postRest(`/sessions/${
      
      sessionId}/shell`, {
    
     read: true, cmd: command })
      if (!res) return
      this.command = ''
      this.cleanData(res)
    },
	cleanData(res) {
    
    
      const {
    
     con_prompt, con_update, con_append } = res
      this.consolePrompt = unescape(con_prompt)

      try {
    
    
        let conAppend = JSON.parse(con_append)
        if (conAppend.length) {
    
    
          conAppend.forEach(item => {
    
    
            this.printLine(unescape(item[1]), item[0])
          })
        } else {
    
    
          if (con_update.length) {
    
    
            this.printLine(unescape(con_update), 'output_line')
          }
        }
      } catch (e) {
    
    
        console.log('error')
      }
    },
    printLine(s, type) {
    
    
      if ((s = String(s))) {
    
    
        let n = document.createElement('pre')

        if (!type) type = 'output_line'

        // IE has to use innerText
        if (n.innerText !== undefined) {
    
    
          n.innerText = s
          // Firefox uses createTextNode
        } else {
    
    
          n.appendChild(document.createTextNode(s))
        }

        n.className = type
        this.$refs.consoleOutput.appendChild(n)
        // 添加完日志后,让父元素滚动到其自身的距离
        this.$refs.consoleWindow.scrollTop = this.$refs.consoleWindow.scrollHeight
      }
    },
    help () {
    
    
      this.printLine('     Web Console Internal Commands\n')
      this.printLine('=========================================\n\n')
      this.printLine(' /help       Show this text\n')
      this.printLine(' /clear      Clear the screen\n')
      this.printLine('\n')
    },
    clear () {
    
    
      this.$refs.consoleOutput.innerHTML = ''
    }
  },
  async mounted() {
    
    
  	this.getData()
  }
}
</script>
<style lang=scss scoped>
.console-wraper{
    
    
    display: flex;
    flex-direction: column;
    height: 100%;
}
.console_window {
    
    
    flex: 1;
    background: #333;
    overflow: auto;
    .console_output {
    
    
        white-space: pre-wrap;
        word-wrap: break-word;
        padding: 12px;
        font-weight: bold;
    }
    /deep/ pre.input_line {
    
    
        font-size: 14px;
        color: yellow;
        opacity: 0.8;
        padding: 0 20px;
    }
    /deep/ pre.output_line {
    
    
        color: #fff;
        font-size: 13px;
        white-space: pre-wrap;
        word-wrap: break-word;
        padding-left: 40px;
        opacity: 0.7;
    }
}

</style>

喜欢可以收藏一下,哈哈哈

猜你喜欢

转载自blog.csdn.net/zhai_865327/article/details/113527424