Mojo Ribbon — идеальная лента или тригонометрия в LESS

в 21:33, , рубрики: css, css3, html, less, Веб-разработка

Доброго времени суток уважаемые читатели. Недавно в одном проекте мне потребовалось сделать ленточку для блоков. Для примера: очень часто сверху делают ленту с надписью «Fork me on GitHub» или же над каждым элементом в портфолио присутствует лента с датой публикации работы. Лучше покажу пример с официального сайта LESS, так как речь идет о такой маленькой детальке, на которую некоторые могли вообще не обратить внимание.

image

Данный Ribbon — это изображение внутри ссылки с абсолютным позиционированием. Чем меня не устраивает данный вариант? Во-первых: я очень люблю современные стандарты CSS, с помощью которых можно создать приятный дизайн, используя минимум изображений, а в данном случае лентой может быть обычный блок с transform rotate. Во-вторых: с недавнего времени я смотрю на веб сквозь Retina дисплей и неоптимизированные img сразу же бросаются в глаза, но и разработчикам обращать внимание на какую то ленточку, оптимизировать ее отображение с media queries, мне кажеться, даже немного смешно.

Создадим блок длинной 500px, высотой 50px, с абсолютным позиционированием, сверху, слева. Получится то, что мы видим на изображении ниже в левой части. Далее, повернем блок на -45 градусов, чтобы у нас получилось что-то похожее на Ribbon с LESS. Результат показан в правой части изображения. Наш элемент вращается от своего центра, вследствие чего получается отступ слева, а часть будущей ленты выезжает за рабочую область.

image

С помощью инспектора мы можем подобрать нужные нам значения для top и -left. Чем меня не устраивает данный вариант? Во-первых — перфекционизм: я хочу, чтобы максимальное количество указанных пикселей (в данном случае 500) отображалось в рабочей области и не выезжало за нее. Во-вторых — лень: я не хочу при изменении позиции (top, right, bottom, left) и градуса наклона вручную подбирать значения, чтобы спрятать все углы.

Геометрия

Как я и сказал, поворот элемента выполняется от центра. Следовательно, и значения отступов измеряются от края рабочей области до центра. Мысленно представим образующиеся фигуры — треугольники, стороны которых нам необходимо вычислить:
image

Получилось два треугольника ABC и A2B2C2, стороны которых нам необходимо вычислить. Нам известно, что С = 500px (width), С2 = 50px (height), угол наклона -65 (deg), следовательно, угол a в треугольнике ABC равен 65 градусам, а угол b — 25 градусам (180 — 90 — 65). В треугольнике A2B2C2 углы a2 и b2 равны 65 и 25 градусам соответственно.

Тригонометрия

Все просто. Синус угла равен отношению противолежащего катета к гипотинузе. Следовательно:

A = sin(a) * C или A = sin(65) * 500;
B = sin(b) * C или B = sin(25) * 500;

A2 = sin(a2) * C2 или A2 = sin(65) * 50;
B2 = sin(b2) * C2 или B2 = sing(25) * 50;

LESS

.MojoRibbon(@width, @height, @deg, @valign) {
	width: @width;
	height: @height;
	-webkit-box-sizing: border-box;  /* Что бы высота не изменялась при padding */
	-moz-box-sizing: border-box;
	-ms-box-sizing: border-box;
	box-sizing: border-box;
	.defineDegree(@deg, @valign) when (@deg < 0) and (@valign = top) {
		@degree: -@deg;  /* Угол треугольника неотрицательный, вычисляем правильный  sin */
		top: @countHeight;
		left: @countWidth;	
		-webkit-transform: rotate(@deg);
		-moz-transform: rotate(@deg);
		-o-transform: rotate(@deg);
		-ms-transform: rotate(@deg);
		transform: rotate(@deg);
	};
	.defineDegree(@deg, @valign) when (@deg < 0) and (@valign = bottom) {
		@degree: -@deg; /* Угол треугольника неотрицательный, вычисляем правильный  sin */
		bottom: @countHeight;
		right: @countWidth; /* Если угол поворота отрицательный и в вертикали объект позицианируется по нижнему краю, логически правильно в горизонтали позицианировать его по правому краю */
		-webkit-transform: rotate(@deg);
		-moz-transform: rotate(@deg);
		-o-transform: rotate(@deg);
		-ms-transform: rotate(@deg);
		transform: rotate(@deg);
	};
	.defineDegree(@deg, @valign) when (@deg > 0) and (@valign = top) {
		@degree: @deg;
		top: @countHeight;
		right: @countWidth;
		-webkit-transform: rotate(@degree);
		-moz-transform: rotate(@degree);
		-o-transform: rotate(@degree);
		-ms-transform: rotate(@degree);
		transform: rotate(@degree);
	};
	.defineDegree(@deg, @valign) when (@deg > 0) and (@valign = bottom) {
		@degree: @deg;
		bottom: @countHeight;
		left: @countWidth;  /* Если угол поворота положительный и в вертикали объект позицианируется по верхнему краю, логически правильно в горизонтали позицианировать его по левому краю */
		-webkit-transform: rotate(@degree);
		-moz-transform: rotate(@degree);
		-o-transform: rotate(@degree);
		-ms-transform: rotate(@degree);
		transform: rotate(@degree);
	};
	.defineDegree(@deg, @valign) when (@deg = 0) {
		@degree: @deg;
		top: 0;
		left: 0;
	};
	.defineDegree(@deg, @valign);
	@angleB: 90-@degree;
	@angleB2: @angleB;
	@sideA: round(sin(@degree), 3)*@width; /* Сторона А */
	@sideB: round(sin(@angleB), 3)*@width; /* Сторона B */
	@sideB2: round(sin(@angleB2), 3)*@height;  /* Сторона А2 */
	@sideA2: round(sin(@degree), 3)*@height; /* Сторона B2 */
	@countHeight: @sideA/2 - @height/2 - @sideB2/2;
	@countWidth: -((@width)-(@sideB))/2 - @sideA2/2;	
}

Демо результата
GitHub

Большое спасибо всем таким же, как и я CSS занудам за внимание.

Автор: ilusha_sergeevich

Источник

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


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