React-window: Совместимость со ScrollSync

Созданный на 8 нояб. 2018  ·  52Комментарии  ·  Источник: bvaughn/react-window

У меня есть вариант использования, когда мне нужно создать сетку с фиксированными заголовками, которые остаются закрепленными наверху. В виртуализированной реакции это может быть достигнуто путем создания двух компонентов сетки (один для заголовков, один для тела сетки) и синхронизации положения прокрутки, чтобы горизонтальная прокрутка сетки заголовка синхронизировалась с основной сеткой.

Я знаю, что вы пытаетесь уменьшить вес окна реакции, как с точки зрения размера пакета, так и концептуальной сложности, в пользу создания дополнительных функций в виде отдельных пакетов. Я согласен с этим направлением. Я думаю, что компонент ScrollSync из react-virtualized может быть извлечен или адаптирован для работы с реактивным окном. Однако для этого response-window необходимо будет принимать реквизиты для scrollLeft и scrollTop, чтобы можно было напрямую управлять смещением прокрутки сетки заголовка.

Хотели бы вы поддержать этот вариант использования? Если нет, можете ли вы посоветовать, в каком направлении мне следует пойти, чтобы реализовать это самостоятельно?

Спасибо за работу над этой библиотекой. Как человек, который пару лет использовал react-virtualized, я ценю простоту начала работы с response-window и то, насколько это было для меня производительным без особого ручного вмешательства.

Самый полезный комментарий

В этом есть смысл. Спасибо за предложение! У меня он заработал, и на самом деле его довольно легко настроить. Настолько просто, что определенно не требует отдельного пакета. Может быть, пример в документации, но я полагаю, это ваш вопрос. Я оставлю здесь ссылку на мой рабочий пример песочницы кода на случай, если кто-то еще столкнется с этой проблемой в будущем.

https://codesandbox.io/s/y3pyp85zm1

TL; DR - поместите ссылку в сетку заголовка и поместите ее в сетку тела.

onScroll={({ scrollLeft }) => this.headerGrid.current.scrollTo({ scrollLeft })}

Все 52 Комментарий

Прежде всего, спасибо за добрые слова и положительный отзыв. Я рад слышать, что окно реакции до сих пор хорошо работает для вас!

Я согласен с тем, что такой компонент, как ScrollSync, может быть выпущен как отдельный пакет, который зависит от окна реакции, но я бы не хотел добавлять его в этот проект, поскольку он не является ядром для работы с окнами.

Что касается вашего конкретного вопроса о свойствах прокрутки, это не то изменение, которое я хотел бы внести в проект, потому что после использования его с react-virtualized я понял, что у него есть некоторые серьезные недостатки. Я написал об этом в блоге React, если вам интересно:

https://reactjs.org/blog/2018/06/07/you-probably-dont-need-dehibited-state.html#anti -pattern-erasing-state-when-props-change

Вы можете использовать API императивной прокрутки, который предлагает response-window, для достижения аналогичного поведения синхронизации. Вам просто нужно будет вызывать эти методы из жизненных циклов коммитов, а не передавать реквизиты.

Надеюсь, это имеет смысл, но если нет, не стесняйтесь задавать дополнительные вопросы!

В этом есть смысл. Спасибо за предложение! У меня он заработал, и на самом деле его довольно легко настроить. Настолько просто, что определенно не требует отдельного пакета. Может быть, пример в документации, но я полагаю, это ваш вопрос. Я оставлю здесь ссылку на мой рабочий пример песочницы кода на случай, если кто-то еще столкнется с этой проблемой в будущем.

https://codesandbox.io/s/y3pyp85zm1

TL; DR - поместите ссылку в сетку заголовка и поместите ее в сетку тела.

onScroll={({ scrollLeft }) => this.headerGrid.current.scrollTo({ scrollLeft })}

Спасибо за ссылку! Это очень задумчиво.

8 ноября 2018 г., 13:09 Рейган Киллер < [email protected] написал:

В этом есть смысл. Спасибо за предложение! У меня это работает,
и на самом деле это довольно легко настроить. Так легко, что это определенно не так.
гарантия на автономный пакет. Может быть, пример в документации, но это ваш
позвоните я полагаю. Я оставлю ссылку на свой рабочий пример Code Sandbox здесь
на случай, если кто-то еще столкнется с этой проблемой в будущем.

https://codesandbox.io/s/y3pyp85zm1

TL; DR - поместите ссылку в сетку заголовка и поместите ее в сетку тела.

onScroll = {({scrollLeft}) => this.headerGrid.current.scrollTo ({scrollLeft})}

