首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在ASCII艺术网格中“动画”更改,一次一个节点,而不冻结浏览器?

如何在ASCII艺术网格中“动画”更改,一次一个节点,而不冻结浏览器?
EN

Stack Overflow用户
提问于 2020-05-01 22:08:36
回答 2查看 176关注 0票数 1

我有一个ASCII艺术“寻路视觉器”,我是建模的一个流行的一个看到这里。ASCII art显示n×m大小的板,其上有n*m个节点数。

我目前的目标是慢慢改变文字在用户面板上的外观,一字一字,直到“动画”完成。我打算通过路径查找算法对节点的“扫描”和从开始节点到结束节点的最短路径进行动画化。动画只是在一系列的div中改变文本,应该需要几秒钟。我还计划添加一个带有颜色的CSS动画。

基本上,用户最终会看到这样的情况,其中*是开始节点,x是结束节点,+表示路径:

代码语言:javascript
复制
....
..*.
..+.
.++.
.x..
.... (. represents an empty space)

正在做什么 一些 研究关于setTimeout、承诺和其他选项之后,我可以告诉您:

JavaScript实际上不允许某人在浏览器中延迟代码执行。

我找到了很多冻结浏览器的方法。我还尝试在setTimeout(resolve, milliseconds)发生后设置一系列要解决的承诺,其中milliseconds稳步增加(参见下面的代码)。我的期望是,numOfAnimationsForPath的承诺数量将被设定,并在每一个承诺解决时触发董事会外观的改变(形成路径的外观)。但是,他们似乎都立即解决了(?)当我看到路径显示时,只要我点击“动画”按钮

代码语言:javascript
复制
const shortestPathAndScanningOrder = dijkstras(grid);
promisesRendering(1000, shortestPathAndScanningOrder[0].path, shortestPathAndScanningOrder[1])

function promisesRendering(animationDelay, algoPath, scanTargets) {
    const numOfAnimationsForScanning = scanTargets.length;
    const numOfAnimationsForPath = algoPath.length;

    for (let i = 1; i < numOfAnimationsForPath - 1; i++) {
        const xCoordinate = algoPath[i][0];
        const yCoordinate = algoPath[i][1];

        renderAfterDelay(animationDelay * i).then(renderNode(xCoordinate, yCoordinate, "path"))
    }
}

function renderAfterDelay(milliseconds) {
    return new Promise(resolve => setTimeout(resolve, milliseconds))
}

function renderNode(x, y, type) {
    if (type === "scan") {
        const targetDiv = getLocationByCoordinates(x, y);
        targetDiv.innerHTML = VISITED_NODE;
    } else if (type === "path") {
        const targetDiv = getLocationByCoordinates(x, y);
        targetDiv.innerHTML = SHORTEST_PATH_NODE;
    } else {
        throw "passed incorrect parameter to 'type' argument"
    }
}

在另一次尝试中,我试图生成pathLength数量的setTimeout,如下所示:

代码语言:javascript
复制
const shortestPathAndScanningOrder = dijkstras(grid);
    renderByTimer(10000, shortestPathAndScanningOrder[0].path, shortestPathAndScanningOrder[1])

function renderByTimer(animationDelay, algoPath, scanTargets) {
    const numOfAnimations = algoPath.length;

    for (let i = 1; i < numOfAnimations - 1; i++) {
        const xCoordinate = algoPath[i][0];
        const yCoordinate = algoPath[i][1];
        setTimeout(i * animationDelay, updateCoordinatesWithTrailMarker(xCoordinate, yCoordinate))
    }
}

...but,这也导致路径被“动画”立即,而不是超过几秒钟,我希望它是。

我相信我想要的是可能的,因为在帖子开头链接的路径可视化器会缓慢地激活它的板,但是我不知道如何处理文本。

所以基本上:

如果有人知道我如何说服我的浏览器发送一个不断增加的延迟值--一系列函数执行--我会全神贯注.

