前書き
- 毎フレーム画面上に時計として機能するQuineが表示される
- PCのChromeで動けばいいとする
- あんま動作確認してないのでバグるかも…
成果物
コピペはコンソールから
コード
<script>s=String.fromCharCode(32);n=String.fromCharCode(10);z=`/*#zounotamag
ohaoisiizouzounotamagohaoisiizouzounotamagohaoisiizouzounotamagohaoisiizouzo
unotamagohaoisiizouzounotamagohaoisiizouzoun otamagohaoisiizouzounotamagoh
aoisi iz ou zouno tamagohao isiiz o uzounotam
agoh ao is ii zo uz oun otamagoha oisiiz ou zounot amagohaoi
siizouzou no tamag oh aoisiizo uzoun ot amago haoisiizou
zounotama g oh aoisi i z ouzounot amagoh ao isii zouzounota
magohaoi s ii zouz o un otamago haoisi iz ouzo unotamagoha
oisiizo uz ou not am agoha oisiiz ou zou notamagohao
isiizo uzo un ot amagohao isiizou zounota ma go haoisiizouzo
u#*/d =new@D at e( );canvas= new@Of fscreenC an vas (76,13+24+16
);co n te xt=ca nvas.g etContext (" 2d");context.
font="13px@sans-serif";context.textBasel ine="top";context.fillText(d.getF
ullYear()+"@/@"+String(d.getMonth()+1).p adStart(2,"0"),4,0);context.font=
"24px@sans-serif";context.fillText(String(d.getDate()).padStart(2,"0"),4,13)
;contex t.fo nt="16px@sans-serif";context.fillText(["Sun
","Mon "," Tue","Wed","Thu","Fri","Sat"][d.getDay()],4
+36,13 + 7);context.fo nt="16px@sans-serif";hhmm=String(d.getHours
()).p adS tart(2,"0") +":"+String(d.getMinutes()).padStart(2,"0")
;cont ext.f illText(hh mm,4,13+24);w=context.measureText(hhmm).wid
th;co ntext .font="13p x@sans-serif";context.fillText(String(d.getS
econd s()). padStart( 2,"0"),4+w +2,13+24+3);imageData=conte
xt.ge tImageD ata(0,0,c anvas.widt h, canvas.height);template=""
;row= "";rowC harCount =0;prevRo wChar Count=0;for(i=0;i<imageDat
a.dat a.lengt h;i+=4){ alpha=imag eData.da ta[ i +3];if(a
lpha= ==0){ro w+="a"; rowCharCoun t++;}els e{r o w+ ="@";}i
f((i/ 4+1)% imageDa ta.width===0 ){if( row C har Count!=
=prev RowCh arCoun t||rowCharCount! == ima g eDa ta.widt
h){te mplat e+=row +n;}row="";prevRowC h arC o unt =rowCha
rCoun t;r owCha rCount=0;}}margin="a " .re p eat (imageD
ata.wi d th)+n; template=marg in+te m pla t e+m argin;d
oc=doc ument .documentEleme nt ; do c .st yle.fon
tFamily ="mono space";doc.styl e.wh i teS pace="n
owrap";text=document.querySelector("script").outerHTML.replace(/@/g,"").repl
aceAll (n, "").replace(/ #[zou ntamghis]*#/,"#"+"#");diffLe
ngth= te mp la te.replace( /@/g , "").replaceAll(n,"").length
-text .le ngth;mi zumasi="" ;fo r(i =0;i<diffLength;i++){mizum
asi+ ="zo unotama goh aoisii zou "[i %21] ;}t ext=text.
repl ace( "#"+"# "," #"+miz uma si+" #") ; r e sult1=""
;res ult2 ="";i= 0;for(j=0;j< tem plat e.length ; j++ ){if(te
mpla te[j ]===n ){result1+="< br> "; result2+ = n;c ontinue
;}if (tem plat e[j]==="@"){re sult1 +="<spa n @s tyle='b
ackg roun d-co lor:yellow;'>&" +"nbsp;</ span>" ;re sult2+=
"@";c ont inu e;}char=text[i++ ];result2 +=cha r;if(cha r==="<")
{char =" &" +"lt;";} else@i f(char== =">" ){char="& "+"gt;";
}resul t1 += cha r; }text= t ext.repla
ce(/#[zountamghis]*#/,"#"+"#");doc.innerHTML=result1+text;console.log(result
2);`.replace(/[\s@]/g,m=>m==="@"?s:"");setInterval(()=>eval(z),16);</script>
読みやすくしたコード
<script>
s = String.fromCharCode(32);
n = String.fromCharCode(10);
z = `
/*#zounotamagohaoisiizou#*/
d = new@Date();
canvas = new@OffscreenCanvas(76, 13 + 24 + 16);
context = canvas.getContext("2d");
context.font = "13px@sans-serif";
context.textBaseline = "top";
context.fillText(d.getFullYear() + "@/@" + String(d.getMonth() + 1).padStart(2, "0"), 4, 0);
context.font = "24px@sans-serif";
context.fillText(String(d.getDate()).padStart(2, "0"), 4, 13);
context.font = "16px@sans-serif";
context.fillText(["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][d.getDay()], 4 + 36, 13 + 7);
context.font = "16px@sans-serif";
hhmm = String(d.getHours()).padStart(2, "0") + ":" + String(d.getMinutes()).padStart(2, "0");
context.fillText(hhmm, 4, 13 + 24);
w = context.measureText(hhmm).width;
context.font = "13px@sans-serif";
context.fillText(String(d.getSeconds()).padStart(2, "0"), 4 + w + 2, 13 + 24 + 3);
imageData = context.getImageData(0, 0, canvas.width, canvas.height);
template = "";
row = "";
rowCharCount = 0;
prevRowCharCount = 0;
for (i = 0; i < imageData.data.length; i += 4) {
alpha = imageData.data[i + 3];
if (alpha === 0) {
row += "a";
rowCharCount++;
}
else {
row += "@";
}
if ((i / 4 + 1) % imageData.width === 0) {
if (rowCharCount !== prevRowCharCount || rowCharCount !== imageData.width) {
template += row + n;
}
row = "";
prevRowCharCount = rowCharCount;
rowCharCount = 0;
}
}
margin = "a".repeat(imageData.width) + n;
template = margin + template + margin;
doc = document.documentElement;
doc.style.fontFamily = "monospace";
doc.style.whiteSpace = "nowrap";
text = document.querySelector("script").outerHTML
.replace(/@/g, "")
.replaceAll(n, "")
.replace(/#[zountamghis]*#/, "#" + "#");
diffLength = template.replace(/@/g, "").replaceAll(n, "").length - text.length;
mizumasi = "";
for (i = 0; i < diffLength; i++) {
mizumasi += "zounotamagohaoisiizou"[i % 21];
}
text = text.replace("#" + "#", "#" + mizumasi + "#");
result1 = "";
result2 = "";
i = 0;
for (j = 0; j < template.length; j++) {
if (template[j] === n) {
result1 += "<br>";
result2 += n;
continue;
}
if (template[j] === "@") {
result1 += "<span@style='background-color:yellow;'>&" + "nbsp;</span>";
result2 += "@";
continue;
}
char = text[i++];
result2 += char;
if (char === "<") {
char = "&" + "lt;";
}
else@if (char === ">") {
char = "&" + "gt;";
}
result1 += char;
}
text = text.replace(/#[zountamghis]*#/, "#" + "#");
doc.innerHTML = result1 + text;
console.log(result2);
`.replace(/[\s@]/g, m => m === "@" ? s : "");
setInterval(() => eval(z), 16);
</script>
仕組み
雑に説明すると、Canvasに時間書いて、それを基にQuineを作っているだけ。
終わりに
QuineアニメーションでCanvasを使うのはチートに近いね。
Canvasに描画したやつを白黒でマッピングするだけだから簡単に実装できる。
CanvasのおかげでHTML/JSのトリッキーなQuineは他の言語と比べてかなり作りやすいかも。
特にHTMLはCSSで色付けが容易なのもいい。
ちょっとずるいかもなんて思ったり。