Вместо предисловия
Привет всем хабражителям!
Взбрело мне как-то, холодным зимним вечером, внести на сайт вместо целых звезд рейтинга — их частичную заливку для дробных чисел (4.5, 3.85 и тд.). Так ведь и глазу милее и информативнее — какое заведение лучше, а какое — хуже. Вот и сели мы с командой думать и гадать.
Как мы путь свой искали
Так как у нас, в основном, топовые заведения и рейтинги 3+, то целые звезды сильно размывают восприятие. Но тут возникли нюансы. Самая распространенная практика — использовать наложение изображения одно поверх другого. С самого-самого начала мы думали сделать все с помощью изображения-маски, но, увы, дизайн не подразумевал, что звездочки могут быть рядом, а контролировать ширину блока заливки и размер звезды не очень удобно.
Тут у Кинопоиска все 10 звезд — одна картинка, где они еще и приклеены друг к другу. Так им очень легко закрасить оранжевым на столько, на сколько душа желает.
<div class='starbar'>
<div class='outer'>
<div class='starbar_w'></div>
</div>
</div>
.starbar .outer {
background: url(/images/starz.gif) no-repeat;
width: 219px;
height: 30px;
position: absolute;
}
.starbar_w {
display: block;
width: 167.09px;
background: url(/images/starz.gif) 0 -62px no-repeat;
height: 30px;
position: absolute;
}
Всегда есть где разгуляться!
Всё же, нам хотелось универсальный и масштабируемый вариант, полностью подчиняющийся BEM методологии. К тому же, в проекте нет ни одного спрайта и все иконки реализованы с помощью собственного набора иконок, бережно собранного в шрифт. Но, думаю, об этом мы поговорим в других статьях ;)
В целом, мы пришли к эксперименту: а почему бы не наложить нашу шрифтовую иконочку поверх другой? Вот мы и сделали.
Каждую звездочку сделали отдельным объектом, состоящей из .stars__out в качестве контейнера и .stars__in в качестве заливки.
<div class="stars">
<!-- ... Полностью зарисованная иконка ... -->
<i class="cfi cfi--star stars__out">
<i class="cfi cfi--star stars__in" style="width: 100%;"></i>
</i>
<!-- ... еще 3 повторения ... -->
<!-- ... Иконочка зарисованная на 33% ... -->
<i class="cfi cfi--star stars__out">
<i class="cfi cfi--star stars__in" style="width: 33.33%;"></i>
</i>
</div>
А вот и CSS:
.cfi.cfi--star { /* ... */ } /* наш аналог Font Awesome, который рисует звезду */
.stars__out {
position: relative;
margin-right: 5px; /* сделаем отступ между зведами */
color: grey;
z-index: 1;
}
.stars__in {
/* разместим ка мы нашу заливку как дочернюю основной иконки и кинем поверх */
position: absolute;
z-index: 2;
color: orange; /* дадим солнечного цвета */
font-size: inherit; /* и шрифту размер родителя */
/* блоку дадим точки отчета по нулям относительно родителя */
display: block;
top: 0; left: 0; bottom: 0;
/* ну и ограничим область видимости, а также ширину установим в 0 по умолчанию */
overflow: hidden;
width: 0;
}
Все. Дальше, когда нам нужно залить на 100% (полная звездочка), мы просто даем ей CSS свойство width: 100%.
А вот для неполных звездочек мы использовали еще одну хитрость. Мы ставим в ширину не x * 100%, а значение по специально рассчитанной формуле.
Все дело в психологии. Нам свойственно визуально воспринимать процент заполнения в объеме, а не в ширине, а поскольку звезда слева и справа имеет весьма малую площадь, что усложняет восприятие, мы придумали заполнять ее по ширине нелинейно:
Для нелинейной модели мы взяли синусоиду. Она как раз отлично описывает быстрое начало и окончание роста, и плавный рост в середине.
Развернули ее, получив arсsin, ужали его в рамки {0; 1} по обеим осям и получили неплохую и простую формулу для расчета «психологической заполненности» звезды.
Код на JavaScript:
var y = Math.asin( 2 * x - 1 ) / Math.PI + 0.5;
Как оказалось, такой принцип хорошо работает в старых браузерах, и даже ничего не ползет на IE9. Довольны были все: и дизайнеры, и заказчики, и даже мое Эго, что и побудило накатать статью.
Искренне надеюсь, что кому-нибудь это понадобится :)
Комментарии (18)
Loki3000
29.12.2016 15:48Для нелинейной модели мы взяли синусоиду. Она как раз отлично описывает плавное начало и окончание роста, и быстрый рост в средине.
Вроде бы синусоида как раз обеспечивает быстрый рост/спад и плавную середину. Да и из контекста повествования вытекает именно такое поведение.
YaMishar
29.12.2016 17:32Мне, как пользователю, достаточно было бы шага 0.1. А то и 0.2. Более мелкий шаг приведёт к замыливанию.
А тут имхо — гораздо быстрее и проще вывести картинки. 50-100 картинок в PNG, а наодной странице скорее 20-30.
Или может я не прав? Я не веб программист, потому был бы благодарен за комментарии.reilag
29.12.2016 17:37Но в целом, шага в 0.1 вполне хватает для того что бы «прикинуть» разницу в рейтинге.
Но проще не будет, ведь все эти 50-100 картинок нужно прорисовать и вывести соответствующую, предварительно округлив до нашего шага.
К тому же, у нас нет картинок для иконок :)Aingis
29.12.2016 18:00Зачем 50-100 картинок? Две картинки: серая и жёлтая звезда с повторением фона. В остальном реализация аналогична.
И с точки зрения доступности лучше не непонятные символы непонятным шрифтом, который может и не загрузится по ряду причин или криво отрендериться, а нормальная aria-разметка.
dom1n1k
29.12.2016 23:17Если хотите оптимизироваться и сэкономить несколько наносекунд — арксинус можно заменить на smoothstep. Форма графика там практически одинаковая.
dom1n1k
29.12.2016 23:33А хотя нет, вам же его ещё на 90 градусов разворачивать нужно — это убьет всю оптимизацию.
reilag
30.12.2016 02:37На таких небольших объемах, вообще не вижу смысла.
Вот если бы мы эти звездочки тысячами рендерили… тогда бы был смысл :)
dryja
30.12.2016 11:35А вот вопрос на тему рейтинга: как заставить его появиться в сниппетах поисковиков? Микроразметка есть, все валидируется в валидаторах гугла и яндекса, но рейтинг в сниппетах не отображает.
Техподдержка яндекса говорит «эта фича тестируется, так что мы ничего не гарантируем», а техподдержка гугла вообще говорит «ничего не гарантируем, у нас супер-пупер система модерации, мы сами не знаем, как она работает».reilag
30.12.2016 11:41Согласен. Это еще та проблема. Мы так-же с этим стараемся бороться, но увы…
Даже встречали стайты, когда на них вообще нет реальных рейтингов или отзывов, а простая микроразметка, в которой указано 5. Гугл это отобразил.
А наш проект так ни одного снипета и не получил в выдаче гугла.
Мы используем JSON микроразметку. Возможно с этом и проблема?dryja
30.12.2016 11:48Да хрен его знает. В мануалах того же яндекса написано, мол используйте микроразметку schema.org и все будет, и тот же яндекс отвечает «мы ничего не обещаем, извиняйте».
Хотя да, я проверял в валидаторах сайты, сниппеты которых отображаются с рейтингом, так там ошибок куча, валидаторы ругаются матом. В общем, странно это все.
dazed
30.12.2016 14:30Попробуйте через HTML код:
<div id="block_rating" itemprop="aggregateRating" itemscope="" itemtype="http://schema.org/AggregateRating"> <meta itemprop="bestRating" content="10"> <meta itemprop="ratingValue" content="7.5"> <!-- Ваша отображаемая кнопка --> </div>
Если, что у яндекса или на schema.org можно почитать.reilag
30.12.2016 17:54А чем JSON-LD может не устраивать Google и Yandex, который входит в спецификацию schema.org?
dazed
01.01.2017 23:56Для Google думаю вполне устраивает, т.к. Вы сами написали, что у них в выдаче эта разметка работает. (Google в инновациях более прогрессивный чем Яндекс)
А вот у Яндекса, может это и работает, но кривовато и с нюансами. Плюс, у них на help странице указаны примеры с HTML разметкой. Возможно упор делают на него, поэтому если действительно важны звездочки в яндекс выдаче, то попробуйте этот вариант, что я порекомендовал выше.
Так же, учитывайте, что яндекс в таких вещах работает с задержкой от 2 недель и выше. Это проявляется во многих вещах, к примеру при добавлении нового сайта, при 301 редиректе на новый сайт итп.
А может быть и такое, что решили пользователю в выдаче не будут нужны ваши зведочки и тогда способ с html разметкой тоже не поможет. И тут остается только работать над сайтом, и ждать когда яндекс будет более благосклонен к сайту.
novoselov
Такая визуализация не информативна для пользователя без цифр.
Нельзя навскидку сказать это 60%, 70%, 80% или на какой из двух картинок больше процентов?
Простое разбиение на 10 частей позволяет сразу ответить на оба вопроса, закрашивая каждый сектор добавляем 10%
m1n7
И считать сектора? На экране телефона? На разбитом? Во время ходьбы?
Apathetic
А чего вы остановились? Продолжайте:
Ходьбы по канату? Будучи одноногим? Под градом пуль? А внизу раскаленная лава? И метеорит вот-вот уже?