-
Вы получаете это, потому что изменили состояние открытия / закрытия.
Ответьте на это письмо напрямую, просмотрите его на GitHub
https://github.com/bvaughn/react-window/issues/86#issuecomment-437156749 ,
или отключить поток
https://github.com/notifications/unsubscribe-auth/AABznTUunzEIs6bVQfVsz7T21L2-Pkkoks5utJ17gaJpZM4YVMd7
.

Для всех, кто наткнулся на это, у меня была синхронизированная с прокруткой верхняя / правая / нижняя / левая замороженная супер-сетка в react-virtualized которую я только что перенес в эту библиотеку. Я скажу, что вышеупомянутое решение, по крайней мере, столь же производительно, если не лучше, и существенно менее болезненно, чем использование ScrollSync в другой библиотеке.

Он также отлично сочетается с новым хуком useRef response (https://reactjs.org/docs/hooks-reference.html#useref)

const topRef = useRef();
const rightRef = useRef();
const bottomRef = useRef();
const leftRef = useRef();
...
<Grid
  onScroll={({ scrollLeft, scrollTop }) => {
    if (leftRef.current) {
      leftRef.current.scrollTo({ scrollTop });
    }
    if (rightRef.current) {
      rightRef.current.scrollTo({ scrollTop });
    }
    if (topRef.current) {
      topRef.current.scrollTo({ scrollLeft });
    }
    if (bottomRef.current) {
      bottomRef.current.scrollTo({ scrollLeft });
    }
  }}
  ...
/>

Отлично! Спасибо, что поделились @ranneyd!

Он также отлично сочетается с новым хуком useRef response (https://reactjs.org/docs/hooks-reference.html#useref)

Спасибо @ranneyd

Можно было бы на рабочем примере поделиться?

@ranneyd еще раз спасибо, я следил за вашим подходом, и синхронизация прокрутки работает нормально.

Скрытое переполнение полос прокрутки вызывает смещение ближе к концу сетки:

gridalignement

Есть предложения, как это исправить?

CodeSandbox Пример

заранее спасибо

@carlosagsmendes это из-за полосы прокрутки. Слева сделайте высоту равной высоте минус размер полосы прокрутки. Чтобы обеспечить согласованность между устройствами, я вручную жестко запрограммировал размер полосы прокрутки с помощью CSS. Если ваш контент настолько динамичен, что может быть или не быть полосы прокрутки, сделайте что-то вроде «количество элементов * размер элементов <ширина (у вас должны быть все эти значения прямо здесь)», а затем измените высоту в зависимости от этого.

Чтобы обеспечить согласованность между устройствами, я вручную жестко запрограммировал размер полосы прокрутки с помощью CSS.

FWIW пакет dom- helpers имеет удобную функцию scrollbarSize которая сообщает вам, какая ширина на текущем устройстве. Может быть лучше, чем жесткое кодирование.

@bvaughn да! Я забыл об этом упомянуть. Однако у нас это не сработало. Я думаю, проблема в том, что мы уже делали жестко запрограммированные полосы прокрутки, и это сбивало с толку.

Спасибо тебе. Я дам ему попробовать!

Это работает с горизонтальным VariableSizeList? Я пробовал, но мой браузер зависает.

onScroll={({ scrollLeft }) => this.headerGrid.current.scrollTo({ scrollLeft })}

Он хорошо работает с VariableSizeGrid, но мой заголовок немного отстает при прокрутке.

@ajaymore, так что проблемы с синхронизацией прокрутки при использовании колеса прокрутки на Mac. В MacOSX есть встроенная функция естественной прокрутки, которая интерполирует прокрутку, чтобы сделать ее «более плавной». Частота обновления на самом деле выше, чем частота кадров анимации в Chrome. Таким образом, прокручиваемый элемент будет анимирован до того, как V8 / ScrollSync успеет обновить DOM. Это техническое ограничение, решения которого я не видел.

Интересный факт: если вы вручную используете полосу прокрутки в браузере (например, прокручиваете по старинке, перетаскивая маленькую вещь), она работает совершенно нормально. Функция интерполяции встроена в колесо прокрутки / прокрутку трекпада.

@ajaymore, так что проблемы с синхронизацией прокрутки при использовании колеса прокрутки на Mac. В MacOSX есть встроенная функция естественной прокрутки, которая интерполирует прокрутку, чтобы сделать ее «более плавной». Частота обновления на самом деле выше, чем частота кадров анимации в Chrome. Таким образом, прокручиваемый элемент будет анимирован до того, как V8 / ScrollSync успеет обновить DOM. Это техническое ограничение, решения которого я не видел.

Интересный факт: если вы вручную используете полосу прокрутки в браузере (например, прокручиваете по старинке, перетаскивая маленькую вещь), она работает совершенно нормально. Функция интерполяции встроена в колесо прокрутки / прокрутку трекпада.

@ranneyd Спасибо за такой быстрый ответ. Согласен, это ограничение. Он будет работать нормально на большинстве устройств, так что тогда это не проблема.

@bvaughn ты когда-нибудь играл с position: sticky ? Я давно не смотрел на это, и я знаю, что поддержка браузеров скудная, но мне интересно, есть ли способ использовать это в браузерах, где это поддерживается ...

У меня та же проблема, что и у @ajaymore , то есть синхронизация двух компонентов тормозит (как уже говорилось, она отлично работает при ручной прокрутке). Я тестирую Chrome на ПК, так что, очевидно, это проблема не только Mac ... Кому-то удалось каким-то образом решить эту проблему?

@alonrbar У меня такая же проблема на ПК с Windows.

На самом деле я только что нашел обходной путь, который мне подходит.
Общая идея состоит в том, чтобы создать «теневую сетку», которая скрывается за исходной сеткой и крадет его событие onScroll, а затем использует его для ручной прокрутки исходной сетки, а также любой другой необходимой сетки. Это немного снижает производительность, но поддерживает синхронизацию всех сеток, поэтому вам придется подумать о компромиссе.

Код выглядит так:

import styled from '@emotion/styled';
import * as React from 'react';
import { VariableSizeGrid, VariableSizeGridProps } from 'react-window';
import { SizeUtils } from '../utils';

export interface SyncableGridProps extends VariableSizeGridProps {
    mainGridRef: React.Ref<VariableSizeGrid>;
    shadowGridRef: React.Ref<VariableSizeGrid>;
    hideVerticalScrollbar?: boolean;
}

export class SyncableGrid extends React.PureComponent<SyncableGridProps> {
    public render() {
        const { height, width } = this.props;
        const {
            onScroll,
            mainGridRef: mainGridRef1,
            shadowGridRef: shadowGridRef1,
            ...mainProps
        } = this.props;
        const {
            children,
            style,
            overscanRowsCount,
            overscanColumnsCount,
            overscanCount,
            useIsScrolling,
            onItemsRendered,
            mainGridRef: mainGridRef2,
            shadowGridRef: shadowGridRef2,
            innerRef,
            outerRef,
            ...shadowProps
        } = this.props;
        return (
            <SyncWrapper
                style={{
                    height,
                    width
                }}
            >
                <MainGrid
                    {...mainProps}
                    style={Object.assign({}, style, {
                        overflowY: 'scroll'
                    })}
                    ref={mainGridRef1}
                />
                <GridShadow
                    {...shadowProps}
                    style={{
                        position: 'absolute',
                        top: 0,
                        left: 0
                    }}
                    ref={shadowGridRef1}
                >
                    {() => null}
                </GridShadow>
            </SyncWrapper>
        );
    }
}

// ---------------- //
//      styles      //
// ---------------- //

const SyncWrapper = styled.div`
    position: relative;
    overflow: hidden;
`;

export interface MainGridProps extends VariableSizeGridProps {
    hideVerticalScrollbar?: boolean;
}

export const MainGrid = styled(VariableSizeGrid) <MainGridProps>`
    overflow-y: scroll;
    box-sizing: content-box;
    ${props => {
        if (!props.hideVerticalScrollbar)
            return '';
        const paddingDir = (props.theme.dir === 'rtl' ? 'padding-left' : 'padding-right');
        return `${paddingDir}: ${SizeUtils.scrollbarWidth}px;`;
    }}
`;

export const GridShadow = styled(MainGrid)`
    opacity: 0;
`;

Затем в другом файле:

<SyncableGrid
    mainGridRef={this.firstGridMain}
    shadowGridRef={this.firstGridShadow}
    onScroll={this.handleFirstGridScroll}
    // other props omitted for bravity...
>
   // children omitted for bravity...
</SyncableGrid>
<SyncableGrid
    mainGridRef={this.secondGridMain}
    shadowGridRef={this.secondGridShadow}
    onScroll={this.handleSecondGridScroll}
    // other props omitted for bravity...
>
   // children omitted for bravity...
</SyncableGrid>

private handleFirstGridScroll = (e: GridOnScrollProps) => {
    const { scrollTop, scrollLeft } = e;

    // synchronize self
    if (this.firstGridMain.current) {
        this.firstGridMain.current.scrollTo({ scrollTop, scrollLeft });
    }

    // synchronize other grid
    if (this.secondGridMain.current) {
        this.secondGridMain.current.scrollTo({ scrollTop, scrollLeft });
        this.secondGridShadow.current.scrollTo({ scrollTop, scrollLeft });
    }
}

@alonrbar, это увлекательно! Я скоро попробую.

Похоже, что теневая сетка живет над реальной сеткой, да? Если так, события щелчка на «реальной сетке» не будут работать, не так ли? Я полагаю, вы могли бы сделать что-нибудь немного взломанное с событиями щелчка + координаты x / y и как-то применить это к основной сетке.

Также @barbalex re: тоже не работает в окнах

На самом деле это может быть хромированный флаг. Посмотрите, есть ли что-нибудь в chrome: // flags. Возможно, Microsoft реализовала нечто подобное.

Проблема определенно связана с тем, что прокрутка «сглаживается» быстрее, чем кадр анимации браузера. Если вас беспокоит, что это чисто производительность / задержка, попробуйте сделать очень простую реализацию синхронизации прокрутки (где при прокрутке одного div устанавливается положение прокрутки другого) и посмотрите, возникнет ли у вас такая же проблема. Возможно, даже сделайте это на чистом ванильном JS, чтобы исключить React как виновника

Аххх @ranneyd , ты прав, он блокирует события щелчка ... Надо еще подумать ...

Я думаю, что функция прокрутки Threaded в chrome: // flags имеет большое влияние: ее отключение делает прокрутку колесиком мыши примерно такой же плавной, как прокрутка с помощью полосы прокрутки для меня.

2019-07-15_00h03_42

@alonrbar как насчет этого:

Добавьте обработчик прокрутки в основную сетку. В нем вы делаете e.preventDefault(); или что-то еще, чтобы предотвратить фактическую прокрутку. Затем вы смотрите на событие, чтобы выяснить, сколько оно БУДЕТ прокручиваться, чтобы переместить другой синхронизированный материал, но затем вы используете это для ручной прокрутки того же элемента. Поэтому вместо прокрутки A, а затем использования этой информации для прокрутки B, вы перехватываете прокрутку на A, отменяете ее, а затем используете ее для прокрутки B и самой A. Это сработает?

Я за компьютером не для проверки. Довольно хакерский, но я мог работать. @bvaughn мысли?

@barbalex , ты прав, у меня это тоже работает, но я не могу, чтобы все мои пользователи включали и выключали хром-флаги 😔

@ranneyd Я только что изучил это и немного ссылка ), но, видимо, вы не можете отключить события прокрутки, единственное, что вы можете сделать, - это захватить все события колеса мыши, касания и клавиатуры, которые могут вызвать прокрутку и предотвратить их. В ссылке (SO) есть короткий фрагмент кода для этого, но это делает его еще более хакерским, поэтому я все еще думаю об этом ...

@alonrbar да, попросить пользователей сделать это не сработает. Вы _ можете_ дать им прямую ссылку, если _до_ хотите попробовать: chrome: // flags / # disable -threaded-scrolling

@alonrbar да, это нехорошо, но это не УЖАСНО, если мы просто применяем его к сетке, которую мы уже вместе взламываем.

Это просто колесо прокрутки, верно? Клавиши со стрелками тоже делают это?

Это фрагмент из принятого ответа здесь: https://stackoverflow.com/questions/4770025/how-to-disable-scrolling-tempohibited

function disableScroll() {
  if (window.addEventListener) // older FF
      window.addEventListener('DOMMouseScroll', preventDefault, false);
  document.addEventListener('wheel', preventDefault, {passive: false}); // Disable scrolling in Chrome
  window.onwheel = preventDefault; // modern standard
  window.onmousewheel = document.onmousewheel = preventDefault; // older browsers, IE
  window.ontouchmove  = preventDefault; // mobile
  document.onkeydown  = preventDefaultForScrollKeys;
}

Как видите, необходимо обработать несколько событий и рассмотреть возможные последствия этого.

На данный момент, поскольку я не могу уделять больше времени попыткам решить эту проблему прямо сейчас, я решил использовать невиртуальное решение (которое тоже не идеально, но лучше подходит для моих случаев использования). Вы можете найти код, который я использую здесь и здесь (это часть написанной мной библиотеки, которая обертывает react-window ).

@alonrbar да, я согласен, что решение SO довольно неприятное.

Однако я нахожу ваше решение очень интересным. Я собираюсь посмотреть, смогу ли я заняться этим самостоятельно и заставить его работать с виртуализированной таблицей.

@alonrbar, поэтому я сделал scrollTo или scrollTop = ... , которые, как я обнаружил, не причиняли мне много горя. Вдобавок к этому полосы прокрутки ВСЕГДА находятся за пределами ВСЕЙ сетки.

Эта вещь, которую я сделал, может динамически иметь "замороженные заголовки" со всех сторон. Это ОЧЕНЬ грубый пример / poc.

Очевидно, что в нем отсутствует виртуализация, что является серьезной проблемой. К сожалению, я не знаю, могу ли я обернуть в нее сетку из этой библиотеки, поскольку эта библиотека в основном работает при прокрутке, и это устраняет прокрутку внутренней сетки. Не уверен, что делать дальше.

https://codesandbox.io/embed/non-virtual-scroll-synced-table-ot467

Я думаю, что к этому можно применить ту же логику виртуализации, которая питает эту библиотеку.

@ranneyd очень крутая реализация!

Мне нравится динамическая композиция сетки :)

