Как мы звезды рейтинга дробили

в 11:11, , рубрики: css, CSS tricks, html, webfonts, верстка

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. Довольны были все: и дизайнеры, и заказчики, и даже мое Эго, что и побудило накатать статью.
Искренне надеюсь, что кому-нибудь это понадобится :)

Автор: reilag

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js