- PVSM.RU - https://www.pvsm.ru -
Две вещи наполняют душу всегда новым и все более сильным удивлением и благоговением, чем чаще и продолжительнее мы размышляем о них, — это звездное небо надо мной и моральный закон во мне. Иммануил Кант
JS1k — ежегодное соревнование, где надо вместить демо, игру или все что угодно, в 1024 символа на JavaScript. В этом году мое демо заняло четвертое место (до третьего не хватило каких-то два балла). Посмотреть демо можно на сайте JS1k [1]. У кого не открывается или не работает, выглядеть должно так:
Минифицированный и полный исходный код лежит на github [2]. А под катом разбор того, как сейчас минифицируют JavaScript для таких конкурсов.
Основная красота демо — фрагментный шейдер [3] авторства Pablo Roman Andrioli. Pablo — художник, который работает с фракталами, и на форуме fractalforums [4] рассказывает некоторые подробности вычислений. Моей же задачей было упаковать шейдер и WebGL код в 1024 байта.
Обертка JS1k при старте демо предоставляет WebGL контекст в глобальной переменной g. Несмотря на это, работа с WebGL очень многословна. Например, чтобы добавить вершинный шейдер к программе необходимо 159 символов:
// Define a new program
p=g.createProgram();
// Basic vertex shader
s=g.createShader(VERTEX_SHADER);
g.shaderSource(s,"attribute vec2 p;void main(){gl_Position=vec4(p,0,1);}");
// Compile and attach it to the program
g.compileShader(s);
g.attachShader(p,s);
Для решения этой проблемы во всех решениях JS1k последних нескольких лет применяется трюк с синонимами функций:
for(i in g){
g[i[0] + i[6]] = g[i];
}
Цикл добавляет для каждой функции (и для любого члена) WebGL контекста синоним, который состоит из первой и 7 буквы. Например createProgram становиться cP, shaderSource — sS, и т.д. Дополнительно обрамляя весь код конструкцией with(g)
(которую использовать в настоящих проектах нельзя [5]), получаем:
with(g){
p=cP();
sS(s=cS(35633),'attribute vec2 p;void main(){gl_Position=vec4(p,1,1);}');
ce(s);
aS(p,s);
}
Оригинальный шейдер занимает 1100 символов. Основные сокращения: удаление лишних переменных, и объединение похожих фрагментов. После всего я пропустил код через онлайн минификатор [6]. В результате от шейдера осталось чуть больше 500 байт.
JSCrush — де факто стандарт для сжатия кода в таких соревнования. Утилита превращает код, в приблизительно такую последовательность:
_='(i a.style="widMj%;hEjvh;:left",g)g[i[0]+i[6]]=g[i];wiMO.u=g.G1f,x=y=k=g)p=cP(35633"tribute 2 p gl_Posit=4?FN"precis mediump ;G Zt,a,x,y Uf`ord.rg/64!-.f.=a;Zc=+xz,v=+yz;m2 m$cc-cc)s$vv-vv)fJf#Ur`Q,,r+`t*2.,t,-2.rJr#Zg=.1,b=Q;Ui`!Kl=Rl<2Rl++){Uo=r+f*;oQ)-mod(o,2.))Ze,n=e=!;Kd=Rd<2Rd++)oo)/dot(o,o)-3,n+o)-ee=oif(l>6)Q-max(!,.3-i+=b+g,g,g)*n*5*b;.73;g+=.1;}i=mix(i)i,.85lor=4(i*.01.lo?ug?bfO=34962,cB()eV(0vA(2,5120bDO,Tw Int8Array([|,|]35044o=,(Lt@-oa@TrHE/TrWidMx@xy@ydr(6,3requestAnimFrame(L)})(down=upk^=1},movek&&(xX,yY)};),3=funct(e){uOf?,"flo}@ce(saS?,slengM(onmouse ;void ma(){Tw De/1e5);incos(for=abs(gl_FragCo,1g*(sS(s=cS(n*n*.001at.5vecionb*=s(=e.page0,!0.#.r=s;$=m2(?(p@"EeightGunimJ.rm;K(t MthO(gQ1.R0;TneU Z `=j:100z/50!|-3';for(Y in $='|zj`ZUTRQOMKJGE@?$#! ')with(_.split($[Y]))_=join(pop());eval(_)
Принцип работы JSCrush можно визуально посмотреть в инструменте для обратного превращения кода [7]. Или почитать подробно в статье [8]. В целом, это кодирование со словарем:
После всех операций у меня в запасе оставалось еще порядка 30 символов, которые можно было использовать для оптимизации производительности. При загрузки демки, можно указать размер canvas’а: на весь экран или фиксированного размера. Запускать на весь экран — красиво, но фрагментный шейдер вызывается для каждого пикселя и работает медленно. Выходом стало — запросить у JS1k canvas’а фиксированного размера (я выбрал 640х640), а потом в коде его увеличить до полноэкранного:
a.style='width:100%;height:100vh;float:left';
Тогда изображение занимает весь экран, но шейдер выполняется только на каждый пиксель оригинального размера canvas.
Автор: Денис Потапов
Источник [9]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/325742
Ссылки в тексте:
[1] сайте JS1k: https://js1k.com/2019-x/demo/4238
[2] лежит на github: https://github.com/denys-potapov/js1k
[3] фрагментный шейдер: https://www.shadertoy.com/view/XlfGRj
[4] fractalforums: http://www.fractalforums.com/new-theories-and-research/very-simple-formula-for-fractal-patterns/
[5] нельзя: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/with
[6] онлайн минификатор: http://ctrl-alt-test.fr/minifier/index
[7] инструменте для обратного превращения кода: https://keithclark.co.uk/labs/jspackvis/
[8] статье: http://nikhilism.com/post/2012/demystifying-jscrush/
[9] Источник: https://habr.com/ru/post/462115/?utm_source=habrahabr&utm_medium=rss&utm_campaign=462115
Нажмите здесь для печати.