Но самое главное идея использования абсолютного позиционирования вместо прокрутки может быть ключом к решению этой проблемы. Введя еще один div, вы отключили событие onscroll от фактической прокрутки контента, что, как мне кажется, открывает новые возможности.

Если бы вы могли вклиниться в react-window «ы render метода вы можете заменить строки 459 и с вашей реализации. Затем вы сможете подключить событие onscroll и при необходимости отключить его эффект (полосы прокрутки всегда будут перемещаться, но вы можете управлять содержимым, чтобы предотвратить его изменение).

@alonrbar, поэтому я фактически повторно реализовал алгоритм виртуализации. Это не так хорошо, но если посмотреть на исходный код, когда эта сетка доходит до фактической виртуализации, это в основном тот же алгоритм. Однако производительность на самом деле достойная. Я могу получить сетку 200x200 без каких-либо задержек, а 500x500 - всего лишь немного. Если мой босс позволит мне еще поработать над этим, я попытаюсь реализовать реализацию метода рендеринга, как вы предложили, что, вероятно, на самом деле довольно просто.

Если вам интересна созданная мною сетка, я могу выложить ее где-нибудь. Если вы хотите взять его и подключить к этой библиотеке, тогда будь моим гостем 😏

@ranneyd да, я хотел бы увидеть созданное вами решение виртуализации. Не знаю, воспользуюсь ли я им, но было бы, конечно, интересно :)

