Динамическое текстовое представление редактора дом


Я создаю текстовый редактор, который потребуется для работы в браузере, подсветка синтаксиса, и позволит пользователям редактировать файлы размером до 50 МБ в размер (т. е. пространство разрешенных индексированные базы данных) без "недостаточно памяти" ошибки и без перевода часов, чтобы загрузить. Мое решение является просто создать эффективный парсер, который динамически визуализации, заменив текст, как пользователь прокручивает файл на основе дружище-парных горизонтальных панелей, и каждая строка замены вверх и вниз (утилизации), как вы выполните прокрутку вверх и вниз. На все мои текущие знания дом, это должно быть чертовски быстр, однако вполне очевидно, зверски медленно в Chrome.

Редактировать: я не знаю, почему, мой код имеет горький неприязнь codeReview (это кирпичи странице по какой-то причине, когда я пытаюсь запустить его в код фрагмента). Так что, пожалуйста, иди к jsfiddle.net чтобы увидеть мой код. Я прошу прощения за неудобства. Ниже-это статическая копия кода (я не осмелюсь позволить вам запустить его на codereview, потому что, конечно, это, вероятно, кирпич тоже вашу страницу).

"use strict";
function getCharWidth(blockcontainer){
    var str = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`~!@#$%^&*()_+-=[]{};\':"|\\?,./ ';
    var ele = document.createElement("span");
    ele.style.display = "inline-block";
    ele.textContent = str;
    blockcontainer.appendChild(ele);
    var width = ele.getBoundingClientRect().width / str.length;
    blockcontainer.removeChild(ele);
    return width;
}
function renderText(ele, text){
    var charWidth = getCharWidth(ele);
    var charHeight = parseFloat(getComputedStyle(ele).fontSize);
    var textData = [], /*dataCur = textData[0],*/ i=0, Len = text.length, curStack="", /*curLine=0,*/ curChar="";
    var maxLineWidth = 0;
    while (i !== Len){
        curChar = text[i];
        if (curChar === "\n" || curChar === "\r") {
            textData.push(curStack);
            maxLineWidth = Math.max(maxLineWidth, curStack.length);
            /*++curLine;*/
            curStack = "";
        } else {
            curStack += curChar
        }
        ++i;
    }
    var horozontalBufferLen = 2;
    //var pageElements = []; // for the two horozontal panels
    var lineElements = []; // for vertically shifting lines
    var width = Math.ceil(window.innerWidth / charWidth);
    var widthInPX = width * charWidth;
    var widthOverPX = Math.floor(widthInPX - (window.innerWidth / charWidth) * charWidth);
    var height = Math.ceil(window.innerHeight / charHeight);
    //var heightInPX = width * charHeight;
    //var heightOverPX = Math.floor(heightInPX - (window.innerHeight / charHeight) * charWidth);
    var heightOverPX = Math.floor(window.innerWidth % charHeight);
    var j = 0, i = 0, v = 0;
    while (j !== horozontalBufferLen){
        console.log("j going");
        var jCurLineArray = [];
        lineElements.push(jCurLineArray);
        i = 0;
        while (i !== height){
            var curElement = document.createElement("div");
            curElement.style.position = "absolute";
            curElement.style.top = i * charHeight + "px";
            var curCharArray = [];
            v = 0;
            while (v !== width){
                var curLetter = document.createElement("div");
                curLetter.style.position = "absolute";
                curLetter.style.top = "0px";
                curLetter.style.left = v * charWidth + "px";
                curLetter.style.color = "#111";
                var curText = document.createTextNode("");
                curCharArray.push(curText);
                curLetter.appendChild(curText);
                curElement.appendChild(curLetter);
                curElement
                ++v;
            }
            curElement.style.position = "absolute";
            curElement.style.left = 0;
            curElement.style.top = charWidth * i;
            ele.appendChild(curElement);
            jCurLineArray.push([curCharArray, curElement]);
            ++i;
        }
        ++j;
    }
    ele.insertAdjacentHTML("beforeend", '<div style="width:' + (maxLineWidth*charWidth) + 'px;height:' + (textData.length*charHeight) + 'px;pointer-events:none"></div>'); // this is just the placeholder to preserve the scrolling space
    console.log(lineElements);
    j = 0;
    var currentScrollTop = -0x7fffffff, currentScrollLeft = -0x7fffffff;
    function refreshLine(arr, top, left){ // refreshes a line of text shown to the user
        var lineData = textData[top];
        if (lineData){
            var lineLen = lineData.length;
            arr.forEach(function(txtNode, index){
                var newVal = (index+left) < lineLen ? lineData[index+left] : " ";
                if (txtNode.nodeValue !== newVal) txtNode.nodeValue = newVal;
            });
        } else {
            arr.forEach(function(txtNode, index){
                if (txtNode.nodeValue !== " ") txtNode.nodeValue = " ";
            });
        }
        // refresh a quadrent of the screen
    }
    var isCurrentlyGoing = false;
    function whenScroll(){
        var newScrollTop = ele.scrollTop;
        var newScrollLeft = ele.scrollLeft;

        var oldScrollTopSect  = Math.floor(currentScrollTop  / charHeight );
        var newScrollTopSect  = Math.floor(newScrollTop      / charHeight );
        var oldScrollLeftSect = Math.floor(currentScrollLeft / widthInPX  );
        var newScrollLeftSect = Math.floor(newScrollLeft     / widthInPX  );

        if (oldScrollTopSect === newScrollTopSect && oldScrollLeftSect === newScrollLeftSect) return;

        // to avoid having to update if the update is not needed
        //var needsonlyOneTop = newScrollTop % charHeight <= heightOverPX;
        var needsonlyOneLeft = newScrollLeft % widthInPX <= widthOverPX;

        var scrollLeftSectDiff = newScrollLeftSect - oldScrollLeftSect;
        if (scrollLeftSectDiff > 0){
            // user scrolled to the right ===>

        } else if (scrollLeftSectDiff < 0) {
            // <=== user scrolled to the left

        }

        var scrollTopSectDiff = newScrollTopSect - oldScrollTopSect;
        if (scrollTopSectDiff < 0){
            // user scrolled up
            var changedElementsFirst = lineElements[0].splice(scrollTopSectDiff);
            var changedElementsSecnd = lineElements[1].splice(scrollTopSectDiff);
            Len = lineElements[0].length, i = 0;
            lineElements[0].unshift.apply(lineElements[0], changedElementsFirst);
            lineElements[1].unshift.apply(lineElements[1], changedElementsFirst);
            while (i !== Len) {
                refreshLine(lineElements[0][i][0], newScrollTopSect+i, 0);
                lineElements[0][i][1].style.top = (newScrollTopSect+i) * charHeight + "px";
                i++;
            }
        } else {
            // user scrolled down
            var changedElementsFirst = lineElements[0].splice(0, scrollTopSectDiff);
            var changedElementsSecnd = lineElements[1].splice(0, scrollTopSectDiff);
            Len = lineElements[0].length, i = Len;
            lineElements[0].push.apply(lineElements[0], changedElementsFirst);
            lineElements[1].push.apply(lineElements[1], changedElementsFirst);
            while (i !== height) {
                refreshLine(lineElements[0][i][0], newScrollTopSect+i, 0);
                lineElements[0][i][1].style.top = (newScrollTopSect+i) * charHeight + "px";
                i++;
            }
        }
        currentScrollTop, currentScrollLeft = newScrollTop, newScrollLeft;
        isCurrentlyGoing = false;
    }
    ele.addEventListener("scroll", function(){
        if (!isCurrentlyGoing){
            isCurrentlyGoing = true;
            requestAnimationFrame(whenScroll);
        }
    }, {passive: 1});
    requestAnimationFrame(whenScroll);
    //ele.appendChild( document.createTextNode(text) );
}
///////////////////////////////////////////////////////////
//// The code below generates the example text content ////
///////////////////////////////////////////////////////////
var numToWords = (function(){
    const num = x => Number(x) || 0;
    const isEmpty = xs => xs.length === 0;
    const reverse = xs => xs.slice(0).reverse();
    const comp = f => g => x => f (g (x));
    const not = x => !x;
    const chunk = n => xs =>
    isEmpty(xs) ? [] : [xs.slice(0, n), ...chunk (n) (xs.slice(n))];
    const a = [
        '', 'one', 'two', 'three', 'four',
        'five', 'six', 'seven', 'eight', 'nine',
        'ten', 'eleven', 'twelve', 'thirteen', 'fourteen',
        'fifteen', 'sixteen', 'seventeen', 'eighteen', 'nineteen'
    ];
    const b = [
        '', '', 'twenty', 'thirty', 'forty',
        'fifty', 'sixty', 'seventy', 'eighty', 'ninety'
    ];
    const g = [
        '', 'thousand', 'million', 'billion', 'trillion', 'quadrillion',
        'quintillion', 'sextillion', 'septillion', 'octillion', 'nonillion'
    ];
    // this part is really nasty still
    // it might edit this again later to show how Monoids could fix this up
    const makeGroup = ([ones,tens,huns]) => {
        return [
            num(huns) === 0 ? '' : a[huns] + ' hundred ',
            num(ones) === 0 ? b[tens] : b[tens] && b[tens] + '-' || '',
            a[tens+ones] || a[ones]
        ].join('');
    };
    const thousand = (group,i) => group === '' ? group : group + " " + g[i];
    return n => {
        // "thousands" constructor; no real good names for this, i guess
        // execute !
        if (typeof n === 'number') return numToWords(String(n));
        if (n === '0')             return 'zero';
        return comp (chunk(3)) (reverse) (Array.from(n))
        .map(makeGroup)
        .map(thousand)
        .filter(comp(not)(isEmpty))
        .reverse()
        .join(' ');
    };
})();
var textToBinary = (new TextEncoder()).encode;
var binaryToText = (new TextDecoder()).decode;
var textToRender = "", i=256;
while (i--) {
    textToRender += numToWords(i**4) + "\n";
}
var renderElement = document.getElementById("codingarea");
renderText(renderElement, textToRender);
html, body {
    width: 100%;
    height: 100%;
    margin: 0;
}
#codingarea {
    position: relative;
    top: 0; bottom: 0;
    left: 0; right: 0;
    width: 100%;
    height: 100%;
    overflow: auto;
    overflow-y: scroll;
    white-space: pre;
    font-family: monospace;
    line-height: 1em;
    font-family: Cousine, monospace;
    line-height: 1em;
}
<div id="codingarea"></div>

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

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

* Примечание * Обратите внимание, что мой следующий код выше имеет только вертикальную прокрутку получилось. Все, что меня волнует на данный момент-это выступление, прежде чем я продолжу с остальными моими кода. Другая вещь, чтобы отметить, что есть причина, почему я делаю каждое письмо индивидуально, а не писать всю строку в одну развертки: подсветка синтаксиса. Каждая буква должна быть в состоянии быть окрашены самостоятельно. Я готов попробовать другие методы, имеющие каждая буква окрашена самостоятельно.



Комментарии
1 ответ

Перезагрузка

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

Как развивающееся приложение, я бы сказал, остановить и запустить снова, еще какие-то работы на нем, так как он сейчас просто толкает коричневый материал Хилл.

Пример плохой.


  • Пользовательский интерфейс является алогичным. Выбрав линии обрезает их на копии.

  • Элемент DOM для каждого персонажа! OMDG чем ты думал???

  • Хранение документа в контексте вы по оперативной памяти, это второй экземпляр, один на диске/хранения в БД. Затем приложение создает копию, (50МБ текст документа вау!! много читать) и тогда первое, что вы делаете в функции двойной+ использование оперативной памяти, сделав копию его снова ('столбца textdata' массив), а потом не удалив ненужные оригинальные. Нет глаз, и никогда не сможет прочитать все 25 000 000 миллионов символов, но вы поддерживаете 3-х экземплярах.

Да 50МБ ничего в эти дни, то есть если у вас низкий конец устройство работает на почти разряженную батарею, или приоритет приложения совместного использования ресурсов.

Несколько советов


  • Вы не должны хранить весь документ.

    Проблемы большого набора данных решается в источнике данных. Вам нужно организовать данные, так что вы можете быстро получить представления (может быть проиндексированы по номеру строки), Когда пользователь меняет вид (например, свиток), то принести/запрос к БД только в соответствующие строки.


  • Выделите строки символов, а не отдельных персонажей например "my text has <span class="highlight">highlighted</span> text"

  • Разрешить браузеру обрабатывать тяжелую работу, как стили и горизонтальной прокрутки.

Смотри и учись

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

Некоторые источники примеры кода поиска Google, github, а также МДН (МДН индексируются ссылки ДБ API будет иметь некоторые извлечения данных примерах я уверен) и даже на странице скрипки, которые вы дали. Код окна подсветки синтаксиса.

Ура

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

3
ответ дан 27 марта 2018 в 05:03 Источник Поделиться