avva: (Default)
[personal profile] avva
(эта запись будет интересна в основном веб-программистам)

Вот это очень круто по-моему - эффект достигнут при помощи менее чем 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 раз в секунду.

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

December 2025

S M T W T F S
  123 4 56
78 9 10 11 1213
1415 1617181920
21 22 23 24 2526 27
28293031   

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Dec. 28th, 2025 12:40 pm
Powered by Dreamwidth Studios