Кроме того, я пытался использовать ваш код сегодня, чтобы внести изменения в метод рендеринга, как я предлагал, но после повторного чтения вашего кода я заметил, что вы на самом деле не отключили прокрутку, как я думал. Я имею в виду, что полосы прокрутки и событие onscroll по-прежнему неотделимы от фактической прокрутки содержимого. Я продвинул вашу идею дальше и реализовал действительно отключенную прокрутку:

https://codesandbox.io/embed/absolute-position-scrolling-1u7vj

Если вы посмотрите на код, то увидите, что если мы удалим свойства top и left из компонента Content содержимое не будет прокручиваться, даже если полосы прокрутки делать . Я также убедился, что на этот раз события мыши не заблокированы. 😂
Я считаю, что теперь можно использовать эту реализацию, чтобы игнорировать событие onscroll и вручную синхронизировать несколько сеток. Но это, конечно, требует дополнительной работы, так что придется подождать до следующего раза ...

Это обсуждение вызвало у меня любопытство, поэтому я настроил проект, чтобы сравнить два окна реакции с синхронизацией прокрутки Grid s с виртуализированным реактивным MultiGrid , чтобы я мог понять, как сравнивается производительность :
https://github.com/bvaughn/react-window-vs-react-virtualized-synced-grids

Я старался сделать каждое использование максимально похожим. Некоторые первоначальные наблюдения:

  • response-window выглядит заметно медленнее в режиме DEV, но кажется немного быстрее / более отзывчивым в производственной сборке. (Здесь трудно исключить предвзятость с моей стороны. Разница в производстве меньше.)
  • response-window также делает намного больше коммитов, поскольку событие прокрутки сначала обновляет активную сетку, а затем пассивную сетку посредством каскадного обновления. Я сделал патч для Grid чтобы поддерживать передачу "собственного" (React) обработчика событий прокрутки (который будет вызываться в пакетном обновлении React), а не текущего onScroll (который вызывается во время фиксации). фаза). Это кажется довольно многообещающим изменением, когда я тестировал его локально, поскольку он позволяет избежать отдельных каскадных отрисовок, поэтому, возможно, я просто изменю время по умолчанию onScroll .

