zoukankan      html  css  js  c++  java
  • d3.js 制作简单的贪吃蛇

    d3.js是一个不错的可视化框架,同时对于操作dom也是十分方便的。今天我们使用d3.js配合es6的类来制作一个童年小游戏–贪吃蛇。话不多说先上图片。

    1. js snaker类

    class Snaker {
        constructor() {
            this._size = 30;
            this._len = 3;
            this._width = 900;
            this._height = 690;
            this._rows = 23;
            this._cols = 30;
            this._colors = d3.scaleLinear().range(['#E75229','#FFBF35']);
            this._svg = null;
            this._currentArray = [[0,2],[0,1],[0,0]];
            this._interval = null;
            this._duration = 1000;
            this._direction = 1;//上右下左0123
            this._randomPosition = [0,6];
            this.initSvg();
            this.addKeyListener();
        }
        initSvg() {
            this._svg = d3.select('.svg-container')
                .append('svg')
                .attr('width', this._width)
                .attr('height', this._height)
            this._svg.selectAll('line.rows')
                .data(d3.range(this._rows))
                .enter()
                .append('line')
                .attr('class', 'line rows')
                .attr('x1', 0)
                .attr('y1', d => d * this._size)
                .attr('x2', this._width)
                .attr('y2', d => d * this._size)
            this._svg.selectAll('line.cols')
                .data(d3.range(this._cols))
                .enter()
                .append('line')
                .attr('class', 'line cols')
                .attr('x1', d => d * this._size)
                .attr('y1', 0)
                .attr('x2', d => d * this._size)
                .attr('y2', this._height)
        }
        addKeyListener() {
            d3.select('body').on('keydown', () => {
                switch (d3.event.keyCode) {
                    case 37:
                        this.rotate(3);
                    break;
                    case 38:
                        this.rotate(0);
                        break;
                    case 39:
                        this.rotate(1);
                        break;
                    case 40:
                        this.rotate(2);
                        break;
                    case 32:
                        console.log('空格');
                        break;
                    case 80:
                        console.log('暂停');
                        break;
                    default:
                        break;
                }
            })
        }
        rotate(num) {
            if(num == this._direction) {
                this.rotateMove();
            } else if(num % 2 != this._direction % 2) {
                this._direction = num;
                this.rotateMove();
            }
        }
        renderSnaker() {
            this._svg.selectAll('rect.active').remove();
            this._svg.selectAll('rect.active')
                .data(this._currentArray)
                .enter()
                .append('rect')
                .attr('class', 'active')
                .attr('x', d => d[1] * this._size)
                .attr('y', d => d[0] * this._size)
                .attr('width', this._size)
                .attr('height', this._size)
                .attr('fill', (d,i) => this._colors(i / this._len))
                .attr('stroke', (d,i) => this._colors(i / this._len))
        }
        canMove() {
            //下一步没有触碰边缘
            let noTouchBorder = true;
            //下一步没有触碰自身
            let noTouchSelf = true;
            //新数组
            let newArray = [];
            //判断方向
            switch(this._direction) {
                case 0:
                    if(this._currentArray[0][0] == 0) {
                        noTouchBorder = false;
                    } else {
                        newArray = this._currentArray.map((c,i,arr) => {
                            if(i == 0) {
                                return [c[0] - 1, c[1]]
                            } else {
                                return arr[i - 1]
                            }
                        })
                    }
                    break;
                case 1:
                    if(this._currentArray[0][1] == this._cols - 1) {
                        noTouchBorder = false;
                    } else {
                        newArray = this._currentArray.map((c,i,arr) => {
                            if(i == 0) {
                                return [c[0], c[1] + 1]
                            } else {
                                return arr[i - 1]
                            }
                        })
                    }
                    break;
                case 2:
                    if(this._currentArray[0][0] == this._rows - 1) {
                        noTouchBorder = false;
                    } else {
                        newArray = this._currentArray.map((c,i,arr) => {
                            if(i == 0) {
                                return [c[0] + 1, c[1]]
                            } else {
                                return arr[i - 1]
                            }
                        })
                    }
                    break;
                case 3:
                    if(this._currentArray[0][1] == 0) {
                        noTouchBorder = false;
                    } else {
                        newArray = this._currentArray.map((c,i,arr) => {
                            if(i == 0) {
                                return [c[0], c[1] - 1]
                            } else {
                                return arr[i - 1]
                            }
                        })
                    }
                    break;
            }
            //判断新数组第一个元素是否出现在后面其他元素中
            for(var i=1; i<newArray.length; i++) {
                if(newArray[0][0] == newArray[i][0] && newArray[0][1] == newArray[i][1]) {
                    noTouchSelf = false;
                }
            }
            return noTouchBorder && noTouchSelf;
        }
        setScoreAndSpeed() {
            d3.select('#score').html(this._len);
            d3.select('#speed').html((this._duration * (1 - this._len / 1000) / 1000).toString().substr(0,8) + 's')
        }
        moveArray() {
            if(this.canMove()) {
                if(this._direction == 0) {
                    if(this._currentArray[0][0] - 1 == this._randomPosition[0] && this._currentArray[0][1] == this._randomPosition[1]) {
                        this._currentArray.unshift(this._randomPosition);
                        this._len ++;
                        this.setScoreAndSpeed();
                        this.removeRandomPosition();
                        this.randomPosition();
                    } else {
                        this._currentArray.unshift([this._currentArray[0][0] - 1,this._currentArray[0][1]])
                        this._currentArray.pop();
                    }
                } else if(this._direction == 1) {
                    if(this._currentArray[0][0] == this._randomPosition[0] && this._currentArray[0][1] + 1 == this._randomPosition[1]) {
                        this._currentArray.unshift(this._randomPosition);
                        this._len ++;
                        this.setScoreAndSpeed();
                        this.removeRandomPosition();
                        this.randomPosition();
                    } else {
                        this._currentArray.unshift([this._currentArray[0][0],this._currentArray[0][1] + 1])
                        this._currentArray.pop();
                    }
                } else if(this._direction == 2) {
                    if(this._currentArray[0][0] + 1 == this._randomPosition[0] && this._currentArray[0][1] == this._randomPosition[1]) {
                        this._currentArray.unshift(this._randomPosition);
                        this._len ++;
                        this.setScoreAndSpeed();
                        this.removeRandomPosition();
                        this.randomPosition();
                    } else {
                        this._currentArray.unshift([this._currentArray[0][0] + 1,this._currentArray[0][1]])
                        this._currentArray.pop();
                    }
                } else if(this._direction == 3) {
                    if(this._currentArray[0][0] == this._randomPosition[0] && this._currentArray[0][1] - 1 == this._randomPosition[1]) {
                        this._currentArray.unshift(this._randomPosition);
                        this._len ++;
                        this.setScoreAndSpeed();
                        this.removeRandomPosition();
                        this.randomPosition();
                    } else {
                        this._currentArray.unshift([this._currentArray[0][0],this._currentArray[0][1] - 1])
                        this._currentArray.pop();
                    }
                }
            } else {
                console.log('game over');
                alert('game over')
            }
        }
        removeRandomPosition() {
            d3.selectAll('rect.random').remove();
        }
        randomPosition() {
            let random = Math.floor(Math.random() * (this._cols * this._rows - this._len));
            let temp = [];
            for(var i=0; i<this._rows; i++) {
                for(var j=0; j<this._cols; j++) {
                    temp.push([i,j])
                }
            }
            let emptyArray = temp.filter(a => !this._currentArray.some(b => b[0] == a[0] && b[1] == a[1]));
            this._randomPosition = emptyArray[random];
            this._svg.append('rect')
                .attr('class', 'random')
                .attr('x', this._randomPosition[1] * this._size)
                .attr('y', this._randomPosition[0] * this._size)
                .attr('width', this._size)
                .attr('height', this._size)
        }
        interval() {
            this._interval = setInterval(() => {
                this.moveArray();
                this.renderSnaker();
            }, this._duration * (1 - this._len / 1000))
        }
        //转弯附带移动一次
        rotateMove() {
            this.moveArray();
            this.renderSnaker();
        }
        initData() {
            this._currentArray = [[0,2],[0,1],[0,0]];
        }
        start() {
            this.initData();
            this.renderSnaker();
            this.interval();
            this.randomPosition();
            this.setScoreAndSpeed();
        }
    }

    2. css 代码

    * {
      padding: 0;
      margin: 0;
    }
    .container {
      width: 100vw;
      height: 100vh;
    }
    .svg-container {
      margin: 50px;
      width: 900px;
      height: 690px;
      border: 3px double #666;
      display: inline-block;
      overflow: hidden;
    }
    aside {
      width: 200px;
      height: 300px;
      display: inline-block;
      vertical-align: top;
      margin-top: 50px;
    }
    .line {
      shape-rendering: crispEdges;
      stroke: #bbbbbb;
    }
    .active {
      stroke-width: 2;
      fill-opacity: 0.5;
    }
    .random {
      fill: #ff00ff;
      fill-opacity: 0.5;
      stroke: #ff00ff;
      stroke-width: 2;
    }

    3. html代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>$Title$</title>
        <link rel="stylesheet" type="text/css" href="css/base.css"/>
        <script type="text/javascript" src="js/d3.v4.js"></script>
        <script type="text/javascript" src="js/base.js"></script>
    </head>
    <body>
        <div class="container">
            <div class="svg-container"></div>
            <aside>
                <table>
                    <tr>
                        <td>当前分数:</td>
                        <td id="score"></td>
                    </tr>
                    <tr>
                        <td>当前速度:</td>
                        <td id="speed"></td>
                    </tr>
                </table>
                <button onclick="start()">开始游戏</button>
            </aside>
        </div>
    <script>
    var snaker = new Snaker();
    function start() {
        snaker.start();
    }
    
    </script>
    </body>
    </html>

    有想预览或者下载demo的朋友请移步至个人博客

    原文地址 http://www.bettersmile.cn

  • 相关阅读:
    Happy Number
    N-Queens
    Palindrome Partitioning
    Linked List Cycle I & II
    leetcode 96: Unique Binary Search Trees java
    cc150 Chapter 2 | Linked Lists 2.6 Given a circular linked list, implement an algorithm which returns node at the beginning of the loop.
    cc150 Chapter 2 | Linked Lists 2.5 add two integer LinkedList, return LinkedList as a sum
    355. Design Twitter [classic design]
    400. Nth Digit
    211. Add and Search Word
  • 原文地址:https://www.cnblogs.com/vadim-web/p/11496275.html
Copyright © 2011-2022 走看看