gpt4 book ai didi

javascript - Canvas 请求动画在 slider 输入到 javascript 变量时超出帧

转载 作者:太空宇宙 更新时间:2023-11-04 15:47:53 25 4
gpt4 key购买 nike

我有一个 Canvas 元素。它根据 art vanishing points 绘制了一些线条.

我正在尝试从一个消失点绘制房子(目前它只是一个盒子)。框的大小由 delta 变量决定。如果我手动更改值,它会这样做:

enter image description here

我想要一个 slider 来改变 delta 变量。但是我得到了一些非常奇怪的效果。也就是说,线条是在框架外向右绘制的。我到处都转储了 console.log 语句,但我仍然找不到问题(甚至如何调试 Canvas 问题?)

enter image description here

var canvas = document.querySelector("canvas");

canvas.width = window.innerWidth;
canvas.height = window.innerHeight;

canvas.width = 1600;
canvas.height = 800;

var ct = canvas.getContext("2d");

// TODO
// 1. Make a center point
// 2. Draw lines jutting from center
// 3. Draw a line parallel to canvas bottom
// 4. Draw an adjoining item upward

// x, y
// right, down

// Nomenclature
// x0a
// coordinate type, vanishingPt#, endPtName

// Vanishing point 0
var x0 = 400;
var y0 = 400;

// Vanishing point end 0a
var x0a = 0;
var y0a = 2 * y0;

// Vanishing point end 0b
var x0b = 2 * x0;
var y0b = 2 * y0;

// Define delta
var delta = 700;

function init() {
console.log(delta, "delta");
console.log(x0b, "x0b");
console.log(y0b, "y0b");
console.log(x0, "x0");
console.log(y0, "y0");
// First Line
ct.beginPath();
ct.moveTo(x0, y0);
ct.lineTo(x0a, y0a);
ct.strokeStyle = 'red';
ct.stroke();

// Second Line
ct.beginPath();
ct.moveTo(x0, y0);
ct.lineTo(x0b, x0b);
ct.strokeStyle = 'green';
ct.stroke();

// House based on second Line
ct.beginPath();
ct.moveTo(x0b, y0b); // starting point
ct.lineTo(x0b + delta, y0b); // right x+100
ct.lineTo(x0b + delta, y0b - delta); // up y-100
ct.lineTo(x0b, y0b - delta); // left x-100
ct.lineTo(x0b, y0b); // down y+100
ct.lineTo(x0b, y0b - delta); // back up y-100
//calculate
ct.lineTo(x0, y0);
ct.lineTo(x0b + delta, y0b - delta);
ct.strokeStyle = 'blue';
ct.stroke();
}

init();

var slider = document.getElementById("myRange");

slider.oninput = function () {
delta = this.value;
requestAnimationFrame(init()); // redraw everything
}
body {
background-color: lightgray;
display: flex;
justify-content: center;
align-items: center;
}
.slideContainer {
position: fixed;
right: 30px;
top: 30px;
background-color: lightblue;
z-index: 20;
}
canvas {
border: 1px dotted red;
padding: 80px;
background-color: lightgray;
transform: scale(0.5);
}
<!DOCTYPE html>
<html>

<head>
<link rel="stylesheet" href="style.css" />
</head>

<body>
<div class="wrapper">
<div class="slideContainer">
<input type="range" min="1" max="800" value="50" class="slider" id="myRange">
</div>
<canvas id="canvas"></canvas>
</div>
<script src="script.js"></script>
</body>

</html>

最佳答案

将渲染与输入事件分离

您的问题已得到回答,但您的问题有一些不好的做法需要指出,否则它们会被复制。

oninput 是鼠标移动驱动的事件。

切勿从任何由鼠标移动事件引起的事件中调用渲染函数或 requestAnimationFrame。许多设备上的鼠标移动事件可以以比显示速率高得多的速率触发(高达每秒 1000 次)。显示器每 60 秒只能显示 1 帧,画得再多就看不到了,而且会耗尽客户的电池。

如果您从鼠标驱动的输入事件中调用 requestAnimationFrame,您最终会将许多渲染排队等待下一次显示刷新,并且当 requestAnimationFrame 尝试平衡负载时,它可能会为下一帧排队渲染,因此最新更新可能最多延迟 2 个显示帧。大多数帧将永远不会被看到,你仍然在消耗能量。