@bvaughn Я попытался создать самый простой, ванильный код с эффектом прокрутки с помощью divA -> onScroll -> setState -> ref.scrollTop, и он все еще не мог обойти эту хромированную прокрутку с резьбой. По общему признанию, я не обошел состояние реакции и не установил ref.scrollTop в обработчике onScroll, но кроме этого я не могу придумать более простого способа сделать это. Если вы не можете получить onScroll -> scrollTop достаточно быстро за минимально возможное количество шагов, как это исправить? Я что-то полностью упускаю? Кажется, что сетки не должны перемещаться на основе прокрутки (или установки scrollTop).

Цель всегда состоит в том, чтобы JavaScript работал как можно быстрее, чтобы не отставать от прокрутки управления потоком.

По общему признанию, я не обошел состояние реакции и не установил ref.scrollTop в обработчике onScroll

Чтобы быть ясным, мое репо тоже не этим занимается. Я просто устанавливаю смещение прокрутки в пассивной сетке в том же обработчике событий (с оболочкой React), что и активная сетка, поэтому React объединяет свои обновления в один рендеринг + фиксацию. Честно говоря, это, вероятно, не имеет большого значения.

@ranneyd и @bvaughn делятся с вами моими наблюдениями на данный момент:

  1. Многие реализации достаточно хорошо работают в настольном браузере, но мой основной вариант использования на самом деле на мобильных устройствах, и там я вижу огромные различия в производительности.

  2. Синхронизация двух простых не виртуальных сеток работает довольно хорошо (не на 100% идеально, но достаточно близко, даже на мобильных устройствах). Это моя наивная, но работающая реализация и тестовый пример, чтобы увидеть ее в действии ( yarn storybook ).

  3. Использование «контролируемой» прокрутки (как я предлагал в этом комментарии ) обеспечивает синхронизацию обеих сеток на 100%, но это довольно медленно на рабочем столе и слишком медленно на мобильном телефоне. Это моя трещина (очень грубая ...): https://github.com/alonrbar/react-window/commit/c39ce7006dbd590d9c640e37f8a1e78826e4688e

    Тем не менее, мне хочется посмотреть, может ли «контролируемая» стратегия каким-то образом работать для решения этой проблемы в единственном виртуальном списке.

  4. Я подумал, как вы уже предположили, что, возможно, перемещение _callPropsCallbacks непосредственно в обработчик _onScroll может помочь уменьшить задержку синхронизации до минимума. РЕДАКТИРОВАТЬ - просто попробовал сейчас, не особо помогает 😞

  5. В любом случае, вы решаете это, хорошая идея ИМХО - отделить логику виртуализации от остальной части компонента (может быть, даже от крючка?), Чтобы рендеринг и материал onscroll можно было обрабатывать отдельно, легко сравнивать и даже переключать во время выполнения на основе компонентные реквизиты. Это также позволит экспортировать эту логику и позволит пользователям реализовывать собственные компоненты (например, решение @ranneyd в этом комментарии ) на основе той же логики.

