Home page with ascii art graphic
我可以看到它是一个动态更新的 SVG,但我很好奇是否有人偶然发现了一个可用于实现相同效果的 JS 库。
Converting an image to ascii art seems to be documented但是,我不明白他们是如何在中间显示“中途”字母的。
最佳答案
从外观上看,这是手工完成的。这是更新文本的代码部分:
function K(e) {
if (requestAnimationFrame(K),
z || (z = .001 * e),
P || (P = e),
!("visible" !== document.visibilityState || e - P < 42)) {
P = e;
for (var a = .001 * e - z, t = function(e) {
return e < .5 ? (1 - F(1 - Math.pow(2 * e, 2))) / 2 : (F(1 - Math.pow(-2 * e + 2, 2)) + 1) / 2
}(J(.5 * (a - 1), 0, 1)), n = (window.innerWidth,
window.innerHeight,
0); n < T.length; n++) {
for (var i = "", r = "", s = 1 - 2 * n / T.length, c = 0; c < M; c++) {
var o = 2 * c / M - 1
, d = F(o * o + s * s)
, l = .1 * a / W(.1, d)
, f = q(l)
, b = D(l)
, u = o * f - s * b
, m = H((o * b + s * f + 1) / 2 * M)
, h = H((u + 1) / 2 * A.length) % A.length
, g = m < 0 || m >= M || h < 0 || h >= T.length ? " " : A[h][m] || " ";
if (n > R && n < R + I.length + 1 && c > B && c < B + I[0].length + 1) {
var p = c - B - 1
, x = n - R - 1
, v = I[x][p] || g
, y = " " != I[x][p - 1]
, j = " " != I[x][p + 1];
if (" " != v || y || j) {
var w = g.charCodeAt(0)
, _ = v.charCodeAt(0);
r += g = String.fromCharCode(H(V(w, _, t))),
1 == t && (g = " ")
} else
r += " ";
c == B + I[0].length && (O[x].textContent = r,
O[x].setAttribute("fill-opacity", t))
}
i += g
}
T[n].textContent = i
}
}
}
首先,让我列出一些内容:
V
(和 J
)看起来像某种插值函数,很可能是一种“字符插值”函数,这样你就可以在某个时间 t 内“平滑地”从 A 到 Z。
var V = function(e, a, t) {
return e * (1 - t) + a * t
}
H
只是混淆了Math.round
, F
是 Math.sqrt
, D
是 Math.cos
, q
是 Math.sin
, W
是 Math.max
O
(和 I
)是组成中途 Logo 和 T
的文本元素的数组。是所有其他文本元素的数组。
e
我认为,是以毫秒为单位的耗时。
R
是中途标志框的“y 坐标”,B
是“x 坐标”。 n
和c
分别是当前循环字符的 y 和 x 坐标。
A
是一个文本字符串数组,随着时间的推移,这些文本字符串会被打乱。
我们现在可以更好地命名这些内容,并尝试稍微整理一下代码以获得如下所示的内容:
function K(e) {
if (requestAnimationFrame(K), z || (z = .001 * e),P || (P = e), !("visible" !== document.visibilityState || e - P < 42)) {
P = e;
for (var a = .001 * e - z; n < otherTextArr.length; n++) {
var t = function(e) {
return e < .5 ? (1 - Math.sqrt(1 - Math.pow(2 * e, 2))) / 2 : (Math.sqrt(1 - Math.pow(-2 * e + 2, 2)) + 1) / 2
}(interpolate(.5 * (a - 1), 0, 1)), n = (window.innerWidth, window.innerHeight, 0)
for (var i = "", r = "", s = 1 - 2 * n / T.length, c = 0; c < M; c++) {
var o = 2 * c / M - 1;
var d = Math.sqrt(o * o + s * s);
var l = .1 * a / Math.max(.1, d);
var f = Math.sin(l);
var b = Math.cos(l);
var u = o * f - s * b;
var m = Math.round((o * b + s * f + 1) / 2 * M);
var h = Math.round((u + 1) / 2 * textStrings.length) % textStrings.length;
var g = m < 0 || m >= M || h < 0 || h >= otherTextArr.length ? " " : textStrings[h][m] || " ";
if (charY > logoY && charY < logoY + midjourneyLogoTextArr.length + 1 &&
charX > logoX && charX < logoX + midjourneyLogoTextArr[0].length + 1) {
var p = c - B - 1;
var x = n - logoX - 1;
var v = midjourneyLogoTextArr[x][p] || g;
var y = " " != midjourneyLogoTextArr[x][p - 1];
var j = " " != midjourneyLogoTextArr[x][p + 1];
if (" " != v || y || j) {
var w = g.charCodeAt(0);
var _ = v.charCodeAt(0);
r += g = String.fromCharCode(Math.round(interpolate(w, _, t))),
1 == t && (g = " ")
} else {
r += " ";
charX == logoX + midjourneyLogoTextArr[0].length && (midjourneyLogoTextArr[x].textContent = r,
midjourneyLogoTextArr[x].setAttribute("fill-opacity", t))
}
}
i += g
}
otherTextArr[n].textContent = i
}
}
}
它很难阅读,因为它被 chrome 开发者工具缩小然后美化,但这是发生的事情:
第一个循环循环遍历所有文本元素,除了构成中间旅程 Logo 的文本元素。
第二个循环循环遍历文本元素中的所有字符。
当我们遍历这些字符时,我相信它是通过检查行(文本元素)和列(字符)是否在中间旅程 Logo 区域内循环遍历一个字符来检查的。在文本元素中)都在一定的矩形边界内。
if (n > R && n < R + I.length + 1 && c > B && c < B + I[0].length + 1)
如果列或行不匹配,代码只会执行通常的文本加扰并更新文本元素。
我认为这个 if 子句的内部看起来如此复杂并包含另一个 if 子句的原因是因为当您加载页面然后它稳定下来时,中途 Logo 文本本身也显示为乱序文本。
所以基本上边界检查 if 子句中的所有内容都是检查 Logo 的状态并对其进行解读,直到 Logo 不再发生变化。
就是这样。如果您想自己进一步研究这一点,一个好的建议是检查任何文本元素,最好是那些经过 Logo 的元素,然后右键单击检查器中的元素 -> 中断 -> 子树修改。这将直接跳转到处理文本更新的函数:
通过使用调试器并单步执行,您可以看到不同变量的值是什么:
关于javascript - Midjourney 主页上的 ascii 艺术是如何工作的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76426202/