Что мы знаем о лисе? - минималистский джаваскрипт [entries|archive|friends|userinfo]
Anatoly Vorobey

[ website | Website ]
[ userinfo | livejournal userinfo ]
[ archive | journal archive ]

Links
[Links:| English-language weblog ]

минималистский джаваскрипт [Янв. 7, 2008|03:26 pm]
Previous Entry в избранное рассказать другу Next Entry
(эта запись будет интересна в основном веб-программистам)

Вот это очень круто по-моему - эффект достигнут при помощи менее чем 256 байтов джаваскрипта. Вот этот код:


p01<i id=d><script>setInterval('for(x=_="in solid #",E=Math,o=99,--O;o--;x+=
"<hr style=\'width:0;margin:auto;border-right:"+E.abs(q?p:P)+_+(9-q)*36+
";border-left:"+E.abs(q?P:p)+_+(8+q)*36+"\'>")q=0>(p=E.cos(O+=22))*(P=E.sin(O));
d.innerHTML=x',O=9)</script>


Давайте разберемся.



1. Расставим немного пробелов и переносов строк.


p01 
<i id=d> 
<script>   
  setInterval(' \       
    for(x = _ = "in solid #", E=Math, o=99, --O; \           
      o--; \            
      x += "<hr style=\'width:0;margin:auto;border-right:" \             
        + E.abs(q ? p : P) + _ + (9-q)*36 \
        + ";border-left:" \             
        + E.abs(q ? P : p) 
        + _ 
        + (8+q)*36 + "\'>" \           
       ) \
      q = 0 > (p = E.cos(O+=22))*(P = E.sin(O)); \
    d.innerHTML=x',O=9)
</script>


Что из этого можно понять? У нас есть элемент <i>, которому дано имя 'd'. Вызов setInterval приводит к тому, что первый аргумент, который заканчивается "d.innerHTML=x", т.е. меняет тело элемента на x, будет вызываться каждые 9 миллисекунд (одновременно переменная O инициализируется в 9). Кроме этого присвоения, все, что происходит, это некий цикл, который исполняется 99 раз (начиная с o=99, условие прекращения o-- выполнится, когда o==0), и от цикла к циклу переменная O уменьшается на 1, но кроме того внутри цикла она 99 раз увеличивается на 22: p = E.cos(O+=22).

2. Переименуем некоторые переменные. Вместо E=Math можно везде писать просто Math. Переменная _ всегда имеет значение строки "in solid #". Раскроем некоторые инициализации отдельно. Сделаем структуру цикла более очевидной, накопление внутри переменной x перенесем в тело цикла. Переименуем o в line, т.к. логично предположить, что 99 раз - это 99 строк, а O в angle, т.к. к нему применяют синус и косинус. Наконец, вместо того, чтобы передавать setInterval одну длинную строку, вынесем этот код в функцию и передадим ее.


p01
<i id=d>
<script>
  angle = 9;
  function one_event() {
    x = "in solid #";
    --angle;
    for(line = 99; line != 0; line--) {
      q = 0 > (p = Math.cos(angle += 22))*(P = Math.sin(angle));
      line_html = "<hr style=\'width:0;margin:auto;border-right:"
           + Math.abs(q ? p : P) + "in solid #" + (9-q)*36
           + ";border-left:" + Math.abs(q ? P : p)
           + "in solid #" + (8+q)*36 + "\'>";
      x += line_html;
    }
    d.innerHTML=x;
  }
  setInterval(one_event, 9);
</script>


Читаем дальше. Начальные значения x и angle не играют роли: они нужны были только для того, чтобы определить эти переменные как можно более компактно. Становится понятен смысл "in solid #": in - это дюймы, сразу перед ними идет число - абсолютное значение либо косинуса, либо синуса угла angle - p или P. # предваряет номер цвета, который равен либо 9*36, либо 8*36, т.к. q равен либо 0, либо 1. Когда q равно 1? Когда косинус и синус угла angle одного знака, т.е. угол находится либо в первом квадранте (от 0 до 90 градусов), либо в третьем (от 180 до 270 градусов). (9-q)*36 и (8+q)*36 выбирают всегда разные цвета из набора двух цветов, в зависимости от q.

3. Переименуем переменные и упростим логику кода. Он стал длиннее, но понятнее:


p01
<i id=d>
<script>
  angle = 0;
  function one_event() {
    x = "";
    angle-=1;
    color1 = "#" + 8*36;
    color2 = "#" + 9*36;
    for(line = 0; line != 99; line++) {
      angle += 22;
      cos = Math.cos(angle);
      sin = Math.sin(angle);
      is_quadrant24 = (cos * sin > 0 ? 1 : 0);
      line_html = "<hr style=\'width:0;margin:auto" +

        " ;border-right:"
        + Math.abs(is_quadrant24 ? cos : sin) + "in"
        + " solid "
        + (is_quadrant24 ? color1 : color2)

        + ";border-left:"
        + Math.abs(is_quadrant24 ? sin : cos) + "in"
        + " solid "
        + (is_quadrant24 ? color2 : color1)

        + "\'>";
      x += line_html;
    }
    d.innerHTML=x;
  }
  setInterval(one_event, 9);
</script>


Как рисуется каждая отдельная линия, понятно: с помощью тага hr, который на самом деле ничего не рисует (width:0), но слева и справа у него есть границы разных цветов и протяжений. У каждой отдельной линии длина левой и правой границы равна соответственно синусу и косинусу угла angle, или наоборот косинусу и синусу, в зависимости от того, в какой квадрант попадает angle (is_quadrant24). Цвета левой и правой границ тоже меняются в зависимости от этого, так что цвет color1 всегда соответствует косинусу угла по длине, а цвет color2 - синусу угла по длине.

Осталось разобраться, как каждая линия соотносится с следующей, и почему происходит эффект кручения.

Почему angle+=22, откуда это число? Тут используется тот факт, что 22/7 - очень хорошее приближение числа пи. Соответственно добавить к углу 22 - все равно, что добавить 7*pi, и еще чуть-чуть (примерно 0.009). Добавление 7*pi не меняет абсолютного значения косинуса и синуса угла, а только их знак на обратный - но меняет на обратный знак и того, и другого, поэтому значение is_quadrant24 от этого остается неизменным. По значениям косинуса и синуса angle при каждом прохождении сквозь цикл отличается от предыдущего очень незначительно: угол увеличивается на 0.008, и либо косинус чуть-чуть увеличивается, а синус чуть-чуть уменьшается, либо наоборот. Так и выходит, что граница между двумя цветами ползет из одной стороны в другую. Общая ширина каждой линии равна сумме синуса и косинуса (точнее, их абсолютных значений); минимум этого значения достигается в углах, кратных pi/2, и равен 1 (одному дюйму, благодаря "in"), максимум - в углах, кратных 45 градусов, и равен квадратному корню из 2 - примерно 1.41. Из-за этого возникает впечатление крутящейся ленты, видимая ширина которой колеблется из-за того, что разные ее части выходят на передний план.

За полный проход цикла сдвиг угол увеличивается на 22*99, но если учитывать только сдвиги по отношению к pi, то их накапливается всего лишь около 0.9 (сто раз по 0.009). В начале следующего цикла команда angle-=1 "отменяет" накопившийся сдвиг, еще и добавляя 0.1 по сравнению с тем, с чего начали предыдущий цикл. В итоге выходит, что по значению угла (опять-таки по модулю pi) следующий цикл немного сдвинут, на 0.1 примерно, относительно предыдущего. И это тоже помогает создать впечатление кручения: граница между цветами ползет не только внутри одного пробега из 99 линий, но и между пробегами все время двигается с определенной скоростью.

Угол angle изменяется примерно на 0.1 (по модулю pi) за один вызов функции one_event(). В какой-то момент он пересекает границу pi/2 - границу в 90, 180, 270 или 360 градусов - т.е. переходит в следующий квадрант. В этот момент значение is_quadrant24 меняется с 0 на 1 или наоборот, и соответственно длины границ и их цвета мгновенно меняются местами. Визуально это воспринимается как закручивание, переход от одного цвета на "переднем" плане к другому. Как часто это происходит? Функция вызывается каждые 9 миллисекунд, и нужно примерно 16 вызовов, чтобы накопить, по 0.1 за вызов, разницу в примерно 1.6 (pi/2). Значит, закручивание должно происходить примерно каждые 144 миллисекунды, т.е. около 7 раз в секунду.

Кажется, все; если что-то непонятно, можно спросить.
ссылкаОтветить

Comments:
[User Picture]From: [info]itman
2008-01-07 01:53 pm none (UTC)

(Link)

Справедливости ради стоит заметить, что это JS + CSS
[User Picture]From: [info]sleeping_death
2008-01-07 02:33 pm none (UTC)

(Link)

а также то, что ни в опере, ни в ИЕ6 не работает. В ие - в принципе, в Опере - чуть-чуть.
[User Picture]From: [info]lost_docs
2008-01-07 02:39 pm none (UTC)

(Link)

в 7ом ишаке тоже, не работает
[User Picture]From: [info]onodera
2008-01-07 06:45 pm none (UTC)

(Link)

В Opera 9.50 работает.
[User Picture]From: [info]sleeping_death
2008-01-07 07:00 pm none (UTC)

(Link)

которая бета?
From: (Anonymous)
2008-01-07 09:53 pm none (UTC)

(Link)

в IE6 работет, ежель вместо hr поставить какой-нить div
[User Picture]From: [info]malfet_
2008-01-08 09:59 am none (UTC)

(Link)

У меня Опера 9.25 и в ней скрипт работает, хотя и медленнее чем в ОгненноЛисе.
[User Picture]From: [info]bret
2008-01-07 02:15 pm none (UTC)

(Link)

правда, процессор грузит на все сто :)
[User Picture]From: [info]alec101
2008-01-07 02:40 pm none (UTC)