Мысли?

@alonrbar мое решение, которое выполняет абсолютное позиционирование, поэтому ни одна из сеток не выполняет прокрутку, работает очень хорошо примерно до 300x300, где оно становится немного вялым (обратите внимание, что оно остается синхронизированным 100% времени, прокрутка просто немного тормозит). При больших размерах я думаю, что это просто обработка / отображение большого массива. Думаю, есть несколько оптимизаций
Однако я могу это сделать, и я не совсем уверен, что это как-то связано с синхронизацией прокрутки, а не с моей довольно простой реализацией виртуализации. Я могу, например, сделать больше кэширования, и я могу вычислить, должна ли ячейка быть виртуализирована или не раньше, чтобы вообще не вызывать функцию рендеринга (а затем, возможно, лучше кэшировать).

Я ничего не тестировал на мобильном телефоне. Я предоставлю вам код, чтобы вы немного опробовали его.

Очень хочу посмотреть тест @bvaughn . Он говорит, что подключение непосредственно к собственному свитку исправляет это, а вы говорите, что нет. Я хочу убедиться в этом сам.

Что касается втягивания логики виртуализации в ловушку или независимую функцию, это становится довольно сложно, потому что логика внутренне связана с представлением. Также кажется, что многие настройки производительности связаны с кешированием и запоминанием, которые может быть трудно инкапсулировать в один крючок или функцию, или, по крайней мере, до такой степени, чтобы они получали ту же производительность. Но я посмотрю, что у меня есть, и посмотрю, сколько логики я смогу извлечь.

PS:
Одна вещь, о которой я только что подумал, которая, вероятно, не сработает, - это сделать что-то вроде денонсирования + переходы css. Если мы выполняем только одно событие прокрутки каждые 100 мс или что-то в этом роде и анимируем движение, это могло бы выглядеть лучше. Это также может показаться значительно менее отзывчивым. Это похоже на то, как они сделали World of Warcraft (если есть задержка или большая задержка, они заставят персонажа двигаться по прямой, а затем исправят это, как только получат информацию о том, куда он на самом деле пошел).

Очень хочу посмотреть тест @bvaughn . Он говорит, что подключение непосредственно к собственному свитку исправляет это, а вы говорите, что нет. Я хочу убедиться в этом сам.

