”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 使用 JavaScript IV 创建俄罗斯方块:canvas

使用 JavaScript IV 创建俄罗斯方块:canvas

发布于2024-08-01
浏览:928

介绍

在该系列的新部分中,我们将了解如何显示棋盘和当前在屏幕上下降的棋子。为此,我们必须在浏览器中绘制它,我们必须执行的选项是 Canvas HTML 元素。

class Canvas {
    static SEPARATION = 2;
    #_painting = false;
    #_element = null;
    #_board = null;
    #_piece = null;

    constructor(element, board)
    {        
        element.width = 5   ( board.cols * Board.PIXEL_SIZE );
        element.height = 5   ( board.rows * Board.PIXEL_SIZE );

        this._board = board;
        this._element = element;
    }

    // más cosas...
}

这个类Canvas表示同名的HTML元素,在构造函数中作为参数传递。由于您要绘制棋盘,因此它也作为参数传递,以便访问要绘制的点。

它所做的第一件事是根据板本身通过其属性 colsrowsCanvas 的大小以适应板]。棋盘还通过 PIXEL_SIZE 告诉我们有多少像素组成了棋盘的每个棋子或每个单元的一个点。

重新绘制游戏

让我们少走弯路。我们必须画出木板和那一刻正在下降的棋子,对吗?好吧,让我们开始吧。


