英文:
How does the ascii art on the Midjourney home page work?
问题
我可以看到这是一个动态更新的SVG,但我想知道是否有人找到了一个可以用来实现相同效果的JS库。
将图像转换为ASCII艺术似乎已经有文档记录,但我无法弄清楚他们如何在中间显示"Midjourney"字母。
英文:
Home page with ascii art graphic
I can see that it's a dynamically updated SVG, but I'm curious if anyone has stumbled across a JS library that can be used to accomplish the same affect.
Converting an image to ascii art seems to be documented, however, I can't figure out how they manage to also display the "Midjourney" letters in the middle.
答案1
得分: 3
根据外观,这是手动完成的。这是更新文本的代码部分:
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
)看起来像是某种插值函数,很可能是一种“字符插值”函数,以便你可以在一段时间内“平滑”地从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
)是由构成“midjourney”标志的文本元素组成的数组,T
是所有其他文本元素的数组。 -
e
,我认为是以毫秒为单位的经过的时间。 -
R
是“midjourney”标志框的“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
<details>
<summary>英文:</summary>
By the looks of it, it is done by hand. Here's the part of the code that updates the texts:
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
}
}
}
First of, let me list what some things are:
`V` (and `J`) looks like some kind of interpolation function, most likely a sort of a "character interpolation" function, so that you can "smoothly" go from A to Z in some time t.
var V = function(e, a, t) {
return e * (1 - t) + a * t
}
`H` is just obfuscated `Math.round`, `F` is `Math.sqrt`, `D` is `Math.cos`, `q` is `Math.sin`, `W` is `Math.max`
`O` (and `I`) is an array of the text elements that make up the midjourney logo and `T` is an array of all the other text elements.
`e`, I think, is the elapsed time in milliseconds.
`R` is the "y-coordinate" of the midjourney logo box and `B` is the "x-coordinate". `n` and `c` respectively are the y and x-coordinates of the current character being looped.
`A` is an array of the text strings that then get scrambled as time goes on.
We can now name the things a bit better and try to tidy up the code a bit to get something like this:
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
}
}
}
It's hard to read as it is minified and then prettified by the chrome developer tools, but here's what's happening:
The first loop loops through all of the text-elements, except the text elements that make up the midjourney-logo.
The second loop loops through all of the characters in a text element.
When we're going through the characters, I believe it is this if which checks if we are looping through a character which would land inside the midjourney-logo area by basically checking if the row (text element) and column (character in a text element) are withing certain rectangular bounds.
`if (n > R && n < R + I.length + 1 && c > B && c < B + I[0].length + 1)`
If the column or the row doesn't match, the code just performs the usual scrambling of the text and updates the text-element.
I think the reason why the inside of this if-clause looks so complicated and contains another if-clause within is because the midjourney logo text itself also appears as a scrambled text when you load up the page and then it settles down.
So basically everything inside the boundary check if-clause is to check the state of the logo and unscramble it until the logo doesn't change anymore.
That's about it. If you would like to investigate this further by yourself, one good tip would be to inspect any of the text elements, preferably the ones that go through the logo and right click the element in the inspector -> break on -> subtree modifications. This will jump you straight to the function which handles the text updates:
[![enter image description here][1]][1]
By playing around with the debugger and stepping the execution, you can see what the values of the different variables are:
[![enter image description here][2]][2]
[1]: https://i.stack.imgur.com/UhyIw.png
[2]: https://i.stack.imgur.com/ucDO4.png
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论