• 《canvas》之第16章 碰撞检测


    第16章 碰撞检测

    16.1 碰撞检测简介

    检测物体与物体间是否发生碰撞。

    16.2 外接矩形判定法

    将物体抽象成矩形来检测碰撞。

    //外接矩形判定法(碰撞检测)
    window.tools.checkRect = function (rectA, rectB) {
        return !(rectA.x + rectA.width < rectB.x ||
                 rectB.x + rectB.width < rectA.x ||
                 rectA.y + rectA.height < rectB.y ||
                 rectB.y + rectB.height < rectA.y);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    Ball.prototype = {
        getRect:function() {//获取包含小球的最小矩形
            var rect = {
                    x: this.x - this.radius,
                    y: this.y - this.radius,
                    width: this.radius * 2,
                    height: this.radius * 2
            }
            return rect;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 外接矩形碰撞检测
    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title></title>
        <meta charset="utf-8" />
        <script src="js/tools.js"></script>
        <script src="js/ball.js"></script>
        <script type="text/javascript">
            function $$(id) {
                return document.getElementById(id);
            }
            window.onload = function () {
                var cnv = $$("canvas");
                var cxt = cnv.getContext("2d");
                var msg = document.getElementById("msg");
    
                //定义一个位置固定的小球ballA
                var ballA = new Ball(cnv.width/2, cnv.height/2, 30);
                //获取ballA的外接矩形
                var rectA = ballA.getRect();
    			
                var mouse = tools.getMouse(cnv);
                (function frame() {
                    window.requestAnimationFrame(frame);
                    cxt.clearRect(0, 0, cnv.width, cnv.height);
    
                    //绘制ballA以及它的外接矩形
                    ballA.fill(cxt);
                    cxt.strokeRect(rectA.x, rectA.y, rectA.width, rectA.height);
    
                    //定义一个位置不固定的小球ballB,小球追随鼠标
                    var ballB = new Ball(mouse.x, mouse.y, 30);
                    //获取ballB的外接矩形
                    var rectB = ballB.getRect();
    
                    //绘制ballB以及它的外接矩形
                    ballB.fill(cxt);
                    cxt.strokeRect(rectB.x, rectB.y, rectB.width, rectB.height);
    
                    //碰撞检测
                    if (tools.checkRect(rectA, rectB)) {
                        msg.innerHTML = "撞上了";
                    } else {
                        msg.innerHTML = "没撞上";
                    }
                })();
            }
        </script>
    </head>
    <body>
        <canvas id="canvas" width="270" height="200" style="border:1px solid silver;"></canvas>
        <p id="msg"></p>
    </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • box.js
    function Box(x, y, width, height, color) {
        //小球中心的x坐标,默认值为0
        this.x = x || 0;
        //小球中心的y坐标,默认值为0
        this.y = y || 0;
        //小球宽度,默认值为80
        this.width = width || 80;
        //小球高度,默认值为40
        this.height = height || 40;
    
        this.color = color || "red";
        //x和y速度
        this.vx = 0;
        this.vy = 0;
    }
    Box.prototype = {
        //绘制“描边”矩形
        stroke: function (cxt) {
            cxt.save();
            cxt.beginPath();
            cxt.rect(this.x, this.y, this.width, this.height);
            cxt.closePath();
            cxt.strokeStyle = this.color;
            cxt.stroke();
            cxt.restore();
        },
        //绘制“填充”矩形
        fill: function (cxt) {
            cxt.save();
            cxt.beginPath();
            cxt.rect(this.x, this.y, this.width, this.height);
            cxt.closePath();
            cxt.fillStyle = this.color;
            cxt.fill();
            cxt.restore();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 俄罗斯方块
    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title></title>
        <meta charset="utf-8" />
        <script src="js/tools.js"></script>
        <script src="js/box.js"></script>
        <script type="text/javascript">
            function $$(id) {
                return document.getElementById(id);
            }
            window.onload = function () {
                var cnv = $$("canvas");
                var cxt = cnv.getContext("2d");
    
                //定义一个用来存放方块的数组boxes
                var boxes = [];
                //定义一个“当前活动”的方块
                var activeBox = createBox();
                //定义方块的Y轴速度
                var vy = 2;
    
                //定义一个函数createBox(),用于创建一个新的方块
                function createBox() {
                    var x = Math.random() * cnv.width;
                    var y = 0;
                    var width = Math.random() * 40 + 10;
                    var height = Math.random() * 40 + 10;
                    var color = tools.getRandomColor();
                    var box = new Box(x, y, width, height, color);
                    //添加到数组boxes中
                    boxes.push(box);
                    return box;
                }
    
                (function frame() {
                    window.requestAnimationFrame(frame);
                    cxt.clearRect(0, 0, cnv.width, cnv.height);
    
                    activeBox.y += vy;
    
                    //边界检测,如果到达底部,则创建一个新的box
                    if (activeBox.y > cnv.height - activeBox.height) {
                        activeBox.y = cnv.height - activeBox.height;
                        activeBox = createBox();
                    }
                    //遍历数组boxes,以便单独处理每一个box
                    boxes.forEach(function (box) {
                        /*如果当前遍历的box不是“活动方块(activeBox)”,并且当前遍历的方块与“活动方块(activeBox)”碰上了,则创建新的方块*/
                        if (activeBox !== box && tools.checkRect(activeBox, box)) {
                            activeBox.y = box.y - activeBox.height;
                            activeBox = createBox();
                        }
                        box.fill(cxt);
                    });
                })();
    
            }
        </script>
    </head>
    <body>
        <canvas id="canvas" width="270" height="200" style="border:1px solid silver;"></canvas>
    </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 键盘控制
    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title></title>
        <meta charset="utf-8" />
        <script src="js/tools.js"></script>
        <script src="js/box.js"></script>
        <script type="text/javascript">
            function $$(id) {
                return document.getElementById(id);
            }
            window.onload = function () {
                var cnv = $$("canvas");
                var cxt = cnv.getContext("2d");
    
                //定义一个用来存放方块的数组boxes
                var boxes = [];
                //定义一个“当前活动”的方块
                var activeBox = createBox();
                //定义方块的Y轴速度
                var vy = 1.5;
    
                //加入鼠标控制
                var key = tools.getKey();
                window.addEventListener("keydown", function () {
                    switch (key.direction) {
                        case "down":
                            activeBox.y += 5;
                            break;
                        case "left":
                            activeBox.x -= 10;
                            break;
                        case "right":
                            activeBox.x += 10;
                            break;
                    }
                }, false);
    
                //定义一个函数createBox(),用于创建一个新的方块
                function createBox() {
                    var x = Math.random() * cnv.width;
                    var y = 0;
                    var width = Math.random() * 40 + 10;
                    var height = Math.random() * 40 + 10;
                    var color = tools.getRandomColor();
                    var box = new Box(x, y, width, height, color);
                    //添加到数组boxes中
                    boxes.push(box);
                    return box;
                }
    
                (function frame() {
                    window.requestAnimationFrame(frame);
                    cxt.clearRect(0, 0, cnv.width, cnv.height);
    
                    activeBox.y += vy;
    
                    //边界检测,如果到达底部,则创建一个新的box
                    if (activeBox.y > cnv.height - activeBox.height) {
                        activeBox.y = cnv.height - activeBox.height;
                        activeBox = createBox();
                    }
                    //遍历数组boxes,以便单独处理每一个box
                    boxes.forEach(function (box) {
                        /*如果当前遍历的box不是“活动方块(activeBox)”,并且当前遍历的方块与
                        “活动方块(activeBox)”碰上了,则创建新的方块*/
                        if (activeBox !== box && tools.checkRect(activeBox, box)) {
                            activeBox.y = box.y - activeBox.height;
                            activeBox = createBox();
                        }
                        box.fill(cxt);
                    });
                })();
    
            }
        </script>
    </head>
    <body>
        <canvas id="canvas" width="270" height="200" style="border:1px solid silver;"></canvas>
    </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81

    16.3 外接圆判定法

    物体看成圆来判定是否碰撞。

    //外接圆判定法(碰撞检测)
    window.tools.checkCircle = function (circleB, circleA) {
        var dx = circleB.x - circleA.x;
        var dy = circleB.y - circleA.y;
        var distance = Math.sqrt(dx * dx + dy * dy);
        if (distance < (circleA.radius + circleB.radius)) {
            return true;
        } else {
            return false;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 外接圆判断
    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title></title>
        <meta charset="utf-8" />
        <script src="js/tools.js"></script>
        <script src="js/ball.js"></script>
        <script type="text/javascript">
            function $$(id) {
                return document.getElementById(id);
            }
            window.onload = function () {
                var cnv = $$("canvas");
                var cxt = cnv.getContext("2d");
                var txt = document.getElementById("txt");
    
                //定义一个位置固定的小球
                var ballA = new Ball(cnv.width / 2, cnv.height / 2, 20, "#FF6699");
                var mouse = tools.getMouse(cnv);
    
                (function frame() {
                    window.requestAnimationFrame(frame);
                    cxt.clearRect(0, 0, cnv.width, cnv.height);
    
                    //定义一个位置不固定的小球,小球追随鼠标
                    var ballB = new Ball(mouse.x, mouse.y, 20, "#66CCFF");
    
                    //碰撞检测
                    if (tools.checkCircle(ballB, ballA)) {
                        txt.innerHTML = "撞上了";
                    } else {
                        txt.innerHTML = "没撞上";
                    }
    
                    ballA.fill(cxt);
                    ballB.fill(cxt);
                })();
            }
        </script>
    </head>
    <body>
        <canvas id="canvas" width="200" height="150" style="border:1px solid silver;"></canvas>
        <p id="txt"></p>
    </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 小球碰撞
    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title></title>
        <meta charset="utf-8" />
        <script src="js/tools.js"></script>
        <script src="js/ball.js"></script>
        <script type="text/javascript">
            function $$(id) {
                return document.getElementById(id);
            }
            window.onload = function () {
                var cnv = $$("canvas");
                var cxt = cnv.getContext("2d");
    
                //定义两个小球:ballA和ballB
                var ballA = new Ball(0, cnv.height / 2, 12, "#FF6699");
                var ballB = new Ball(cnv.width, cnv.height / 2, 12, "#66CCFF");
                //定义小球X轴速度
                var vx = 2;
    
                (function frame() {
                    window.requestAnimationFrame(frame);
                    cxt.clearRect(0, 0, cnv.width, cnv.height);
    
                    ballA.x += vx;
                    ballB.x += -vx;
    
                    //如果发生碰撞,则速度取反
                    if (tools.checkCircle(ballB, ballA)) {
                        vx = -vx;
                    }
    
                    ballA.fill(cxt);
                    ballB.fill(cxt);
                })();
            }
        </script>
    </head>
    <body>
        <canvas id="canvas" width="270" height="200" style="border:1px solid silver;"></canvas>
    </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    16.4 多物体碰撞

    16.4.1 排列组合

    n个物体,n×(n-1)/2种碰撞情况。

    16.4.2 多物体碰撞

    balls.forEach(function(ballA, i) {
    	for (var j = i + 1; j < balls.length; j++) {
    		var ballB = balls[j];
    		if (tools.checkCircle(ballB, ballA)) {
    			//...
    		}
    	}
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 存在小球重叠bug
    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title></title>
        <meta charset="utf-8" />
        <script src="js/tools.js"></script>
        <script src="js/ball.js"></script>
        <script type="text/javascript">
            function $$(id) {
                return document.getElementById(id);
            }
            window.onload = function () {
                var cnv = $$("canvas");
                var cxt = cnv.getContext("2d");
    
                var n = 8;
                var balls = [];
    
                //生成n个小球,小球的x、y、color、vx、vy属性取的都是随机值
                for (var i = 0; i < n; i++) {
                    ball = new Ball();
                    ball.x = Math.random() * cnv.width;
                    ball.y = Math.random() * cnv.height;
                    ball.radius = 10;
                    ball.color = tools.getRandomColor();
                    ball.vx = Math.random() * 6 - 3;
                    ball.vy = Math.random() * 6 - 3;
                    //添加到数组balls中
                    balls.push(ball);
                }
    
                //碰撞检测(小球与小球)
                function checkCollision(ballA, i) {
                    for (var j = i + 1; j < balls.length; j++) {
                        var ballB = balls[j];
                        //如果两个小球碰撞,则碰撞后vx、vy都取相反值
                        if (tools.checkCircle(ballB, ballA)) {
                            ballA.vx = -ballA.vx;
                            ballA.vy = -ballA.vy;
                            ballB.vx = -ballB.vx;
                            ballB.vy = -ballB.vy;
                        }
                    }
                }
    
                //边界检测(小球与边界)
                function checkBorder(ball) {
                    //碰到左边界
                    if (ball.x < ball.radius) {
                        ball.x = ball.radius;
                        ball.vx = -ball.vx;
                        //碰到右边界
                    } else if (ball.x > canvas.width - ball.radius) {
                        ball.x = canvas.width - ball.radius;
                        ball.vx = -ball.vx;
                    }
                    //碰到上边界
                    if (ball.y < ball.radius) {
                        ball.y = ball.radius;
                        ball.vy = -ball.vy;
                        //碰到下边界
                    } else if (ball.y > canvas.height - ball.radius) {
                        ball.y = canvas.height - ball.radius;
                        ball.vy = -ball.vy;
                    }
                }
    
                //绘制小球
                function drawBall(ball) {
                    ball.fill(cxt);
                    ball.x += ball.vx;
                    ball.y += ball.vy;
                }
    
                (function frame() {
                    window.requestAnimationFrame(frame);
                    cxt.clearRect(0, 0, cnv.width, cnv.height);
    
                    //碰撞检测
                    balls.forEach(checkCollision);
                    //边界检测
                    balls.forEach(checkBorder);
                    //绘制小球
                    balls.forEach(drawBall);
    
                })();
            }
        </script>
    </head>
    <body>
        <canvas id="canvas" width="200" height="150" style="border:1px solid silver;"></canvas>
    </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 碰撞小球加上偏移量
    <!DOCTYPE html>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <title></title>
        <script src="js/tools.js"></script>
        <script src="js/ball.js"></script>
        <script type="text/javascript">
            function $$(id) {
                return document.getElementById(id);
            }
            window.onload = function () {
                var cnv = $$("canvas");
                var cxt = cnv.getContext("2d");
    
                var n = 8;
                var balls = [];
    
                //生成n个小球,小球的x、y、color、vx、vy属性取的都是随机值
                for (var i = 0; i < n; i++) {
                    ball = new Ball();
                    ball.x = Math.random() * cnv.width;
                    ball.y = Math.random() * cnv.height;
                    ball.radius = 10;
                    ball.color = tools.getRandomColor();
                    ball.vx = Math.random() * 6 - 3;
                    ball.vy = Math.random() * 6 - 3;
                    //添加到数组balls中
                    balls.push(ball);
                }
    
                //碰撞检测(小球与小球)
                function checkCollision(ballA, i) {
                    for (var j = i + 1; j < balls.length; j++) {
                        var ballB = balls[j];
                        //如果两个小球碰撞,则碰撞后vx、vy都取相反值
                        if (tools.checkCircle(ballB, ballA)) {
                            ballA.vx = -ballA.vx;
                            ballA.vy = -ballA.vy;
                            ballB.vx = -ballB.vx;
                            ballB.vy = -ballB.vy;
    
                            //每次碰撞,小球的x、y都加入偏移量,避免相互重叠
                            if (ballA.vx > 0) {
                                ballA.x += 5;
                            } else {
                                ballA.x -= 5;
                            }
                            if (ballA.vy > 0) {
                                ballA.y += 5;
                            } else {
                                ballA.y -= 5;
                            }
                            if (ballB.vx > 0) {
                                ballB.x += 5;
                            } else {
                                ballB.x -= 5;
                            }
                            if (ballB.vy > 0) {
                                ballB.y += 5;
                            } else {
                                ballB.y -= 5;
                            }
                        }
                    }
                }
    
                //边界检测(小球与边界)
                function checkBorder(ball) {
                    //碰到左边界
                    if (ball.x < ball.radius) {
                        ball.x = ball.radius;
                        ball.vx = -ball.vx;
                        //碰到右边界
                    } else if (ball.x > canvas.width - ball.radius) {
                        ball.x = canvas.width - ball.radius;
                        ball.vx = -ball.vx;
                    }
                    //碰到上边界
                    if (ball.y < ball.radius) {
                        ball.y = ball.radius;
                        ball.vy = -ball.vy;
                        //碰到下边界
                    } else if (ball.y > canvas.height - ball.radius) {
                        ball.y = canvas.height - ball.radius;
                        ball.vy = -ball.vy;
                    }
                }
    
                //绘制小球
                function drawBall(ball) {
                    ball.fill(cxt);
                    ball.x += ball.vx;
                    ball.y += ball.vy;
                }
    
                (function frame() {
                    window.requestAnimationFrame(frame);
                    cxt.clearRect(0, 0, cnv.width, cnv.height);
    
                    //碰撞检测
                    balls.forEach(checkCollision);
                    //边界检测
                    balls.forEach(checkBorder);
                    //绘制小球
                    balls.forEach(drawBall);
    
                })();
            }
        </script>
    </head>
    <body>
        <canvas id="canvas" width="200" height="150" style="border:1px solid silver;"></canvas>
    </body>
    </html>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
  • 相关阅读:
    Tableau:详细表达式(LOD表达式)的计算过程
    如何公网远程访问本地群晖NAS File Station文件夹
    CI/CD:持续集成/持续部署
    Vijos P1422 教主的难题 求调!
    Vue3+elementplus搭建通用管理系统实例八:通用表格实现中
    opencv改变像素点的颜色---------c++
    谷粒商城-商品服务(平台属性)
    速盾:服务器cdn加速超时如何解决?
    R语言dplyr包summarise_at函数计算dataframe数据中多个数据列(通过向量指定)的方差
    安全巡检管理系统如何使用
  • 原文地址:https://blog.csdn.net/oqqyx1234567/article/details/125383812