简体   繁体   English

HTML5 Canvas Javascript - 如何使用瓦片制作可移动的 ZFCC790C72A86190DE1B549D'DZ3D'55

[英]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:有几件事正在发生:

Immediately invoking 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();

Finding remainders using non integers使用非整数求余数

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.roundMath.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 .

Using a container and css to hide shifting parts of the canvas使用容器和 css 隐藏 canvas 的变速部件

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全部一起

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM