1
我的编辑器能玩贪吃蛇,一起玩不?
发布于: 2021 年 06 月 10 日
摘要:你们说,能不能把这个贪吃蛇游戏插入到编辑中呢?
本文分享自华为云社区《整个贪吃蛇到编辑器里玩吧》,原文作者:DevUI。
能不能把这个贪吃蛇游戏插入到编辑中呢?
按照以下四个步骤来就行:
- 第一步:自定义工具栏按钮 
- 第二步:自定义 Blot 内容 
- 第三步:在 Quill 注册自定义 Blot 
- 第四步:调用 Quill 的 API 插入自定义内容 
第一步:自定义工具栏按钮
这个非常简单:
const TOOLBAR_CONFIG = [  [{ header: ['1', '2', '3', false] }],  ['bold', 'italic', 'underline', 'link'],  [{ list: 'ordered' }, { list: 'bullet' }],  ['clean'],  ['card', 'divider', 'emoji', 'file', 'tag'],  ['dragon', 'snake'], // 新增的];复制代码
 自定义工具栏按钮图标:
const snakeIcon = `<svg>...</svg>`;const icons = Quill.import('ui/icons');icons.snake = snakeIcon;复制代码
 增加工具栏按钮事件:
const quill = new Quill('#editor', {  theme: 'snow',  modules: {    toolbar: {      container: TOOLBAR_CONFIG,      handlers: {        ...        // 增加一个空的事件        snake(value): void {          console.log('snake~~');        },      },    }  },});复制代码
 第二步:自定义 Blot 内容 SnakeBlot
不再啰嗦,参考之前的文章直接写就好:
《如何将龙插入到编辑器中?》
《Quill 富文本编辑器的实践》
snake.ts
import Quill from 'quill';import GreedySnake from '../../shared/greedy-snake';
const BlockEmbed = Quill.import('blots/block/embed');
class SnakeBlot extends BlockEmbed {  static blotName = 'snake';  static tagName = 'canvas';
  static create(value): any {    const node = super.create(value);    const { id, width, height } = value;
    node.setAttribute('id', id || SnakeBlot.blotName);    if (width !== undefined) {      node.setAttribute('width', width);    }    if (height !== undefined) {      node.setAttribute('height', height);    }
    // 绘制贪吃蛇游戏的代码参考对半同学的文章:https://juejin.cn/post/6959789039566192654    new GreedySnake(node).start();        return node;  }}
export default SnakeBlot;复制代码
 绘制贪吃蛇
由于对半同学花一个小时写出来的代码实在非常优雅,忍不住将其代码贴出来了(侵删),文章源码来源于对半同学的文章:
canvas 300 行代码实现一个贪吃蛇
greedy-snake.ts
// 大小为64 * 40export default class GreedySnake {  canvas;  ctx;  maxX;  maxY;  itemWidth;  direction;  speed;  isStop;  isOver;  isStart;  score;  timer;  j;  canChange;  grid;  snake;  food;
  // mask;  // scoreDom;
  constructor(container) {    this.canvas = typeof container === 'string' ? document.querySelector(container) : container;    this.canvas.setAttribute('width', 640);    this.canvas.setAttribute('height', 400);    this.canvas.setAttribute('style', 'border: solid 2px #ddd');    this.ctx = this.canvas.getContext('2d');    this.maxX = 64;          // 最大行    this.maxY = 40;          // 最大列    this.itemWidth = 10;     // 每个点的大小    this.direction = 'right'; // up down right left 方向    this.speed = 150;        // ms 速度    this.isStop = false;     // 是否暂停    this.isOver = false;     // 是否结束    this.isStart = false;    // 是否开始    this.score = 0;          // 分数    this.timer = null;       // 移动定时器    this.j = 1;    this.canChange = true;
    this.grid = new Array();
    // this.scoreDom = document.querySelector('#score');    // this.mask = document.querySelector('#mask');
    for (let i = 0; i < this.maxX; i++) {      for (let j = 0; j < this.maxY; j++) {        this.grid.push([i, j]);      }    }
    this.drawGridLine();    this.getDirection();
    document.addEventListener('keydown', (event) => {      if (event.keyCode === 13) {        if (!this.isStart) return;        this.start();      }    });  }
  // 开始  start(): void {    if (this.timer) {      clearTimeout(this.timer);    }    if (!this.isStart) {      this.isStart = true;    }    this.score = 0;    this.speed = 150;    this.isStop = false;    this.isOver = false;    this.direction = 'right';    this.createSnake();    this.createFood();    this.draw();    this.move();    // this.mask.style.display = 'none';  }
  // 创建蛇主体  createSnake(): void {    this.snake = [      [4, 25],      [3, 25],      [2, 25],      [1, 25],      [0, 25]    ];  }
  // 移动  move(): void {    if (this.isStop) {      return;    }
    let [x, y] = this.snake[0];    switch (this.direction) {      case 'left':        x--;        break;      case 'right':        x++;        break;      case 'up':        y--;        break;      case 'down':        y++;        break;    }
    // 如果下一步不是食物的位置    if (x !== this.food[0] || y !== this.food[1]) {      this.snake.pop();    } else {      this.createFood();    }
    if (this.over([x, y])) {      this.isOver = true;      // this.mask.style.display = 'block';      // this.mask.innerHTML = '结束';      return;    }    if (this.completed()) {      // this.mask.style.display = 'block';      // this.mask.innerHTML = '恭喜您,游戏通关';      return;    }
    this.snake.unshift([x, y]);
    this.draw();    this.canChange = true;    this.timer = setTimeout(() => this.move(), this.speed);  }
  // 暂停游戏  stop(): void {    if (this.isOver) {      return;    }    this.isStop = true;    // this.mask.style.display = 'block';    // this.mask.innerHTML = '暂停';  }
  // 继续游戏  continue(): void {    if (this.isOver) {      return;    }    this.isStop = false;    this.move();    // this.mask.style.display = 'none';  }
  getDirection(): void {    // 上38 下40 左37 右39 不能往相反的方向走    document.onkeydown = (e) => {      // 在贪吃蛇移动的间隔内不能连续改变两次方向      if (!this.canChange) {        return;      }      switch (e.keyCode) {        case 37:          if (this.direction !== 'right') {            this.direction = 'left';            this.canChange = false;          }          break;        case 38:          if (this.direction !== 'down') {            this.direction = 'up';            this.canChange = false;          }          break;        case 39:          if (this.direction !== 'left') {            this.direction = 'right';            this.canChange = false;          }          break;        case 40:          if (this.direction !== 'up') {            this.direction = 'down';            this.canChange = false;          }          break;        case 32:          // 空格暂停与继续          if (!this.isStop) {            this.stop();          } else {            this.continue();          }          break;      }    };  }  createPos(): any {    // tslint:disable-next-line: no-bitwise    const [x, y] = this.grid[(Math.random() * this.grid.length) | 0];
    for (const item of this.snake) {      if (item[0] === x && item[1] === y) {        return this.createPos();      }    }    // for (let i = 0; i < this.snake.length; i++) {    //   if (this.snake[i][0] === x && this.snake[i][1] === y) {    //     return this.createPos();    //   }    // }
    return [x, y];  }  // 生成食物  createFood(): void {    this.food = this.createPos();
    // 更新分数    // this.scoreDom.innerHTML = 'Score: ' + this.score++;
    if (this.speed > 50) {      this.speed--;    }  }
  // 结束  over([x, y]): boolean {    if (x < 0 || x >= this.maxX || y < 0 || y >= this.maxY) {      return true;    }
    if (this.snake.some(v => v[0] === x && v[1] === y)) {      return true;    }  }
  // 完成  completed(): boolean {    if (this.snake.length === this.maxX * this.maxY) {      return true;    }  }
  // 网格线  drawGridLine(): void {    for (let i = 1; i < this.maxY; i++) {      this.ctx.moveTo(0, i * this.itemWidth);      this.ctx.lineTo(this.canvas.width, i * this.itemWidth);    }
    for (let i = 1; i < this.maxX; i++) {      this.ctx.moveTo(i * this.itemWidth, 0);      this.ctx.lineTo(i * this.itemWidth, this.canvas.height);    }    this.ctx.lineWidth = 1;    this.ctx.strokeStyle = '#ddd';    this.ctx.stroke();  }
  // 绘制  draw(): void {    // 清空画布    this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
    this.drawGridLine();
    this.ctx.fillStyle = '#000';    this.ctx.fillRect(      this.food[0] * this.itemWidth + this.j,      this.food[1] * this.itemWidth + this.j,      this.itemWidth - this.j * 2,      this.itemWidth - + this.j * 2    );    // tslint:disable-next-line: no-bitwise    this.j ^= 1;
    this.ctx.fillStyle = 'green';    this.ctx.fillRect(      this.snake[0][0] * this.itemWidth + 0.5,      this.snake[0][1] * this.itemWidth + 0.5,      this.itemWidth - 1,      this.itemWidth - 1    );    this.ctx.fillStyle = 'red';    for (let i = 1; i < this.snake.length; i++) {      this.ctx.fillRect(        this.snake[i][0] * this.itemWidth + 0.5,        this.snake[i][1] * this.itemWidth + 0.5,        this.itemWidth - 1,        this.itemWidth - 1      );    }  }}复制代码
 第三步:在 Quill 注册自定义 Blot
有了 SnakeBlot,还需要将其注册到 Quill 中才能使用:
import SnakeBlot from './formats/snake';Quill.register('formats/snake', SnakeBlot);复制代码
 第四步:调用 Quill 的 API 插入自定义内容
调用完 API 就可以玩贪吃蛇游戏啦,开心到飞起!
const quill = new Quill('#editor', {  theme: 'snow',  modules: {    toolbar: {      container: TOOLBAR_CONFIG,      handlers: {        ...        snake(value): void {          console.log('snake~~');          const index = this.quill.getSelection().index;          // 插入自定义内容          this.quill.insertEmbed(index, 'snake', {            id: 'canvas-snake',          });        },      },    }  },});复制代码
 效果图:
 
 划线
评论
复制
发布于: 2021 年 06 月 10 日阅读数: 16
版权声明: 本文为 InfoQ 作者【华为云开发者社区】的原创文章。
原文链接:【http://xie.infoq.cn/article/3af7f59a234258eba2fde4179】。文章转载请联系作者。

华为云开发者社区
关注
提供全面深入的云计算技术干货 2020.07.14 加入
华为云开发者社区,提供全面深入的云计算前景分析、丰富的技术干货、程序样例,分享华为云前沿资讯动态,方便开发者快速成长与发展,欢迎提问、互动,多方位了解云计算! 传送门:https://bbs.huaweicloud.com/











 
    
评论