Я этого не говорил: smile: Я просто сказал, что это позволяет избежать лишнего ненужного рендеринга и мутации DOM. Мне неясно, насколько это повлияет на реальную производительность. Тем не менее, это действительно похоже на общее положительное изменение.

@alonrbar @bvaughn Мне действительно нужно задокументировать свой код, но вот последняя версия:

https://github.com/ranneyd/synced-table

@alonrbar @bvaughn, вот забавная вещь, которую я только что обнаружил:

Мое решение НЕ работает в Chrome 75 на экране MacBook. Когда у моих коллег не было обновлений Chrome, это работало. Если они используют внешний монитор, он работает. На экране их ноутбука лагает.
😩

Хм ... есть некоторые отличия от внешних мониторов, с частотой обновления или масштабированием. Когда вы говорите, что "не работает", что вы имеете в виду конкретно?

Виноват. То есть прокрутка уже не синхронизируется. Если вы клонируете мое репо и запускаете его, вы можете сравнить внешний монитор с экраном ноутбука. На мониторе они идеально синхронизированы. На экране ноутбука мигают заголовки (не обновляются с той же скоростью, что и элемент прокрутки).

На самом деле я сдался и пробую с position: sticky . Это действительно работает. Поддержка браузеров не на 100%, но на самом деле она намного лучше, чем была год назад.

Есть эта библиотека, которая не является полифиллом, который может работать, но браузеры, на которые мы нацелены, поддерживают эту функцию.
https://github.com/dollarshaveclub/stickybits

@alonrbar @bvaughn вот последняя версия. Он использует position: sticky . Немного поясняю в ридми. Код еще нужно задокументировать.

https://github.com/ranneyd/sticky-table

Мое решение НЕ работает в Chrome 75 на экране MacBook. Когда у моих коллег не было обновлений Chrome, это работало. Если они используют внешний монитор, он работает. На экране их ноутбука лагает.
😩

@ranneyd Сегодня мне пришло в голову, что это может быть связано с частотой кадров. Частота кадров для внешнего монитора может составлять, например, 30 кадров в секунду, и в этом случае я думаю, что браузер может отправлять меньше событий «прокрутки» (поскольку я думаю, что большинство браузеров отправляют только одно для каждого кадра / отрисовки). Может поэтому у вас на внешнем мониторе лаг более заметен?

@bvaughn Думаю, так и должно быть. ОС ГОВОРИТ, что отправляет 60 Гц, и я ДУМАЮ, что характеристики монитора говорят о 60 Гц, но меня не удивит, если кто-то лжет 😂

@ranneyd Мне жаль, но я не очень доступен прямо сейчас, поэтому я не мог должным образом взглянуть на ваше решение, но с первого взгляда я заметил, что оно выглядит довольно аккуратно, и что вы создали useVirtual и элегантно отделили логику от рендеринга.
Я думаю, было бы здорово, если бы вы могли каким-то образом создать запрос на перенос для react-window , использовать его там и, возможно, предоставить какой-то метод renderTable в который вы могли бы подключить свою логику рендеринга. Таким образом, ваша библиотека может просто обернуть окно реакции вместо его замены. Я считаю, что было бы предпочтительнее, если бы мы могли использовать этот подход и решить проблему внутри react-window который уже в значительной степени протестирован и широко распространен.

С другой стороны, я думаю, что если scrollTo будет использовать прямую манипуляцию с DOM (возможно, в дополнение к настройке состояния, но в любом случае до этого), то результат синхронизации двух таблиц станет более плавным. Если я не ошибаюсь, @bvaughn тоже что-то предлагал в этом направлении.

@ranneyd, твое npm ?

@bvaughn @ranneyd

Итак, по прошествии некоторого времени я недавно вернулся к этой проблеме. Используя код и идеи из ваших библиотек, а также из recyclerlistview и react-virtual-grid, я наконец смог добиться желаемого поведения, то есть высокопроизводительной сетки с фиксированными строками и столбцами, которая хорошо работает как на настольных компьютерах, так и на мобильных устройствах. устройства (плавная прокрутка без пустых / белых ячеек).

TL; DR заключается в том, что я использую переработку вместе с липким позиционированием, и самый интересный фрагмент кода можно найти здесь, в этом методе .

Кредиты и многое другое о мотивации написания еще одного решения здесь . Спасибо!

Я оставлю здесь ссылку на мой рабочий пример песочницы кода на случай, если кто-то еще столкнется с этой проблемой в будущем.
https://codesandbox.io/s/y3pyp85zm1

Очень хорошо, пока вы полностью не прокрутите вправо: тогда заголовки не совпадают с содержимым (по ширине вертикальной полосы прокрутки прокрутки).
Вы бы случайно нашли решение этого?
Спасибо

