Я как-то раньше никогда не задумывался над такой штукой, как калейдоскоп на странице. Видел их как-то раньше, но не обращал особо внимания. А тут увидел у Лебедева в портфолио калейдоскоп на флеше, покрутил по нему мышкой, понял принцип работы и подумал «ёлки, это же не сложно!».
Конечно, нельзя сказать, что это действительно очень просто. И нельзя сказать, что получилось совсем всё, что хотелось. Но есть на что посмотреть, за чем приглашаю под кат.
UPD
В посте добавил улучшенный вариант от хабрачеловека hlomzik, который не работает в ИЕ, но с меньшим кол-вом кода и с более правильным поведением.
В самом начале, я надеялся, что он будет работать совсем везде и хорошо. Не совсем получилось. В результате:
Сразу дам ссылку на рабочий пример, чтоб было понятно, о чем вообще речь: kaleidoscope.terion.name
Итак, поехали.
Калейдоскоп состоит из 12 секторов, собранных в диск. Соответственно возникает вопрос: как сделать сектор? На самом деле, это очень просто. Нам понадобится 4 блока и немного CSS. Выглядеть это должно так:
Блок-контейнер, один блок повернутый на -15°, внутри блок повернутый на 30°, внутри еще один повернутый на -15°.
У сектора установлена фоновая картинка, которую мы будем смещать.
Код, соответственно очень простой:
Да, мы сразу предполагаем, что на странице может быть несколько калейдоскопов, поэтому используем только классы.
Важное условие: размер калейдоскопа должен меняться путем изменения размеров только контейнера, соответственно внутри всё должно быть в процентах.
Сразу отмечу, что значения в процентах были подобраны методом научного тыка, т.к. мои знания тригонометрии не столь глубоки, чтобы рассчитать всё это :)
Итак, у нас есть код для сектора. Теперь этих секторов должно быть 12 и ни должны быть завернуты в 2 контейнера, т.к. внутренний используется для позиционирования элементов, а внешний — для задания размеров и обрезки лишнего.
В итоге весь хтмл-код будет таким:
Классы s1 — s12 для определения положения каждого сектора.
Класс pattern определяет, какая картинка будет использована (спасибо eto_moy_nick за паттерн :) )
Расставляем блоки по местам:
В общем-то и всё.
Но ведь есть ИЕ, который всего этого не понимает! Но для ИЕ есть фильтры filter: progid:DXImageTransform.Microsoft.Matrix(...).
Поэтому, в условный комментарий [if IE 8] добавляем следующий код:
Далее:
К моему удивлению, FilterType='nearest neighbor' не принес результата (без бикубического сглаживания, скорее всего, не было бы таких тормозов).
Для расчетов матрицы я пользовался вот этим инструментом, просто спасшим мне жизнь: www.boogdesign.com/examples/transforms/matrix-calculator.html
Маржинами были вручную расставлены блоки. Если есть желание заставить это все работать в ИЕ7 — нужно лишь для него переписать эти самые маржины.
Javascript. Нужно двигать фон секторов за мышкой. При этом мы помним, что калейдоскоп на странице не один. Код ниже, читаем комментарии:
В общем-то, готово.
Opera:
Если блоку .pattern добавить overflow:hidden — пропадает ВСЁ. В итоге пустая страница со скроллами в пустоту на ширину «веера».
Если overflow:hidden убрать, то в «самом быстром браузере на земле»© эта конструкция работает медленнее, чем в ИЕ.
Это полный провал.
Надеюсь, вам было интересно :)
UPD
А вот и улучшенный вариант от хабрачеловека hlomzik:
quaint.su/for/habrahabr/kaleidoscope
Обсуждение, в котором это родилось, вот: habrahabr.ru/blogs/css/99019/?reply_to=3057019#comment_3054307
Конечно, нельзя сказать, что это действительно очень просто. И нельзя сказать, что получилось совсем всё, что хотелось. Но есть на что посмотреть, за чем приглашаю под кат.
UPD
В посте добавил улучшенный вариант от хабрачеловека hlomzik, который не работает в ИЕ, но с меньшим кол-вом кода и с более правильным поведением.
В самом начале, я надеялся, что он будет работать совсем везде и хорошо. Не совсем получилось. В результате:
- Firefox / Safari / Chrome — Работает идеально (кто бы сомневался).
- IE8 — работает, но только если калейдоскоп небольшой (до 300х300). Если больше — начинает серьезно тормозить.
- IE7 — будет работать, если добавить для него отдельный CSS в котором поправить позиционирование блоков.
- Opera — ничерта не работает. Это отдельная песня. Сразу обращаюсь к ребятам из Оперы, которые это прочтут — посмотрите на демо и оформите это как багрепорт. В «самом быстром браузере на земле»© CSS-transform тормозит на столько безбожно, что это не лезет ни в какие ворота. И опять оперная вечная проблема — полная лажа при использовании overflow:hidden и не static-позиционированных элементах. СДЕЛАЙТЕ С ЭТИМ ЧТО-ТО!
Сразу дам ссылку на рабочий пример, чтоб было понятно, о чем вообще речь: kaleidoscope.terion.name
Итак, поехали.
Задача 1: понять, как вообще это сделать.
Калейдоскоп состоит из 12 секторов, собранных в диск. Соответственно возникает вопрос: как сделать сектор? На самом деле, это очень просто. Нам понадобится 4 блока и немного CSS. Выглядеть это должно так:
Блок-контейнер, один блок повернутый на -15°, внутри блок повернутый на 30°, внутри еще один повернутый на -15°.
У сектора установлена фоновая картинка, которую мы будем смещать.
Задача 2: собственно, собрать это все.
Код, соответственно очень простой:
<div class="sc s1">
<div class="rl">
<div class="rr">
<div class="sv">
</div>
</div>
</div>
</div>
Да, мы сразу предполагаем, что на странице может быть несколько калейдоскопов, поэтому используем только классы.
Важное условие: размер калейдоскопа должен меняться путем изменения размеров только контейнера, соответственно внутри всё должно быть в процентах.
Сразу отмечу, что значения в процентах были подобраны методом научного тыка, т.к. мои знания тригонометрии не столь глубоки, чтобы рассчитать всё это :)
- .scope_container .sc {
- width:50%;
- height:50%;
- -webkit-transform-origin: top center;
- -moz-transform-origin: top center;
- -o-transform-origin:top center;
- transform-origin:top center;
- position:absolute;
- top:50%;
- left:25%;
- z-index:-1;
- }
-
- .scope_container .sc div {
- overflow:hidden}
-
- .scope_container .rl {
- height:110%;
- width:60%;
- -webkit-transform: rotate(-15deg);
- -moz-transform: rotate(-15deg);
- -o-transform: rotate(-15deg);
- transform: rotate(-15deg);
- position:relative;
- top:1.5%;
- left:4.5%;
-
- }
-
- .scope_container .rr {
- height:100%;
- width:100%;
- -webkit-transform: rotate(30deg);
- -moz-transform: rotate(30deg);
- -o-transform: rotate(30deg);
- transform: rotate(30deg);
- position:relative;
- top:7%;
- left:51%;
- }
-
- .scope_container .sv {
- width:105%;
- height:105%;
- -webkit-transform: rotate(-15deg);
- -moz-transform: rotate(-15deg);
- -o-transform: rotate(-15deg);
- transform: rotate(-15deg);
- position:relative;
- top:-2%;
- left:-29%;
- }
Итак, у нас есть код для сектора. Теперь этих секторов должно быть 12 и ни должны быть завернуты в 2 контейнера, т.к. внутренний используется для позиционирования элементов, а внешний — для задания размеров и обрезки лишнего.
В итоге весь хтмл-код будет таким:
- <div class="parent">
- <div class="scope_container pattern">
-
- <div class="sc s1"><div class="rl"><div class="rr"><div class="sv"></div></div></div></div>
- <div class="sc s2"><div class="rl"><div class="rr"><div class="sv"></div></div></div></div>
- <div class="sc s3"><div class="rl"><div class="rr"><div class="sv"></div></div></div></div>
- <div class="sc s4"><div class="rl"><div class="rr"><div class="sv"></div></div></div></div>
- <div class="sc s5"><div class="rl"><div class="rr"><div class="sv"></div></div></div></div>
- <div class="sc s6"><div class="rl"><div class="rr"><div class="sv"></div></div></div></div>
- <div class="sc s7"><div class="rl"><div class="rr"><div class="sv"></div></div></div></div>
- <div class="sc s8"><div class="rl"><div class="rr"><div class="sv"></div></div></div></div>
- <div class="sc s9"><div class="rl"><div class="rr"><div class="sv"></div></div></div></div>
- <div class="sc s10"><div class="rl"><div class="rr"><div class="sv"></div></div></div></div>
- <div class="sc s11"><div class="rl"><div class="rr"><div class="sv"></div></div></div></div>
- <div class="sc s12"><div class="rl"><div class="rr"><div class="sv"></div></div></div></div>
-
- </div>
- </div>
Классы s1 — s12 для определения положения каждого сектора.
Класс pattern определяет, какая картинка будет использована (спасибо eto_moy_nick за паттерн :) )
Расставляем блоки по местам:
- .scope_container .s1 {
- -webkit-transform: rotate(0deg);
- -moz-transform: rotate(0deg);
- -o-transform: rotate(0deg);
- transform: rotate(0deg);
- }
- .scope_container .s2 {
- -webkit-transform: rotate(30deg);
- -moz-transform: rotate(30deg);
- -o-transform: rotate(30deg);
- transform: rotate(30deg);
- }
И так до 12го с шагом в 30 градусов.В общем-то и всё.
Но ведь есть ИЕ, который всего этого не понимает! Но для ИЕ есть фильтры filter: progid:DXImageTransform.Microsoft.Matrix(...).
Поэтому, в условный комментарий [if IE 8] добавляем следующий код:
- .scope_container .s1 {
- -webkit-transform: rotate(0deg);
- -moz-transform: rotate(0deg);
- -o-transform: rotate(0deg);
- transform: rotate(0deg);
- }
- .scope_container .s2 {
- -webkit-transform: rotate(30deg);
- -moz-transform: rotate(30deg);
- -o-transform: rotate(30deg);
- transform: rotate(30deg);
- }
Как показали опыты, с абсолютным позиционированием и матрицей у ИЕ беда. Поэтому пришлось такие костыли городить.Далее:
- .s2 {
- filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.86602540, M12=-0.50000000, M21=0.50000000, M22=0.86602540,SizingMethod='auto expand',FilterType='nearest neighbor');
- margin:-64.5% 0 0 -25%;
- }
- .s3 {
- filter: progid:DXImageTransform.Microsoft.Matrix(M11=0.50000000, M12=-0.86602540, M21=0.86602540, M22=0.50000000,SizingMethod='auto expand',FilterType='nearest neighbor');
- margin:-60.5% 0 0 -32.8%;
- }
И т.п.К моему удивлению, FilterType='nearest neighbor' не принес результата (без бикубического сглаживания, скорее всего, не было бы таких тормозов).
Для расчетов матрицы я пользовался вот этим инструментом, просто спасшим мне жизнь: www.boogdesign.com/examples/transforms/matrix-calculator.html
Маржинами были вручную расставлены блоки. Если есть желание заставить это все работать в ИЕ7 — нужно лишь для него переписать эти самые маржины.
Задача 3: заставить это двигаться
Javascript. Нужно двигать фон секторов за мышкой. При этом мы помним, что калейдоскоп на странице не один. Код ниже, читаем комментарии:
- <script type="text/javascript">
-
- //Мы используем классы, но ИЕ не умеет их выбирать. Компенсируем этот недостаток.
- if(document.getElementsByClassName) {
-
- getElementsByClass = function(classList, node) {
- return (node || document).getElementsByClassName(classList)
- }
-
- } else {
-
- getElementsByClass = function(classList, node) {
- var node = node || document,
- list = node.getElementsByTagName('*'),
- length = list.length,
- classArray = classList.split(/\s+/),
- classes = classArray.length,
- result = [], i,j
- for(i = 0; i < length; i++) {
- for(j = 0; j < classes; j++) {
- if(list[i].className.search('\\b' + classArray[j] + '\\b') != -1) {
- result.push(list[i])
- break
- }
- }
- }
-
- return result
- }
- }
-
-
- //Получаем координаты мыши
- function mousePageXY(e)
- {
- var x = 0, y = 0;
-
- if (!e) e = window.event;
-
- if (e.pageX || e.pageY)
- {
- x = e.pageX;
- y = e.pageY;
- }
- else if (e.clientX || e.clientY)
- {
- x = e.clientX + (document.documentElement.scrollLeft || document.body.scrollLeft) - document.documentElement.clientLeft;
- y = e.clientY + (document.documentElement.scrollTop || document.body.scrollTop) - document.documentElement.clientTop;
- }
-
- return {"x":x, "y":y};
- }
-
-
- window.onload = function() {
-
- var scope_cont = getElementsByClass('scope_container', document);
-
- //Калейдоскопов может быть несколько, учитываем это.
- for (i=0;i<scope_cont.length;i++)
- {
- scope_cont[i].onmouseover = function() {
- var sect = getElementsByClass('sv', this);
- var curscope = this;
-
- this.onmousemove = function(e){
- var mCur = mousePageXY(e);
- for (n=0;n<sect.length;n++)
- {
- //У четных секторов фон двигается в одну сторону
- if (n%2) {
- sect[n].style.backgroundPosition = mCur.x + 'px ' + mCur.y + 'px';
- }
- //У нечетных — в другую
- else {
- sect[n].style.backgroundPosition = mCur.x*(-1) + 'px ' + mCur.y + 'px'
- }
- }
-
- }
- }
- scope_cont[i].onmouseout = function() {
- //Убираем за собой, чтоб не перегружать браузер
- document.onmousemove = null;
- }
- }
- }
- </script>
В общем-то, готово.
Opera:
Если блоку .pattern добавить overflow:hidden — пропадает ВСЁ. В итоге пустая страница со скроллами в пустоту на ширину «веера».
Если overflow:hidden убрать, то в «самом быстром браузере на земле»© эта конструкция работает медленнее, чем в ИЕ.
Это полный провал.
Надеюсь, вам было интересно :)
UPD
А вот и улучшенный вариант от хабрачеловека hlomzik:
quaint.su/for/habrahabr/kaleidoscope
Обсуждение, в котором это родилось, вот: habrahabr.ru/blogs/css/99019/?reply_to=3057019#comment_3054307