Tweetimer
В воскресенье сел готовить выступление BeerJS Summit Minsk, но вместо текста и/или слайдов получился таймер для этого выступления (потому что в macOS до сих пор нет встроенного и потому что прокрастинация). Потом захотелось ужать его до минимального размера, so here I am, рассказываю как тратить время на то, что Uglify/Babel-minify делают за доли секунды…
В начале процесса усиленной минификации, таймер выглядел как-то так:
<style type="text/css">
*{padding:0;margin:0}#d{width:100%;height:100%;background:#000}
#t{color:#fff;font-size:70vh;font-family:sans-serif;mix-blend-mode:difference}
</style>
<body onload="((w,h,s,c,f,d)=>{
s=new Date
f=()=>{
d=(new Date-s)/1000
d=d<h?d:h
c=h-parseInt(d)
w.d.style.width=`${100*(h-d)/h}%`
w.t.innerText=`${parseInt(c/60)}:`+`${c%60}`.padStart(2,0)
c&&requestAnimationFrame(f)
}
f()
})(window,300)">
<div id=d>
<span id=t>
Код уже как-то минифицирован, например:
- В коде страницы нет тегов вроде
<html>
, которые браузеры сами могут подставить. То же самое с закрытием тегов<div>
и<span>
— браузеры сами закроют их - JS засунут в
<body onload=>
, что короче, чем<script></script>
- все переменные определены в сигнатуре функции (которая, конечно же, определена в короткой форме
=>
), чтобы не тратиться наvar
/let
/const
/whatever - Элементам страницы даны односимвольные идентификаторы, чтобы до них можно было достучаться с помощью
window.d
/window.t
- Сброс стандартных padding/margin у
body
указан с помощью*
457 символов, великовато даже для новомодных твитов в 280
<style>
Больше трети таймера занимают стили
- Убираем
#d { width: 100% }
потому что оно нужно только в самом начале и перезапишетсяw.d.style.width=`${100*(h-d)/h}%`
font: 70vh futura;
намного короче, чемfont-size:70vh;font-family:sans-serif;
- Указываем стили инлайном. Так не надо тратить символы на тэг стилей. Плюс, CSS reset у
body
может ограничитьсяmargin:0
После этих манипуляций, таймер ужался до 390 символов
<body style=margin:0 onload="((w,h,s,c,f,d)=>{
s=new Date
f=()=>{
d=(new Date-s)/1000
d=d<h?d:h
c=h-parseInt(d)
w.d.style.width=`${100*(h-d)/h}%`
w.t.innerText=`${parseInt(c/60)}:`+`${c%60}`.padStart(2,0)
c&&requestAnimationFrame(f)
}
f()
})(window,300)">
<div style=height:100%;background:#000 id=d>
<span style="color:#fff;font:70vh futura;mix-blend-mode:difference" id=t>
Почему без CSS Transition?
Потому что Сафари (и, возможно, другие браузеры) могут приостанавливать transition’ы, если вкладка не в фокусе. Цифры таймера могут дотикать до 0:00, а бэкграунд — ещё будет «убывать»
JS
requestAnimationFrame(f)
производительно, но too long.setTimeout(f,30)
будет достаточно для таймера- определение самой
f
можно засунуть в сигнатуруonload
функции, благодаря параметрам по умолчанию в ES2015 (заодно немного поменял порядок аргументов). Так избавляемся от кучи символов, например, от{}
вокруг телаonload
функции:((w,h,s,c,d,f=()=>{ // ... })=>f() )(window,300,new Date)
parseInt(x)
можно заменить наx|0
. Главное — не напутать с приоритетом операторов- Понятное
легко заменяется на специализированное:+`${c%60}`.padStart(2,0)
+(с%60>9?``:0)+с%60
Вытащить ноль из кавычек додумался уже после отправки твита про таймер — JS сам его к строке приведёт
1000
=>1e3
- Заменяем
window
наthis
потому что можно - Если поменять порядок операций, то можно избавиться от переменной
c
, которая полностью зависит отd
, которая, в свою очередь, переопределяется на каждый запускf()
. То есть, вместоc=h-d|0
, сделатьd=h-d|0
- Не JS, но напишу здесь.
<p>
вместо<span>
Уже кажется, что вот-вот влезет в твит… Но нет, 329 символа:
<body style=margin:0 onload="((w,h,s,d,f=()=>{
d=(new Date-s)/1e3
d=d<h?d:h
w.d.style.width=`${100*(h-d)/h}%`
d=h-d|0
w.t.innerText=`${d/60|0}:`+(d%60>9?``:0)+d%60
d&&setTimeout(f,30)
})=>f())(this,300,new Date)">
<div style=height:100%;background:#000 id=d>
<p style="color:#fff;font:70vh futura;mix-blend-mode:difference" id=t>
Режем красоту
Благодаря mix-blend-mode:difference
, таймер отлично выглядит, но 26 символов (считая ;
в стилях) — слишком большая цена для такой красоты
Но прогресс-бар хочется, так что просто сделаю его пониже. Плюс, в случае низкого прогресс-бара, стандартный margin
браузера не так сильно мозолит глаза
<div style=height:10%;background:#000 id=d>
можно было бы его сделать красным, чтобы срезать символ с цвета, но это слишком
Без особой красоты, таймер влезает в твит даже с переносами строк. Плюс, можно переименовать аргументы функции onload
, чтобы хоть там была красота:
<body onload="((t,i,m,e,r=()=>{
e=(new Date-m)/1e3
e=e<i?e:i
t.d.style.width=`${100*(i-e)/i}%`
e=i-e|0
t.t.innerText=`${e/60|0}:`+(e%60>9?``:0)+e%60
e&&setTimeout(r,30)
})=>r())(this,300,new Date)">
<div style=height:10%;background:#000 id=d>
<p style="font:70vh futura" id=t>
Suddenly
Если попробовать запостить этот код в твиттер, сервис посчитает t.d.style
за ссылку и вместо девяти символов насчитает 23. Так что придётся завернуть стаааайл в квадратные скобки: t.d['style']
Даже не убирая переносы строк, получилось 279 символа. Good (not-a-) job, me!
<body onload="((t,i,m,e,r=()=>{
e=(new Date-m)/1e3
e=e<i?e:i
t.d['style'].width=`${100*(i-e)/i}%`
e=i-e|0
t.t.innerText=`${e/60|0}:`+(e%60>9?``:0)+e%60
e&&setTimeout(r,30)
})=>r())(this,300,new Date)">
<div style=height:10%;background:#000 id=d>
<p style="font:70vh futura" id=t>