我有一个ASCII艺术“寻路视觉器”,我是建模的一个流行的一个看到这里。ASCII art显示n×m大小的板,其上有n*m个节点数。
我目前的目标是慢慢改变文字在用户面板上的外观,一字一字,直到“动画”完成。我打算通过路径查找算法对节点的“扫描”和从开始节点到结束节点的最短路径进行动画化。动画只是在一系列的div中改变文本,应该需要几秒钟。我还计划添加一个带有颜色的CSS动画。
基本上,用户最终会看到这样的情况,其中*是开始节点,x是结束节点,+表示路径:
....
..*.
..+.
.++.
.x..
.... (. represents an empty space)在正在做什么 一些 研究关于setTimeout、承诺和其他选项之后,我可以告诉您:
JavaScript实际上不允许某人在浏览器中延迟代码执行。
我找到了很多冻结浏览器的方法。我还尝试在setTimeout(resolve, milliseconds)发生后设置一系列要解决的承诺,其中milliseconds稳步增加(参见下面的代码)。我的期望是,numOfAnimationsForPath的承诺数量将被设定,并在每一个承诺解决时触发董事会外观的改变(形成路径的外观)。但是,他们似乎都立即解决了(?)当我看到路径显示时,只要我点击“动画”按钮
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,如下所示:
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代码的修改版本,它最终为我完成了任务.
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()
}谢谢托宾斯基!
发布于 2020-05-01 23:02:04
使用setTimeout绝对可以做到这一点。可能问题是,您立即注册了10,000个超时。你的道路越长,这种方法就越糟糕。
因此,与其立即调度所有更新,不如使用递归算法,其中每个“帧”都会安排下一个帧的超时。就像这样:
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次超时,使浏览器过载。这种方法将支持很长的路径!
发布于 2020-05-01 23:11:28
这是一种通用的动画技术,并不是ASCII艺术所独有的,只是一次呈现一个(慢的)角色,而不是一次呈现一个快速像素帧。(我已经足够大了,我还记得在9600 new的速度下,通过硬连线的甘道夫调制解调器,从本地的z19老一代mainframe...everything终端到一个新的mainframe...everything终端,再看一遍ASCII“电影”!)
无论如何,排队排队的setTimeouts并不是最好的计划,海事组织。相反,您应该做的是使用window.requestAnimationFrame或setTimeout排队等待下一个事件。我建议使用rAF,因为它不会在浏览器选项卡没有显示时触发。
接下来,一旦您在事件中,您将看到时钟增量(使用performance.now()的快照)来计算在"now“和您的函数上一次运行之间应该绘制什么。然后更新显示,并触发下一个事件。
这将产生一个平滑的动画,这将很好地发挥您的系统资源。
https://stackoverflow.com/questions/61551554
复制相似问题