使用信号量和标准渲染循环来监控信号量并仅在需要时重绘,并且每帧仅重绘一次。 (见示例)

不要缩小 Canvas 。

除非您将 Canvas 转换为动画的一部分,否则不要通过 CSS 规则 transform: scale(0.5);(或任何其他缩放方法)缩小 Canvas 其次,如果您将显示 Canvas 的大小减半,这意味着您需要渲染 4 倍的像素,并使用 4 倍的内存。

您可以通过 Canvas 2D API 进行缩放,这样做可以节省客户端电池生命周期并提高性能。

例子

我已经完全重写了代码,希望它能有所帮助。两个要点,Updates,Scale 都注释掉了。添加代码以使用点而不是 x,y 坐标,因为我很懒。

requestAnimationFrame(update); // start anim loop

const ctx = canvas.getContext("2d");
const width = 1600; // The ideal resolution
const height = 800; // used to scale content
canvas.width = innerWidth;
canvas.height = innerHeight;


//Scales 2D context to always show the ideal resolution area
const scaleToFit = () => { // sets canvas scale to fit content
var scale = Math.min(canvas.width / width, canvas.height / height);
ctx.setTransform(scale, 0, 0, scale, 0, 0);
}

var redraw = true; // when true scene is redrawn ready for the next display refresh

// Working with points is easier
const point = (x = 0, y = 0) => ({x, y});
const pointCpy = (p, x = 0, y = 0) => ({x: p.x + x, y: p.y + y});
const scalePoint = (origin, point, scale) => {
point.x = (point.x - origin.x) * scale + origin.x;
point.y = (point.y - origin.y) * scale + origin.y;
};

const p1 = point(400,400);
const pA = point(p1.x, p1.y * 2);
const pB = point(p1.x * 2, p1.y * 2);

var delta = 50;

// the slider input event should not directly trigger a render
slider.addEventListener("input",(e) => {
delta = Number(e.target.value);
redraw = true; // use a semaphore to indicate content needs to redraw.
});

function update() { // this is the render loop it only draws when redraw is true
if (redraw) { // monitor semaphore
redraw = false; // clear semaphore
ctx.setTransform(1,0,0,1,0,0); // resets transform
ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
scaleToFit();
draw();
}
requestAnimationFrame(update);
}

// this was your function init()
function draw() {
drawLine(p1, pA, "red");
drawLine(p1, pB, "green");
drawVBox(pB, delta, p1, "blue");
}


function drawVBox(p, size, vp, col, width) { // p is bottom left vp is vanish point
ctx.strokeStyle = col;
ctx.lineWidth = width;
const p0 = pointCpy(p); // get corners
const p1 = pointCpy(p, size);
const p2 = pointCpy(p, size, -size);
const p3 = pointCpy(p, 0, -size);
drawPoly(col, width, p0, p1, p2, p3)

ctx.beginPath(); // draw vanish lines
pathLine(p0, vp);
pathLine(p1, vp);
pathLine(p2, vp);
pathLine(p3, vp);
ctx.stroke();

const scale = 1 - size / (800 * 2);
scalePoint(vp, p0, scale);
scalePoint(vp, p1, scale);
scalePoint(vp, p2, scale);
scalePoint(vp, p3, scale);
drawPoly(col, width, p0, p1, p2, p3);
}

// Use function to do common tasks and save your self a lot of typing
function drawLine(p1, p2, col, width = 1) {
ctx.strokeStyle = col;
ctx.lineWidth = width;
ctx.beginPath();
ctx.lineTo(p1.x, p1.y); // First point after beginPath can be lineTo
ctx.lineTo(p2.x, p2.y);
ctx.stroke();
}
function drawPoly(col,width, ...points) {
ctx.strokeStyle = col;
ctx.lineWidth = width;
ctx.beginPath();
for(const p of points){
ctx.lineTo(p.x, p.y); // First point after beginPath can be lineTo
}
ctx.closePath(); // draw closing line
ctx.stroke();
}
function pathLine(p1, p2) {
ctx.moveTo(p1.x, p1.y);
ctx.lineTo(p2.x, p2.y);
}
canvas {

position : absolute;
top: 0px;
left: 0px;
background-color: lightgray;
z-index: -20;
}
<canvas id="canvas"></canvas>
<input type="range" min="1" max="800" value="50" id="slider">
<code id="info"></code>

关于javascript - Canvas 请求动画在 slider 输入到 javascript 变量时超出帧,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53464401/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com