|
1 | | -# RS Lang |
| 1 | +# Описание библиотек, фреймворков, технологий, использованных на проекте RSLang |
| 2 | + |
| 3 | +## Библиотеки: |
| 4 | + |
| 5 | +### [1. CanvasJS](https://canvasjs.com/) |
| 6 | + |
| 7 | +<img src="https://cdn.discordapp.com/attachments/721440499197804577/735169983004541088/unknown.png"/> |
| 8 | + |
| 9 | +*Пример графика:* |
| 10 | +<img src="https://cdn.discordapp.com/attachments/721440499197804577/735170428791947274/unknown.png"/> |
| 11 | + |
| 12 | + |
| 13 | +- В бесплатной (`trial`) версии присутствуют водяные знаки и их можно убрать через свойство в API только в платной (`commercial`) версии |
| 14 | +- Вполне можно создавать самостоятельные и разнообразные стили для графиков, но, судя по документации, тут есть большие ограничения, когда хочется сделать что-то больше чем поменять цвет надписей, жирность шрифта, расположения по вертикали/горизонтали и шрифта (то есть, мы лично, не нашли возможности стилизовать засечки) |
| 15 | +- Есть форматтеры, по которым сама библиотека понимает как форматировать строку или же объект с данными (то есть `new Date()` может быть форматирована как только вашему глазу угодно) |
| 16 | +- Библиотека основана на `<canvas>` |
| 17 | +- Нет `NPM`, только `CDN` и исходники, которые пришлось адаптировать к `eslint`, чтобы не выдавались ошибки и предупреждения |
| 18 | + |
| 19 | +<img src="https://media.discordapp.net/attachments/721440499197804577/735171010076344371/unknown.png"/> |
| 20 | + |
| 21 | +Вот как легко был реализован наш график |
| 22 | + |
| 23 | +<img src="https://cdn.discordapp.com/attachments/721440499197804577/735194586137231511/unknown.png"/> |
| 24 | + |
| 25 | +в проекте `RSLang` с помощью данной библиотеки |
| 26 | +```js |
| 27 | +initChart(data = {}, shouldRender = true) { |
| 28 | + const fontFamily = 'Montserrat, sans-serif'; |
| 29 | + const fontSize = 12; |
| 30 | + const fontWeight = 400; |
| 31 | + const datasetColor = 'rgba(54, 158, 173, 0.7)'; |
| 32 | + |
| 33 | + const axisOptions = { |
| 34 | + labelFontFamily: fontFamily, |
| 35 | + labelFontSize: fontSize, |
| 36 | + |
| 37 | + titleFontFamily: fontFamily, |
| 38 | + titleFontSize: fontSize + 2, |
| 39 | + titleFontWeight: fontWeight, |
| 40 | + }; |
| 41 | + |
| 42 | + const { title, dataset } = data; |
| 43 | + |
| 44 | + this.chart = new CanvasJS.Chart('chartContainer', { |
| 45 | + animationEnabled: true, |
| 46 | + zoomEnabled: true, |
| 47 | + culture: 'ru', |
| 48 | + title: { |
| 49 | + text: title, |
| 50 | + padding: 10, |
| 51 | + fontFamily, |
| 52 | + fontSize: 18, |
| 53 | + }, |
| 54 | + subtitles: [{ |
| 55 | + text: '*график можно приближать и растягивать', |
| 56 | + fontFamily, |
| 57 | + fontSize: 12, |
| 58 | + horizontalAlign: 'right', |
| 59 | + padding: 0, |
| 60 | + }], |
| 61 | + axisX: { |
| 62 | + title: 'По дням', |
| 63 | + valueFormatString: 'D, MMM', |
| 64 | + margin: 0, |
| 65 | + intervalType: 'month', |
| 66 | + |
| 67 | + ...axisOptions, |
| 68 | + }, |
| 69 | + axisY: { |
| 70 | + title: 'Количество изученных слов', |
| 71 | + valueFormatString: '###', |
| 72 | + margin: 10, |
| 73 | + |
| 74 | + ...axisOptions, |
| 75 | + }, |
| 76 | + data: [ |
| 77 | + { |
| 78 | + type: 'splineArea', |
| 79 | + padding: 10, |
| 80 | + color: datasetColor, |
| 81 | + markerSize: 3, |
| 82 | + toolTipContent: '{x}<br/>Изучено {y}', |
| 83 | + xValueFormatString: 'D MMMM YYYY, HH:MM', |
| 84 | + yValueFormatString: '#', |
| 85 | + dataPoints: dataset, |
| 86 | + }, |
| 87 | + ], |
| 88 | + }); |
| 89 | + |
| 90 | + if (shouldRender) { |
| 91 | + this.chart.render(); |
| 92 | + } |
| 93 | + } |
| 94 | + ``` |
| 95 | + |
| 96 | +### [2. SwiperJS](https://swiperjs.com/) |
| 97 | + |
| 98 | +Библиотека для быстрого создания сменяющихся слайдов для сайтов |
| 99 | + |
| 100 | +Использовался [ранее](https://github.com/rolling-scopes-school/tasks/blob/master/tasks/movie-search.md) в работах [RSSchool](https://rs.school/). |
| 101 | +- Очень удобный в использовании |
| 102 | +- Бесплатный (лицензия `MIT`) |
| 103 | +- Огромное и действительно богатое `API` и документация |
| 104 | +- Есть собственные события, и механизм подписывания на эти события (подобный `jQuery` `.on`) |
| 105 | +- Есть его пакетная версия (`NPM`) с `EcmaScript Modules`, где можно управлять какие части функционала будут загружены на сайт, в отличие от его `CDN` версии |
| 106 | + |
| 107 | +**НО:** |
| 108 | +- Во время разработки, разработчики данной библиотеки обновили мажорную версию на `6.0.0` и сломали `CDN`, в связи с чем, пришлось ставить более ранние версии библиотеки через `NPM` |
| 109 | +- Вёрстка самого слайдера порой включает в себя `!important` и довольно сложно отцентрировать управляющие элементы (стрелки влево/вправо) |
| 110 | +- Из-за встроенного автоматического перерисовывания возможны такие баги, когда экран браузера расширился или сузился по высоте/ширине, а Swiper.js остался отрисованным на старых размерах экрана, в связи с чем видны следующие/предыдущие слайды |
| 111 | +- Не совсем очевидно, что если разработчик хочет подключить лишь часть функционала, то нужно импортировать ещё и стили `.scss` с соответствующим названием модуля (то есть, нельзя просто `@import 'swiper/swiper.scss'` - в нём нет зависимостей кроме как на общие стили и переменные) |
| 112 | + |
| 113 | +### [3. ProgressBarJS](https://kimmobrunfeldt.github.io/progressbar.js/) |
| 114 | + |
| 115 | +Простая библиотека для создания красивых, мощных и анимированных полос загрузки. |
| 116 | + |
| 117 | +- Очень проста в использовании |
| 118 | +- Анимация выполняется с помощью `JavaScript` с использованием `requestAnimationFrame` (метода, который указывает браузеру, что нужно произвести анимацию, и просит его запланировать перерисовку на следующем кадре анимации) |
| 119 | +- Анимация с помощью `JS` дает больший контроль над ней и поддерживается во всех основных современных браузерах. |
| 120 | +- Легковесная |
| 121 | +- Бесплатная (`MIT`) |
| 122 | +- Поддерживается всеми популярными браузерами, включая IE9+ |
| 123 | +- Есть в `NPM` варианте |
| 124 | + |
| 125 | +--- |
| 126 | +## Технологии: |
| 127 | + |
| 128 | +### 1. MPA (Multi Page Application) |
| 129 | + |
| 130 | +Данная методология была использована, чтобы избавиться от роутинга через hash и отслеживания изменения путей на сайте через событие "hashchanged". Также, это позволило использовать возможность Webpack и его плагина HTMLWebpackPlugin, который позволяет собирать различные .html и упаковывать в нужную структуру на сайте и разумно делегировать код благодаря чанкам (кускам кода) на нужную .html страницу, подобно тому, как раньше в 2005-ых годах делали, импортируя явно код на .html страницы, только сейчас - всё это делает `Webpack`. Весь код, собираемый в bundle был разбит на чанки, согласно заданию множественных точек входа (согласно всё тому же `MPA`). |
| 131 | +Так было сделано следующее: |
| 132 | +- Путь `'/'` - точка входа для промо-страницы |
| 133 | +- Путь `'/main'` - путь, доступный только авторизованным пользователям, содержащий основное `SPA` приложение |
| 134 | +- Пути вида `'/<название_игры_на_английском>'` (напр. `'/fillword'`, `'/speakit'`) - путь, доступный только авторизованным пользователям, содержащий `SPA` приложения самих мини-игр |
| 135 | + |
| 136 | +### 2. SPA (Single Page Application) |
| 137 | + |
| 138 | +Данная методология была использована, внутри общей `MPA`, для того, чтобы пользователю не нужно было постоянно перезагружать страницу и видеть постоянные индикаторы загрузок, кроме наших красивых анимированных быстрых лоадеров |
| 139 | + |
| 140 | +### 3. Event Emitter (Event Bus) ~ Паттерн "Наблюдатель" [ссылка на файл кода в проекте](https://github.com/Nastya07s/rslang/blob/develop/src/js/utils/eventBus.js) |
| 141 | + |
| 142 | +Всё просто - у нас есть метод `subscribe`, чтобы подписаться на событие (подобно .addEventListener), у нас есть метод `unsubscribe`, чтобы отписаться от события (подобно removeEventListener), есть массив/объект хранящий слушатели событий (где ключи - строковое название события, а значения - сами `callback` функции), добавленные через метод `subscribe` или `on`. Событие порождается по аналогии со стандартным `Custom Events API`, только через метод `emit`, где указывается какой вид события породить. |
| 143 | + |
| 144 | +**Что происходит?** |
| 145 | + |
| 146 | +Один компонент подписывается на какое-то событие (к примеру, `update`), через метод `subscribe()`, где первым аргументом указывается тип события (`update`), а вторым - функция-коллбэк, которая будет выполнена при наступлении события. |
| 147 | +Затем, в ходе работы, другой компонент может быть сделал какую-то работу и хочет, чтобы первый компонент был обновлён. Для этого он посылает событие `update` через метод `emit()`, где первым аргументом указывается тип события (`update`), происходит поиск данного события по ключам в объекте `EventBus`, и если оно находится, то запускается функция-коллбэк. Таким образом, происходит обновление первого компонента без явного указания ему, чтобы он обновился. |
| 148 | + |
| 149 | +**Зачем это нужно?** |
| 150 | + |
| 151 | +В первую очередь, чтобы избавиться от бесконечных прокидываний созданных объектов классов (инстансов) внутрь новых объектов (компонентов). Связанность кода повышается вместе со сложностью кода, а также, становится явной проблема доступа вложенных компонент или же модулей к внешним родительским компонентам. |
| 152 | +Гораздо проще обращаться к каким-бы то ни было компонентам или модулям, просто посылая событие "change" или "update", чтобы подписанные компоненты на изменения могли спокойно узнать, что произошло событие обновления и выполнить свои `callback` функции, нежели бесконечные цепочки из обращений к родительским элементам `this.parent.parent.parent.parent.update()` |
0 commit comments