如果你认为做不到,我也想在评论中知道这一点,这样我就知道我必须选择一种替代方法来慢慢地改变文本。

编辑:一个朋友告诉我,setTimeout应该能做到.如果我想出办法的话,我会更新这个w/ a解决方案

Edit2:这里是@torbinsky代码的修改版本,它最终为我完成了任务.

代码语言:javascript
复制
function renderByTimer(algoPath, scanTargets) {
    const numOfAnimations = algoPath.length - 1; // - 1 because we don't wanna animate the TARGET_NODE at the end
    let frameNum = 1;

    // Renders the current frame and schedules the next frame
    // This repeats until we have exhausted all frames
    function renderIn() {
        if (frameNum >= numOfAnimations) {
            // end recursion
            console.log("Done!")
            return
        }

        // Immediately render the current frame
        const xCoordinate = algoPath[frameNum][0];
        const yCoordinate = algoPath[frameNum][1];
        frameNum = frameNum + 1;
        updateCoordinatesWithTrailMarker(xCoordinate, yCoordinate);

        // Schedule the next frame for rendering
        setTimeout(function () {
            renderIn(1000)
        }, 1000);
    }
    // Render first frame
    renderIn()
}

谢谢托宾斯基!

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-05-01 23:02:04

使用setTimeout绝对可以做到这一点。可能问题是,您立即注册了10,000个超时。你的道路越长,这种方法就越糟糕。

因此,与其立即调度所有更新,不如使用递归算法,其中每个“帧”都会安排下一个帧的超时。就像这样:

代码语言:javascript
复制
const shortestPathAndScanningOrder = dijkstras(grid);
    renderByTimer(10000, shortestPathAndScanningOrder[0].path, shortestPathAndScanningOrder[1])

function renderByTimer(animationDelay, algoPath, scanTargets) {
    const numOfAnimations = algoPath.length;

    // Renders the current frame and schedules the next frame
    // This repeats until we have exhausted all frames
    function renderIn(msToNextFrame, frameNum){
        if(frameNum >= numOfAnimations){
            // end recursion
            return
        }

        // Immediately render the current frame
        const xCoordinate = algoPath[frameNum][0];
        const yCoordinate = algoPath[frameNum][1];
        updateCoordinatesWithTrailMarker(xCoordinate, yCoordinate);

        // Schedule the next frame for rendering
        setTimeout(msToNextFrame, function(){
            renderIn(msToNextFrame, frameNum + 1)
        });
    }
    // Render first frame
    renderIn(1000, 1)
}

注意:我在StackOverflow代码片段中编写了这段代码。所以我无法测试它,因为我没有剩下的代码来完全运行它。将其视为伪代码,即使它可能有效;)

在任何情况下,我使用的方法是在任何给定的时间只安排一个超时。这样,您就不会同时安排1000次超时,使浏览器过载。这种方法将支持很长的路径!

票数 1
EN

Stack Overflow用户

发布于 2020-05-01 23:11:28

这是一种通用的动画技术,并不是ASCII艺术所独有的,只是一次呈现一个(慢的)角色,而不是一次呈现一个快速像素帧。(我已经足够大了,我还记得在9600 new的速度下,通过硬连线的甘道夫调制解调器,从本地的z19老一代mainframe...everything终端到一个新的mainframe...everything终端,再看一遍ASCII“电影”!)

无论如何,排队排队的setTimeouts并不是最好的计划,海事组织。相反,您应该做的是使用window.requestAnimationFrame或setTimeout排队等待下一个事件。我建议使用rAF,因为它不会在浏览器选项卡没有显示时触发。

接下来,一旦您在事件中,您将看到时钟增量(使用performance.now()的快照)来计算在"now“和您的函数上一次运行之间应该绘制什么。然后更新显示,并触发下一个事件。

这将产生一个平滑的动画,这将很好地发挥您的系统资源。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61551554

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档