- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在用 Canvas 做一个游戏,但我的方法 drawImage(...);
有一个小问题,该方法应该裁剪 Sprite 表以获得正确的 Sprite 。当我们运行时,尤其是当我们跳跃时,我们可以看到相邻 Sprite 的一些部分。
(注意:如果您想运行此代码,请确保您运行 Firefox 或 Chrome,因为仅这些浏览器支持为 image-rendering
指定的值)。
var ctx;
var heightCanvas;
var widthCanvas;
var player;
var reqAnim;
var stopped;
left = false;
right = false;
up = false;
window.onload = function() {
var canvas = document.getElementById('canvas');
heightCanvas = canvas.height;
widthCanvas = canvas.width;
ctx = canvas.getContext('2d');
ctx.imageSmoothingEnabled = false;
//Detection of arrow keys
document.onkeydown = function(e) {
if (e.keyCode == 37) left = true;
if (e.keyCode == 39) right = true;
if (e.keyCode == 38) up = true;
}
document.onkeyup = function(e) {
if (e.keyCode == 37) left = false;
if (e.keyCode == 39) right = false;
if (e.keyCode == 38) up = false;
}
//The animation begins when the sprite sheet is loaded
img = new Image();
img.onload = function() {
player = new Player(img,10,50);
reqAnim = requestAnimationFrame(updateCanvas);
stopped = false;
}
img.src = "https://i.imgur.com/6eKrMOI.png";
}
function updateCanvas() {
ctx.clearRect(0, 0, widthCanvas, heightCanvas);
player.updatePos();
player.updateStateDirection();
player.updateSprite();
player.updateDisplay()
reqAnim = requestAnimationFrame(updateCanvas);
}
function startStop() {
if (stopped) {
reqAnim = requestAnimationFrame(updateCanvas);
stopped = false;
} else {
cancelAnimationFrame(reqAnim);
stopped = true;
}
}
//----------------------------------//
//----------------------------------//
//----------Code of Player----------//
//----------------------------------//
//----------------------------------//
function Player(spritesheet, x, y) {
this.spritesheet = spritesheet;
this.x = x;
this.y = y;
//The direction of the player. false = left, true = right
this.direction = true;
//The state of the player. 0 = stand, 1 = run
this.state = 0;
//The dimensions of a sprite in the sprite sheet
this.width = 41;
this.height = 40;
//All the attributes beginning with 'ss' are related with the sprite sheet.
//The coordinates of the current sprite in the sprite sheet
this.ssX = 0;
this.ssY = 200;
//The number of times that we have repeated the current sprite
this.ssRepeat = 0;
this.speed = 2.5;
this.gravity = 0.3;
this.gravitySpeed = 0;
this.jumping = false;
//state: 0 = stand, 1 = run
//direction: false = left, true = right
this.updateStateDirection = function() {
if (left) { //If left is pressed
if (this.state != 1 || this.direction) { //If the player wasn't running
this.state = 1; //or if he was running in the opposite direction
this.ssY = 0;
}
this.direction = false;
} else if (right) { //If right is pressed
if (this.state != 1 || !this.direction) { //If the player wasn't running
this.state = 1; //or if he was running in the opposite direction
this.ssY = 80;
}
this.direction = true;
} else if (this.state != 0) { //If neither right nor left are pressed and the state isn't stand
this.state = 0;
if (this.direction) this.ssY = 200;
else this.ssY = 160;
}
}
this.updateSprite = function() {
if (this.state == 0) { //If the state is stand
if (this.ssRepeat < 15) //We display the same sprite 15 times before passing to the next one
this.ssRepeat++;
else {
this.ssRepeat = 0;
if (this.ssX < 205) this.ssX += this.width; //If we didn't reach the end of the sprite sheet
else this.ssX = 0;
}
} else if (this.state == 1) { //If the state is run
if (this.ssRepeat < 5) //We display the same sprite 5 times before passing to the next one
this.ssRepeat++;
else {
this.ssRepeat = 0;
if (this.ssX < 205) this.ssX += this.width; //If we didn't reach the end of the sprite sheet
else {
this.ssX = 0;
if (this.ssY < 40) this.ssY = 40; //If we reached the end of the first line of the SS
else if (this.ssY < 80) this.ssY = 0; //the end of the second
else if (this.ssY < 120) this.ssY = 120; //the third
else this.ssY = 80; //the fourth
}
}
}
}
//Display the proper sprite of the spritesheet
this.updateDisplay = function() {
ctx.drawImage(this.spritesheet, this.ssX, this.ssY,
this.width, this.height, this.x, this.y, this.width, this.height);
}
//Updates the position of the sprite according to the user's inputs
this.updatePos = function() {
this.jump();
this.gravitySpeed += this.gravity;
this.y += this.gravitySpeed;
this.hitBottom();
this.move();
}
this.hitBottom = function() {
var rockbottom = heightCanvas - this.height;
if (this.y > rockbottom) {
this.y = rockbottom;
this.gravitySpeed = 0;
this.jumping = false;
}
}
this.move = function() {
if (left) player.x -= this.speed;
if (right) player.x += this.speed;
}
this.jump = function() {
if (!this.jumping) {
if (up) {
this.gravitySpeed = -5.2;
this.jumping = true;
}
}
}
}
<!DOCTYPE html>
<html>
<head>
<title>Forto</title>
<meta charset="UTF-8">
<style>
canvas {
border: 1px solid black;
background-color: #9e9eaf;
image-rendering: optimizespeed; /*Firefox*/
image-rendering: pixelated; /*Chrome*/
}
</style>
<script src="forto.js"></script>
</head>
<body>
<canvas id="canvas" width="300" height="100"></canvas>
<br>
<button onclick="startStop()">Start/Stop</button>
</body>
</html>
如果您看不到边缘渗色,这就是问题的一部分,每个浏览器上都没有类似的支持,我想要一个在每个浏览器上都相同的解决方案。这是我所看到的:
感谢您的帮助。
<小时/>编辑1:有一个类似的帖子here ,但它并没有真正帮助,因为经过验证的答案使用 2D 上下文的 setTransform(...);
方法,但即使它适用于 Safari 和 IE,id 也不起作用(在至少)对于 Firefox ( see my output of the validated answer )。这个解决方案太“依赖浏览器”,我想要一个得到强烈支持的解决方案。
这篇文章的第二个答案是关于在 Sprite 表中的每个 Sprite 周围添加一个 1 像素的空边框,以避免边缘出血。这需要完全重新设计 Sprite 表,所以只有在没有更简单的解决方案的情况下我才会接受这个答案。
最佳答案
对于像素艺术,始终以整数值绘制,这样可以避免接近的 Sprite 流失。
这意味着,您的上下文也必须将其转换矩阵设置为整数值,并且您必须对传递给 drawImage
方法的所有值进行舍入。
在您的代码中,当您移动时,对象的 x
和 y
值是 float 值,因为您的 重力
和 >速度
值是 float 。
这本身不是问题,你只需要在渲染阶段对其进行舍入即可。
在下面的代码片段中,我添加了一个条件 fillRect,每次这些值不是整数时都会触发它。
var ctx;
var heightCanvas;
var widthCanvas;
var player;
var reqAnim;
var stopped;
left = false;
right = false;
up = false;
window.onload = function() {
var canvas = document.getElementById('canvas');
heightCanvas = canvas.height;
widthCanvas = canvas.width;
ctx = canvas.getContext('2d');
ctx.imageSmoothingEnabled = false;
//Detection of arrow keys
document.onkeydown = function(e) {
e.preventDefault();
if (e.keyCode == 37) left = true;
if (e.keyCode == 39) right = true;
if (e.keyCode == 38) up = true;
}
document.onkeyup = function(e) {
if (e.keyCode == 37) left = false;
if (e.keyCode == 39) right = false;
if (e.keyCode == 38) up = false;
}
//The animation begins when the sprite sheet is loaded
img = new Image();
img.onload = function() {
player = new Player(img, 10, 50);
reqAnim = requestAnimationFrame(updateCanvas);
stopped = false;
}
img.src = "https://i.imgur.com/6eKrMOI.png";
}
function updateCanvas() {
ctx.clearRect(0, 0, widthCanvas, heightCanvas);
player.updatePos();
player.updateStateDirection();
player.updateSprite();
player.updateDisplay()
reqAnim = requestAnimationFrame(updateCanvas);
}
function startStop() {
if (stopped) {
reqAnim = requestAnimationFrame(updateCanvas);
stopped = false;
} else {
cancelAnimationFrame(reqAnim);
stopped = true;
}
}
//----------------------------------//
//----------------------------------//
//----------Code of Player----------//
//----------------------------------//
//----------------------------------//
function Player(spritesheet, x, y) {
this.spritesheet = spritesheet;
this.x = x;
this.y = y;
//The direction of the player. false = left, true = right
this.direction = true;
//The state of the player. 0 = stand, 1 = run
this.state = 0;
//The dimensions of a sprite in the sprite sheet
this.width = 41;
this.height = 40;
//All the attributes beginning with 'ss' are related with the sprite sheet.
//The coordinates of the current sprite in the sprite sheet
this.ssX = 0;
this.ssY = 200;
//The number of times that we have repeated the current sprite
this.ssRepeat = 0;
this.speed = 2.5;
this.gravity = 0.3;
this.gravitySpeed = 0;
this.jumping = false;
//state: 0 = stand, 1 = run
//direction: false = left, true = right
this.updateStateDirection = function() {
if (left) { //If left is pressed
if (this.state != 1 || this.direction) { //If the player wasn't running
this.state = 1; //or if he was running in the opposite direction
this.ssY = 0;
}
this.direction = false;
} else if (right) { //If right is pressed
if (this.state != 1 || !this.direction) { //If the player wasn't running
this.state = 1; //or if he was running in the opposite direction
this.ssY = 80;
}
this.direction = true;
} else if (this.state != 0) { //If neither right nor left are pressed and the state isn't stand
this.state = 0;
if (this.direction) this.ssY = 200;
else this.ssY = 160;
}
}
this.updateSprite = function() {
if (this.state == 0) { //If the state is stand
if (this.ssRepeat < 15) //We display the same sprite 15 times before passing to the next one
this.ssRepeat++;
else {
this.ssRepeat = 0;
if (this.ssX < 205) this.ssX += this.width; //If we didn't reach the end of the sprite sheet
else this.ssX = 0;
}
} else if (this.state == 1) { //If the state is run
if (this.ssRepeat < 5) //We display the same sprite 5 times before passing to the next one
this.ssRepeat++;
else {
this.ssRepeat = 0;
if (this.ssX < 205) this.ssX += this.width; //If we didn't reach the end of the sprite sheet
else {
this.ssX = 0;
if (this.ssY < 40) this.ssY = 40; //If we reached the end of the first line of the SS
else if (this.ssY < 80) this.ssY = 0; //the end of the second
else if (this.ssY < 120) this.ssY = 120; //the third
else this.ssY = 80; //the fourth
}
}
}
}
//Display the proper sprite of the spritesheet
this.updateDisplay = function() {
// since speed and gravity are floats, our coords also are: we need to round them
var x = Math.round(this.x),
y = Math.round(this.y);
// simply to show these are floating values
if(this.x !== x || this.y !== y) {
ctx.fillRect(0,0,50,50);
}
ctx.drawImage(this.spritesheet, this.ssX, this.ssY,
this.width, this.height, x, y, this.width, this.height);
}
//Updates the position of the sprite according to the user's inputs
this.updatePos = function() {
this.jump();
this.gravitySpeed += this.gravity;
this.y += this.gravitySpeed;
this.hitBottom();
this.move();
}
this.hitBottom = function() {
var rockbottom = heightCanvas - this.height;
if (this.y > rockbottom) {
this.y = rockbottom;
this.gravitySpeed = 0;
this.jumping = false;
}
}
this.move = function() {
if (left) player.x -= this.speed;
if (right) player.x += this.speed;
}
this.jump = function() {
if (!this.jumping) {
if (up) {
this.gravitySpeed = -5.2;
this.jumping = true;
}
}
}
}
canvas {
border: 1px solid black;
background-color: #9e9eaf;
image-rendering: optimizespeed;
/*Firefox*/
image-rendering: pixelated;
/*Chrome*/
}
<canvas id="canvas" width="300" height="100"></canvas>
<button onclick="startStop()">Start/Stop</button>
关于javascript - 样式表在 <canvas> 上未正确裁剪,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49567219/
我有一台 MySQL 服务器和一台 PostgreSQL 服务器。 需要从多个表中复制或重新插入一组数据 MySQL 流式传输/同步到 PostgreSQL 表。 这种复制可以基于时间(Sync)或事
如果两个表的 id 彼此相等,我尝试从一个表中获取数据。这是我使用的代码: SELECT id_to , email_to , name_to , status_to
我有一个 Excel 工作表。顶行对应于列名称,而连续的行每行代表一个条目。 如何将此 Excel 工作表转换为 SQL 表? 我使用的是 SQL Server 2005。 最佳答案 这取决于您使用哪
我想合并两个 Django 模型并创建一个模型。让我们假设我有第一个表表 A,其中包含一些列和数据。 Table A -------------- col1 col2 col3 col
我有两个表:table1,table2,如下所示 table1: id name 1 tamil 2 english 3 maths 4 science table2: p
关闭。此题需要details or clarity 。目前不接受答案。 想要改进这个问题吗?通过 editing this post 添加详细信息并澄清问题. 已关闭 1 年前。 Improve th
下面两个语句有什么区别? newTable = orginalTable 或 newTable.data(originalTable) 我怀疑 .data() 方法具有性能优势,因为它在标准 AX 中
我有一个表,我没有在其中显式定义主键,它并不是真正需要的功能......但是一位同事建议我添加一个列作为唯一主键以随着数据库的增长提高性能...... 谁能解释一下这是如何提高性能的? 没有使用索引(
如何将表“产品”中的产品记录与其不同表“图像”中的图像相关联? 我正在对产品 ID 使用自动增量。 我觉得不可能进行关联,因为产品 ID 是自动递增的,因此在插入期间不可用! 如何插入新产品,获取产品
我有一个 sql 表,其中包含关键字和出现次数,如下所示(尽管出现次数并不重要): ____________ dog | 3 | ____________ rat | 7 | ____
是否可以使用目标表中的LAST_INSERT_ID更新源表? INSERT INTO `target` SELECT `a`, `b` FROM `source` 目标表有一个自动增量键id,我想将其
我正在重建一个搜索查询,因为它在“我看到的”中变得多余,我想知道什么 (albums_artists, artists) ( ) does in join? is it for boosting pe
以下是我使用 mysqldump 备份数据库的开关: /usr/bin/mysqldump -u **** --password=**** --single-transaction --databas
我试图获取 MySQL 表中的所有行并将它们放入 HTML 表中: Exam ID Status Assigned Examiner
如何查询名为 photos 的表中的所有记录,并知道当前用户使用单个查询将哪些结果照片添加为书签? 这是我的表格: -- -- Table structure for table `photos` -
我的网站都在 InnoDB 表上运行,目前为止运行良好。现在我想知道在我的网站上实时发生了什么,所以我将每个页面浏览量(页面、引荐来源网址、IP、主机名等)存储在 InnoDB 表中。每秒大约有 10
我在想我会为 mysql 准备两个表。一个用于存储登录信息,另一个用于存储送货地址。这是传统方式还是所有内容都存储在一张表中? 对于两个表...有没有办法自动将表 A 的列复制到表 B,以便我可以引用
我不是程序员,我从这个表格中阅读了很多关于如何解决我的问题的内容,但我的搜索效果不好 我有两张 table 表 1:成员 id*| name | surname -------------------
我知道如何在 ASP.NET 中显示真实表,例如 public ActionResult Index() { var s = db.StaffInfoDBSet.ToList(); r
我正在尝试运行以下查询: "insert into visits set source = 'http://google.com' and country = 'en' and ref = '1234
我是一名优秀的程序员,十分优秀!