Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/420.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/search/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 如何";动画化;ASCII艺术网格中的更改,一次一个节点,而不冻结浏览器?_Javascript_Html - Fatal编程技术网

Javascript 如何";动画化;ASCII艺术网格中的更改,一次一个节点,而不冻结浏览器?

Javascript 如何";动画化;ASCII艺术网格中的更改,一次一个节点,而不冻结浏览器?,javascript,html,Javascript,Html,我有一个ASCII艺术的“寻路可视化工具”,我是模仿一个流行的。ASCII art显示一个n*m大小的板,板上有n*m个节点 我目前的目标是慢慢地改变面向用户板上文本的外观,一个角色一个角色地改变,直到“动画”完成。我打算通过寻路算法为节点的“扫描”和从开始节点到结束节点的最短路径设置动画。动画只是在一系列div中更改文本,应该需要几秒钟的时间。我还计划添加一个带有颜色或其他东西的CSS动画 基本上,用户最终会看到这样的情况,其中*是开始节点,x是结束节点,+表示路径: .... ..*. ..

我有一个ASCII艺术的“寻路可视化工具”,我是模仿一个流行的。ASCII art显示一个n*m大小的板,板上有n*m个节点

我目前的目标是慢慢地改变面向用户板上文本的外观,一个角色一个角色地改变,直到“动画”完成。我打算通过寻路算法为节点的“扫描”和从开始节点到结束节点的最短路径设置动画。动画只是在一系列div中更改文本,应该需要几秒钟的时间。我还计划添加一个带有颜色或其他东西的CSS动画

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

....
..*.
..+.
.++.
.x..
.... (. represents an empty space)
打开setTimeout和其他选项后,我可以告诉您:

JavaScript的设计并不是为了允许用户延迟浏览器中的代码执行

我找到了很多冻结浏览器的方法。我还尝试在
setTimeout(resolve,毫秒)
发生后设置一系列承诺以解决问题,其中
毫秒持续增加(请参见下面的代码)。我的期望是,
numOfAnimationsForPath
承诺的数量将被设置,并在每个承诺解决时触发电路板外观的变化(形成路径的外观)。但是,当我单击“动画”按钮时看到路径显示时,它们似乎都会立即解决(?)

const shortestpath和scanningorder=dijkstras(网格);
承诺描述(1000,最短路径和扫描顺序[0]。路径,最短路径和扫描顺序[1])
函数承诺描述(动画延迟、算法路径、扫描目标){
const numOfAnimationsForScanning=扫描目标.length;
const numOfAnimationsForPath=algoPath.length;
for(设i=1;isetTimeout(解析,毫秒))
}
函数renderNode(x,y,type){
如果(类型==“扫描”){
const targetDiv=getLocationBy坐标(x,y);
targetDiv.innerHTML=已访问\u节点;
}else if(类型==“路径”){
const targetDiv=getLocationBy坐标(x,y);
targetDiv.innerHTML=最短路径节点;
}否则{
throw“向'type'参数传递了不正确的参数”
}
}
在我的另一次尝试中,我尝试生成
pathLength
setTimeout
s的数量,如下所示:

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))
    }
}

谢谢@torbinsky

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

因此,与立即安排所有更新不同,您应该使用递归算法,其中每个“帧”都安排下一帧的超时。大概是这样的:

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代码snipppet中编写了这段代码。因此,我无法测试它,因为我没有您的其余代码来完全运行它。将其视为伪代码,即使它可能有效;)


在任何情况下,我使用的方法是在任何给定时间只安排1个超时。这样,您就不会在浏览器上同时出现1000次超时。这种方法将支持非常长的路径

这是一种通用的动画技术,并不是ASCII艺术独有的,除了老式ASCII艺术一次渲染一个(慢速)字符,而不是一次渲染一个快速像素帧。(我已经足够大了,还记得在本地主机上以9600bps的速度通过硬连线甘道夫调制解调器到z19终端观看ASCII“电影”流……一切旧的东西都是新的!:)

无论如何,在我看来,排队等待一系列设置超时并不是最好的计划。相反,你应该做的是用window.requestAnimationFrame或setTimeout将下一个事件排队。我推荐rAF,因为它不会在浏览器选项卡未显示时触发

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


这将产生一个平滑的动画,可以很好地利用系统资源播放。

只需稍加修改。干得好,谢谢你的帮助!
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)
}