предисловие
Вчера, когда я проверил историю чата в чат-системе моего проекта с открытым исходным кодом, я обнаружил, что если в сообщении есть полоса прокрутки с изображением, положение полосы прокрутки будет неправильным, что приведет к неточному расположению последнего сообщения.
После некоторого расследования я, наконец, решил эту проблему. В этой статье я поделюсь с вами своими решениями и идеями. Заинтересованные разработчики могут прочитать эту статью.
анализ проблемы
Как показано на рисунке ниже, мы нажали на окно чата.Последним сообщением была картинка.Положение полосы прокрутки было рассчитано неправильно и не касалось дна,в результате картинка отображалась не полностью.При подтягивании и загрузке истории сообщений, положение полосы прокрутки также было вызвано картинкой.Просчет, неправильное определение местоположения последнего просмотренного сообщения.
Анализ нижней части полосы прокрутки
Давайте посмотрим на код реализации для расчета положения полосы прокрутки при достижении нижней точки:
nextTick().then(() => {
let scrollHeight = 0;
if (messagesContainer.value == null) return;
// 获取消息容器滚动区域高度
scrollHeight = messagesContainer.value.scrollHeight;
// 当前滚动条在底部或者当前消息为发送端所发送的则修改滚动条位置
if (isBottomOut.value || data.isSendMessages.value) {
// 新消息渲染完成,修改滚动条位置
messagesContainer.value.scrollTop = scrollHeight;
}
});
Как показано в приведенном выше коде, мы получаем высоту области прокрутки контейнера сообщения в обратном вызове nextTick, а затем изменяем положение полосы прокрутки на высоту области прокрутки, чтобы полоса прокрутки была внизу, в логике нет проблем. , а в текстовом сообщении нормально.
Тогда проблема может заключаться в том, что высота контейнера сообщений была получена неправильно, поэтому я попробовал следующееscrollHeightизменить на99999, поэтому его полоса прокрутки определенно находится внизу.
Однако все прошло не так гладко, как я ожидал, и изменился на99999После этого положение полосы прокрутки по-прежнему неправильное.
Ну, я думаю, что вопрос должен бытьnextTick()После того, как полоса прокрутки действительно достигла дна, но изображение еще не загружено, положение полосы прокрутки снова меняется после загрузки изображения.
На данный момент мы нашли проблему, тогда мы можем получить следующие решения:
- Получить все изображения чата на странице
- Перемещайтесь по полученным изображениям
- После загрузки каждого изображения получите высоту прокручиваемого контейнера, а затем измените положение полосы прокрутки.
Анализ верхней части полосы прокрутки
При загрузке данных в верхней части он также был из-за изображения, который привел к неправильному положению положения прокрутки. Сначала я решил использовать решение, когда дно касается, а после нагрузки IMG Высота контейнера прокрутки, а затем используется высота контейнера текущего сообщения - высота последнего сохраненного контейнера сообщений, так что положение полосы прокрутки можно рассчитать, когда последнее сообщение было просмотрено.
После реализации вышеизложенных идей положение полосы прокрутки по-прежнему неправильное.После некоторой отладки обнаруживается, что DOM будет перезагружаться каждый раз, когда он попадает в верхнюю часть.Естественно, изображения, которые были загружены, будут перезагружаться снова, и положение полосы прокрутки будет естественным.даже неправильным.
Немного подумав, я нашел решение, так как ждать загрузки изображения не получается, тогда я буду использовать таймер.
- После nextTick() подождите 150 мс, затем получите прокручиваемую высоту контейнера сообщения.
- Вычислить положение полосы прокрутки
- Изменить положение полосы прокрутки
код реализации
Далее, давайте взглянем на конкретный код реализации.
Полоса прокрутки заканчивается
Ниже показана часть кода, когда полоса прокрутки достигает нижней точки. Перейдите к полному коду:messageParsing.ts
nextTick().then(() => {
const scrollHeight = 0;
// 获取页面内所有的聊天图片
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;
// 置底滚动条
bottomScrollBar(
scrollHeight,
messagesContainer as Ref<HTMLDivElement>,
isBottomOut,
msgListPanelHeight,
isFirstLoading
);
};
}
});
const bottomScrollBar = (
scrollHeight: number,
messagesContainer: Ref<HTMLDivElement>,
isBottomOut: Ref<boolean>,
msgListPanelHeight: Ref<number>,
isFirstLoading: Ref<boolean>
) => {
const data = initData();
// 显示消息内容
data.msgShowStatus.value = "";
// 获取容器高度
scrollHeight = messagesContainer.value.scrollHeight;
// 当前滚动条在底部或者当前消息为发送端所发送的则修改滚动条位置
if (isBottomOut.value || data.isSendMessages.value) {
// 新消息渲染完成,修改滚动条位置
messagesContainer.value.scrollTop = scrollHeight;
// 更新消息记录容器高度
msgListPanelHeight.value = scrollHeight;
// 修改组件第一次加载状态为false
isFirstLoading.value = false;
// 修改消息发送端状态为false
data.isSendMessages.value = false;
}
};
полоса прокрутки сверху
Часть кода, когда полоса прокрутки касается верхней части, показана ниже, пожалуйста, переместите весь код:messageParsing.ts
nextTick().then(() => {
// 隐藏消息内容
data.msgShowStatus.value = "hidden";
if (data.pageNo.value > 20) {
// 数据加载超过20条,加载时间改为400ms
loadingTime = 400;
}
setTimeout(() => {
if (messagesContainer.value == null) return;
scrollHeight = messagesContainer.value.scrollHeight;
// 加载历史消息,修改滚动条位置:当前消息记录容器高度 - 消息记录容器高度
messagesContainer.value.scrollTop =
scrollHeight - msgListPanelHeight.value;
// 一条消息渲染完成,待渲染消息总条数自减
msgTotals.value--;
// 判断消息是否渲染完成
if (msgTotals.value === 0) {
// 显示消息内容
data.msgShowStatus.value = "";
// 关闭加载动画
isLoading.value = false;
// 加载历史消息完成,更新消息记录容器高度
msgListPanelHeight.value = scrollHeight;
}
}, loadingTime);
});
В приведенном выше коде время таймера является динамическим, потому что я обнаружил, что когда загруженное сообщение превышает 20 страниц, ожидание в течение 150 мс не позволяет получить правильную высоту прокручиваемого контейнера, и мне нужно ждать 400 мс.
добиться эффекта
Далее, давайте взглянем на окончательный эффект реализации.
полоса прокрутки сверху
В приведенном выше коде реализации я также сделал оптимизацию: после nextTick я скрываю содержимое сообщения, после вычисления положения полосы прокрутки содержимое сообщения снова отображается.
Что касается того, почему эта оптимизация сделана, позвольте мне описать это через изображение GIF.Давайте сначала посмотрим на эффект верхней загрузки без оптимизации, как показано ниже:
Как показано на рисунке выше, когда сообщение о загрузке не оптимизировано, сначала будет мигать сообщение в неправильном положении, а затем будет отображаться правильное сообщение, что очень неудобно смотреть.
Далее давайте взглянем на оптимизированный эффект следующим образом:
После оптимизации визуальный эффект намного лучше, чем когда он не был оптимизирован.Хотя он все еще немного недоработан, он будет немного мерцать.Я не могу придумать другие решения в данный момент, поэтому я могу сделать это только сначала. Если у вас есть лучшее решение, вы можете поделиться им в комментариях.
Полоса прокрутки заканчивается
Когда полоса прокрутки касается дна, положение полосы прокрутки необходимо изменить после загрузки изображения.Если изображение не загружено, интерфейс сначала высветит сообщение о неправильном положении, а затем правильное сообщение.
При достижении дна я использую то же решение, что и при достижении верха.Запись чата отображается только после вычисления положения полосы прокрутки.Эффект следующий:
адрес проекта
-
Адрес онлайн-опыта:chat-system
-
Адрес гитхаба:chat-system-github
напиши в конце
- Если в статье есть ошибки, исправьте их в комментариях, если статья вам поможет, ставьте лайк и подписывайтесь 😊
- Эта статья была впервые опубликована на Наггетс, перепечатка без разрешения запрещена 💌