68
Редактор Mail.ru Александр Русаков, Ведущий программист

Редактор Mail.ru. Frontend

Embed Size (px)

Citation preview

Page 1: Редактор Mail.ru. Frontend

Редактор Mail.ru

Александр Русаков, Ведущий программист

Page 2: Редактор Mail.ru. Frontend

Редактор

Page 3: Редактор Mail.ru. Frontend

• Просмотр документов

• Превью документов

• Редактирование документов

Задачи

Page 4: Редактор Mail.ru. Frontend

• Просмотр документов

• Превью документов

Как решить задачу «в лоб»?

Page 5: Редактор Mail.ru. Frontend

Этапы обработки документа

Получение модели

документа от сервера

Расчёт документа

Генерация HTML

Page 6: Редактор Mail.ru. Frontend

Модель

{

"sections": [{

"elements": [{

"type": "paragraph",

"elements": [{

"type": "text",

"text": "Test\n",

"font": {

"bold": true

}

}],...

Page 7: Редактор Mail.ru. Frontend

Не подходит для редактирования:

• Нельзя разделить на части

• Нет простого механизма адресации по

вложенной структуре

Модель. Минусы

Page 8: Редактор Mail.ru. Frontend

Модель. Адресация

{

"sections": [{

"elements": [{

"type": "paragraph",

"elements": [{

"type": "text",

"text": "Test \n",

"font": {

"bold": true

}

}],...

Как адресовать символ?

Page 9: Редактор Mail.ru. Frontend

Этапы обработки документа

Получение модели

документа от сервера

Расчёт документа

Генерация HTML

Page 10: Редактор Mail.ru. Frontend

Заливка у слов разной высоты

Картинки «вокруг рамки»

Расчет документа. Зачем?

Page 11: Редактор Mail.ru. Frontend

Браузер width

Chrome 38 285.53…

Firefox 33 283.17…

IE 11 286

Opera 12.16 280

Расчет документа. Получение размера

ctx.font = '28pt Arial';

ctx.measureText('Редактор Mail.ru');

Размеры слов и букв различаются между браузерами

Page 12: Редактор Mail.ru. Frontend

Документ в разных браузерах

рассчитывается по разному

Расчет документа. Минусы

Firefox IE11

Page 13: Редактор Mail.ru. Frontend

Этапы обработки документа

Получение модели

документа от сервера

Расчёт документа

Генерация HTML

Page 14: Редактор Mail.ru. Frontend

HTML

Page 15: Редактор Mail.ru. Frontend

• Отображение текста различается между

браузерами

• Низкая скорость манипуляций с DOM

HTML. Минусы

Page 16: Редактор Mail.ru. Frontend

Исполняем js-код на сервере

НО PhantomJS:

• Медленный

• Нестабильный

Превью. PhantomJS

Page 17: Редактор Mail.ru. Frontend

Формулировка проблем

Использовали Проблема

Модель вложенная Нельзя разделить на части

Сложный механизм адресации

Размер шрифта вычисляется в

браузере

Разный размер букв и слов в

разных браузерах

HTML Разное отображение текста

Медленные манипуляции с DOM

PhantomJS Медленный

Нестабильный

Page 18: Редактор Mail.ru. Frontend

Отказываемся от получения модели от сервера.

От сервера принимаем набор команд.

Модель вложенная. Как разделить на части?

Page 19: Редактор Mail.ru. Frontend

Команды – низкоуровневые операции, изменяющие документ.

Например:

• TextInsert (start : number, text : string)

• TextDelete (start : number, end : number)

• ParagraphUpdate (end : number, props: Object)

• …

Команды

Page 20: Редактор Mail.ru. Frontend

Команды. Пример

TextInsert(0,'П')

TextInsert(1,'р')

TextInsert(2,'у')

TextInsert(3,'д')

Page 21: Редактор Mail.ru. Frontend

Команды. Пример

TextDelete(0, 1)

TextInsert(0,'Т')

Page 22: Редактор Mail.ru. Frontend

Приходят с сервера

Создаются на клиенте на каждое действие пользователя:

• Ввод текста

• Изменение свойств (шрифт, цвет и т.д.)

Команды. Откуда?

Page 23: Редактор Mail.ru. Frontend

Отправляются на сервер:

• По таймауту (автосохранение)

• По нажатию кнопки «Сохранить»

Команды. Куда?

Page 24: Редактор Mail.ru. Frontend

Модель вложенная. Как адресовать символ?

Модель вложенная

Параграф Таблица

Текст ЯчейкаТекст Ячейка

ТаблицаПараграф

ЯчейкаТекст

Page 25: Редактор Mail.ru. Frontend

Новая плоская модель

Модель плоская

Параграф 1 Параграф N

Текст

Таблица 1 Таблица M

Ячейка 1 Ячейка L

Page 26: Редактор Mail.ru. Frontend

Команды формируют модель

{

"text": "Hello, world!\n

}

TextInsert(0,'Hello, world!\n')

Page 27: Редактор Mail.ru. Frontend

Команды формируют модель на клиенте

RunUpdate(0, 4, {

bold: true

})

{

"text": "Hello, world!\n",

"runs": [{

"start": 0,

"end": 4,

"bold": true

}]

}

Page 28: Редактор Mail.ru. Frontend

Команды формируют модель на клиенте

{

"text": "Hello, world!\n",

"runs": [{

"start": 0,

"end": 4,

"bold": true

}],

"paragraphs": [{

"end": 13

}]

}

Paragraph(13)

Page 29: Редактор Mail.ru. Frontend

Модель. «Плоская»

{

"text": "Hello, world!\n",

"runs": [{

"start": 0,

"end": 4,

"bold": true

}],

"paragraphs": [{

"end": 13

}]

}

Свойство text – весь текст

документа.

Адресация по индексу

строки text.

Page 30: Редактор Mail.ru. Frontend

Каждый браузер измеряет по-своему

Расчет документа. Разные размеры букв

Page 31: Редактор Mail.ru. Frontend

Каждый браузер измеряет по-своему

Решение. Присылаем размеры шрифта с сервера

Расчет документа. Разные размеры букв

Page 32: Редактор Mail.ru. Frontend

Каждый браузер измеряет по-своему

Решение. Присылаем размеры шрифта с сервера

В HTML нельзя указать размер слова по ширине

Расчет документа. Разные размеры букв

Page 33: Редактор Mail.ru. Frontend

Каждый браузер измеряет по-своему

Решение. Присылаем размеры шрифта с сервера

В HTML нельзя указать размер слова по ширине

Решение. Canvas может принудительно уместить слово

по ширине

Расчет документа. Разные размеры букв

Page 34: Редактор Mail.ru. Frontend

Шрифтов много хороших и разных…

Какие шрифты нужны?

Расчет документа. Шрифты

Page 35: Редактор Mail.ru. Frontend

Расчет документа. Статистика по шрифтам

Times New Roman Calibri

Tahoma Arial

Courier New Symbol

Wingdings Cambria

Verdana

Page 36: Редактор Mail.ru. Frontend

Расчет документа. Размеры шрифта

{

"widths": {

"a": 1139, ...

},

"ascent": 1921,

"descent": -434,

"factor": 2048

}

C сервера приходят JSON c рассчитанными размерами шрифта

Расчет размеров букв теперь не зависят от браузера!

Page 37: Редактор Mail.ru. Frontend

Расчет документа. Итоговая структура

Документ

Секция

Страница

Строка

Слово

Page 38: Редактор Mail.ru. Frontend

Расчет документа. Итоговая структура

Документ

Секция

Страница

Строка

Слово

Page 39: Редактор Mail.ru. Frontend

Расчет документа. Итоговая структура

Документ

Секция

Страница

Строка

Слово

Page 40: Редактор Mail.ru. Frontend

Расчет документа. Итоговая структура

Документ

Секция

Страница

Строка

Слово

Page 41: Редактор Mail.ru. Frontend

Расчет документа. Слова

Простое слово

Пробельное слово

Табуляция

Разрыв страницы

Перенос строки

Page 42: Редактор Mail.ru. Frontend

Расчет документа. Процесс

Слово большее по высоте сдвигает строку вниз

Page 43: Редактор Mail.ru. Frontend

Расчет документа. Процесс

Если слово длиннее строки, то слово разделяется на две части

Page 44: Редактор Mail.ru. Frontend

Расчет документа. Что-то посложнее

Как рассчитать подобное?

Картинка имеет обтекание текстом – «вокруг рамки»

Page 45: Редактор Mail.ru. Frontend

Расчет документа. Что-то посложнее

Сначала располагаем картинку относительно

левого верхнего угла параграфа

Page 46: Редактор Mail.ru. Frontend

Расчет документа. Что-то посложнее

Пытаем получить прямоугольник для строки – пересечение!

Page 47: Редактор Mail.ru. Frontend

Расчет документа. Что-то посложнее

Строка разбивается на несколько допустимых участков

Page 48: Редактор Mail.ru. Frontend

Расчет документа. Что-то посложнее

Размещаем слова по допустимым участкам

Page 49: Редактор Mail.ru. Frontend

Расчет документа. Что-то посложнее

Итак весь параграф

Page 50: Редактор Mail.ru. Frontend

4 аргумент – максимальная ширина текста

Canvas

ctx.fillText(text, x, y, maxWidth);

Canvas может принудительно уместить слово в

заданный размер

Page 51: Редактор Mail.ru. Frontend

• Полный контроль над расположением элементов и их размерами

• Простая логика на уровне View

• Производительность (client side and server side)

• Используем node.js + node-canvas вместо phantomjs

• C согласия пользователя к репорту прикрепляется скриншот

документа

Canvas. Плюсы

Page 52: Редактор Mail.ru. Frontend

Нет DOM:

• Самостоятельная обработка событий пользователя

• Нельзя заменить содержимое тега, поменять стиль => только

перерисовать

Canvas. Минусы

Page 53: Редактор Mail.ru. Frontend

Используем два <canvas> на странице

Canvas. Больше одного на странице

Один – для текста, второй – для выделения и курсора

Page 54: Редактор Mail.ru. Frontend

ctx.fillRect() должен работать с целыми числами, нужно округлять

Canvas. Округление и целые числа

ctx.fillRect(

round(x),

round(y),

round(w),

round(h)

);

Page 55: Редактор Mail.ru. Frontend

Canvas. Округление и целые числа

y = 10.5;

h = 4.7;

round(y) === 11;

round(h) === 5;

Page 56: Редактор Mail.ru. Frontend

Canvas. Округление и целые числа

y = 10.5;

h = 4.7;

round(y) === 11;

round(h) === 5;

round(y) === 11;

round(y + h) - round(y) === 4;

Page 57: Редактор Mail.ru. Frontend

Заливка находится под текстом, но перед фоном страницы

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

Canvas. Заливка и обрезка

Page 58: Редактор Mail.ru. Frontend

Рисуем фон под текстом (текст + фон за один проход)

Canvas. Off-screen canvas

ctx.globalCompositeOperation = 'destination-over';

Копируем off-screen canvas в основной с помощью drawImage

var offSreenCanvas = document.createElement('canvas');

ctx.drawImage(offSreenCanvas, ...);

Page 59: Редактор Mail.ru. Frontend

Canvas. Превью и печать

Модуль node-canvas – Canvas API для Node.js

Дает возможность генерировать PDF (печать!)

ctx.addPage(width, height);

Page 60: Редактор Mail.ru. Frontend

Canvas. PDF, первый подход

Использование Off-screen canvas растеризовало текст

Отказываемся от off-sreen canvas в пользу метода clip() для обрезки

ctx.save();

ctx.rect(x, y, w, h);

ctx.clip();

// render

ctx.restore();

Page 61: Редактор Mail.ru. Frontend

Canvas. Почему все стало тормозить?!

save() / restore() действительно сбрасывают clip()

Но:clip() оперирует путем (path)

Путь всегда один

Путь не сбрасывается во время restore()

Page 62: Редактор Mail.ru. Frontend

Canvas. Правильное использование clip()

ctx.save();

ctx.beginPath();

ctx.rect(x, y, w, h);

ctx.clip();

// render

ctx.restore();

Page 63: Редактор Mail.ru. Frontend

Тень вокруг листа.

В превью не нужна тень!

Убрали тень в генерации

превью (Node.js) – время

снизилось в среднем на

50мс

Canvas. Ускоряем превью

Page 64: Редактор Mail.ru. Frontend

Canvas. Дисплеи retina и devicePixelRatio

ctx.scale(devicePixelRatio, devicePixelRatio);

<canvas width="1486" height="434" style="height: 347px; width:1189px;">

</canvas>

Увеличиваем размер Canvas в devicePixeRatio раз и

сжимаем обратно с помощью CSS

В отрисовке увеличиваем масштаб

Page 65: Редактор Mail.ru. Frontend

Результат

Было Стало

Отображение в разных

браузерах

Разное Идентичное

Генерация превью PhantomJS – 600 мс Node.js – 350 мс

Печать Средствами

браузера

Генерация PDF для

печати на сервере

Page 66: Редактор Mail.ru. Frontend

СПАСИБОЗА ВНИМАНИЕ[email protected]

Page 67: Редактор Mail.ru. Frontend

Инструмент Функции

git + phabricator + arcanist Управление и review кода

Grunt Сборка проекта

SASS + autoprefixer Работаем c css

Karma+PhantomJS, instabul Запуск тестов, code coverage

Что мы используем?

Page 68: Редактор Mail.ru. Frontend

Библиотека Функции

Requirejs AMD модули

jQuery Обработка событий + AJAX

Mail.ru FileAPI Загрузка картинок на сервер

Jasmine Unit тесты

Node-canvas Серверная реализация Canvas API

Что мы используем?