Будьте добры сообщите какой срок проверки заданий и каким способом я буду оповещен! |
Обработка данных
HTML + JS: Практическое занятие №2
Стилизация приложения
Задание: продолжая проект, полученный в результате выполнения первого практического задания, измените внешний вид приложения в соответствии со спецификой выбранной темы (источников данных) и рекомендациями ниже.
- Не забудьте убедиться, что ваше приложение корректно выглядит при разных размерах экрана и в разных состояниях (snap, fill, full).
- В качестве подтверждения выполнения лабораторной работы от вас потребуется предоставить скриншот стилизованного приложения (главный экран).
Темная или светлая
Первое практическое задание мы закончили на приложении, которое выглядит примерно следующим образом:
Это стандартная темная тема приложения с темным фоном и светлым шрифтом. В своем приложении вы можете как целиком переопределить цветовую схему используя CSS, так и отталкиваться от базовой темы, изменяя лишь отдельные детали.
Windows Library for JavaScript, подключаемая по умолчанию во всех шаблонах, предоставляет вам две готовые темы: светлую и темную, описанные соответственно в файлах ui-light.css и ui-dark.css.
В заголовке каждой станицы вы легко найдете соответствующие ссылки:
<link href="//Microsoft.WinJS.1.0/css/ui-dark.css" rel="stylesheet" />
Чтобы заменить темную тему на светлую, замените везде ui-dark.css на ui-light.css:
Связывание данных
Для начала давайте попробуем разобраться, как данные попадают на страницы. Для этого используется связывание данных (Data-Binding). В WinJS для этого есть специальные шаблоны, которые описываются через data-атрибуты как WinJS.Binding.Template.
Откройте страницу groupedItems.html и найдите описание шаблона с классом "itemtemplate":
<div class="itemtemplate" data-win-control="WinJS.Binding.Template"> <div class="item"> <img class="item-image" src="#" data-win-bind="src: backgroundImage; alt: title" /> <div class="item-overlay"> <h4 class="item-title" data-win-bind="textContent: title"></h4> <h6 class="item-subtitle win-type-ellipsis" data-win-bind="textContent: subtitle"></h6> </div> </div> </div>
Этот шаблон используется для отображения плиток на главной странице приложения. Через data-win-bind атрибуты в нем прописано, как данные должны проецироваться на отображение, точнее в какие атрибуты html-элементов они должны быть прописаны при выполнении приложения.
Названия полей данных (в примере выше, это, например, backgroundImage, title, subtitle) соответствуют описанию данных в коде на JavaScript.
Откройте файл groupedItems.js и найдите функцию _initializeLayout—в ней как раз указывается, какой источник данных нужно использовать при наполнении нашего listView и далее в шаблонах:
listView.itemDataSource = Data.items.dataSource;
В свою очередь Data – это глобально доступный объект, определенный в файле data.js, который определяли в первом практическом упражнении. Давайте вернемся к этому файлу, чтобы доопределить недостающие нам данные (например, ссылку на изображение).
Перейдите к функции getItemsFromRSSFeed, занимающейся разбором отдельных постов. Добавьте перед строчкой "var postItem = {" следующий код:
var tempElement = document.createElement("div"); tempElement.innerHTML = postContent; var image = tempElement.querySelector("img"); var imglink = (image != null) ? "url('" + image.src + "')" : "";
Здесь мы создаем временный элемент с контентом поста и извлекаем (querySelector) из него первую же картинку, чтобы использовать ее в качестве фоновой на плитках к постам. В случае, если картинки нет, используем пустую строку, которую передадим в соответствующее правило CSS.
Замечание: здесь мы также можем попасть в ситуацию, когда явной картинки в посте нет, но есть невидимая, применяемая для подсчета числа просмотров. Именно такая ситуация имеет место для блогов, используемых в ходе статьи, однако, эти картинки прозрачные, поэтому в нашем случае это не критично.
Добавьте в объект-описание поста (postItem) следующие строчки в конце, не забыв добавить запятую строчкой выше:
backgroundImage: imglink
Должно получиться примерно вот так:
var postItem = { ... link: post.querySelector("link").textContent, backgroundImage: imglink };
Если вы сейчас запустите проект на отладку, вы увидите, что визуально ничего не поменялось.
Вернитесь в файл pages\groupedItems\groupedItems.html к нашему шаблону itemtemplate. Двумя строчками ниже вы увидите описание картинки:
<img class="item-image" src="#" data-win-bind="src: backgroundImage; alt: title" />
Если вы знакомы с основами HTML, вы наверняка уже догадались, почему в отображении ничего не поменялось: формат, который мы использовали выше, рассчитан на использование в CSS, а не явную вставку ссылки на изображение через img.
Замечание: одна из причин, почему в данном случае CSS является предпочтительнее, заключается в более гибких настройках отображения и, в частности, наличие возможности манипуляций с изображением, в том числе с сохранением пропорций.
Удалите эту строчку для вставки изображения.
Поднимитесь на уровень выше и добавьте в строчке:
<div class="item">
связывание данных для фонового изображение через соответствующее свойство в CSS:
<div class="item" data-win-bind="style.backgroundImage: backgroundImage">
Попробуйте запустить приложение на отладку:
Изменение плиток
Стало лучше, но по-прежнему не очень. Например, что вполне ожидаемо, в некоторых постах нет изображений — и вместо них мы получили отсутствие картинок или пустые заглушки. Давайте для таких постов поставим цветной фон. Для этого откройте в той же папке файл groupedItems.css.
Найдите следующую строчку, описывающую отображение отдельного элемента:
.groupeditemspage .groupeditemslist .item { -ms-grid-columns: 1fr; -ms-grid-rows: 1fr 90px; display: -ms-grid; height: 250px; width: 250px; }
Добавьте в конце этого CSS-правила описание фона:
background-color: rgb(0, 204, 255);
Дополнительно можно сделать еще несколько манипуляций над фоновым изображением, например, центрировать его, отменить повторения и задать режим масштабирования:
background-position: 50% 50%; background-repeat: no-repeat; background-size: cover;
Запускаем приложение:
Теперь давайте перейдем к исправлению отображения текстового содержимого.
Обновление текстовых стилей
Вернитесь к файлу groupedItems.html и описанию шаблона элемента (itemtemplate). В конце элемента вы найдете описание подзаголовка (item-subtitle). В нашем случае никакого подзаголовка нет, зато есть дата, поэтому замените эту строчку:
<h6 class="item-subtitle win-type-ellipsis" data-win-bind="textContent: subtitle"></h6>
на следующие:
<h6 class="item-date win-type-ellipsis"> <span data-win-bind="textContent: day"></span> <span data-win-bind="textContent: month"></span> </h6>
Здесь мы также используем связывание с доступными нам полями в описании каждого поста: day и month. Аналогично вы можете определить любые другие необходимые поля и использовать их в отображении объектов.
Если вы попробуете сейчас запустить, вы увидите, что дата куда-то съехала и залезла на заголовок.
Чтобы это поправить, перейдите назад к CSS-файлу. Найдите описание подзаголовка:
.groupeditemspage .groupeditemslist .item .item-overlay .item-subtitle { -ms-grid-row: 2; width: 220px; }
Замените его на соответствующее описание отображение даты:
.groupeditemspage .groupeditemslist .item .item-overlay .item-date { -ms-grid-row: 2; width: 220px; text-transform: uppercase; text-align:right; }
В данном случае мы также добавили выравнивание по правому краю и отображение текста в верхнем регистре.
Замечание: обратите внимание, что для позиционирования элементов использует модуль CSS 3 Grid Layout. Мы не будем останавливаться на деталях его использования, но если вы планируете создавать приложения для Windows Store на HTML/JS, рекомендуем хорошо изучить его возможности.
Теперь давайте попробуем сделать заголовок записи побольше и изменить его положение на плитке.
Для начала его необходимо вынести на уровень выше. Перейдите к файлу groupedItems.html и внутри шаблона элемента вынесите описание заголовка на уровень выше, добавив обертку. Должно получиться примерно так:
<div class="item" data-win-bind="style.backgroundImage: backgroundImage"> <!-- Заголовок --> <div class="item-title-container"> <h4 class="item-title" data-win-bind="textContent: title"></h4> </div> <div class="item-overlay"> <h6 class="item-date win-type-ellipsis"> <span data-win-bind="textContent: day"></span> <span data-win-bind="textContent: month"></span> </h6> </div> </div>
Вернитесь к CSS-файлу, в нем необходимо отобразить изменение иерархии и прописать обновленные стили.
После правила .groupeditemspage .groupeditemslist .item { … } добавьте новое:
.groupeditemspage .groupeditemslist .item .item-title-container { -ms-grid-row: 1; margin: 10px; padding: 8px; opacity: 0.85; }
Сразу после него перенесите находящееся ниже описание заголовка (.item-overlay надо заменить на .item-title-container):
.groupeditemspage .groupeditemslist .item .item-title-container .item-title { overflow: hidden; display: inline; font-size: 1.6em; line-height: 1.5em; font-family: 'Segoe UI Light'; background: rgb(145, 0, 145); box-shadow: rgb(145, 0, 145) 0 0 0 8px; color: white; }
Попробуйте запустить проект:
Чтобы убрать подложку, оставшуюся от изначального макета, перейдите ниже по коду в CSS-файле к строчкам (оно находится внутри media query – описания):
.groupeditemspage .groupeditemslist .item .item-overlay { background: rgba(0,0,0,0.65); }
Замените указанный цвет на transparent.
Попробуйте самостоятельно увеличить размер текста у даты и поменять шрифт на "Segoe UI Semibold" и название приложения на другое. Самостоятельно попробуйте поменять внешний вид заголовков, чтобы они приняли примерно такой вид (это может быть удобнее делать в Expression Blend):
Подберите наиболее подходящий индивидуальный стиль для вашего приложения.
Внутренние страницы и режимы отображения
Аналогично тому, как мы изменили внешний вид основной странице приложения к тому же стилю необходимо привести и внутренние страницы (группа и индивидуальная страница для каждого элемента контента).
Сделайте это самостоятельно на свое усмотрение. Например, для блогов, посвященных Windows это могло бы выглядеть так:
Переведите приложение в Snapped-режим (если вы работаете в Expression Blend, на вкладке Device можно менять режим отображения). Здесь также нужно поправить стили с учетом выбранной стилистики.
Причем корректность отображения необходимо проверить для всех внутренних экранов.
Стили для Snap-режима корректируются с помощью Media Queries:
@media screen and (-ms-view-state: snapped) { ... }
Ограничение вывода данных
Если вы попробуете еще раз проследить, как получаются и отображаются данные, вы легко заметите, что мы их никак не ограничиваем. Приложение получает на входе потоки данных из RSS, преобразовывает их во внутреннюю структуру, используемую для связывания данных, и далее проецирует их в коллекцию на экране. Фактически, мы сразу видим все-все данные прямо с первого экрана.
В конечном счете, в нашем случае вместо того, чтобы показывать быстро и целиком всю картину последних событий, для чего было бы достаточным выводить 6-8 последних записей, мы сразу обрушиваем на пользователя весь поток новостей. Большой набор данных также не дает возможности быстро переключаться на соседние группы — приходится слишком долго прокручивать (забегая сильно вперед, скажу, что частично эту проблему сглаживает Semantic Zoom).
Таким образом, наша первоочередная задача — ограничить поток данных на первом экране.
Для этого откройте файл pages\groupedItems\groupedItems.js.
В начале функции в области переменных создайте еще две:
var groupedItems; var tilesLimit = 6;
В первой мы будем хранить отфильтрованную коллекцию. Вторая указывает, сколько максимум плиток можно выводить (в идеале, подобные параметры нужно выносить в отдельные файлы настроек или аналогичные структуры данных, описывающих параметры проекта).
Далее найдите следующую строчку:
_initializeLayout: function (listView, viewState) {
Эта функция вызывается при инициализации страницы и прописывает, какие данные необходимо вывести на экран в зависимости от используемого режима отображения. В качестве источника данных используется глобальный объект Data, описанный в файле data.js.
Добавьте в начале функции следующие строчки:
groupedItems = Data.items.createFiltered(function (item) { return item.index < tilesLimit; }).createGrouped( function groupKeySelector(item) { return item.group.key; }, function groupDataSelector(item) { return item.group; } );
Напомню, что в свойстве items объекта Data прописана коллекция данных (WinJS.Binding.List), сгруппированных по принадлежности к тем или иным RSS-потокам.
Чтобы отфильтровать данные, мы берем эту коллекцию и оставляем только элементы, индекс которых меньше нашего ограничения, после чего заново группируем коллекцию в соответствии с группами.
Замечание: в данном случае мы явно ограничиваем количество элементов шестью (tilesLimit), однако, в общем случае это неправильный подход. На большом мониторе количество плиток может оказаться меньше желаемого, к тому же могут появляться одиноко висящие плитки в конце списка. Самое правильное: динамически рассчитывать лимит в зависимости от доступного пространства.
Далее в этой же функции замените встречающиеся ниже ссылки на Data и Data.items на переменную groupedItems:
if (viewState === appViewState.snapped) { listView.itemDataSource = groupedItems.groups.dataSource; listView.groupDataSource = null; listView.layout = new ui.ListLayout(); } else { listView.itemDataSource = groupedItems.dataSource; listView.groupDataSource = groupedItems.groups.dataSource; listView.layout = new ui.GridLayout({ groupHeaderPosition: "top" }); }
Аналогичную операцию проделайте внутри функции _itemInvoked:
_itemInvoked: function (args) { if (appView.value === appViewState.snapped) { // If the page is snapped, the user invoked a group. var group = groupedItems.groups.getAt(args.detail.itemIndex); this.navigateToGroup(group.key); } else { // If the page is not snapped, the user invoked an item. var item = groupedItems.getAt(args.detail.itemIndex); nav.navigate("/pages/itemDetail/itemDetail.html", { item: Data.getItemReference(item) }); } }
Запустите проект:
Отлично! Теперь мы сразу видим все самое свежее.
Дополнительно
Если вы хотите дополнительно разнообразить внешний вид вашего приложения, попробуйте добавить поддержку плиток разного размера.
Как это сделать, описано в этом руководстве: http://msdn.microsoft.com/ru-ru/library/windows/apps/jj657974.aspx.