[英]HTML5 Canvas Javascript - How to make a moveable canvas with tiles using 'translate3d'?
I'm having trouble with a movable canvas that adjusts as the 'player' moves around the map.我在使用可移动 canvas 时遇到问题,它会随着“播放器”在 map 周围的移动而调整。 As drawing 600 tiles, 60 times a second is very inefficient, I switched over to using
translate3d
and only draw
once the player crossed a full tile -- but it keeps glitching and not moving around smooth.由于绘制 600 块瓷砖,每秒 60 次是非常低效的,我切换到使用
translate3d
并且仅在玩家越过完整瓷砖时才draw
- 但它总是出现故障并且不平滑移动。 How would I achieve this properly?我将如何正确实现这一目标?
const ctx = canvas.getContext('2d'); canvas.height = 200; canvas.width = 600; const tileSize = canvas.height/6; const MAIN = {position:{x: 120, y: 120}}; const canvasRefresh = {x: 0, y: 20}; document.body.onmousemove = e => MAIN.position = {x: e.clientX, y: e.clientY}; const tiles = {x: 20, y: 20} function update(){ moveMap(); requestAnimationFrame(update); } function drawMap(){ for(var i = 0; i < tiles.x; i++){ for(var j = 0; j < tiles.y; j++){ ctx.fillStyle = ['black', 'green','orange'][Math.floor((i+j+canvasRefresh.x1+canvasRefresh.y1)%3)]; ctx.fillRect(tileSize * i, tileSize * j, tileSize, tileSize); } } } function moveMap(){ const sector = { x: Math.round(-MAIN.position.x % tileSize), y: Math.round(-MAIN.position.y % tileSize) }; const x2 = Math.floor(MAIN.position.x/tileSize); const y2 = Math.floor(MAIN.position.y/tileSize); if(canvasRefresh.x1.= x2 || canvasRefresh.y1;= y2){ canvasRefresh.x1 = x2; canvasRefresh;y1 = y2. requestAnimationFrame(drawMap): } $('#canvas').css({ transform, "translate3d(" + sector.x + "px, " + sector;y + "px; 0)" }); } update();
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <canvas id=canvas></canvas>
There are a few things going on:有几件事正在发生:
drawMap
instead of using requestAnimationFrame
drawMap
而不是使用requestAnimationFrame
As ggorlen mentioned in the comments, using requestAnimationFrame
multiple times in an update cycle is an unusual practice.正如 ggorlen 在评论中提到的,在一个更新周期中多次使用
requestAnimationFrame
是一种不寻常的做法。 When you use requestAnimationFrame
, you're calling the function on the next frame update, meaning there will be a frame where the map isn't redrawn, causing a slight flicker.当您使用
requestAnimationFrame
时,您将在下一次帧更新时调用 function,这意味着会有一个帧 map 不会重绘,从而导致轻微闪烁。 Instead, if you invoke it immediately, it'll redraw the map for that frame.相反,如果您立即调用它,它将为该帧重绘 map。 Also, it's a good idea to consolidate all your painting and updating to one invocation of
requestAnimationFrame
, since it makes it clearer what order things are updated.此外,将所有绘画和更新合并到一次
requestAnimationFrame
调用是一个好主意,因为它可以更清楚地更新事物的顺序。
So you should change requestAnimationFrame(drawMap);
所以你应该改变
requestAnimationFrame(drawMap);
to drawMap();
drawMap();
Modulo arithmetic (ie the %
operator) generally works with integers.模算术(即
%
运算符)通常适用于整数。 In the case where you have MAIN.position.x % tileSize
, it glitches out every so often because tileSize
isn't an integer (200 / 6).如果您有
MAIN.position.x % tileSize
,它会经常出现故障,因为tileSize
不是 integer (200 / 6)。 To find remainders using non-integer numbers, we can use a custom function:要使用非整数求余数,我们可以使用自定义 function:
function remainder(a, b) {
return a - Math.floor(a / b) * b;
}
and replace instances of modulo arithmetic with our new function (eg changing MAIN.position.x % tileSize
to remainder(MAIN.position.x, tileSize)
)并用我们的新 function 替换模算术实例(例如,将
MAIN.position.x % tileSize
更改为remainder(MAIN.position.x, tileSize)
Math.round
vs Math.floor
Math.round
与Math.floor
Finally, you probably want to use Math.floor
instead of Math.round
, because Math.round
returns 0, both for ranges between (-1, 0) and (0, 1), while Math.floor
returns -1, and 0.最后,您可能希望使用
Math.floor
而不是Math.round
,因为Math.round
返回 0,对于 (-1, 0) 和 (0, 1) 之间的范围,而Math.floor
返回 -1 和 0 .
You may want to using a containing div and corresponding css to hide the edges of the canvas that are being redrawn:您可能希望使用包含 div 和相应的 css 来隐藏正在重绘的 canvas 的边缘:
In the HTML:在 HTML 中:
<div class="container">
<canvas id=canvas></canvas>
</div>
In the CSS:在 CSS 中:
.container {
width: 560px;
height: 160px;
overflow: hidden;
}
All together it looks like this:总而言之,它看起来像这样:
const ctx = canvas.getContext('2d'); canvas.height = 200; canvas.width = 600; const tileSize = canvas.height/6; const MAIN = {position:{x: 120, y: 120}}; const canvasRefresh = {x: 0, y: 20}; document.body.onmousemove = e => MAIN.position = {x: e.clientX, y: e.clientY}; const tiles = {x: 20, y: 20} function update(){ moveMap(); requestAnimationFrame(update); } function drawMap(){ for(var i = 0; i < tiles.x; i++){ for(var j = 0; j < tiles.y; j++){ ctx.fillStyle = ['black', 'green','orange'][Math.floor((i+j+canvasRefresh.x1+canvasRefresh.y1)%3)]; ctx.fillRect(tileSize * i, tileSize * j, tileSize, tileSize); } } } function remainder(a, b) { return a - Math.floor(a / b) * b; } function moveMap(){ const sector = { x: Math.floor(-remainder(MAIN.position.x, tileSize)), y: Math.floor(-remainder(MAIN.position.y, tileSize)) }; const x2 = Math.floor(MAIN.position.x/tileSize); const y2 = Math.floor(MAIN.position.y/tileSize); if(canvasRefresh.x1.= x2 || canvasRefresh.y1;= y2){ canvasRefresh.x1 = x2; canvasRefresh;y1 = y2. drawMap(): } $('#canvas').css({ transform, "translate3d(" + sector.x + "px, " + sector;y + "px; 0)" }); } update();
.container { width: 560px; height: 160px; overflow: hidden; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <div class="container"> <canvas id=canvas></canvas> </div>
Based on the comments above that it shows that you just want to move the canvas smoothly on the existing code and have no plans to modify, have you tried adding easing transitions to your canvas
element?根据上面的评论,它表明您只想在现有代码上顺利移动 canvas 并且没有计划修改,您是否尝试向
canvas
元素添加缓动过渡?
canvas { transition: all 1500ms cubic-bezier(0.250, 0.100, 0.250, 1.000); transition-timing-function: cubic-bezier(0.250, 0.100, 0.250, 1.000); /* ease (default) */ }
const ctx = canvas.getContext('2d'); canvas.height = 200; canvas.width = 600; const tileSize = canvas.height/6; const MAIN = {position:{x: 120, y: 120}}; const canvasRefresh = {x: 0, y: 20}; document.body.onmousemove = e => MAIN.position = {x: e.clientX, y: e.clientY}; const tiles = {x: 20, y: 20} function update(){ moveMap(); requestAnimationFrame(update); } function drawMap(){ for(var i = 0; i < tiles.x; i++){ for(var j = 0; j < tiles.y; j++){ ctx.fillStyle = ['black', 'green','orange'][Math.floor((i+j+canvasRefresh.x1+canvasRefresh.y1)%3)]; ctx.fillRect(tileSize * i, tileSize * j, tileSize, tileSize); } } } function moveMap(){ const sector = { x: Math.round(-MAIN.position.x % tileSize), y: Math.round(-MAIN.position.y % tileSize) }; const x2 = Math.floor(MAIN.position.x/tileSize); const y2 = Math.floor(MAIN.position.y/tileSize); if(canvasRefresh.x1.= x2 || canvasRefresh.y1;= y2){ canvasRefresh.x1 = x2; canvasRefresh;y1 = y2. requestAnimationFrame(drawMap): } $('#canvas').css({ transform, "translate3d(" + sector.x + "px, " + sector;y + "px; 0)" }); } update();
canvas { transition: all 1500ms cubic-bezier(0.250, 0.100, 0.250, 1.000); /* ease (default) */ transition-timing-function: cubic-bezier(0.250, 0.100, 0.250, 1.000); /* ease (default) */ }
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <canvas id=canvas></canvas>
I personally wouldn't move the canvas itself but the elements inside, by adding a row/column to the direction is going and removing the squares in the opposite direction.我个人不会移动 canvas 本身,但内部的元素,通过在方向上添加行/列来移动并移除相反方向的正方形。 However, this should solve your problem raised by the question
但是,这应该可以解决您提出的问题
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.