**您需要以全屏模式运行代码段。
看一下下面的例子。如果你点击左右,你会注意到高速行驶时球几乎变得模糊。
有什么方法可以解决这个问题吗?这是由于60fps?如果有,有办法增加它吗?
当球以更高的速度间隔开时,似乎会发生这种情况。您可以在下面的图片中看到这一点。
'use strict';
// Todo
// - Make the ball spin
// - Make the ball squish
// - Add speed lines
(function () {
const canvas = document.getElementsByClassName('canvas')[0],
c = canvas.getContext('2d');
// -----------------------------------
// Resize the canvas to be full screen
// -----------------------------------
window.addEventListener('resize', resizeCanvas, false);
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// ---------
// Variables
// ---------
var circleRadius = 40,
x = (canvas.width/2) - circleRadius, // inital x position of the ball
y = (canvas.height/2) - circleRadius, // inital y position of the ball
vx = 0, // velocity
vy = 0, // velocity
gravity = 0.8,
dampening = 0.5,
pullStrength = 0.04,
segments = 4,
bezieCircleFormula = (4/3)*Math.tan(Math.PI/(2*segments)), // http://stackoverflow.com/a/27863181/2040509
pointOffset = {
positive: bezieCircleFormula*circleRadius,
negative: circleRadius-(bezieCircleFormula*circleRadius)
},
// Each side has 3 points, bezier 1, circle point, bezier 2
// These are listed below in clockwise order.
// So top has: left bezier, circle point, right bezier
// Right has: top bezier, circle point, bottom bezier
circlePoints = {
top: [
[x+pointOffset.negative, y],
[x+circleRadius, y],
[x+pointOffset.positive+circleRadius, y]
],
right: [
[x+circleRadius*2, y+pointOffset.negative],
[x+circleRadius*2, y+circleRadius],
[x+circleRadius*2, y+pointOffset.positive+circleRadius]
],
bottom: [
[x+pointOffset.positive+circleRadius, y+circleRadius*2],
[x+circleRadius, y+circleRadius*2],
[x+pointOffset.negative, y+circleRadius*2]
],
left: [
[x, y+pointOffset.positive+circleRadius],
[x, y+circleRadius],
[x, y+pointOffset.negative]
]
};
// --------------------
// Ball squish function
// --------------------
// For `side` you can pass `top`, `right`, `bottom`, `left`
// For `amount` use an interger
function squish (side, squishAmount) {
for (let i = 0; i < circlePoints[side].length; i++) {
if (side === 'top') {
circlePoints[side][i][1] += squishAmount;
} else if (side === 'right') {
circlePoints[side][i][0] -= squishAmount;
} else if (side === 'bottom') {
circlePoints[side][i][1] -= squishAmount;
} else if (side === 'left') {
circlePoints[side][i][0] += squishAmount;
}
}
}
// ------------------
// Animation Function
// ------------------
function render () {
// Clear the canvas
c.clearRect(0, 0, canvas.width, canvas.height);
// -----------------
// Draw the elements
// -----------------
// Ground
let groundHeight = 200;
c.beginPath();
c.fillStyle = '#9cccc8';
c.fillRect(0, canvas.height - groundHeight, canvas.width, groundHeight);
// Bezier circle
c.beginPath();
c.fillStyle = '#cf2264';
c.moveTo(circlePoints.left[1][0], circlePoints.left[1][1]);
c.bezierCurveTo(circlePoints.left[2][0], circlePoints.left[2][1], circlePoints.top[0][0], circlePoints.top[0][1], circlePoints.top[1][0], circlePoints.top[1][1]);
c.bezierCurveTo(circlePoints.top[2][0], circlePoints.top[2][1], circlePoints.right[0][0], circlePoints.right[0][1], circlePoints.right[1][0], circlePoints.right[1][1]);
c.bezierCurveTo(circlePoints.right[2][0], circlePoints.right[2][1], circlePoints.bottom[0][0], circlePoints.bottom[0][1], circlePoints.bottom[1][0], circlePoints.bottom[1][1]);
c.bezierCurveTo(circlePoints.bottom[2][0], circlePoints.bottom[2][1], circlePoints.left[0][0], circlePoints.left[0][1], circlePoints.left[1][0], circlePoints.left[1][1]);
c.fill();
c.closePath();
// -------------------------------
// Recalculate circle co-ordinates
// -------------------------------
circlePoints = {
top: [
[x+pointOffset.negative, y],
[x+circleRadius, y],
[x+pointOffset.positive+circleRadius, y]
],
right: [
[x+circleRadius*2, y+pointOffset.negative],
[x+circleRadius*2, y+circleRadius],
[x+circleRadius*2, y+pointOffset.positive+circleRadius]
],
bottom: [
[x+pointOffset.positive+circleRadius, y+circleRadius*2],
[x+circleRadius, y+circleRadius*2],
[x+pointOffset.negative, y+circleRadius*2]
],
left: [
[x, y+pointOffset.positive+circleRadius],
[x, y+circleRadius],
[x, y+pointOffset.negative]
]
};
// -----------------
// Animation Gravity
// -----------------
// Increment gravity
vy += gravity;
// Increment velocity
y += vy;
x += vx;
// ----------
// Boundaries
// ----------
// Bottom boundary
if ((y + (circleRadius * 2)) > canvas.height - groundHeight/2) {
y = canvas.height - groundHeight/2 - (circleRadius * 2);
vy *= -1;
// Dampening
vy *= dampening;
vx *= dampening;
console.log(vy);
if (vy > -2.4) {
dampening = 0;
} else {
// squish('top', 20);
}
}
// Right boundary
if ((x + (circleRadius * 2)) > canvas.width) {
x = canvas.width - (circleRadius * 2);
vx *= -1;
// Dampening
vy *= dampening;
vx *= dampening;
}
// Left boundary
if ((x + (circleRadius * 2)) < 0 + (circleRadius * 2)) {
x = 0;
vx *= -1;
// Dampening
vy *= dampening;
vx *= dampening;
}
// Top boundary
if (y < 0) {
y = 0;
vy *= -1;
// Dampening
vy *= dampening;
vx *= dampening;
}
requestAnimationFrame(render);
}
// -----------
// Click event
// -----------
canvas.addEventListener('mousedown', function (e) {
let dx = e.pageX - x,
dy = e.pageY - y;
if (dampening === 0) {
dampening = 0.5;
}
vx += dx * pullStrength;
vy += dy * pullStrength;
});
render();
}
resizeCanvas();
})();
body {
margin: 0;
}
canvas {
background: #ddf6f5;
display: block;
}
<canvas class="canvas"></canvas>
答案 0 :(得分:2)
目前使用requestAnimationFrame时,您将从浏览器获得60fps。您可以获得更快的帧速率,因为很难保持同步甚至知道显示器运行的帧速率。与一些运行120 + fps的本机应用程序和机器相比,60fps速度较慢,但它是目前浏览器中最好的。
我刚刚为你的代码添加了一些代码(对不起,我的代码有点乱),只显示了renderTime&#34;绿线&#34; (代码花费每帧渲染场景的时间)和帧速率&#34;红线&#34;再加上averages.I不包括渲染我添加的行和文本的时间,所以它们不会影响渲染时间,但会影响帧速率。
你不会在渲染时间低于帧时间的情况下对图形施加沉重的负担,但是你可能会注意到红线上偶尔会出现尖峰。当浏览器丢弃某些帧并导致动画看起来不平滑时会发生这种情况。
你只为你的javascript(不包括工人)获得一个线程,所以对于大多数机器来说这不到cpu功率的1/8,而且与处理CSS动画的本机代码相比,javascript是一种慢速语言。 (我不确定,但CSS动画也可能从javascript无法访问的线程中获得一些好处)
如果偶然的话,我添加的红线遍布整个地方(根本没有平坦的直线部分)且平均帧速率不接近60fps,您的浏览器可能会禁用显示同步,并且只是在您显示场景时立即显示完成了。
我添加了此代码,以直观地展示性能和帧速率。对于实际测试,您不应该有显示部分,只测量时间仅在测试期后显示结果。
'use strict';
// Todo
// - Make the ball spin
// - Make the ball squish
// - Add speed lines
(function () {
const canvas = document.getElementsByClassName('canvas')[0],
c = canvas.getContext('2d');
// -----------------------------------
// Resize the canvas to be full screen
// -----------------------------------
window.addEventListener('resize', resizeCanvas, false);
function resizeCanvas() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// ---------
// Variables
// ---------
var circleRadius = 40,
x = (canvas.width/2) - circleRadius, // inital x position of the ball
y = (canvas.height/2) - circleRadius, // inital y position of the ball
vx = 0, // velocity
vy = 0, // velocity
gravity = 0.8,
dampening = 0.5,
pullStrength = 0.04,
segments = 4,
bezieCircleFormula = (4/3)*Math.tan(Math.PI/(2*segments)), // http://stackoverflow.com/a/27863181/2040509
pointOffset = {
positive: bezieCircleFormula*circleRadius,
negative: circleRadius-(bezieCircleFormula*circleRadius)
},
// Each side has 3 points, bezier 1, circle point, bezier 2
// These are listed below in clockwise order.
// So top has: left bezier, circle point, right bezier
// Right has: top bezier, circle point, bottom bezier
circlePoints = {
top: [
[x+pointOffset.negative, y],
[x+circleRadius, y],
[x+pointOffset.positive+circleRadius, y]
],
right: [
[x+circleRadius*2, y+pointOffset.negative],
[x+circleRadius*2, y+circleRadius],
[x+circleRadius*2, y+pointOffset.positive+circleRadius]
],
bottom: [
[x+pointOffset.positive+circleRadius, y+circleRadius*2],
[x+circleRadius, y+circleRadius*2],
[x+pointOffset.negative, y+circleRadius*2]
],
left: [
[x, y+pointOffset.positive+circleRadius],
[x, y+circleRadius],
[x, y+pointOffset.negative]
]
};
// --------------------
// Ball squish function
// --------------------
// For `side` you can pass `top`, `right`, `bottom`, `left`
// For `amount` use an interger
function squish (side, squishAmount) {
for (let i = 0; i < circlePoints[side].length; i++) {
if (side === 'top') {
circlePoints[side][i][1] += squishAmount;
} else if (side === 'right') {
circlePoints[side][i][0] -= squishAmount;
} else if (side === 'bottom') {
circlePoints[side][i][1] -= squishAmount;
} else if (side === 'left') {
circlePoints[side][i][0] += squishAmount;
}
}
}
// ------------------
// Animation Function
// ------------------
var lastTime = new Date().valueOf();
var frameTimes = [];
var renderTimes = []
var frameTimeWritePos = 0;
var frameTimeReadStartPos = 0;
var frameTimeMaxSample = Math.floor(canvas.width/8);
var maxTime = 0;
console.log(maxTime);
function recordFrameTime(time,renderTime){
frameTimes[frameTimeWritePos % frameTimeMaxSample] = time-lastTime;
renderTimes[frameTimeWritePos % frameTimeMaxSample] = renderTime;
maxTime = Math.min(1000/20,Math.max(maxTime, renderTime, time-lastTime));
lastTime = time;
frameTimeWritePos = (frameTimeWritePos + 1) % frameTimeMaxSample;
if(frameTimeWritePos === frameTimeReadStartPos){
frameTimeReadStartPos = (frameTimeReadStartPos + 1) % frameTimeMaxSample;
}
}
function drawFrameTimes(){
var yScale,xScale;
var t1 = 0;
var t2 = 0;
var c1 = 0;
var h = canvas.height;
yScale = h / maxTime;
xScale = canvas.width / frameTimeMaxSample;
c.lineWidth = 2;
c.strokeStyle = "red";
var i = frameTimeReadStartPos;
var sx = i;
c.beginPath();
c.moveTo((i-sx)*xScale ,h - frameTimes[(i %frameTimeMaxSample)]*yScale);
while ( (i %frameTimeMaxSample) !== frameTimeWritePos){
c.lineTo((i-sx)*xScale ,h - frameTimes[(i %frameTimeMaxSample)]*yScale);
t1 += frameTimes[(i %frameTimeMaxSample)];
c1 += 1;
i += 1;
}
t1 /= c1;
t1 = (1000/t1).toFixed(2);
c1 = 0;
c.stroke();
i = frameTimeReadStartPos;
c.strokeStyle = "Green";
c.beginPath();
c.moveTo((i-sx)*xScale ,h - renderTimes[(i %frameTimeMaxSample)]*yScale);
while ( (i %frameTimeMaxSample) !== frameTimeWritePos){
c.lineTo((i-sx)*xScale ,h - renderTimes[(i %frameTimeMaxSample)]*yScale);
i += 1;
t2 += renderTimes[(i %frameTimeMaxSample)];
c1 += 1;
}
t2/= c1;
c.stroke();
//c.beginPath();
//c.strokeStyle = "white";
//c.moveTo(0,h-(1000/60)*yScale);
// c.lineTo(canvas.width,h-(1000/60)*yScale);
// c.stroke();
c.font="36px arial";
c.fillStyle = "black";
c.fillText("R:" + t2.toFixed(2)+ "ms "+t1+"fps", 20,40);
}
var bRenders = 0;
function render (time) {
if(isNaN(time)){
time = performance.now();
bRenders += 1;
}
var startTime = performance.now();
// Clear the canvas
c.clearRect(0, 0, canvas.width, canvas.height);
// -----------------
// Draw the elements
// -----------------
// Ground
let groundHeight = 200;
c.beginPath();
c.fillStyle = '#9cccc8';
c.fillRect(0, canvas.height - groundHeight, canvas.width, groundHeight);
// Bezier circle
c.beginPath();
c.fillStyle = '#cf2264';
c.moveTo(circlePoints.left[1][0], circlePoints.left[1][1]);
c.bezierCurveTo(circlePoints.left[2][0], circlePoints.left[2][1], circlePoints.top[0][0], circlePoints.top[0][1], circlePoints.top[1][0], circlePoints.top[1][1]);
c.bezierCurveTo(circlePoints.top[2][0], circlePoints.top[2][1], circlePoints.right[0][0], circlePoints.right[0][1], circlePoints.right[1][0], circlePoints.right[1][1]);
c.bezierCurveTo(circlePoints.right[2][0], circlePoints.right[2][1], circlePoints.bottom[0][0], circlePoints.bottom[0][1], circlePoints.bottom[1][0], circlePoints.bottom[1][1]);
c.bezierCurveTo(circlePoints.bottom[2][0], circlePoints.bottom[2][1], circlePoints.left[0][0], circlePoints.left[0][1], circlePoints.left[1][0], circlePoints.left[1][1]);
c.fill();
c.closePath();
// -------------------------------
// Recalculate circle co-ordinates
// -------------------------------
circlePoints = {
top: [
[x+pointOffset.negative, y],
[x+circleRadius, y],
[x+pointOffset.positive+circleRadius, y]
],
right: [
[x+circleRadius*2, y+pointOffset.negative],
[x+circleRadius*2, y+circleRadius],
[x+circleRadius*2, y+pointOffset.positive+circleRadius]
],
bottom: [
[x+pointOffset.positive+circleRadius, y+circleRadius*2],
[x+circleRadius, y+circleRadius*2],
[x+pointOffset.negative, y+circleRadius*2]
],
left: [
[x, y+pointOffset.positive+circleRadius],
[x, y+circleRadius],
[x, y+pointOffset.negative]
]
};
// -----------------
// Animation Gravity
// -----------------
// Increment gravity
vy += gravity;
// Increment velocity
y += vy;
x += vx;
// ----------
// Boundaries
// ----------
// Bottom boundary
if ((y + (circleRadius * 2)) > canvas.height - groundHeight/2) {
y = canvas.height - groundHeight/2 - (circleRadius * 2);
vy *= -1;
// Dampening
vy *= dampening;
vx *= dampening;
// console.log(vy);
if (vy > -2.4) {
dampening = 0;
} else {
// squish('top', 20);
}
}
// Right boundary
if ((x + (circleRadius * 2)) > canvas.width) {
x = canvas.width - (circleRadius * 2);
vx *= -1;
// Dampening
vy *= dampening;
vx *= dampening;
}
// Left boundary
if ((x + (circleRadius * 2)) < 0 + (circleRadius * 2)) {
x = 0;
vx *= -1;
// Dampening
vy *= dampening;
vx *= dampening;
}
// Top boundary
if (y < 0) {
y = 0;
vy *= -1;
// Dampening
vy *= dampening;
vx *= dampening;
}
requestAnimationFrame(render);
recordFrameTime(time,(performance.now()-startTime));
drawFrameTimes();
}
// -----------
// Click event
// -----------
canvas.addEventListener('mousedown', function (e) {
let dx = e.pageX - x,
dy = e.pageY - y;
if (dampening === 0) {
dampening = 0.5;
}
vx += dx * pullStrength;
vy += dy * pullStrength;
});
render();
}
resizeCanvas();
})();
&#13;
body {
margin: 0;
}
canvas {
background: #ddf6f5;
display: block;
}
&#13;
<canvas class="canvas"></canvas>
&#13;