(Link)

Ну не на сто, а процентов на 40-50 всего :)
[User Picture]From: [info]psi_storm
2008-01-07 02:52 pm none (UTC)

(Link)

это потому что у вас core 2 duo
[User Picture]From: [info]alec101
2008-01-07 02:54 pm none (UTC)

(Link)

Ты зналъ, ты зналъ :)
[User Picture]From: [info]cmm
2008-01-07 02:53 pm none (UTC)

(Link)

а это у Вас процессор двойной.
[User Picture]From: [info]ivan_ghandhi
2008-01-07 02:59 pm none (UTC)

(Link)

О блин, сколько открытий чудных. И <i>, и <hr>, и border-left, border-right, и как цвет лихо задаётся. Так ведь можно круглые углы легко имплементировать. Блин...
[User Picture]From: [info]ivanvr
2008-01-07 03:37 pm none (UTC)

(Link)

Может тогда понравится и эта ссылка:
http://www.smashingmagazine.com/2007/01/19/53-css-techniques-you-couldnt-live-without/
From: [info]selfmade
2008-01-07 03:29 pm none (UTC)

(Link)

В нашем корпоративный билде Firefox 2.0.0.9 не работает. В IE6 тоже.
[User Picture]From: [info]sleeping_death
2008-01-07 03:50 pm none (UTC)