Я оставлю здесь ссылку на мой рабочий пример песочницы кода на случай, если кто-то еще столкнется с этой проблемой в будущем.
https://codesandbox.io/s/y3pyp85zm1

Очень хорошо, пока вы полностью не прокрутите вправо: тогда заголовки не совпадают с содержимым (по ширине вертикальной полосы прокрутки прокрутки).
Вы бы случайно нашли решение этого?
Спасибо

Полное раскрытие: я фактически сделал свою собственную версию с нуля. У него есть собственная виртуализация, в значительной степени основанная на этой библиотеке. Причина, по которой я это сделал, заключалась в том, что есть проблема на MacBook и с некоторыми флагами Chrome, когда анимация для прокрутки происходит с другим временем, чем JS. Использование ScrollSync требовало слишком большого количества передаваемых вызовов функций и было слишком медленным. По сути, мне пришлось переделать библиотеку с прикрепленными заголовками в ее ядре (я не использовал position: sticky как я сказал ранее в этом потоке. В какой-то момент мне нужно загрузить то, что у меня есть сейчас).

Тем не менее, я могу обойти эту проблему либо с помощью overflow: scroll чтобы заставить полосу прокрутки, а затем добавить это заполнение к последней ячейке, либо я динамически определяю, есть ли полоса прокрутки (и какова ее ширина), используя ref и поместив в него невидимый div, измерив размер полосы прокрутки и удалив узел DOM.

Я использовал overflow-y: overlay который накладывает полосу прокрутки. К сожалению, это работает только для браузеров webkit.

Хорошо, спасибо за информацию. На самом деле кажется, что липкие заголовки настолько распространены, что простое добавление параметра липкой первой строки в response-window во многих других случаях сделает его заменой react-virtualized. Вы так не думаете?

@ranneyd еще раз спасибо, я следил за вашим подходом, и синхронизация прокрутки работает нормально.

Скрытое переполнение полос прокрутки вызывает смещение ближе к концу сетки:

gridalignement

Есть предложения, как это исправить?

CodeSandbox Пример

заранее спасибо

Поздно к вечеринке, но если вы просто добавите одну строку в leftRef и поместите ее переполнение в скрытое, вы в основном решите pb. (Я сделал это также для того, чтобы не синхронизировать leftRef -> main Grid

  const headerRef = React.useRef();
  const leftRef   = React.useRef();

  return <Box classes={{
            root:classes.tableContainer
          }}>
      <AutoSizer>
        {({ height, width }) => (<>
        {/*---------------- LA TABLE -------------*/}
          <Grid
            columnCount={1000}
            columnWidth={100}
            height={height}
            rowCount={1000}
            rowHeight={35}
            width={width}
            onScroll={({ scrollLeft, scrollTop }) => {
              if (leftRef.current) {
                leftRef.current.scrollTo({ scrollTop });
              }
              if (headerRef.current) {
                headerRef.current.scrollTo({ scrollLeft });
              }
            }}
          >
            {({ columnIndex, rowIndex, style }) => (
              <Box style={style} classes={{root:classes.cell}}>
                Item {rowIndex},{columnIndex}
              </Box>
            )}
          </Grid>
          {/*---------------- HEADER -------------*/}
          <Grid
            ref={headerRef}
            outerElementType={React.forwardRef((props, ref) => (
              <div ref={ref}  {...props} style={{...props.style,position:"absolute",overflow:"hidden",top:0,right:0,left:150}} />
            ))}
            columnCount={1001}  /*columns count +1 for scroll problems*/
            columnWidth={100}
            height={60}
            rowCount={1}
            rowHeight={60}
            width={width}
          >
            {({ columnIndex, rowIndex, style }) => (
              <Box style={style} classes={{root:classes.headerCell}}>
                Header {rowIndex},{columnIndex}
              </Box>
            )}
          </Grid>  
          {/*---------------- LEFT COL -------------*/}
          <Grid
            ref={leftRef}
            outerElementType={React.forwardRef((props, ref) => (
              <div ref={ref}  {...props} style={{...props.style,position:"absolute",overflow:"hidden",top:60,left:0}} />
            ))}
            columnCount={1}
            columnWidth={150}
            height={height}
            rowCount={251} /** add 1 for scroll problems at the end */
            rowHeight={140}
            width={150}
          >
            {({ columnIndex, rowIndex, style }) => (
              <Box style={style} classes={{root:classes.headerCell}}>
                Left {rowIndex},{columnIndex}
              </Box>
            )}
          </Grid>  
        </>)}
      </AutoSizer>
    </Box>
Была ли эта страница полезной?
0 / 5 - 0 рейтинги