Очень медленные сборки React Native UI при добавлении / удалении текстовых элементов - PullRequest
0 голосов
/ 06 октября 2019

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

Мой подход к отбраковке - просто запустить setState, когда нужно показать разные элементы. Это работает, но поток JS падает почти до 0 FPS, и очередь setStates помещается в очередь, поэтому очень неприятно наблюдать, как отбраковка делает свое дело долго после того, как вы перестанете панорамировать.

Это мой код для части сетки(MapState управляет картой с помощью Reanimated):

import React, { useState, useEffect } from 'react';
import { View, Text } from 'react-native';
import Animated from 'react-native-reanimated';
import { cellSize, convertToMapPosition } from '../../game/Grid';
import { useMapState } from './MapState';

const { add, multiply } = Animated;

export function GridLayer() {
    const mapState = useMapState();
    const cellDims = cellSize * mapState.imageScale;

    const [state, setState] = useState(() => ({
        timer: startCulling(),
        showLabels: false,
        minX: 0,
        maxX: 0,
        minY: 0,
        maxY: 0,
    }));

    useEffect(() => {
        return () => clearInterval(state.timer);
    }, []);

    function startCulling() {
        let prevState = {} as typeof state;
        let inFlight =  false;

        function buildCullingState() {
            const scale = mapState.currentScale;

            const minXPos = -mapState.currentX / scale;
            const minYPos = -mapState.currentY / scale;
            const maxXPos = minXPos + (mapState.viewWidth / scale);
            const maxYPos = minYPos + (mapState.viewHeight / scale);

            const minX = Math.max(Math.floor(minXPos / cellDims) - 1, 0);
            const maxX = Math.max(Math.ceil(maxXPos / cellDims) + 2, 0);
            const minY = Math.max(Math.floor(minYPos / cellDims) - 1, 0);
            const maxY = Math.max(Math.ceil(maxYPos / cellDims) + 2, 0);

            const cellScreenSize = cellDims * scale;
            const showLabels = cellScreenSize >= 80;

            return { ...state, showLabels, minX, maxX, minY, maxY };
        }

        function doCulling() {
            if (inFlight) {
                return;
            }

            let newState = buildCullingState();
            if (newState.showLabels != prevState.showLabels ||
                newState.minX != prevState.minX ||
                newState.maxX != prevState.maxX ||
                newState.minY != prevState.minY ||
                newState.maxY != prevState.maxY
            ) {
                inFlight = true;
                setState(() => {
                    inFlight = false;
                    return buildCullingState();
                });
            }

            prevState = newState;
        }

        return setInterval(doCulling, 15);
    }

    const cellCount = Math.floor((mapState.imageWidth + cellDims - 1) / cellDims);
    const cellOffsets = [];
    for (let i = 0; i < cellCount; i++) {
        cellOffsets.push(i * cellDims);
    }
    const cellOffsetsW = cellOffsets.slice(state.minX, state.maxX);
    const cellOffsetsH = cellOffsets.slice(state.minY, state.maxY);

    const posWidth = 40;
    const posHeight = 25;
    const posAdjustX = multiply(posWidth, add(-1, mapState.scaleInv), 0.5);
    const posAdjustY = multiply(posHeight, add(-1, mapState.scaleInv), 0.5);

    return (
        <View style={{ position: 'absolute', width: 8192, height: 8192 }}>
            {cellOffsetsW.map(x =>
                <Animated.View
                    key={`X-${x}`}
                    style={{
                        position: 'absolute',
                        left: x,
                        top: 0,
                        width: mapState.scaleInv,
                        height: mapState.mapHeight,
                        backgroundColor: '#0007',
                    }} />
            )}

            {cellOffsetsH.map(y =>
                <Animated.View
                    key={`Y-${y}`}
                    style={{
                        position: 'absolute',
                        left: 0,
                        top: y,
                        width: mapState.mapWidth,
                        height: mapState.scaleInv,
                        backgroundColor: '#0007',
                    }} />
            )}

            {state.showLabels && cellOffsetsW.flatMap(x => cellOffsetsH.map(y =>
                <Animated.View
                    key={`L-${x}-${y}`}
                    style={{
                        position: 'absolute',
                        left: x,
                        top: y,
                        width: posWidth,
                        height: posHeight,
                        margin: 4,
                        transform: [
                            { translateX: posAdjustX },
                            { translateY: posAdjustY },
                            { scale: mapState.scaleInv },
                        ],
                    }}>

                    <Text style={{ color: '#000a' }}>
                        {convertToMapPosition(x / mapState.imageScale, y / mapState.imageScale)}
                    </Text>
                </Animated.View>
            ))}

        </View>
    );
}

Я пробовал несколько вещей, чтобы попытаться уменьшить количество вызовов setState, но не дал очень хороших результатов с ним.

Что я могу сделать, чтобы ускорить это? Возможно переработка старых элементов? Разные подходы я мог бы использовать? Я хочу остаться в Экспо, но до сих пор я пытался использовать React Native Maps (моя карта не в нужном формате листов, не имею представления о сетках) и сам отображал ее с помощью GLView + Pixijs (не удалось получитьвход работает).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...