(Link)

Демосцена всегда славилась умением максимально эффективно использовать возможности языка... в ущерб производительности и понятности.
[User Picture]From: [info]dimrub
2008-01-07 04:04 pm none (UTC)

(Link)

Круть...
[User Picture]From: [info]nikto
2008-01-07 05:21 pm none (UTC)

(Link)

Вот конструкция
x = _ = "in solid #"

интуитивно не очень понятна (для тех, у кого JS не основной рабочий инструмент). Сначала надо уяснить, что символ "_" может быть именем переменной, а потом еще догадаться, что оператор присваивания в JS, наверное, тоже правоассоциативный, как в C++ и Java. Не люблю, когда код, с которым приходится работать, написан в таком стиле.
[User Picture]From: [info]avva
2008-01-07 05:50 pm none (UTC)

(Link)

Ну конечно так нельзя писать.
[User Picture]From: [info]bolk
2008-01-08 04:37 pm none (UTC)

(Link)

А зачем делать код интуитивнопонятным для тех, кто не знает язык?
[User Picture]From: [info]bolk
2008-01-08 04:41 pm none (UTC)

(Link)

а вызов

function(o) {
o.call(o.callee)
}($)

вам мозг не рвёт? ;)
From: (Anonymous)
2008-01-08 04:48 pm none (UTC)

