JavaScript俄罗斯方块Τетрис

效果

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>俄罗斯方块 Τетрис</title>
    <style>
        * {
     
     
            margin: 0;
            padding: 0;
        }

        div {
     
     
            text-align: center;
            vertical-align: top;
        }

        .score-board {
     
     
            display: inline-block;
            padding-bottom: 75vh;
        }

        .peek {
     
     
            width: 15vh;
            height: 15vh;
        }

        .game {
     
     
            width: 48vh;
            height: 90vh;
        }
    </style>
</head>

<body>
    <!-- 三条背景 -->
    <div style="position: fixed;width:100%;">
        <div style="width:100%;height:34vh;background-color:white;">
        </div>
        <div style="width:100%;height:33vh;background-color:blue;font-size:20vh;text-align:right;color:white;">
            Τетрис
        </div>
        <div style="width:100%;height:33vh;background-color:red;">
        </div>
    </div>
    <!-- 圣巴索教堂图形 -->
    <span style="display:block;position:fixed;width:100%;">
        <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="948.78" height="1000"
            viewBox="0 0 948.78 1000">
            <defs>
                <clipPath id="a" transform="translate(0 0.3)">
                    <path
                        d="M702.74,552.47c-4,66.62-154,66.62-158,0,3.65-47,76.56-38.77,79-150.75C626.31,514,699,505.27,702.74,552.47Z"
                        fill="none" />
                </clipPath>
                <clipPath id="b" transform="translate(0 0.3)">
                    <path
                        d="M279.55,529.28c-4.28,71.19-164.54,71.19-168.83,0,3.9-50.19,81.81-41.43,84.42-161.1C197.88,488.15,275.51,478.85,279.55,529.28Z"
                        fill="none" />
                </clipPath>
                <clipPath id="c" transform="translate(0 0.3)">
                    <path
                        d="M588.91,521.5c-4.37,72.74-168.11,72.74-172.49,0,4-51.27,83.59-42.33,86.25-164.59C505.47,479.49,584.78,470,588.91,521.5Z"
                        fill="none" />
                </clipPath>
                <clipPath id="d" transform="translate(0 0.3)">
                    <path
                        d="M660.22,656.8c-3,50.13-115.87,50.14-118.88,0,2.74-35.34,57.61-29.18,59.44-113.44C602.71,627.84,657.38,621.29,660.22,656.8Z"
                        fill="none" />
                </clipPath>
            </defs>
            <g>
                <polygon points="464.72 489.54 290.07 489.54 349.28 238.71 405.51 238.71 464.72 489.54"
                    fill="#a32e28" />
                <rect x="366.44" y="7.99" width="21.91" height="5.93" fill="#0b564b" />
                <rect x="374.43" width="5.93" height="49.79" fill="#0b564b" />
                <circle cx="377.39" cy="49.79" r="14.34" fill="#fbb03b" />
                <path
                    d="M424.2,138.82c-2.37,39.48-91.24,39.48-93.61,0,2.16-27.83,45.36-23,46.8-89.33C378.92,116,422,110.85,424.2,138.82Z"
                    transform="translate(0 0.3)" fill="#fbb03b" />
                <rect x="348.71" y="162.8" width="57.36" height="5.93" fill="#fff" />
                <rect x="348.71" y="168.73" width="57.36" height="57.36" fill="#a32e28" />
                <rect x="348.71" y="226.08" width="57.36" height="12.62" fill="#16ac96" />
                <polygon points="488.47 633.99 266.32 633.99 290.07 534.79 464.72 534.79 488.47 633.99"
                    fill="#a32e28" />
                <rect x="290.07" y="476.92" width="174.65" height="12.62" fill="#16ac96" />
                <g>
                    <path d="M406.5,563.6H348.29a29.11,29.11,0,1,1,58.21,0Z" transform="translate(0 0.3)" fill="#fff"
                        stroke="#a32e28" stroke-miterlimit="10" stroke-width="3.87" />
                    <path d="M348.29,563.6H290.07a29.11,29.11,0,0,1,58.22,0Z" transform="translate(0 0.3)" fill="#fff"
                        stroke="#a32e28" stroke-miterlimit="10" stroke-width="3.87" />
                    <path d="M464.72,563.6H406.5a29.11,29.11,0,0,1,58.22,0Z" transform="translate(0 0.3)" fill="#fff"
                        stroke="#a32e28" stroke-miterlimit="10" stroke-width="3.87" />
                </g>
                <g>
                    <path d="M410.46,596.67H344.33a33.07,33.07,0,1,1,66.13,0Z" transform="translate(0 0.3)" fill="#fff"
                        stroke="#a32e28" stroke-miterlimit="10" stroke-width="3.87" />
                    <path d="M344.33,596.67H278.19a33.07,33.07,0,0,1,66.14,0Z" transform="translate(0 0.3)" fill="#fff"
                        stroke="#a32e28" stroke-miterlimit="10" stroke-width="3.87" />
                    <path d="M476.6,596.67H410.46a33.07,33.07,0,0,1,66.14,0Z" transform="translate(0 0.3)" fill="#fff"
                        stroke="#a32e28" stroke-miterlimit="10" stroke-width="3.87" />
                </g>
                <g>
                    <path d="M414.42,633.69H340.37a37,37,0,0,1,74.05,0Z" transform="translate(0 0.3)" fill="#fff"
                        stroke="#a32e28" stroke-miterlimit="10" stroke-width="3.87" />
                    <path d="M340.37,633.69H266.32a37,37,0,0,1,74.05,0Z" transform="translate(0 0.3)" fill="#fff"
                        stroke="#a32e28" stroke-miterlimit="10" stroke-width="3.87" />
                    <path d="M488.47,633.69H414.42a37,37,0,0,1,74.05,0Z" transform="translate(0 0.3)" fill="#fff"
                        stroke="#a32e28" stroke-miterlimit="10" stroke-width="3.87" />
                </g>
                <rect x="266.32" y="633.99" width="222.16" height="253.75" fill="#a32e28" stroke="#d93d35"
                    stroke-miterlimit="10" stroke-width="4.12" />
                <rect x="290.07" y="489.54" width="174.65" height="45.64" fill="#a32e28" />
            </g>
            <g>
                <polygon
                    points="723.42 888.4 524.07 888.4 524.18 888.35 544.75 867.55 702.74 867.55 723.31 888.34 723.31 888.35 723.32 888.35 723.42 888.4"
                    fill="#16ac96" />
                <g clip-path="url(#a)">
                    <g>
                        <rect x="539.11" y="429.73" width="192.13" height="178.24"
                            transform="translate(-131.75 809.79) rotate(-60)" fill="#009245" />
                        <g>
                            <rect x="675.95" y="450.27" width="13.63" height="192.13"
                                transform="translate(364.64 -267.89) rotate(30)" fill="#fff" />
                            <rect x="699.63" y="463.94" width="13.63" height="192.13"
                                transform="translate(374.65 -277.9) rotate(30)" fill="#fff" />
                            <rect x="557.08" y="381.63" width="13.63" height="192.13"
                                transform="translate(314.4 -217.65) rotate(30)" fill="#fff" />
                            <rect x="628.1" y="422.64" width="13.63" height="192.13"
                                transform="translate(344.41 -247.67) rotate(30)" fill="#fff" />
                            <rect x="580.75" y="395.3" width="13.63" height="192.13"
                                transform="translate(324.4 -227.66) rotate(30)" fill="#fff" />
                            <rect x="651.71" y="436.27" width="13.63" height="192.13"
                                transform="translate(354.39 -257.65) rotate(30)" fill="#fff" />
                            <rect x="604.43" y="408.97" width="13.63" height="192.13"
                                transform="translate(334.41 -237.66) rotate(30)" fill="#fff" />
                        </g>
                    </g>
                </g>
                <rect x="575.35" y="592.73" width="96.8" height="10" fill="#fff" />
                <rect x="575.35" y="602.73" width="96.8" height="59.82" fill="#d93d35" />
                <rect x="612.32" y="358.28" width="22.85" height="6.18" fill="#0b564b" />
                <rect x="544.75" y="709.57" width="157.99" height="157.99" fill="#d93d35" stroke="#d93d35"
                    stroke-miterlimit="10" stroke-width="5.13" />
                <rect x="620.66" y="349.95" width="6.18" height="51.92" fill="#0b564b" />
                <circle cx="623.75" cy="401.87" r="14.95" fill="#fbb03b" />
                <polygon points="702.74 709.57 544.75 709.57 575.35 662.55 672.14 662.55 702.74 709.57"
                    fill="#16ac96" />
                <g>
                    <g>
                        <path d="M582.56,711v-1.69a41.19,41.19,0,0,1,82.37,0V711Z" transform="translate(0 0.3)"
                            fill="#d93d35" />
                        <path
                            d="M623.75,669.78a39.49,39.49,0,0,1,39.49,39.49h-79a39.49,39.49,0,0,1,39.5-39.49m0-3.38a42.93,42.93,0,0,0-42.88,42.87v3.38h85.75v-3.38a42.92,42.92,0,0,0-42.87-42.87Z"
                            transform="translate(0 0.3)" fill="#16ac96" />
                    </g>
                    <g>
                        <path d="M543.06,711V668.09h1.69a41.23,41.23,0,0,1,41.19,41.18V711Z"
                            transform="translate(0 0.3)" fill="#d93d35" />
                        <path
                            d="M544.75,669.78a39.49,39.49,0,0,1,39.5,39.49h-39.5V669.78m0-3.38h-3.37v46.25h46.25v-3.38a42.93,42.93,0,0,0-42.88-42.87Z"
                            transform="translate(0 0.3)" fill="#16ac96" />
                    </g>
                    <g>
                        <path d="M661.55,711v-1.69a41.23,41.23,0,0,1,41.19-41.18h1.69L704.34,711Z"
                            transform="translate(0 0.3)" fill="#d93d35" />
                        <path
                            d="M702.74,669.78l-.08,39.49H663.24a39.49,39.49,0,0,1,39.5-39.49m3.38-3.38h-3.38a42.92,42.92,0,0,0-42.87,42.87v3.38H706v-3.37l.09-39.5V666.4Z"
                            transform="translate(0 0.3)" fill="#16ac96" />
                    </g>
                </g>
            </g>
            <g>
                <polygon
                    points="301.66 888.26 88.62 888.26 88.74 888.19 110.72 865.98 279.55 865.98 301.54 888.19 301.54 888.19 301.55 888.2 301.66 888.26"
                    fill="#16ac96" />
                <g clip-path="url(#b)">
                    <g>
                        <rect x="93.81" y="403.34" width="178.24" height="192.13"
                            transform="translate(-225.2 158.67) rotate(-30)" fill="#c1272d" />
                        <g>
                            <rect x="223.72" y="375.86" width="13.63" height="192.13"
                                transform="translate(-205.08 178.79) rotate(-30)" fill="gray" />
                            <rect x="247.39" y="362.19" width="13.63" height="192.13"
                                transform="translate(-195.07 188.79) rotate(-30)" fill="gray" />
                            <rect x="104.84" y="444.49" width="13.63" height="192.13"
                                transform="translate(-255.32 128.55) rotate(-30)" fill="gray" />
                            <rect x="175.86" y="403.49" width="13.63" height="192.13"
                                transform="translate(-225.3 158.56) rotate(-30)" fill="gray" />
                            <rect x="128.51" y="430.83" width="13.63" height="192.13"
                                transform="translate(-245.31 138.55) rotate(-30)" fill="gray" />
                            <rect x="199.47" y="389.86" width="13.63" height="192.13"
                                transform="translate(-215.32 168.54) rotate(-30)" fill="gray" />
                            <rect x="152.19" y="417.16" width="13.63" height="192.13"
                                transform="translate(-235.31 148.56) rotate(-30)" fill="gray" />
                        </g>
                    </g>
                </g>
                <rect x="143.42" y="572.28" width="103.44" height="10.69" fill="#fff" />
                <rect x="143.42" y="582.97" width="103.44" height="63.93" fill="#d93d35" />
                <rect x="182.93" y="321.75" width="24.41" height="6.61" fill="#0b564b" />
                <rect x="110.72" y="697.15" width="168.83" height="168.83" fill="#d93d35" stroke="#d93d35"
                    stroke-miterlimit="10" stroke-width="5.48" />
                <rect x="191.84" y="312.84" width="6.61" height="55.49" fill="#0b564b" />
                <circle cx="195.14" cy="368.33" r="15.98" fill="#fbb03b" />
                <polygon points="279.55 697.14 110.72 697.14 143.42 646.9 246.86 646.9 279.55 697.14" fill="#16ac96" />
                <g>
                    <g>
                        <path d="M151.13,698.65v-1.8a44,44,0,0,1,88,0v1.8Z" transform="translate(0 0.3)"
                            fill="#d93d35" />
                        <path
                            d="M195.14,654.64a42.21,42.21,0,0,1,42.21,42.21H152.93a42.21,42.21,0,0,1,42.21-42.21m0-3.61a45.88,45.88,0,0,0-45.82,45.82v3.61H241v-3.61A45.87,45.87,0,0,0,195.14,651Z"
                            transform="translate(0 0.3)" fill="#16ac96" />
                    </g>
                    <g>
                        <path d="M108.92,698.65V652.84h1.8a44.06,44.06,0,0,1,44,44v1.8Z" transform="translate(0 0.3)"
                            fill="#d93d35" />
                        <path
                            d="M110.72,654.64a42.21,42.21,0,0,1,42.21,42.21H110.72V654.64m0-3.61h-3.61v49.43h49.43v-3.61A45.87,45.87,0,0,0,110.72,651Z"
                            transform="translate(0 0.3)" fill="#16ac96" />
                    </g>
                    <g>
                        <path d="M235.54,698.65v-1.8a44.06,44.06,0,0,1,44-44h1.81l-.09,45.81Z"
                            transform="translate(0 0.3)" fill="#d93d35" />
                        <path
                            d="M279.55,654.64l-.08,42.21H237.35a42.2,42.2,0,0,1,42.2-42.21m3.62-3.61h-3.62a45.87,45.87,0,0,0-45.81,45.82v3.61h49.33v-3.6l.08-42.21V651Z"
                            transform="translate(0 0.3)" fill="#16ac96" />
                    </g>
                </g>
            </g>
            <g>
                <polygon
                    points="611.5 888.26 393.84 888.26 393.96 888.19 416.42 865.49 588.91 865.49 611.38 888.19 611.38 888.19 611.39 888.2 611.5 888.26"
                    fill="#16ac96" />
                <g clip-path="url(#c)">
                    <g>
                        <polygon
                            points="502.67 373.38 502.67 373.38 416.42 373.38 416.42 576.35 502.67 576.35 502.67 576.35 588.91 576.35 588.91 373.38 502.67 373.38"
                            fill="#fff" />
                        <g>
                            <polygon
                                points="567.35 525.88 567.35 525.81 556.57 515.24 545.79 525.81 545.79 525.81 535.01 515.25 524.23 525.81 524.23 525.81 513.45 515.25 502.67 525.81 502.67 525.88 491.89 515.31 481.11 525.88 481.11 525.81 470.32 515.24 459.54 525.81 459.54 525.81 448.76 515.25 437.98 525.81 437.98 525.81 427.2 515.25 416.42 525.81 416.42 535.91 427.2 525.34 437.98 535.91 437.98 535.91 448.76 525.34 459.54 535.91 459.54 535.9 470.32 525.34 481.11 535.9 481.11 535.97 491.89 525.41 502.67 535.97 502.67 535.91 513.45 525.34 524.23 535.91 524.23 535.91 535.01 525.34 545.79 535.91 545.79 535.9 556.57 525.34 567.35 535.9 567.35 535.97 578.13 525.41 588.91 535.97 588.91 525.88 578.13 515.31 567.35 525.88"
                                fill="#ed1c24" />
                            <polygon
                                points="567.35 505.69 567.35 505.62 556.57 495.06 545.79 505.62 545.79 505.63 535.01 495.06 524.23 505.63 524.23 505.63 513.45 495.06 502.67 505.63 502.67 505.69 491.89 495.13 481.11 505.69 481.11 505.62 470.32 495.06 459.54 505.62 459.54 505.63 448.76 495.06 437.98 505.63 437.98 505.63 427.2 495.06 416.42 505.63 416.42 515.72 427.2 505.16 437.98 515.72 437.98 515.72 448.76 505.16 459.54 515.72 459.54 515.71 470.32 505.15 481.11 515.71 481.11 515.78 491.89 505.22 502.67 515.78 502.67 515.72 513.45 505.16 524.23 515.72 524.23 515.72 535.01 505.16 545.79 515.72 545.79 515.71 556.57 505.15 567.35 515.71 567.35 515.78 578.13 505.22 588.91 515.78 588.91 505.69 578.13 495.13 567.35 505.69"
                                fill="#ed1c24" />
                            <polygon
                                points="567.35 546.07 567.35 546 556.57 535.43 545.79 546 545.79 546 535.01 535.44 524.23 546 524.23 546 513.45 535.44 502.67 546 502.67 546.07 491.89 535.5 481.11 546.07 481.11 546 470.32 535.43 459.54 546 459.54 546 448.76 535.44 437.98 546 437.98 546 427.2 535.44 416.42 546 416.42 556.1 427.2 545.53 437.98 556.1 437.98 556.1 448.76 545.53 459.54 556.1 459.54 556.09 470.32 545.53 481.11 556.09 481.11 556.16 491.89 545.6 502.67 556.16 502.67 556.1 513.45 545.53 524.23 556.1 524.23 556.1 535.01 545.53 545.79 556.1 545.79 556.09 556.57 545.53 567.35 556.09 567.35 556.16 578.13 545.6 588.91 556.16 588.91 546.07 578.13 535.5 567.35 546.07"
                                fill="#ed1c24" />
                            <polygon
                                points="578.13 474.94 567.35 485.5 567.35 485.43 556.57 474.87 545.79 485.43 545.79 485.44 535.01 474.87 524.23 485.44 524.23 485.44 513.45 474.87 502.67 485.44 502.67 485.5 491.89 474.94 481.11 485.5 481.11 485.43 470.32 474.87 459.54 485.43 459.54 485.44 448.76 474.87 437.98 485.44 437.98 485.44 427.2 474.87 416.42 485.44 416.42 495.54 427.2 484.97 437.98 495.54 437.98 495.53 448.76 484.97 459.54 495.53 459.54 495.53 470.32 484.96 481.11 495.53 481.11 495.6 491.89 485.03 502.67 495.6 502.67 495.53 513.45 484.97 524.23 495.54 524.23 495.53 535.01 484.97 545.79 495.53 545.79 495.53 556.57 484.96 567.35 495.53 567.35 495.6 578.13 485.03 588.91 495.6 588.91 485.5 578.13 474.94"
                                fill="#ed1c24" />
                            <polygon
                                points="567.35 566.26 567.35 566.19 556.57 555.62 545.79 566.19 545.79 566.19 535.01 555.63 524.23 566.19 524.23 566.19 513.45 555.63 502.67 566.19 502.67 566.26 491.89 555.69 481.11 566.26 481.11 566.19 470.32 555.62 459.54 566.19 459.54 566.19 448.76 555.63 437.98 566.19 437.98 566.19 427.2 555.63 416.42 566.19 416.42 576.29 427.2 565.72 437.98 576.29 437.98 576.29 448.76 565.72 459.54 576.29 459.54 576.28 470.32 565.72 481.11 576.28 481.11 576.35 491.89 565.79 502.67 576.35 502.67 576.29 513.45 565.72 524.23 576.29 524.23 576.29 535.01 565.72 545.79 576.29 545.79 576.28 556.57 565.72 567.35 576.28 567.35 576.35 578.13 565.79 588.91 576.35 588.91 566.26 578.13 555.69 567.35 566.26"
                                fill="#ed1c24" />
                            <polygon
                                points="437.98 434.42 437.98 434.42 448.76 423.86 459.54 434.42 459.54 434.42 470.32 423.85 481.11 434.42 481.11 434.49 491.89 423.92 502.67 434.49 502.67 434.42 513.45 423.86 524.23 434.42 524.23 434.42 535.01 423.86 545.79 434.42 545.79 434.42 556.57 423.85 567.35 434.42 567.35 434.49 578.13 423.92 588.91 434.49 588.91 424.39 578.13 413.83 567.35 424.39 567.35 424.32 556.57 413.76 545.79 424.32 545.79 424.33 535.01 413.76 524.23 424.33 524.23 424.33 513.45 413.76 502.67 424.33 502.67 424.39 491.89 413.83 481.11 424.39 481.11 424.32 470.32 413.76 459.54 424.32 459.54 424.33 448.76 413.76 437.98 424.33 437.98 424.33 427.2 413.76 416.42 424.33 416.42 434.42 427.2 423.86 437.98 434.42"
                                fill="#ed1c24" />
                            <polygon
                                points="437.98 414.23 437.98 414.23 448.76 403.67 459.54 414.23 459.54 414.23 470.32 403.67 481.11 414.23 481.11 414.3 491.89 403.74 502.67 414.3 502.67 414.23 513.45 403.67 524.23 414.23 524.23 414.23 535.01 403.67 545.79 414.23 545.79 414.23 556.57 403.67 567.35 414.23 567.35 414.3 578.13 403.74 588.91 414.3 588.91 404.21 578.13 393.64 567.35 404.21 567.35 404.14 556.57 393.57 545.79 404.14 545.79 404.14 535.01 393.58 524.23 404.14 524.23 404.14 513.45 393.58 502.67 404.14 502.67 404.21 491.89 393.64 481.11 404.21 481.11 404.14 470.32 393.57 459.54 404.14 459.54 404.14 448.76 393.58 437.98 404.14 437.98 404.14 427.2 393.58 416.42 404.14 416.42 414.23 427.2 403.67 437.98 414.23"
                                fill="#ed1c24" />
                            <polygon
                                points="437.98 454.61 437.98 454.61 448.76 444.05 459.54 454.61 459.54 454.61 470.32 444.04 481.11 454.61 481.11 454.68 491.89 444.11 502.67 454.68 502.67 454.61 513.45 444.05 524.23 454.61 524.23 454.61 535.01 444.05 545.79 454.61 545.79 454.61 556.57 444.04 567.35 454.61 567.35 454.68 578.13 444.11 588.91 454.68 588.91 444.58 578.13 434.02 567.35 444.58 567.35 444.51 556.57 433.95 545.79 444.51 545.79 444.52 535.01 433.95 524.23 444.52 524.23 444.52 513.45 433.95 502.67 444.52 502.67 444.58 491.89 434.02 481.11 444.58 481.11 444.51 470.32 433.95 459.54 444.51 459.54 444.52 448.76 433.95 437.98 444.52 437.98 444.52 427.2 433.95 416.42 444.52 416.42 454.61 427.2 444.05 437.98 454.61"
                                fill="#ed1c24" />
                            <polygon
                                points="437.98 394.05 437.98 394.05 448.76 383.48 459.54 394.05 459.54 394.04 470.32 383.48 481.11 394.04 481.11 394.11 491.89 383.55 502.67 394.11 502.67 394.05 513.45 383.48 524.23 394.05 524.23 394.05 535.01 383.48 545.79 394.05 545.79 394.04 556.57 383.48 567.35 394.04 567.35 394.11 578.13 383.55 588.91 394.11 588.91 384.02 578.13 373.45 567.35 384.02 567.35 383.95 556.57 373.38 545.79 383.95 545.79 383.95 535.01 373.39 524.23 383.95 524.23 383.95 513.45 373.39 502.67 383.95 502.67 384.02 491.89 373.45 481.11 384.02 481.11 383.95 470.32 373.38 459.54 383.95 459.54 383.95 448.76 373.39 437.98 383.95 437.98 383.95 427.2 373.39 416.42 383.95 416.42 394.05 427.2 383.48 437.98 394.05"
                                fill="#ed1c24" />
                            <polygon
                                points="437.98 474.8 437.98 474.8 448.76 464.24 459.54 474.8 459.54 474.8 470.32 464.23 481.11 474.8 481.11 474.87 491.89 464.3 502.67 474.87 502.67 474.8 513.45 464.24 524.23 474.8 524.23 474.8 535.01 464.24 545.79 474.8 545.79 474.8 556.57 464.23 567.35 474.8 567.35 474.87 578.13 464.3 588.91 474.87 588.91 464.77 578.13 454.21 567.35 464.77 567.35 464.7 556.57 454.14 545.79 464.7 545.79 464.71 535.01 454.14 524.23 464.71 524.23 464.71 513.45 454.14 502.67 464.71 502.67 464.77 491.89 454.21 481.11 464.77 481.11 464.7 470.32 454.14 459.54 464.7 459.54 464.71 448.76 454.14 437.98 464.71 437.98 464.71 427.2 454.14 416.42 464.71 416.42 474.8 427.2 464.24 437.98 474.8"
                                fill="#ed1c24" />
                        </g>
                    </g>
                </g>
                <rect x="449.83" y="565.43" width="105.68" height="10.92" fill="#fff" />
                <rect x="449.83" y="576.35" width="105.68" height="65.31" fill="#d93d35" />
                <rect x="490.19" y="309.47" width="24.94" height="6.75" fill="#0b564b" />
                <rect x="416.42" y="693" width="172.49" height="172.49" fill="#d93d35" stroke="#d93d35"
                    stroke-miterlimit="10" stroke-width="5.6" />
                <rect x="499.29" y="300.37" width="6.75" height="56.69" fill="#0b564b" />
                <circle cx="502.67" cy="357.06" r="16.33" fill="#fbb03b" />
                <polygon points="588.91 693 416.42 693 449.83 641.66 555.51 641.66 588.91 693" fill="#16ac96" />
                <g>
                    <g>
                        <path d="M457.7,694.55v-1.84a45,45,0,1,1,89.93,0v1.84Z" transform="translate(0 0.3)"
                            fill="#d93d35" />
                        <path
                            d="M502.67,649.58a43.13,43.13,0,0,1,43.12,43.13H459.54a43.13,43.13,0,0,1,43.13-43.13m0-3.68a46.86,46.86,0,0,0-46.81,46.81v3.68h93.62v-3.68a46.86,46.86,0,0,0-46.81-46.81Z"
                            transform="translate(0 0.3)" fill="#16ac96" />
                    </g>
                    <g>
                        <path d="M414.58,694.55V647.74h1.84a45,45,0,0,1,45,45v1.84Z" transform="translate(0 0.3)"
                            fill="#d93d35" />
                        <path
                            d="M416.42,649.58a43.12,43.12,0,0,1,43.12,43.13H416.42V649.58m0-3.68h-3.69v50.49h50.5v-3.68a46.86,46.86,0,0,0-46.81-46.81Z"
                            transform="translate(0 0.3)" fill="#16ac96" />
                    </g>
                    <g>
                        <path d="M544,694.55v-1.84a45,45,0,0,1,45-45h1.85l-.1,46.81Z" transform="translate(0 0.3)"
                            fill="#d93d35" />
                        <path
                            d="M588.91,649.58l-.09,43.13h-43a43.12,43.12,0,0,1,43.12-43.13m3.7-3.68h-3.7a46.86,46.86,0,0,0-46.81,46.81v3.68h50.4v-3.68l.09-43.12V645.9Z"
                            transform="translate(0 0.3)" fill="#16ac96" />
                    </g>
                </g>
            </g>
            <g>
                <polygon
                    points="674.09 888.26 524.07 888.26 524.15 888.21 539.63 872.57 658.52 872.57 674 888.21 674 888.21 674.01 888.21 674.09 888.26"
                    fill="#16ac96" />
                <polygon
                    points="675.79 888.26 525.77 888.26 525.85 888.21 541.34 872.57 660.22 872.57 675.71 888.21 675.71 888.21 675.71 888.21 675.79 888.26"
                    fill="#16ac96" />
                <rect x="541.33" y="799.09" width="118.89" height="73.47" fill="#ff483e" stroke="#ff483e"
                    stroke-miterlimit="10" stroke-width="3.03" />
                <rect x="564.36" y="739.71" width="72.84" height="59.38" fill="#16ac96" />
                <g clip-path="url(#d)">
                    <g>
                        <rect x="526.8" y="556.82" width="140.12" height="151.04"
                            transform="translate(-236.21 383.45) rotate(-30)" fill="#009245" />
                        <g>
                            <rect x="628.93" y="535.21" width="10.72" height="151.04"
                                transform="translate(-220.39 399.26) rotate(-30)" fill="#fff" />
                            <rect x="647.54" y="524.47" width="10.72" height="151.04"
                                transform="translate(-212.52 407.13) rotate(-30)" fill="#fff" />
                            <rect x="535.47" y="589.17" width="10.72" height="151.04"
                                transform="translate(-259.89 359.76) rotate(-30)" fill="#fff" />
                            <rect x="591.31" y="556.94" width="10.72" height="151.04"
                                transform="translate(-236.29 383.36) rotate(-30)" fill="#fff" />
                            <rect x="554.08" y="578.43" width="10.72" height="151.04"
                                transform="translate(-252.02 367.63) rotate(-30)" fill="#fff" />
                            <rect x="609.87" y="546.22" width="10.72" height="151.04"
                                transform="translate(-228.44 391.21) rotate(-30)" fill="#fff" />
                            <rect x="572.7" y="567.68" width="10.72" height="151.04"
                                transform="translate(-244.16 375.5) rotate(-30)" fill="#fff" />
                        </g>
                    </g>
                </g>
                <rect x="564.36" y="687.17" width="72.84" height="7.53" fill="#fff" />
                <rect x="564.36" y="694.7" width="72.84" height="45.02" fill="#ff483e" />
                <rect x="592.18" y="510.75" width="17.19" height="4.65" fill="#0b564b" />
                <rect x="598.45" y="504.47" width="4.65" height="39.07" fill="#0b564b" />
                <circle cx="600.78" cy="543.55" r="11.25" fill="#fbb03b" />
                <circle cx="600.78" cy="759.51" r="19.79" fill="#ff483e" stroke="#16ac96" stroke-miterlimit="10"
                    stroke-width="3.03" />
                <path d="M646.86,740.51v37.4a19.79,19.79,0,1,1,0-37.4Z" transform="translate(0 0.3)" fill="#ff483e"
                    stroke="#16ac96" stroke-miterlimit="10" stroke-width="3.03" />
                <circle cx="620.57" cy="779.3" r="19.79" fill="#ff483e" stroke="#16ac96" stroke-miterlimit="10"
                    stroke-width="3.03" />
                <path d="M581,759.21a19.81,19.81,0,0,1-26.29,18.7v-37.4A19.81,19.81,0,0,1,581,759.21Z"
                    transform="translate(0 0.3)" fill="#ff483e" stroke="#16ac96" stroke-miterlimit="10"
                    stroke-width="3.03" />
                <circle cx="580.98" cy="779.3" r="19.79" fill="#ff483e" stroke="#16ac96" stroke-miterlimit="10"
                    stroke-width="3.03" />
                <path d="M620.57,798.8H581a19.8,19.8,0,1,1,39.59,0Z" transform="translate(0 0.3)" fill="#ff483e"
                    stroke="#16ac96" stroke-miterlimit="10" stroke-width="3.03" />
                <path d="M541.37,798.8V759.21a19.8,19.8,0,0,1,0,39.59Z" transform="translate(0 0.3)" fill="#ff483e"
                    stroke="#16ac96" stroke-miterlimit="10" stroke-width="3.03" />
                <path d="M660.19,759.21V798.8a19.8,19.8,0,0,1,0-39.59Z" transform="translate(0 0.3)" fill="#ff483e"
                    stroke="#16ac96" stroke-miterlimit="10" stroke-width="3.03" />
                <path d="M581,798.8H541.4a19.79,19.79,0,1,1,39.58,0Z" transform="translate(0 0.3)" fill="#ff483e"
                    stroke="#16ac96" stroke-miterlimit="10" stroke-width="3.03" />
                <path d="M660.16,798.8H620.57a19.8,19.8,0,1,1,39.59,0Z" transform="translate(0 0.3)" fill="#ff483e"
                    stroke="#16ac96" stroke-miterlimit="10" stroke-width="3.03" />
                <g>
                    <g>
                        <polygon points="573.35 871.06 600.81 803.58 628.28 871.06 573.35 871.06" fill="#ff483e" />
                        <path d="M600.81,807.28l25.23,62H575.59l25.22-62m0-8-29.71,73h59.42l-29.71-73Z"
                            transform="translate(0 0.3)" fill="#fff" />
                    </g>
                    <g>
                        <polygon points="632.76 871.06 658.72 807.29 658.72 871.06 632.76 871.06" fill="#ff483e" />
                        <path d="M657.21,814.69v54.57H635l22.21-54.57m3-15.4-29.7,73h29.7v-73Z"
                            transform="translate(0 0.3)" fill="#fff" />
                    </g>
                    <g>
                        <polygon points="542.9 871.06 542.9 807.29 568.86 871.06 542.9 871.06" fill="#ff483e" />
                        <path d="M544.41,814.69l22.21,54.57H544.41V814.69m-3-15.4v73h29.7l-29.7-73Z"
                            transform="translate(0 0.3)" fill="#fff" />
                    </g>
                </g>
            </g>
            <g>
                <polygon
                    points="410.47 888.26 246.88 888.26 246.97 888.21 263.85 871.15 393.5 871.15 410.38 888.21 410.38 888.21 410.39 888.21 410.47 888.26"
                    fill="#16ac96" />
                <rect x="263.85" y="791.03" width="129.64" height="80.12" fill="#ff483e" stroke="#ff483e"
                    stroke-miterlimit="10" stroke-width="3.03" />
                <rect x="288.96" y="726.27" width="79.43" height="64.75" fill="#16ac96" />
                <path
                    d="M393.5,635.89c-3.29,54.67-126.36,54.67-129.65,0,3-38.53,62.82-31.81,64.82-123.7C330.78,604.31,390.39,597.16,393.5,635.89Z"
                    transform="translate(0 0.3)" fill="#fcee21" />
                <rect x="288.96" y="668.98" width="79.43" height="8.21" fill="#fff" />
                <rect x="288.96" y="677.19" width="79.43" height="49.09" fill="#ff483e" />
                <rect x="319.3" y="476.59" width="18.75" height="5.07" fill="#0b564b" />
                <rect x="326.14" y="469.75" width="5.07" height="42.61" fill="#0b564b" />
                <circle cx="328.67" cy="512.36" r="12.27" fill="#fbb03b" />
                <circle cx="328.67" cy="747.86" r="21.58" fill="#ff483e" stroke="#16ac96" stroke-miterlimit="10"
                    stroke-width="3.3" />
                <path d="M378.92,727.17V768a21.59,21.59,0,1,1,0-40.79Z" transform="translate(0 0.3)" fill="#ff483e"
                    stroke="#16ac96" stroke-miterlimit="10" stroke-width="3.3" />
                <circle cx="350.26" cy="769.44" r="21.58" fill="#ff483e" stroke="#16ac96" stroke-miterlimit="10"
                    stroke-width="3.3" />
                <path d="M307.09,747.56A21.6,21.6,0,0,1,278.42,768V727.17a21.6,21.6,0,0,1,28.67,20.39Z"
                    transform="translate(0 0.3)" fill="#ff483e" stroke="#16ac96" stroke-miterlimit="10"
                    stroke-width="3.3" />
                <circle cx="307.09" cy="769.44" r="21.58" fill="#ff483e" stroke="#16ac96" stroke-miterlimit="10"
                    stroke-width="3.3" />
                <path d="M350.26,790.73H307.09a21.59,21.59,0,0,1,43.17,0Z" transform="translate(0 0.3)" fill="#ff483e"
                    stroke="#16ac96" stroke-miterlimit="10" stroke-width="3.3" />
                <path d="M263.89,790.73V747.56a21.59,21.59,0,0,1,0,43.17Z" transform="translate(0 0.3)" fill="#ff483e"
                    stroke="#16ac96" stroke-miterlimit="10" stroke-width="3.3" />
                <path d="M393.46,747.56v43.17a21.59,21.59,0,0,1,0-43.17Z" transform="translate(0 0.3)" fill="#ff483e"
                    stroke="#16ac96" stroke-miterlimit="10" stroke-width="3.3" />
                <path d="M307.09,790.73H263.92a21.59,21.59,0,0,1,43.17,0Z" transform="translate(0 0.3)" fill="#ff483e"
                    stroke="#16ac96" stroke-miterlimit="10" stroke-width="3.3" />
                <path d="M393.43,790.73H350.26a21.59,21.59,0,0,1,43.17,0Z" transform="translate(0 0.3)" fill="#ff483e"
                    stroke="#16ac96" stroke-miterlimit="10" stroke-width="3.3" />
                <g>
                    <g>
                        <polygon points="298.71 869.5 328.67 795.42 358.64 869.5 298.71 869.5" fill="#ff483e" />
                        <path d="M328.67,799.53l27.52,68h-55l27.52-68m0-8.8-32.41,80.12h64.82l-32.41-80.12Z"
                            transform="translate(0 0.3)" fill="#fff" />
                    </g>
                    <g>
                        <polygon points="363.53 869.5 391.85 799.5 391.85 869.5 363.53 869.5" fill="#ff483e" />
                        <path d="M390.2,807.68v59.87H366l24.22-59.87m3.29-16.95-32.41,80.12h32.41V790.73Z"
                            transform="translate(0 0.3)" fill="#fff" />
                    </g>
                    <g>
                        <polygon points="265.5 869.5 265.5 799.5 293.81 869.5 265.5 869.5" fill="#ff483e" />
                        <path d="M267.15,807.68l24.22,59.87H267.15V807.68m-3.3-16.95v80.12h32.41l-32.41-80.12Z"
                            transform="translate(0 0.3)" fill="#fff" />
                    </g>
                </g>
            </g>
            <rect y="887.74" width="948.78" height="112.26" fill="#ff483e" />
            <g>
                <g>
                    <polygon points="817.63 790.29 863.22 580.2 884.33 580.2 929.92 790.29 817.63 790.29"
                        fill="#16ac96" />
                    <path
                        d="M883.12,581.41l44.94,207.08H819.49l44.93-207.08h18.7m2.42-3H862L815.77,791.49h116L885.54,578.41Z"
                        transform="translate(0 0.3)" fill="#fcee21" />
                </g>
                <g>
                    <polygon points="839.28 790.61 867.53 579.88 880.01 579.88 908.27 790.61 839.28 790.61"
                        fill="#16ac96" />
                    <path
                        d="M879,580.77l27.94,208.37h-66.3l27.94-208.37H879m2.07-2.36H866.5L837.93,791.49h71.69L881.05,578.41Z"
                        transform="translate(0 0.3)" fill="#fcee21" />
                </g>
                <rect x="866.5" y="471.94" width="14.55" height="3.94" fill="#0b564b" />
                <rect x="871.81" y="466.63" width="3.94" height="33.07" fill="#0b564b" />
                <path
                    d="M893,546c-1,20.61-37.44,20.61-38.41,0,.88-14.52,18.61-12,19.2-46.63C874.4,534.14,892.06,531.44,893,546Z"
                    transform="translate(0 0.3)" fill="#16ac96" />
                <circle cx="873.77" cy="499.7" r="9.52" fill="#fbb03b" />
                <rect x="862.01" y="558.7" width="23.54" height="20" fill="#fff" />
                <rect x="815.77" y="783.41" width="116" height="8.38" fill="#16ac96" />
                <rect x="815.77" y="791.79" width="116" height="95.94" fill="#a32e28" />
                <polygon
                    points="948.78 887.74 798.76 887.74 798.85 887.69 815.77 872.05 931.78 872.05 948.7 887.69 948.7 887.69 948.71 887.69 948.78 887.74"
                    fill="#16ac96" />
            </g>
        </svg>

    </span>
    <!-- 游戏区域 -->
    <div style="position:fixed;width:100%;margin-top:5vh;">
        <div>
            <div class="score-board peek">
                <canvas id="next" width="150" height="150" class="peek"></canvas>
            </div>
            <div class="score-board game">
                <canvas id="canvas" width="480" height="900" class="game"></canvas>
            </div>
            <div class="score-board peek">
                <div style="position:absolute;white-space:nowrap;text-align:left;color:#0008;">
                    <div style="margin-bottom:2vh;">
                        <span style="font-size:4vmin;">得分:</span>
                        <span id="score" style="font-size:7vmin;">0</span>
                    </div style="text-align:left;">
                    <div style="text-align:left;">
                        <span style="font-size:4vmin">级别:</span>
                        <span id="lvl" style="font-size:4vmin;">0</span>
                    </div>
                </div>
            </div>
        </div>
    </div>
