写点什么

我的编辑器能玩贪吃蛇,一起玩不?

发布于: 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
用户头像

提供全面深入的云计算技术干货 2020.07.14 加入

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

评论

发布
暂无评论
我的编辑器能玩贪吃蛇,一起玩不?