This is the fifth day of my participation in the August More text Challenge. For details, see:August is more challenging
A, requirements,
When a program is running in the back end, the front end often displays a dynamic progress bar, or a loading animation, or a percentage number
Even better, of course, is to show the current user the details of how the program is running, such as the logs logged during the run, or the process in real time
Such as
[12:01:59] The application process is starting. [12:02:00] The process is successfully started. Obtaining network resources... [12:02:01] The download task is successfully started. [12:02:02] Download.... [12:02:03] Network resources are being parsed. [12:02:04] Related programs are being installed. [12:02:05] The installation is successful, and the application process is terminatedCopy the code
Of course, I’m just writing to show you what we want the log to look like. When there is a lot of log content or process content, the best thing to do is to have the page automatically scroll down as the content increases, so that the user can see the latest news
Second, the analysis
In this process, on the one hand, we need to constantly call the interface to get the latest data, on the other hand, we need to render the data to the page, and let the page scroll
Note that there are three conditions to make the page scroll
- The interface cannot return full data each time to directly replace the page data, which would result in a page without scrolling animation
- The CSS style of the page is set correctly to ensure that content only scrolls within the viewable area of the parent element and not the entire page
- Use the correct API to complete the scrolling effect
Three, the implementation method
- The first condition is well done and we can use it
setInterval
var timer = setInertval(() = >{
getLogList().then(res= >{
randerView(res.data)
})
},2000)
Copy the code
However, this method has a disadvantage, that is, when the user network is not smooth, or the server is crowded, the interface that has been called is always in the state of pendding, and the later interface is continuously called, which will make the congested service even worse
So I prefer to use setTimeout
async function getLogData(){
const logData = await getLogList()
randerView(logData.data)
setTimeout(getLogData, 2000)
}
getLogData()
Copy the code
-
For the second style problem, you only need to correctly add a fixed height and overflow-y: Scroll to the parent element.
-
Let’s move on to the third question, how do I get things to scroll down automatically, because this is the most important part, so I’ll do it in a separate section
Discussion on the scheme of automatic page scrolling
In general, there are two ways to get content to scroll down automatically.
scrollIntoView
The scrollIntoView method scrolls the parent container of the element, making the element that called scrollIntoView visible in the parent element.
The syntax is as follows:
element.scrollIntoView(); // Equal to Element.scrollintoView (true) element.scrollintoView (alignToTop); // Equal to Element.scrollintoView (true) Element.scrollintoView (alignToTop); // Boolean element. ScrollIntoView (scrollIntoViewOptions); // This parameter is of type Object
Parameter description:
parameter | type | The default value | note |
---|---|---|---|
alignToTop | boolean | true | Is the top of the element aligned with the top of the visible area of the scroll area in which it is located |
scrollIntoViewOptions | object | {behavior: “auto”, block: “start”, inline: “nearest”} | Behavior: Defines a scrolling animation transition, optionalauto 、smooth ;Block: Defines vertical alignment. This option is optional start .center .end Or,nearest ;Inline: Specifies horizontal alignment. Optional start .center .end Or,nearest |
Usage:
<template>
<div >
<strong>The process log</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>Temporarily no data</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 { @Prop() private LOGS: Array<object>; private name: string = 'processLog'; private logs: Array<string> = []; Private getData () {this.logs = this.logs? this.LOGS.map( (item: object): string => '[' + formatTime(item.updatedTime) + '] ' + item.content + '\n' ) : []} @watch ('LOGS') scrollLogs (newValue) {this.getData() // After log rendering, $nextTick(() => {if (newvalue.length! == 0) { (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>Copy the code
conclusion
ScrollIntoView is not very friendly to ios, Safari and IE. What’s wrong with other browsers
In addition, this method has no requirements on the layout, it is simple and easy to understand, and only needs to be called for the last rendered log
scrollTo
This is a commonplace method for scrolling content to specified coordinates.
The syntax is as follows:
scrollTo(xpos,ypos)
Parameter description:
parameter | type | The default value | note |
---|---|---|---|
xpos | number | – | A necessity. The x coordinate of the document to be displayed in the upper-left corner of the window document display area |
ypos | number | – | A necessity. The y coordinate of the document to be displayed in the upper-left corner of the window document display area |
Usage:
<template>
<div class="console-wraper">
<div class="console_window" ref="consoleWindow">
<div id="console_output" ref="consoleOutput">< / > < / > < / > < / ></div>
</div>
</div>
</template>
<script lang='ts'>
import { Component, Vue, Watch, Prop } from 'vue-property-decorator'
import { post } from '@/utils/http'
@Component
export default class extends Vue {
async getData() {
const res = await post(`/api/get/log`, { read: true })
this.formatData(res.data)
}
formatData(data) {
try {
if (data.length) {
data.forEach(item= > {
this.printLine('[' + item.updateTime + '] ' + item.value)
})
} else {
this.printLine('No data yet'.'input_line')}}catch (e) {
console.log('error')}}printLine(s, type) {
if ((s = String(s))) {
let n = document.createElement('pre')
if(! type) type ='output_line'
if(n.innerText ! = =undefined) {
// IE has to use innerText
n.innerText = s
} else {
// Firefox uses createTextNode
n.appendChild(document.createTextNode(s))
}
n.className = type
this.$refs.consoleOutput.appendChild(n)
// After adding the log, let the parent element scroll to its own distance
this.$refs.consoleWindow.scrollTop = this.$refs.consoleWindow.scrollHeight
}
}
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>
Copy the code
This is the end of this article, the case code has not been fully tested, have any questions in the comments section oh 🙂