</body>

</html>
<script type="text/javascript">
    var canvas = document.getElementById('canvas');
    var nextBox = document.getElementById('next');
    var scoreBox = document.getElementById('score');
    var lvlBox = document.getElementById('lvl');
    var ctx = canvas.getContext('2d');
    var ntx = nextBox.getContext('2d');
    /*==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====*/
    const LEFT = 0;//游戏区左界
    const TOP = 0;//游戏区上界
    const RIGHT = 16;//游戏区右界
    const BOTTOM = 30;//游戏区下界
    const maxDelay = 35;//方块下落一格所需最大帧数,游戏开始时方块下落一格所需帧数
    const minDelay = 7;//方块下落一格所需最小帧数,游戏得分很高时方块下落速度达到最大
    var lvl = 0;//游戏等级越高下落越快
    var exp = 0;//升级所需得分
    var blockWidth = 1;//方块绘制宽度
    var score = 0;//得分
    var living = false;//游戏是否结束
    var restartNow = false;//开局指示
    /*==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ==== ====*/
    var hold = null;//玩家控制方块
    var next = null;//下一个方块
    var fancy = 'north';//当前方块方向
    var moveX = 0;//横向移动值
    var fillNow = true;//控制速落
    var static = [];//已经放置的方块
    var floating = [];//玩家控制方块的块
    var delay = undefined;//当前方块下落一格所需帧数
    function createRand(a, b) {
     
     
        let ret = new Object();
        let M = Math.max(a, b);
        let m = Math.min(a, b);
        let randoms = [];
        ret.rand = function () {
     
     
            if (randoms.length <= 0) {
     
     //随机表空
                randoms = [];
                let arr = [];
                for (let i = m; i < M; i++) {
     
     //添加随机数
                    let count = parseInt(Math.random() * 5) + 10;
                    for (let i1 = 0; i1 < count; i1++)
                        arr.push(i);
                }
                let safe = 1024;
                do {
     
     //检查随机数是否足够“随机”
                    let count = 0;//当前计数
                    let maxCount = 0;//最大连续
                    let zCount = 0;//不连续总计
                    let mem = -1;//上一个随机值
                    let len = arr.length;
                    for (let i = 0; i < len; i++) {
     
     
                        if (mem == arr[i])//连续
                            count++;
                        else {
     
     //不连续
                            maxCount = count;//记录最大计数
                            if (count <= 1)//如果不连续
                                zCount++
                            count = 0;//重计数
                        }
                        mem = arr[i];//记录为上一个随机值
                    }
                    if (maxCount <= 3 && zCount >= (len * 0.75))//足够“随机”的条件
                        break;
                    arr.sort(function (n1, n2) {
     
      let r = Math.random(); return r > .5 ? 1 : (r < .5 ? -1 : 0); });//打乱
                } while (safe-- > 0);
                let len = arr.length;
                for (let i = 0; i < len; i++)
                    randoms.push(arr[i]);
            }
            return randoms.shift();
        }
        return ret;
    }
    function restart() {
     
     //开局
        static = [];
        floating = [];
        delay = maxDelay;
        score = 0;
        lvl = 0;
        exp = 1;
    }
    document.onkeydown = function (event) {
     
     //设置键盘事件
        var e = event || window.event || arguments.callee.caller.arguments[0];
        if (e) {
     
     
            switch (e.keyCode) {
     
     
                case 37:// 按← 
                case 65://a
                    moveX = -1;
                    break;
                case 38:// 按↑ 
                case 87://w
                    switch (fancy) {
     
     
                        case 'north':
                            fancy = 'east';
                            break;
                        case 'east':
                            fancy = 'south';
                            break;
                        case 'south':
                            fancy = 'west';
                            break;
                        case 'west':
                            fancy = 'north';
                            break;
                        default:
                            fancy = 'north';
                            break;
                    }
                    break;
                case 39:// 按→
                case 68://d
                    moveX = 1;
                    break;
                case 40:// 按↓
                case 83://s
                    fillNow = true;
                    break;
                default:
                    break;
            }
            if (!living)//游戏结束后按任意键重开
                restartNow = true;
        }
    };
    function createTetris(data) {
     
     //创建方块
        let ret = new Object();
        ret.x = data.x ? data.x : 0;
        ret.y = data.y ? data.y : 0;
        ret.color = data.color ? data.color : '#000';
        let north = data.north ? data.north : [];
        let south = data.south ? data.south : [];
        let east = data.east ? data.east : [];
        let west = data.west ? data.west : [];
        ret.export = function (fancy) {
     
     //获取方块各个正方形的坐标
            let arr = [];
            switch (fancy) {
     
     
                case 'north':
                    arr = north;
                    break;
                case 'east':
                    arr = east;
                    break;
                case 'south':
                    arr = south;
                    break;
                case 'west':
                    arr = west;
                    break;
                default:
                    arr = north;
                    break;
            }
            let len = arr.length;
            let ctrl = false;
            let res = undefined;
            do {
     
     //直到没有活动方块无横向超出屏幕的情况才跳出循环,这要求一个俄罗斯方块的宽度小于屏幕宽度
                res = [];
                ctrl = false;
                for (let i = 0; i < len; i++) {
     
     //将方块限制在游戏区内
                    const ele = arr[i];
                    let loc = new Object();
                    loc.x = ele.x + ret.x;
                    loc.y = ele.y + ret.y;
                    let x = loc.x - LEFT;
                    let y = loc.y - TOP;
                    if (x < LEFT) {
     
     
                        ret.x += -x;
                        ctrl = true;
                    } else if (x >= RIGHT) {
     
     
                        ret.x -= x - (RIGHT - 1);
                        ctrl = true;
                    }
                    if (y < TOP) {
     
     
                        ret.y += -y;
                        ctrl = true;
                    }
                    else if (y >= BOTTOM) {
     
     
                        ret.y += y - (BOTTOM - 1);
                        ctrl = true;
                    }
                    loc.color = ret.color;
                    res.push(loc);
                }
            } while (ctrl);
            return res;
        }
        ret.move = function (x, y) {
     
     //设置方块坐标
            ret.x += x;
            ret.y += y;
        }
        return ret;
    }
    var rand5 = createRand(0, 5);
    var rand11 = createRand(0, 11);
    function randomTetris() {
     
     //随机化方块形状和颜色
        let x = parseInt((RIGHT - LEFT) / 2);
        let color = 'black';
        switch (rand11.rand()) {
     
     
            case 0: color = '#0c8';
                break;
            case 1: color = '#08f';
                break;
            case 2: color = '#80f';
                break;
            case 3: color = '#4c0';
                break;
            case 4: color = '#f80';
                break;
            case 5: color = '#f08';
                break;
            case 6: color = '#f44';
                break;
            case 7: color = '#4c4';
                break;
            case 8: color = '#44f';
                break;
            case 9: color = '#4cc';
                break;
            default://随机颜色
                let total = 384;
                let vals = [];
                let r = parseInt(Math.random() * total);
                total -= r;
                vals.push(Math.min(240, r));
                let g = parseInt(Math.random() * total);
                total -= g;
                vals.push(Math.min(240, g));
                let b = total;
                vals.push(Math.min(255, b));
                vals.sort(function (n1, n2) {
     
      let r = Math.random(); return r > .5 ? 1 : (r < .5 ? -1 : 0); });
                color = 'rgba(' + vals[0] + ',' + vals[1] + ',' + vals[2] + ',' + 255 + ')';
                break;
        }
        switch (rand5.rand()) {
     
     //随机形状
            case 0:
                if (Math.random() < .5)
                    return createTetris({
     
     //L
                        x: x,
                        y: 0,
                        color: color,
                        north: [{
     
      x: 0, y: 0 }, {
     
      x: 0, y: -1 }, {
     
      x: 1, y: 1 }, {
     
      x: 0, y: 1 }],
                        east: [{
     
      x: 0, y: 0 }, {
     
      x: -1, y: 0 }, {
     
      x: 1, y: 0 }, {
     
      x: 1, y: -1 }],
                        south: [{
     
      x: 0, y: 0 }, {
     
      x: 0, y: -1 }, {
     
      x: -1, y: -1 }, {
     
      x: 0, y: 1 }],
                        west: [{
     
      x: 0, y: 0 }, {
     
      x: 1, y: 0 }, {
     
      x: -1, y: 1 }, {
     
      x: -1, y: 0 }]
                    });
                else
                    return createTetris({
     
     //J
                        x: x,
                        y: 0,
                        color: color,
                        north: [{
     
      x: 0, y: 0 }, {
     
      x: 0, y: -1 }, {
     
      x: -1, y: 1 }, {
     
      x: 0, y: 1 }],
                        east: [{
     
      x: 0, y: 0 }, {
     
      x: -1, y: 0 }, {
     
      x: 1, y: 0 }, {
     
      x: 1, y: 1 }],
                        south: [{
     
      x: 0, y: 0 }, {
     
      x: 0, y: -1 }, {
     
      x: 1, y: -1 }, {
     
      x: 0, y: 1 }],
                        west: [{
     
      x: 0, y: 0 }, {
     
      x: 1, y: 0 }, {
     
      x: -1, y: -1 }, {
     
      x: -1, y: 0 }]
                    });
            case 1:
                if (Math.random() < .5)
                    return createTetris({
     
     //Z
                        x: x,
                        y: 0,
                        color: color,
                        north: [{
     
      x: 0, y: 0 }, {
     
      x: -1, y: 0 }, {
     
      x: 1, y: 1 }, {
     
      x: 0, y: 1 }],
                        east: [{
     
      x: 0, y: 0 }, {
     
      x: 0, y: 1 }, {
     
      x: 1, y: 0 }, {
     
      x: 1, y: -1 }],
                        south: [{
     
      x: 0, y: 0 }, {
     
      x: -1, y: 0 }, {
     
      x: 0, y: 1 }, {
     
      x: 1, y: 1 }],
                        west: [{
     
      x: 0, y: 0 }, {
     
      x: -1, y: 0 }, {
     
      x: 0, y: -1 }, {
     
      x: -1, y: 1 }]
                    });
                else
                    return createTetris({
     
     //S
                        x: x,
                        y: 0,
                        color: color,
                        north: [{
     
      x: 0, y: 0 }, {
     
      x: 1, y: 0 }, {
     
      x: -1, y: 1 }, {
     
      x: 0, y: 1 }],
                        east: [{
     
      x: 0, y: 0 }, {
     
      x: 0, y: -1 }, {
     
      x: 1, y: 0 }, {
     
      x: 1, y: 1 }],
                        south: [{
     
      x: 0, y: 0 }, {
     
      x: -1, y: 0 }, {
     
      x: 0, y: -1 }, {
     
      x: 1, y: -1 }],
                        west: [{
     
      x: 0, y: 0 }, {
     
      x: -1, y: 0 }, {
     
      x: -1, y: -1 }, {
     
      x: 0, y: 1 }]
                    });
            case 2:
                return createTetris({
     
     //I
                    x: x,
                    y: 0,
                    color: color,
                    north: [{
     
      x: 0, y: 0 }, {
     
      x: 0, y: 1 }, {
     
      x: 0, y: 2 }, {
     
      x: 0, y: -1 }],
                    east: [{
     
      x: 0, y: 0 }, {
     
      x: 1, y: 0 }, {
     
      x: 2, y: 0 }, {
     
      x: -1, y: 0 }],
                    south: [{
     
      x: 0, y: 0 }, {
     
      x: 0, y: 1 }, {
     
      x: 0, y: -1 }, {
     
      x: 0, y: -2 }],
                    west: [{
     
      x: 0, y: 0 }, {
     
      x: -1, y: 0 }, {
     
      x: -2, y: 0 }, {
     
      x: 1, y: 0 }]
                });
            case 3:
                return createTetris({
     
     //T
                    x: x,
                    y: 0,
                    color: color,
                    north: [{
     
      x: 0, y: 0 }, {
     
      x: -1, y: 0 }, {
     
      x: 1, y: 0 }, {
     
      x: 0, y: 1 }],
                    east: [{
     
      x: 0, y: 0 }, {
     
      x: 1, y: 0 }, {
     
      x: 0, y: 1 }, {
     
      x: 0, y: -1 }],
                    south: [{
     
      x: 0, y: 0 }, {
     
      x: 1, y: 0 }, {
     
      x: -1, y: 0 }, {
     
      x: 0, y: -1 }],
                    west: [{
     
      x: 0, y: 0 }, {
     
      x: 0, y: -1 }, {
     
      x: 0, y: 1 }, {
     
      x: -1, y: 0 }]
                });
            case 4:
                return createTetris({
     
     //田
                    x: x,
                    y: 0,
                    color: color,
                    north: [{
     
      x: 0, y: 0 }, {
     
      x: 1, y: 0 }, {
     
      x: 1, y: 1 }, {
     
      x: 0, y: 1 }],
                    east: [{
     
      x: 0, y: 0 }, {
     
      x: 1, y: 0 }, {
     
      x: 1, y: 1 }, {
     
      x: 0, y: 1 }],
                    south: [{
     
      x: 0, y: 0 }, {
     
      x: 1, y: 0 }, {
     
      x: 1, y: 1 }, {
     
      x: 0, y: 1 }],
                    west: [{
     
      x: 0, y: 0 }, {
     
      x: 1, y: 0 }, {
     
      x: 1, y: 1 }, {
     
      x: 0, y: 1 }]
                });
            default:
                return createTetris({
     
     //T
                    x: x,
                    y: 0,
                    color: color,
                    north: [{
     
      x: 0, y: 0 }, {
     
      x: -1, y: 0 }, {
     
      x: 1, y: 0 }, {
     
      x: 0, y: 1 }],
                    east: [{
     
      x: 0, y: 0 }, {
     
      x: 1, y: 0 }, {
     
      x: 0, y: 1 }, {
     
      x: 0, y: -1 }],
                    south: [{
     
      x: 0, y: 0 }, {
     
      x: 1, y: 0 }, {
     
      x: -1, y: 0 }, {
     
      x: 0, y: -1 }],
                    west: [{
     
      x: 0, y: 0 }, {
     
      x: 0, y: -1 }, {
     
      x: 0, y: 1 }, {
     
      x: -1, y: 0 }]
                });
        }
    }
    function remove() {
     
     //移除整行
        let ret = 0;//得分
        let len = static.length;
        for (let y = TOP; y <= BOTTOM; y++) {
     
     //从高向低遍历
            let count = 0;//行方块数量
            for (let i = 0; i < len; i++) {
     
     //遍历方块
                const ele = static[i];
                if (ele && ele.y == y) //是本行方块
                    count++;
            }
            if (count >= RIGHT - LEFT) {
     
     //数量与行宽度相同
                ret++;
                for (let i = 0; i < len; i++) {
     
     //遍历清除单行
                    const ele = static[i];
                    if (ele) {
     
     
                        if (ele.y == y)//是本行方块
                            static[i] = undefined;//清除
                        else if (ele.y < y)//上方方块下沉
                            ele.y++;
                    }
                }
            }
        }
        return ret;
    }
    function fill() {
     
     //放置
        if (floating) {
     
     
            let len = floating.length;
            for (let index = 0; index < len; index++) {
     
     //遍历活动方块
                const ele = floating[index];
                if (ele.y <= TOP + 1) //堆积到顶端
                    living = false;
                static.push(ele);//活动方块全部放置
            }
            floating = [];//清空活动
            hold = null;//当前方块放置
        }
        return remove();
    }
    function set() {
     
     
        if (next == null)
            next = randomTetris();
        if (hold == null) {
     
     //方块已经放置
            hold = next;//选取下一个方块
            next = randomTetris();//随机化下一个
            fillNow = false;
        }
        hold.move(0, 1);//向下移动一个格
    }
    function op() {
     
     //移动方块
        if (hold) {
     
     
            let len = static.length;
            let loop = true;//循环控制
            let xMove = moveX;
            moveX = 0;//重置横向移动
            hold.move(xMove, 0);//向下移动一个格
            floating = hold.export(fancy);//获取活动方块表,就是当前玩家控制的方块的每一个格绝对位置表
            let len1 = floating.length;
            let safe = (RIGHT - LEFT) * (BOTTOM - TOP);//最多只遍历所有方块
            let removed = false;
            do {
     
     
                loop = true;//重置循环控制
                let sign = false;//活动方块是否与已经防止的方块重合
                let canFill = false;//可以自行放置
                for (let i1 = 0; i1 < len1; i1++) {
     
     
                    const ele1 = floating[i1];//活动方块
                    if (ele1)
                        for (let i = 0; i < len; i++) {
     
     //遍历方块
                            const ele = static[i];
                            if (ele) {
     
     
                                if (ele.x == ele1.x && ele.y == ele1.y) {
     
     //活动方块与放置方块同行同列
                                    if (removed)//撤销旋转
                                        switch (fancy) {
     
     
                                            case 'north':
                                                fancy = 'west';
                                                break;
                                            case 'east':
                                                fancy = 'north';
                                                break;
                                            case 'south':
                                                fancy = 'east';
                                                break;
                                            case 'west':
                                                fancy = 'south';
                                                break;
                                            default:
                                                fancy = 'north';
                                                break;
                                        }
                                    else {
     
     
                                        hold.move(-xMove, 0);//撤销移动
                                        removed = true;
                                    }
                                    floating = hold.export(fancy);//重置活动表
                                    sign = true;//继续最外层do-while循环
                                    canFill = false;
                                    i = len + 1;//强制跳出外层for循环
                                    break;//终止内层for循环
                                } else if ((ele.x == ele1.x && ele1.y == ele.y - 1)) //活动方块恰在放置方块正上方
                                    canFill = canFill || true;
                            }
                        }
                    if (ele1.y == BOTTOM - 1) {
     
     //恰在屏幕底端
                        canFill = true;
                        break;
                    }
                }
                loop = sign;//
                if (canFill && !loop) {
     
     
                    score += fill();//放置且跳出
                    break;
                }
            } while (loop && safe-- > 0);
            if (safe <= 0)//方块无法放置
                living = false;//方块已经堆满
        }
    }
    function print(arr, ctx, blockWidth) {
     
     //绘制所有方块
        let len = arr.length;
        for (let i = 0; i < len; i++) {
     
     
            const ele = arr[i];
            if (ele) {
     
     
                let color = ele.color;
                let x = ele.x;
                let y = ele.y;
                ctx.fillStyle = color;
                ctx.fillRect(x * blockWidth, y * blockWidth, blockWidth, blockWidth);//画一个格
            }
        }
    }
    function printBg(ctx) {
     
     //绘制棋盘
        const dark = '#f8f8f8ee';
        const light = '#f0f0f0ee';
        for (let x = LEFT; x < RIGHT; x++) {
     
     
            let color = x % 2 == 0 ? light : dark;
            for (let y = TOP; y < BOTTOM; y++) {
     
     
                ctx.fillStyle = color;
                ctx.fillRect(x * blockWidth, y * blockWidth, blockWidth, blockWidth);
                color = color == light ? dark : light;
            }
        }
    }
    var move = 0;
    function run() {
     
     
        blockWidth = canvas.width / (RIGHT - LEFT);//更新
        op();
        canvas.height = canvas.height;//清空图像
        printBg(ctx);//画棋盘
        print(floating, ctx, blockWidth);//绘制活动
        print(static, ctx, blockWidth);//绘制已经放好的方块
        if (next) {
     
     //绘制下一个方块
            let nextFloating = next.export(fancy);
            let len = nextFloating.length;
            for (let i = 0; i < len; i++) {
     
     
                nextFloating[i].x -= (RIGHT - LEFT) / 2 - 2;
            }
            nextBox.height = nextBox.height;
            printBg(ntx);
            print(nextFloating, ntx, blockWidth);
        }
        let str = score + ' (' + exp + ')';
        if (scoreBox.innerHTML != str)
            scoreBox.innerHTML = str;//输出游戏得分
        if (lvlBox.innerHTML != lvl)
            lvlBox.innerHTML = lvl;//输出速度等级
    }
    run();
    setInterval(function () {
     
     //计时器跑帧
        if (living) {
     
     
            if (move >= delay) {
     
     //延迟达到最大,现在该活动方块自然下落
                set();//自然下落
                move = 0;//重设当前移动延迟
                delay = Math.max(10, maxDelay - lvl * 1.75);//更新延迟
                if (score >= exp) {
     
     
                    exp += (score + 1);//增速减慢
                    if (lvl <= maxDelay - minDelay)//等级小于可升级范围时才升级,否则方块速度过快将导致不可玩
                        lvl += 1;//速度升级
                }
            }
            move++;//延迟计数
            while (fillNow) {
     
     
                set();//连续自然下落
                op();
            }
            run();
        }
        if (restartNow) {
     
     //必须在这里设置重开,否则手速快不同步出问题
            restart();
            living = true;
            restartNow = false;
        }
    }, 15);
</script>

猜你喜欢

转载自blog.csdn.net/dscn15848078969/article/details/109880038