(Link)

вот это вот ($) в конце - не очень понятно, что хотел сказать этим автор :)
[User Picture]From: [info]nikto
2008-01-08 04:50 pm none (UTC)

(Link)

сорри, это я был.
[User Picture]From: [info]bolk
2008-01-09 08:56 pm none (UTC)

(Link)

Это объект передаётся. Это я на память воспроизвёл или что-то из jQuery или из какого-то примера его использования. Там основной объект носит имя «$».
[User Picture]From: [info]tty01
2008-01-07 06:34 pm none (UTC)

(Link)

Прикольно. Firefox 2.0.0.11 -- работает. Но процент использования CPU высокий.
[User Picture]From: [info]sleeping_death
2008-01-07 07:07 pm none (UTC)

(Link)

посмотрите на странице группы демку Mandelbrot Rotozoom и сравните ее скорость в Опере :) FF в принципе тормознее.
[User Picture]From: [info]neograff
2008-01-08 02:19 am none (UTC)

(Link)

очень ресурсоёмко.
[User Picture]From: [info]toucha
2008-01-08 03:28 am none (UTC)

(Link)

Sorry i am completely out of context - just reading you for sometimes..... and since you program and perhaps know others who do that... may you be you can recommend me someone - i need to make an online store for my business, and remake the website from flash to html. (hopefully not crazy expensively).... thank you, and sorry for intruding like that...
[User Picture]From: [info]avva
2008-01-08 03:57 pm none (UTC)

(Link)

Sorry, I don't really have any contacts to recommend for the job. There're probably specialized communities and such that can help you.
From: (Anonymous)
2008-01-10 12:45 pm none (UTC)

(Link)

ok, thank you ...
From: [info]dp074
2008-01-08 08:42 am none (UTC)

(Link)

К вопросу о минималистских и красивых скриптах:
http://centrolit.kulichki.com/centrolit/cgi/br_grade.cgi?grade=13078
(примерчик древний, показывает у меня в IE7, но не показывает в FF2.0)
From: [info]dp074
2008-01-08 09:02 am none (UTC)

(Link)

И еще:
http://centrolit.kulichki.com/centrolit/cgi/br_grade.cgi?grade=13120
Все это умельцы вводят в узеньких текстовых полях ввода для стихов на www.burme.com, естественно, ни для чего такого не предназначенных. При этом ввод обязан удовлетворять правилам буриме (оканчиваться на заданные рифмы).
[User Picture]From: [info]malfet_
2008-01-08 10:39 am none (UTC)

(Link)

Демосцены на JS это конечно забавно.Но такие названия переменных(особенно "_") - это уже какая-то обфускация - причем непонятно зачем она нужна в JS коде...
[User Picture]From: [info]bolk
2008-01-08 04:39 pm none (UTC)

(Link)

Чем это хуже букв? В JS можно создать переменную «$», например.
[User Picture]From: [info]malfet_
2008-01-08 07:38 pm none (UTC)

(Link)

Можно. Но нужно ли превращать JavaScript в еще один Perl?
[User Picture]From: [info]bolk
2008-01-09 08:58 pm none (UTC)

(Link)

Не нужно. Это не какая-то специальная конструкция, это JS позволяет вполне легально использовать этот символ в переменных. Например, вполне стандартное:

if (/MSIE\s+(\d+)/.test(navigator.userAgent)) alert(RexExp.$1)
[User Picture]From: [info]kamacity_perm
2008-01-10 03:42 pm none (UTC)

(Link)

Спасибо за разбор....в практике пригодится!
http://www.registrator-ooo.ru/
From: (Anonymous)
2008-01-12 04:08 pm none (UTC)

(Link)

WOW
From: (Anonymous)
2008-01-12 09:58 pm none (UTC)

(Link)

круто пишете, что сказать.
From: (Anonymous)
2008-01-13 09:26 am none (UTC)

(Link)

Я уверен, что когда-нибуть девелоперы gecko engine найдут вас, и за malformed javascript с особой жестокостью отомстят.