javascript - Midjourney 主页上的 ascii 艺术是如何工作的?

标签 javascript svg css-animations

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 , FMath.sqrt , DMath.cos , qMath.sin , WMath.max

O (和 I )是组成中途 Logo 和 T 的文本元素的数组。是所有其他文本元素的数组。

e我认为,是以毫秒为单位的耗时。

R是中途标志框的“y 坐标”,B是“x 坐标”。 nc分别是当前循环字符的 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 的元素,然后右键单击检查器中的元素 -> 中断 -> 子树修改。这将直接跳转到处理文本更新的函数:

enter image description here

通过使用调试器并单步执行,您可以看到不同变量的值是什么:

enter image description here

关于javascript - Midjourney 主页上的 ascii 艺术是如何工作的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76426202/

相关文章:

javascript - 在模板中加载相同的 Angular Controller 两次,但没有第二次 AJAX 调用?

css - SVG 如何使容器大于 svg

javascript - 如何制作一个动画,用从各个侧面聚集在一起的相同尺寸的碎片构建最终图像?

html - 动画不工作。从左边移动 :0px to left :200px

css - 仅在悬停(鼠标悬停)时重新启动 svg 动画 svg 或 css 解决方案

javascript - 如何在Android 4.2中调用JavaScript

javascript - 为什么我的 for 循环似乎没有返回正确的索引?

javascript - jquery如何将参数传递给回调函数到管道中

javascript - jQuery mouseleave 多次触发

svg - 如何将 svg 导入 antd -> Icon 组件