我用javascript为一个冒烟的房子做了一个动画。从烟囱向上流动的每一部分烟雾都有三种功能。它们由一个滑块控制,该滑块切换烟囱烟雾存在的速度。一切正常,除非从左到右切换滑块,上升时烟雾闪烁。谁能告诉我为什么会这样?
由于
HTML:
<!DOCTYPE html>
<html>
<head>
<title>Carrey, Justin, Myshkin, Rost</title>
<meta charset="utf-8"/>
<link rel="stylesheet" href="style.css"/>
</head>
<body>
<canvas id="canvas" width="500" height="500">Get a new Browser!</canvas>
<script src="script.js" ></script>
<form>
<input type="range" min="10" max="250" value="100" id="speedCont"/>
<p>
Rostislav Myshkin A00787633 rmyshkin@my.bcit.ca
<br />
Completed:3-D house, smoke, animation for smoke, slider for speed.
<br />
Challanges: animating the smoke.
</p>
</form>
</body>
</html>
的javascript:
var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');
ctx.lineWidth = 4;
ctx.strokeLinecap = 'round';
var a = 1;
var speed = 100;
var posY = 100,
posY2 = 120,
posY3 = 140,
posX = 100,
vx = 5,
vy = 5;
function foundation() {
//grass
ctx.fillStyle = "green";
ctx.strokeStyle = "#000000";
ctx.beginPath();
ctx.moveTo(25, 375);
ctx.lineTo(125, 325);
ctx.lineTo(471, 325);
ctx.lineTo(400, 375);
ctx.lineTo(25, 375);
ctx.fill();
ctx.stroke();
//front face ground
ctx.fillStyle = "#873600";
ctx.strokeStyle = "#000000";
ctx.beginPath();
ctx.moveTo(25, 375); //top left
ctx.lineTo(25, 425); //bottom left
ctx.lineTo(400, 425); //bottom right
ctx.lineTo(400, 375); //top right
ctx.lineTo(25, 375); //top line
ctx.fill();
ctx.stroke();
//east face ground
ctx.fillStyle = "#872000";
ctx.strokeStyle = "#000000";
ctx.beginPath();
ctx.moveTo(475, 325); //top right
ctx.lineTo(475, 375); //bottom right
ctx.lineTo(400, 425); //bottom line
ctx.lineTo(400, 375); //top left
ctx.lineTo(475, 325); //top right
ctx.fill();
ctx.stroke();
}
function house() {
//front face
ctx.fillStyle = "#2980B9";
ctx.strokeStyle = "#000000";
ctx.beginPath();
ctx.moveTo(110, 365);
ctx.lineTo(110, 200);
ctx.lineTo(375, 200);
ctx.lineTo(375, 365);
ctx.lineTo(110, 365);
ctx.fill();
ctx.stroke();
//east face
ctx.fillStyle = "#1760B4";
ctx.strokeStyle = "#000000";
ctx.beginPath();
ctx.moveTo(375, 200); //lower left
ctx.lineTo(415, 180); //
ctx.lineTo(415, 340);
ctx.lineTo(375, 365);
ctx.lineTo(375, 200);
ctx.fill();
ctx.stroke();
//roof front face
ctx.fillStyle = "#B41717";
ctx.strokeStyle = "#000000";
ctx.beginPath();
ctx.moveTo(95, 210);
ctx.lineTo(160, 140);
ctx.lineTo(395, 140);
ctx.lineTo(365, 210);
ctx.lineTo(365, 210);
ctx.lineTo(95, 210);
ctx.fill();
ctx.stroke();
//roof east face
ctx.fillStyle = "darkred";
ctx.strokeStyle = "#000000";
ctx.beginPath();
ctx.moveTo(365, 210);
ctx.lineTo(425, 190);
ctx.lineTo(395, 140);
ctx.lineTo(365, 210);
ctx.fill();
ctx.stroke();
//door
ctx.fillStyle = "darkred";
ctx.strokeStyle = "#000000";
ctx.beginPath();
ctx.moveTo(300, 365);
ctx.lineTo(300, 295);
ctx.lineTo(250, 295);
ctx.lineTo(250, 365);
ctx.lineTo(300, 365);
ctx.fill();
ctx.stroke();
//doorknob
ctx.fillStyle = "yellow";
ctx.strokeStyle = "#000000";
ctx.beginPath();
ctx.arc(290, 335, 5, 0, 2 * Math.PI, false);
ctx.fill();
ctx.stroke();
//walkway
ctx.fillStyle = "gray";
ctx.strokeStyle = "#000000";
ctx.beginPath();
ctx.moveTo(250, 365); //left point
ctx.lineTo(240, 375); //left side
ctx.lineTo(290, 375);
ctx.lineTo(300, 365);
ctx.fill();
ctx.stroke();
//window living room
ctx.fillStyle = "blue";
ctx.strokeStyle = "#000000";
ctx.beginPath();
ctx.moveTo(143, 347);
ctx.lineTo(143, 295);
ctx.lineTo(212, 295);
ctx.lineTo(212, 347);
ctx.lineTo(143, 347);
ctx.fill();
ctx.stroke();
//window top left
ctx.fillStyle = "blue";
ctx.strokeStyle = "#000000";
ctx.beginPath();
ctx.moveTo(143, 275);
ctx.lineTo(143, 225);
ctx.lineTo(212, 225);
ctx.lineTo(212, 275);
ctx.lineTo(143, 275);
ctx.fill();
ctx.stroke();
//window top right
ctx.fillStyle = "blue";
ctx.strokeStyle = "#000000";
ctx.beginPath();
ctx.moveTo(263, 275);
ctx.lineTo(263, 225);
ctx.lineTo(332, 225);
ctx.lineTo(332, 275);
ctx.lineTo(263, 275);
ctx.fill();
ctx.stroke();
//chimney front
ctx.fillStyle = "black";
ctx.strokeStyle = "#000000";
ctx.beginPath();
ctx.moveTo(170, 130); //top left
ctx.lineTo(170, 180); //left side line
ctx.lineTo(200, 180); //bottom line
ctx.lineTo(200, 130); //right side line
ctx.lineTo(170, 130); //top side line
ctx.fill();
ctx.stroke();
//chimney east
ctx.fillStyle = "black";
ctx.strokeStyle = "#000000";
ctx.beginPath();
ctx.moveTo(200, 130); //top left
ctx.lineTo(215, 123); //top side line
ctx.lineTo(215, 170); //right side line
ctx.lineTo(200, 180); //
ctx.fill();
ctx.stroke();
//chimney top
ctx.fillStyle = "black";
ctx.strokeStyle = "#000000";
ctx.beginPath();
ctx.moveTo(170, 130); //top left
ctx.lineTo(185, 122); //left side
ctx.lineTo(210, 122); //top side
ctx.lineTo(200, 130);
ctx.fill();
ctx.stroke();
}
function smoke1(){
posY += -vy;
posX += vx;
if (posY < -15) posY = 100;
ctx.fillStyle = "aqua";
ctx.fillRect(0,0, 220, 127);
ctx.fillStyle = "rgba(0,0,0,0.5)";
ctx.beginPath();
ctx.arc(200, posY, 15, 0, Math.PI*2, true);
ctx.fill();
}
function smoke2(){
posY2 += -vy;
posX += vx;
if (posY2 < -13) posY2 = 110;
ctx.fillStyle = "rgba(0,0,0,0.5)";
ctx.beginPath();
ctx.arc(185, posY2, 10, 0, Math.PI*2, true);
ctx.fill();
}
function smoke3(){
posY3 += -vy;
posX += vx;
if (posY3 < -13) posY3 = 110;
ctx.fillStyle = "rgba(0,0,0,0.5s)";
ctx.beginPath();
ctx.arc(210, posY3, 6, 0, Math.PI*2, true);
ctx.fill();
}
function animate() {
smoke1();
var speed = document.getElementById('speedCont').value;
window.setTimeout(animate, speed);
}
function animate2() {
smoke2();
var speed = document.getElementById('speedCont').value;
window.setTimeout(animate2, speed);
}
function animate3() {
smoke3();
var speed = document.getElementById('speedCont').value;
window.setTimeout(animate3, speed);
}
/** if (a == 1) {
ctx.clearRect(0, 0, 260, 105);
smoke();
a++;
} else if (a == 2) {
ctx.clearRect(0, 0, 260, 105);
smokeMed();
a++;
} else if (a == 3) {
ctx.clearRect(0, 0, 260, 105);
smokeBig();
a = 1;
} else {
ctx.clearRect(0, 0, 260, 105);
}
window.setTimeout(animate2, speed);
}
**/
window.onload = function all() {
foundation();
house();
animate();
animate2();
animate3();
}
window.addEventListener("load", all, false);
//window.setInterval(animate2, 1000);
//window.setTimeout(animate2, speed);
的CSS:
#canvas {
background-color: aqua;
border: 1px solid black;
margin-bottom: 10px ;
}
body {
background-color: gray;
}
input[type=range] {
-webkit-appearance: none;
border: 3px solid black;
width: 500px;
border-radius: 20px;
}
input[type=range]::-webkit-slider-runnable-track {
width: 500px;
height: 10px;
background: #ddd;
border: none;
border-radius: 20px;
}
input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
border: 3px solid black;
height: 30px;
width: 30px;
border-radius: 50%;
background: red;
margin-top: -8px;
}
input[type=range]:focus {
outline: none;
}
input[type=range]:focus::-webkit-slider-runnable-track {
background: #ccc;
}
答案 0 :(得分:0)
flickr与多个setTimeout
函数有关。由于您删除了smoke1()
上烟雾的先前状态,因此一旦更改速度,就会出现差异。如果您只使用一个setTimeout,它应该在您的特定情况下正常工作。这里以JSFIDDLE为例。
答案 1 :(得分:0)
对DOM的所有渲染都是双缓冲的。始终对backBuffer进行渲染,它不会被绘制到屏幕上。当场景完全渲染时,应该在屏幕上显示。
当函数退出(返回空闲)时,DOM假定您已完成所有渲染以及显示结果的内容。这是将后备缓冲区移动到屏幕可以看到的位置。
如果你只渲染部分场景并退出DOM,则不知道你还没有完成渲染,并且会按原样呈现后备缓冲区。屏幕刷新速度比写入后备缓冲区的速度慢得多。当DOM将后缓冲器呈现给屏幕时,显示硬件可以在将像素扫描到显示硬件上的任何点处。结果是不一致的闪烁和剪切。
您可以通过仅使用一个函数来完成所有渲染来解决问题。问题仍然是当函数退出时,它可能在显示扫描的任何点。你仍会受到一些剪切。
与显示硬件保持同步并防止任何闪烁和剪切使用requestAnimationFrame
(在SO中有无穷无尽的答案)来进行所有渲染。
DOM将requestAnimationFrame
调用的所有回调函数视为特殊函数,并将延迟任何DOM视觉更改从后备缓冲区移动到屏幕,直到显示硬件处于垂直刷新阶段。此时,自上次垂直刷新以来的所有更改都从后缓冲区移动到屏幕。 (这适用于所有视觉效果,而不仅仅是画布)。
修复代码
您想要一个固定的更新速度。
var speed = 100;
var nextUpdateTime;
function updateAll(time){
if(nextUpdateTime=== undefined){
nextUpdateTime= time - speed; // first update now
}
if(time >= nextUpdateTime){
nextUpdateTime= time + (speed - (time - nextUpdateTime)); // get time of next update
smoke1();
smoke2();
smoke3()
}
requestAnimationFrame(updateAll);
}
requestAnimationFrame(updateAll);
如果您希望每个FX都有自己的速度,则必须创建一个speed和nextUpdateTime值的数组。请求的动画帧通常以60fps运行。时间始终是请求的回调函数的第一个参数。
最好以60fps
运行我建议您不要使用较慢的更新速度(100毫秒是10fps),而是修改代码以减慢动画速度,以便在加速到60fps时顺畅运行
以下内容将修改您的动画以播放相同但帧率更高的动画。
var speed = 100; // the old frame update delay
var frameRate = 60; // requestAnimationFrame frame rate
// vx and vy where the update delta move vectors for the smoke
// that need to be adjusted for the new frame rate.
vx = (vx * (1000 / speed)) / frameRate;
vy = (vy * (1000 / speed)) / frameRate;
function updateAll(time){
smoke1();
smoke2();
smoke3()
requestAnimationFrame(updateAll);
}
requestAnimationFrame(updateAll);
注意如果您提供大量渲染工作,浏览器可能无法跟上。如果它不能在1/60秒内完成,并且在下一次垂直刷新之前不会显示任何内容,帧速率将从60fps降至30fps。 如果您怀疑会发生这种情况,那么您应该监控帧之间的时间并使用它来计算每帧的新烟雾位置。