?

Log in

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

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

Links
[Links:| English-language weblog ]

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

Вот это очень круто по-моему - эффект достигнут при помощи менее чем 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: itman
2008-01-07 01:53 pm
Справедливости ради стоит заметить, что это JS + CSS
(Ответить) (Thread)
[User Picture]From: sleeping_death
2008-01-07 02:33 pm
а также то, что ни в опере, ни в ИЕ6 не работает. В ие - в принципе, в Опере - чуть-чуть.
(Ответить) (Parent) (Thread)
From: ex_lost_docs526
2008-01-07 02:39 pm
в 7ом ишаке тоже, не работает
(Ответить) (Parent) (Thread)
[User Picture]From: onodera
2008-01-07 06:45 pm
В Opera 9.50 работает.
(Ответить) (Parent) (Thread)
[User Picture]From: sleeping_death
2008-01-07 07:00 pm
которая бета?
(Ответить) (Parent) (Thread)
From: (Anonymous)
2008-01-07 09:53 pm
в IE6 работет, ежель вместо hr поставить какой-нить div
(Ответить) (Parent) (Thread)
[User Picture]From: malfet_
2008-01-08 09:59 am
У меня Опера 9.25 и в ней скрипт работает, хотя и медленнее чем в ОгненноЛисе.
(Ответить) (Parent) (Thread)
[User Picture]From: bret
2008-01-07 02:15 pm
правда, процессор грузит на все сто :)
(Ответить) (Thread)
[User Picture]From: alec101
2008-01-07 02:40 pm
Ну не на сто, а процентов на 40-50 всего :)
(Ответить) (Parent) (Thread)
[User Picture]From: psi_storm
2008-01-07 02:52 pm
это потому что у вас core 2 duo
(Ответить) (Parent) (Thread)
[User Picture]From: alec101
2008-01-07 02:54 pm
Ты зналъ, ты зналъ :)
(Ответить) (Parent) (Thread)
[User Picture]From: cmm
2008-01-07 02:53 pm
а это у Вас процессор двойной.
(Ответить) (Parent) (Thread)
[User Picture]From: juan_gandhi
2008-01-07 02:59 pm
О блин, сколько открытий чудных. И <i>, и <hr>, и border-left, border-right, и как цвет лихо задаётся. Так ведь можно круглые углы легко имплементировать. Блин...
(Ответить) (Thread)
[User Picture]From: ivanvr
2008-01-07 03:37 pm
Может тогда понравится и эта ссылка:
http://www.smashingmagazine.com/2007/01/19/53-css-techniques-you-couldnt-live-without/
(Ответить) (Parent) (Thread)
From: selfmade
2008-01-07 03:29 pm
В нашем корпоративный билде Firefox 2.0.0.9 не работает. В IE6 тоже.
(Ответить) (Thread)
[User Picture]From: sleeping_death
2008-01-07 03:50 pm
Демосцена всегда славилась умением максимально эффективно использовать возможности языка... в ущерб производительности и понятности.
(Ответить) (Thread)
[User Picture]From: dimrub
2008-01-07 04:04 pm
Круть...
(Ответить) (Thread)
[User Picture]From: klvov
2008-01-07 05:21 pm
Вот конструкция
x = _ = "in solid #"

интуитивно не очень понятна (для тех, у кого JS не основной рабочий инструмент). Сначала надо уяснить, что символ "_" может быть именем переменной, а потом еще догадаться, что оператор присваивания в JS, наверное, тоже правоассоциативный, как в C++ и Java. Не люблю, когда код, с которым приходится работать, написан в таком стиле.
(Ответить) (Thread)
[User Picture]From: avva
2008-01-07 05:50 pm
Ну конечно так нельзя писать.
(Ответить) (Parent) (Thread)
[User Picture]From: bolk
2008-01-08 04:37 pm
А зачем делать код интуитивнопонятным для тех, кто не знает язык?
(Ответить) (Parent) (Thread)
[User Picture]From: bolk
2008-01-08 04:41 pm
а вызов

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

