preface
Yesterday, when I was looking at chat logs in my open source project chat-System, I found that if there was a picture scrollbar in a message, it would miscalculate the location of the last message.
After some investigation, I finally solved this problem. In this article, I would like to share my solution and ideas with you. Welcome interested developers to read this article.
Problem analysis
As shown in the figure below, we clicked on a chat window, and the last message was the picture. The scrollbar position was incorrectly calculated and did not touch the bottom, so the picture was not displayed completely. When loading historical messages, the scrollbar position was incorrectly calculated due to the picture, and the message position of the last browsing was not correctly located.
Scrollbar bottom analysis
Let’s take a look at the bottom of the scrollbar position calculation implementation code:
nextTick().then(() = > {
let scrollHeight = 0;
if (messagesContainer.value == null) return;
// Get the scrolling area height of the message container
scrollHeight = messagesContainer.value.scrollHeight;
// If the current scrollbar is at the bottom or the current message is sent by the sender, change the scrollbar position
if (isBottomOut.value || data.isSendMessages.value) {
// The new message is rendered and the scrollbar position is changedmessagesContainer.value.scrollTop = scrollHeight; }});Copy the code
As shown in the code above, we get the scrollzone height of the message container in the nextTick callback, and then change the scrollbar position to scrollzone height so that the scrollbar hits the bottom, which is logically fine and normal for text-only messages.
So, the problem might be that I didn’t get the message container height correctly, so I tried changing the scrollHeight to 99999 so that its scrollbar was definitely at the bottom.
However, it didn’t go as well as I expected, and when I changed it to 99999, the scroll bar was still in the wrong position.
So, I think the problem is that the nextTick() scroll bar does get to the bottom, but the image doesn’t finish loading, and then the scroll bar changes position again.
At this point, we have found the problem, so we can get the following solution:
- Get all the chat pictures in the page
- Iterate over the captured image
- Get the height of the scrollable container after each image is loaded, and then modify the scrollbar position
Scrollbar touch top analysis
Peak load data, also because of the picture, led to the scroll bar position calculation error, at the beginning when I choose to use the bottom of the solution, such as img after completion of loading for the height of the rolling container, and then with the current message container height – the last saved height of container, so you can calculate the last position browsing the news of the scroll bar.
After some debugging, IT was found that the DOM would be reloaded every time the top was touched. Naturally, the images that had been loaded would be reloaded again, so the position of the scrollbar would also be wrong.
After some thought, I came up with a solution, since waiting for images to load won’t work, I’ll use a timer instead.
- After nextTick(), wait 150ms, and then get the scrollable height of the message container.
- Calculates the position of the scroll bar
- Modify the scroll bar position
The implementation code
Next, let’s look at the implementation code.
Scroll bar hits bottom
Part of the code that comes to the bottom of the scroll bar is shown here, but to complete the code, go to messageParsing
nextTick().then(() = > {
const scrollHeight = 0;
// Get all the chat pictures in the page
const previewablePanel = document.getElementsByClassName("previewable");
if (messagesContainer.value == null) return;
for (let i = 0; i < previewablePanel.length; i++) {
const item = previewablePanel.item(i) as HTMLImageElement;
item.onload = () = > {
if (messagesContainer.value == null) return;
// Set the bottom scroll bar
bottomScrollBar(
scrollHeight,
messagesContainer asRef<HTMLDivElement>, isBottomOut, msgListPanelHeight, isFirstLoading ); }; }});Copy the code
const bottomScrollBar = (
scrollHeight: number,
messagesContainer: Ref<HTMLDivElement>,
isBottomOut: Ref<boolean>,
msgListPanelHeight: Ref<number>,
isFirstLoading: Ref<boolean>
) = > {
const data = initData();
// Displays the message content
data.msgShowStatus.value = "";
// Get the container height
scrollHeight = messagesContainer.value.scrollHeight;
// If the current scrollbar is at the bottom or the current message is sent by the sender, change the scrollbar position
if (isBottomOut.value || data.isSendMessages.value) {
// The new message is rendered and the scrollbar position is changed
messagesContainer.value.scrollTop = scrollHeight;
// Update message record container height
msgListPanelHeight.value = scrollHeight;
// Change the component's first load state to false
isFirstLoading.value = false;
// Change the status of the sender to false
data.isSendMessages.value = false; }};Copy the code
Scroll bar hits top
Part of the code that comes to the top of the scroll bar is shown below, but to complete the code, go to messageParsing
nextTick().then(() = > {
// Hide the message content
data.msgShowStatus.value = "hidden";
if (data.pageNo.value > 20) {
// The load time is changed to 400ms
loadingTime = 400;
}
setTimeout(() = > {
if (messagesContainer.value == null) return;
scrollHeight = messagesContainer.value.scrollHeight;
// Load the history message and change the scrollbar position: current message record container height - Message record container height
messagesContainer.value.scrollTop =
scrollHeight - msgListPanelHeight.value;
// When a message is rendered, the total number of messages to be rendered decreases
msgTotals.value--;
// Determine if the message is rendered
if (msgTotals.value === 0) {
// Displays the message content
data.msgShowStatus.value = "";
// Close loading animation
isLoading.value = false;
// Update the height of the message record container
msgListPanelHeight.value = scrollHeight;
}
}, loadingTime);
});
Copy the code
In the code above, the timer time was dynamic because I found that waiting 150ms was no longer enough to get the correct scrollable container height when I loaded more than 20 pages of messages, and waiting 400ms was required.
Implementation effect
Next, let’s look at the final implementation.
Scroll bar hits top
In the implementation code above, I also made an optimization to hide the message content after nextTick and redisplay the message content after the scrollbar position has been calculated.
As for why we need to do this optimization, I will describe it through the GIF image, let’s first look at the top loading effect without optimization, as shown below:
As shown in the figure above, loading messages without optimization will flash the wrong message before displaying the correct message, which is painful to watch.
Next, let’s take a look at the optimized effect, as shown below:
After optimization, the visual effect is much better than not optimized, although there are still some flaws, will flicker 😂, can not think of other solutions at present, can only be so, if we have a better solution, can be discussed together in the comments section.
Scroll bar hits bottom
When the scrollbar hits the bottom, it is necessary to modify the position of the scrollbar after the image is loaded. When the image is not loaded, the interface will flash the message of the wrong position first, and then the correct message.
When I hit the bottom, I used the same solution as when I hit the top. After the scrollbar position is calculated, the chat history is displayed. The result is as follows:
The project address
-
Online experience address: chat-system
-
GitHub address: chat-system-github
Write in the last
- If there are any errors in this article, please correct them in the comments section. If this article helped you, please like it and follow 😊
- This article was first published in nuggets. Reprint is prohibited without permission 💌