类画布 { // 更多的东西... 画() { if (this._painting) { 返回; } const ctx = this.element.getContext( "2d" ); const SEP = Canvas.SEPARATION; this._painting = true; this.clear(); this.paintBoard( ctx, SEP ); this.paintPiece( ctx, SEP ); this._painting = false; } 清除() { const ctx = this.element.getContext( "2d" ); ctx.clearRect( 0, 0, this.element.width, this.element.height ); } }
class Canvas {
    // más cosas...

    paint()
    {
        if ( this._painting ) {
            return;
        }

        const ctx = this.element.getContext( "2d" );
        const SEP = Canvas.SEPARATION;

        this._painting = true;
        this.clear();

        this.paintBoard( ctx, SEP );
        this.paintPiece( ctx, SEP );

        this._painting = false;
    }

    clear()
    {
        const ctx = this.element.getContext( "2d" );

        ctx.clearRect( 0, 0, this.element.width, this.element.height );
    }
}
首先,我们获取 2D 上下文,这将允许我们在画布上绘图。出于好奇,还有一个 3D 上下文,它基于 WebGL。

我们有一些守卫(

_painting),它们可以防止多个线程在给定时间同时(在不同点)执行该方法。如果该方法的执行时间长于重绘之间的时间,则可能会发生这种情况。虽然好吧,那样的话我们还会遇到很多其他问题......

下一步是删除先前重绘中屏幕上的内容(

)。我们使用 clear() 方法来执行此操作,该方法使用 clearRect() 删除画布上的图像。

然后我们画木板,然后画那一刻掉下来的那块。嗯,就这样了。麦芽酒,交付完成。

我说不。让我们看看棋盘和棋子是如何绘制的。首先是给木板上漆。 SEP 是我们在棋子和棋盘方块之间留下的间隔。这个框是我们在标题为

绘制框架的代码段中绘制的第一个东西。 它是一个简单的矩形,可以使用 StrokeRect() 绘制,它接受四个参数,其中包括左上角顶点的位置,然后是其宽度和高度。

画板

类画布 { // 更多的东西... 画板(ctx,SEP) { //画框 ctx.笔划宽度 = 1; ctx. StrokeStyle = this.board.color; ctx. 笔划矩形( 1, 1, this.element.width - 1, this.element.height -1 ); //画板 for(让 numRow = 0; numRow class Canvas { // más cosas... paint() { if ( this._painting ) { return; } const ctx = this.element.getContext( "2d" ); const SEP = Canvas.SEPARATION; this._painting = true; this.clear(); this.paintBoard( ctx, SEP ); this.paintPiece( ctx, SEP ); this._painting = false; } clear() { const ctx = this.element.getContext( "2d" ); ctx.clearRect( 0, 0, this.element.width, this.element.height ); } } 接下来是一个嵌套循环(行和列),因此我们将查看板上的哪些单元格有内容(整数 1,与整数 0),然后绘制一个边长为 PIXEL_SIZE 的小正方形。

因此,第一个循环循环遍历行,直到

Board.rows。然后我们使用 getRow() 方法获取完整的行,并用内循环遍历它,直到 Board.cols.

因此,给定行/列中的一个单元格

f/cBoard.getCell(f, c),以及考虑到 JavaScript 有一个 Boolean 构造函数,它接受除 0 之外的任何值的整数,表示 true,我们绘制一个边长为 PIXEL_SIZE 的正方形。因此,要知道在哪里绘制行 f,我们必须乘以 PIXEL_SIZE 并添加板框和第一个单元格之间的间距。由于它们是正方形,我们将以相同的方式找到列 c:SEP (c * PIXEL_SIZE).

画出这件作品

我们对这些作品做了类似的事情。通过拥有一个形状 (

shape)(它只不过是一个矩阵),我们将再次拥有两个循环,外部循环用于行,内部循环用于列。

类画布 { // 更多的东西... 油漆片(ctx,SEP) { const SHAPE = this.piece.shape; for(让 numRow = 0; numRow class Canvas { // más cosas... paint() { if ( this._painting ) { return; } const ctx = this.element.getContext( "2d" ); const SEP = Canvas.SEPARATION; this._painting = true; this.clear(); this.paintBoard( ctx, SEP ); this.paintPiece( ctx, SEP ); this._painting = false; } clear() { const ctx = this.element.getContext( "2d" ); ctx.clearRect( 0, 0, this.element.width, this.element.height ); } } 同样,如果我们找到 1,我们将绘制一个边长为 PIXEL_SIZE 的正方形。绘制构成该块的每个方块的位置由该块本身的行/列位置给出(

Piece.row/Piece. 卷心菜)。您必须将其乘以 PIXEL_SIZE 并添加与框的分隔。

El juego Insertrix en su estado actual

现在,我们将能够看到的非常......平淡。棋盘是空的,我们没有游戏循环,所以棋子甚至不会落下。我们将在下一部分中讨论该主题,以便我们可以开始看到与上图类似的内容。

版本声明 本文转载于:https://dev.to/baltasarq/creando-un-tetris-con-javascript-iv-canvas-1h7k?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • eval()vs. ast.literal_eval():对于用户输入,哪个Python函数更安全?
    eval()vs. ast.literal_eval():对于用户输入,哪个Python函数更安全?
    称量()和ast.literal_eval()中的Python Security 在使用用户输入时,必须优先确保安全性。强大的python功能eval()通常是作为潜在解决方案而出现的,但担心其潜在风险。 This article delves into the differences betwee...
    编程 发布于2025-05-14
  • Java中Lambda表达式为何需要“final”或“有效final”变量?
    Java中Lambda表达式为何需要“final”或“有效final”变量?
    Lambda Expressions Require "Final" or "Effectively Final" VariablesThe error message "Variable used in lambda expression shou...
    编程 发布于2025-05-14
  • 使用jQuery如何有效修改":after"伪元素的CSS属性?
    使用jQuery如何有效修改":after"伪元素的CSS属性?
    在jquery中了解伪元素的限制:访问“ selector 尝试修改“:”选择器的CSS属性时,您可能会遇到困难。 This is because pseudo-elements are not part of the DOM (Document Object Model) and are th...
    编程 发布于2025-05-14
  • 为什么不使用CSS`content'属性显示图像?
    为什么不使用CSS`content'属性显示图像?
    在Firefox extemers属性为某些图像很大,&& && && &&华倍华倍[华氏华倍华氏度]很少见,却是某些浏览属性很少,尤其是特定于Firefox的某些浏览器未能在使用内容属性引用时未能显示图像的情况。这可以在提供的CSS类中看到:。googlepic { 内容:url(&#...
    编程 发布于2025-05-14
  • 如何同步迭代并从PHP中的两个等级阵列打印值?
    如何同步迭代并从PHP中的两个等级阵列打印值?
    同步的迭代和打印值来自相同大小的两个数组使用两个数组相等大小的selectbox时,一个包含country代码的数组,另一个包含乡村代码,另一个包含其相应名称的数组,可能会因不当提供了exply for for for the uncore for the forsion for for ytry...
    编程 发布于2025-05-14
  • C++成员函数指针正确传递方法
    C++成员函数指针正确传递方法
    如何将成员函数置于c [&& && && && && && && && && && &&&&&&&&&&&&&&&&&&&&&&&华仪的函数时,在接受成员函数指针的函数时,要在函数上既要提供指针又可以提供指针和指针到函数的函数。需要具有一定签名的功能指针。要通过成员函数,您需要同时提供对象指针(此...
    编程 发布于2025-05-14
  • 在GO中构造SQL查询时,如何安全地加入文本和值?
    在GO中构造SQL查询时,如何安全地加入文本和值?
    在go中构造文本sql查询时,在go sql queries 中,在使用conting and contement和contement consem per时,尤其是在使用integer per当per当per时,per per per当per. [&​​&&&&&&&&&&&&&&&默元组方法在...
    编程 发布于2025-05-14
  • 在PHP中如何高效检测空数组?
    在PHP中如何高效检测空数组?
    在PHP 中检查一个空数组可以通过各种方法在PHP中确定一个空数组。如果需要验证任何数组元素的存在,则PHP的松散键入允许对数组本身进行直接评估:一种更严格的方法涉及使用count()函数: if(count(count($ playerList)=== 0){ //列表为空。 } 对...
    编程 发布于2025-05-14
  • 如何使用Java.net.urlConnection和Multipart/form-data编码使用其他参数上传文件?
    如何使用Java.net.urlConnection和Multipart/form-data编码使用其他参数上传文件?
    使用http request 上传文件上传到http server,同时也提交其他参数,java.net.net.urlconnection and Multipart/form-data Encoding是普遍的。 Here's a breakdown of the process:Mu...
    编程 发布于2025-05-14
  • Java的Map.Entry和SimpleEntry如何简化键值对管理?
    Java的Map.Entry和SimpleEntry如何简化键值对管理?
    A Comprehensive Collection for Value Pairs: Introducing Java's Map.Entry and SimpleEntryIn Java, when defining a collection where each element com...
    编程 发布于2025-05-14
  • 如何使用“ JSON”软件包解析JSON阵列?
    如何使用“ JSON”软件包解析JSON阵列?
    parsing JSON与JSON软件包 QUALDALS:考虑以下go代码:字符串 } func main(){ datajson:=`[“ 1”,“ 2”,“ 3”]`` arr:= jsontype {} 摘要:= = json.unmarshal([] byte(...
    编程 发布于2025-05-14
  • MySQL中如何高效地根据两个条件INSERT或UPDATE行?
    MySQL中如何高效地根据两个条件INSERT或UPDATE行?
    在两个条件下插入或更新或更新 solution:的答案在于mysql的插入中...在重复键更新语法上。如果不存在匹配行或更新现有行,则此功能强大的功能可以通过插入新行来进行有效的数据操作。如果违反了唯一的密钥约束。实现所需的行为,该表必须具有唯一的键定义(在这种情况下为'名称'...
    编程 发布于2025-05-14
  • 如何使用组在MySQL中旋转数据?
    如何使用组在MySQL中旋转数据?
    在关系数据库中使用mySQL组使用mySQL组进行查询结果,在关系数据库中使用MySQL组,转移数据的数据是指重新排列的行和列的重排以增强数据可视化。在这里,我们面对一个共同的挑战:使用组的组将数据从基于行的基于列的转换为基于列。 Let's consider the following ...
    编程 发布于2025-05-14
  • JavaScript计算两个日期之间天数的方法
    JavaScript计算两个日期之间天数的方法
    How to Calculate the Difference Between Dates in JavascriptAs you attempt to determine the difference between two dates in Javascript, consider this s...
    编程 发布于2025-05-14
  • 如何使用FormData()处理多个文件上传?
    如何使用FormData()处理多个文件上传?
    )处理多个文件输入时,通常需要处理多个文件上传时,通常是必要的。 The fd.append("fileToUpload[]", files[x]); method can be used for this purpose, allowing you to send multi...
    编程 发布于2025-05-14

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3