CF Rating

Вместо предисловия


Привет всем хабражителям!
Взбрело мне как-то, холодным зимним вечером, внести на сайт вместо целых звезд рейтинга — их частичную заливку для дробных чисел (4.5, 3.85 и тд.). Так ведь и глазу милее и информативнее — какое заведение лучше, а какое — хуже. Вот и сели мы с командой думать и гадать.


Как мы путь свой искали


Так как у нас, в основном, топовые заведения и рейтинги 3+, то целые звезды сильно размывают восприятие. Но тут возникли нюансы. Самая распространенная практика — использовать наложение изображения одно поверх другого. С самого-самого начала мы думали сделать все с помощью изображения-маски, но, увы, дизайн не подразумевал, что звездочки могут быть рядом, а контролировать ширину блока заливки и размер звезды не очень удобно.

Тут у Кинопоиска все 10 звезд — одна картинка, где они еще и приклеены друг к другу. Так им очень легко закрасить оранжевым на столько, на сколько душа желает.

image

<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%, а значение по специально рассчитанной формуле.
Все дело в психологии. Нам свойственно визуально воспринимать процент заполнения в объеме, а не в ширине, а поскольку звезда слева и справа имеет весьма малую площадь, что усложняет восприятие, мы придумали заполнять ее по ширине нелинейно:
image
Для нелинейной модели мы взяли синусоиду. Она как раз отлично описывает быстрое начало и окончание роста, и плавный рост в середине.
Развернули ее, получив arсsin, ужали его в рамки {0; 1} по обеим осям и получили неплохую и простую формулу для расчета «психологической заполненности» звезды.

image

Код на JavaScript:
var y = Math.asin( 2 * x - 1 ) / Math.PI + 0.5;


Как оказалось, такой принцип хорошо работает в старых браузерах, и даже ничего не ползет на IE9. Довольны были все: и дизайнеры, и заказчики, и даже мое Эго, что и побудило накатать статью.
Искренне надеюсь, что кому-нибудь это понадобится :)
Поделиться с друзьями
-->

Комментарии (18)


  1. novoselov
    29.12.2016 14:28
    -8

    Такая визуализация не информативна для пользователя без цифр.
    Нельзя навскидку сказать это 60%, 70%, 80% или на какой из двух картинок больше процентов?
    Простое разбиение на 10 частей позволяет сразу ответить на оба вопроса, закрашивая каждый сектор добавляем 10%
    image


    1. m1n7
      29.12.2016 16:49
      -1

      И считать сектора? На экране телефона? На разбитом? Во время ходьбы?


      1. Apathetic
        30.12.2016 00:17
        +6

        А чего вы остановились? Продолжайте:
        Ходьбы по канату? Будучи одноногим? Под градом пуль? А внизу раскаленная лава? И метеорит вот-вот уже?


  1. Antonto
    29.12.2016 15:21
    +6

    Вот за нелинейную заливку прям спасибо вам!


  1. Loki3000
    29.12.2016 15:48

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

    Вроде бы синусоида как раз обеспечивает быстрый рост/спад и плавную середину. Да и из контекста повествования вытекает именно такое поведение.


    1. reilag
      29.12.2016 16:51

      Ой, запутался :)
      Спасибо большое, поправил.


  1. YaMishar
    29.12.2016 17:32

    Мне, как пользователю, достаточно было бы шага 0.1. А то и 0.2. Более мелкий шаг приведёт к замыливанию.
    А тут имхо — гораздо быстрее и проще вывести картинки. 50-100 картинок в PNG, а наодной странице скорее 20-30.
    Или может я не прав? Я не веб программист, потому был бы благодарен за комментарии.


    1. reilag
      29.12.2016 17:37

      Но в целом, шага в 0.1 вполне хватает для того что бы «прикинуть» разницу в рейтинге.
      Но проще не будет, ведь все эти 50-100 картинок нужно прорисовать и вывести соответствующую, предварительно округлив до нашего шага.

      К тому же, у нас нет картинок для иконок :)


      1. Aingis
        29.12.2016 18:00

        Зачем 50-100 картинок? Две картинки: серая и жёлтая звезда с повторением фона. В остальном реализация аналогична.

        И с точки зрения доступности лучше не непонятные символы непонятным шрифтом, который может и не загрузится по ряду причин или криво отрендериться, а нормальная aria-разметка.


  1. dom1n1k
    29.12.2016 23:17

    Если хотите оптимизироваться и сэкономить несколько наносекунд — арксинус можно заменить на smoothstep. Форма графика там практически одинаковая.


    1. dom1n1k
      29.12.2016 23:33

      А хотя нет, вам же его ещё на 90 градусов разворачивать нужно — это убьет всю оптимизацию.


    1. reilag
      30.12.2016 02:37

      На таких небольших объемах, вообще не вижу смысла.
      Вот если бы мы эти звездочки тысячами рендерили… тогда бы был смысл :)


  1. dryja
    30.12.2016 11:35

    А вот вопрос на тему рейтинга: как заставить его появиться в сниппетах поисковиков? Микроразметка есть, все валидируется в валидаторах гугла и яндекса, но рейтинг в сниппетах не отображает.
    Техподдержка яндекса говорит «эта фича тестируется, так что мы ничего не гарантируем», а техподдержка гугла вообще говорит «ничего не гарантируем, у нас супер-пупер система модерации, мы сами не знаем, как она работает».


    1. reilag
      30.12.2016 11:41

      Согласен. Это еще та проблема. Мы так-же с этим стараемся бороться, но увы…
      Даже встречали стайты, когда на них вообще нет реальных рейтингов или отзывов, а простая микроразметка, в которой указано 5. Гугл это отобразил.

      А наш проект так ни одного снипета и не получил в выдаче гугла.
      Мы используем JSON микроразметку. Возможно с этом и проблема?


      1. dryja
        30.12.2016 11:48

        Да хрен его знает. В мануалах того же яндекса написано, мол используйте микроразметку schema.org и все будет, и тот же яндекс отвечает «мы ничего не обещаем, извиняйте».
        Хотя да, я проверял в валидаторах сайты, сниппеты которых отображаются с рейтингом, так там ошибок куча, валидаторы ругаются матом. В общем, странно это все.


      1. 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 можно почитать.


        1. reilag
          30.12.2016 17:54

          А чем JSON-LD может не устраивать Google и Yandex, который входит в спецификацию schema.org?


          1. dazed
            01.01.2017 23:56

            Для Google думаю вполне устраивает, т.к. Вы сами написали, что у них в выдаче эта разметка работает. (Google в инновациях более прогрессивный чем Яндекс)
            А вот у Яндекса, может это и работает, но кривовато и с нюансами. Плюс, у них на help странице указаны примеры с HTML разметкой. Возможно упор делают на него, поэтому если действительно важны звездочки в яндекс выдаче, то попробуйте этот вариант, что я порекомендовал выше.

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

            А может быть и такое, что решили пользователю в выдаче не будут нужны ваши зведочки и тогда способ с html разметкой тоже не поможет. И тут остается только работать над сайтом, и ждать когда яндекс будет более благосклонен к сайту.