вам мозг не рвёт? ;)
(Ответить) (Parent) (Thread)
From: (Anonymous)
2008-01-08 04:48 pm
вот это вот ($) в конце - не очень понятно, что хотел сказать этим автор :)
(Ответить) (Parent) (Thread)
[User Picture]From: klvov
2008-01-08 04:50 pm
сорри, это я был.
(Ответить) (Parent) (Thread)
[User Picture]From: bolk
2008-01-09 08:56 pm
Это объект передаётся. Это я на память воспроизвёл или что-то из jQuery или из какого-то примера его использования. Там основной объект носит имя «$».
(Ответить) (Parent) (Thread)
[User Picture]From: tty01
2008-01-07 06:34 pm
Прикольно. Firefox 2.0.0.11 -- работает. Но процент использования CPU высокий.
(Ответить) (Thread)
[User Picture]From: sleeping_death
2008-01-07 07:07 pm
посмотрите на странице группы демку Mandelbrot Rotozoom и сравните ее скорость в Опере :) FF в принципе тормознее.
(Ответить) (Parent) (Thread)
[User Picture]From: neograff
2008-01-08 02:19 am
очень ресурсоёмко.
(Ответить) (Thread)
[User Picture]From: toucha
2008-01-08 03:28 am
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...
(Ответить) (Thread)
[User Picture]From: avva
2008-01-08 03:57 pm
Sorry, I don't really have any contacts to recommend for the job. There're probably specialized communities and such that can help you.
(Ответить) (Parent) (Thread)
From: (Anonymous)
2008-01-10 12:45 pm
ok, thank you ...
(Ответить) (Parent) (Thread)
[User Picture]From: dp074
2008-01-08 08:42 am
К вопросу о минималистских и красивых скриптах:
http://centrolit.kulichki.com/centrolit/cgi/br_grade.cgi?grade=13078
(примерчик древний, показывает у меня в IE7, но не показывает в FF2.0)
(Ответить) (Thread)
[User Picture]From: dp074
2008-01-08 09:02 am
И еще:
http://centrolit.kulichki.com/centrolit/cgi/br_grade.cgi?grade=13120
Все это умельцы вводят в узеньких текстовых полях ввода для стихов на www.burme.com, естественно, ни для чего такого не предназначенных. При этом ввод обязан удовлетворять правилам буриме (оканчиваться на заданные рифмы).
(Ответить) (Parent) (Thread)
[User Picture]From: malfet_
2008-01-08 10:39 am
Демосцены на JS это конечно забавно.Но такие названия переменных(особенно "_") - это уже какая-то обфускация - причем непонятно зачем она нужна в JS коде...
(Ответить) (Thread)
[User Picture]From: bolk
2008-01-08 04:39 pm
Чем это хуже букв? В JS можно создать переменную «$», например.
(Ответить) (Parent) (Thread)
[User Picture]From: malfet_
2008-01-08 07:38 pm
Можно. Но нужно ли превращать JavaScript в еще один Perl?
(Ответить) (Parent) (Thread)
[User Picture]From: bolk
2008-01-09 08:58 pm
Не нужно. Это не какая-то специальная конструкция, это JS позволяет вполне легально использовать этот символ в переменных. Например, вполне стандартное:

if (/MSIE\s+(\d+)/.test(navigator.userAgent)) alert(RexExp.$1)
(Ответить) (Parent) (Thread)
From: kamacity_perm
2008-01-10 03:42 pm
Спасибо за разбор....в практике пригодится!
http://www.registrator-ooo.ru/
(Ответить) (Thread)
From: (Anonymous)
2008-01-12 04:08 pm
WOW
(Ответить) (Thread)
From: (Anonymous)
2008-01-12 09:58 pm
круто пишете, что сказать.
(Ответить) (Thread)
From: (Anonymous)
2008-01-13 09:26 am
Я уверен, что когда-нибуть девелоперы gecko engine найдут вас, и за malformed javascript с особой жестокостью отомстят.
(Ответить) (Thread)