Сегодня День защиты детей, давайте поиграем всей змейкой в ​​редакторе

внешний интерфейс Angular.js
Сегодня День защиты детей, давайте поиграем всей змейкой в ​​редакторе

Все взрослые когда-то были детьми, но лишь немногие помнят. --"маленький принц"

DevUIЭто интерфейсное решение с открытым исходным кодом для корпоративных продуктов среднего и внутреннего уровня.沉浸,灵活,至简Разрабатывайте ценности, призывайте дизайнеров удовлетворять реальные потребности, проектируйте для большинства людей и отказывайтесь от броских и привлекательных дизайнов. если вы развиваетесьToBиз工具类产品, DevUI будет очень хорошим выбором!

Kagol.png

введение

Сегодня День защиты детей, я вдруг вспомнил, что видел некоторое время назад对半Игра про змей, написанная одноклассниками, говорят, что对半Одноклассникам понадобился всего час, чтобы написать его.

«Canvas 300 строк кода для реализации змеи»

Я лежал на кровати и чистил телефон, когда увидел对半Статья однокурсника сразу подсела, а прочитав внимательно, сразу поставил лайк и прокомментировал.

图片.png

Хоть эта статья и не загорелась, но это не влияет на то, что это хорошая статья.

Так что я думал

Можно ли эту игру про змейку вставить в редактор?

Сталкиваетесь ли вы в своей повседневной работе со странным контентом, вставленным в редактор?

Добро пожаловать для обсуждения в области комментариев.

Все еще вставляется пользовательский контент

Без лишних слов, просто обратитесь к предыдущей статье:

«Обитый текстовый редактор тренировок»

Просто выполните следующие четыре шага:

  • Шаг 1. Настройте кнопки панели инструментов
  • Шаг 2. Настройте содержимое блота
  • Шаг 3. Зарегистрируйте пользовательский блот в Quill
  • Шаг 4. Вызов API Quill для вставки пользовательского содержимого

Шаг 1. Настройте кнопки панели инструментов

Это очень просто:

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~~');
        },
      },
    }
  },
});

Шаг 2. Настройте содержимое блота SnakeBlot

Без лишних слов, просто обратитесь к предыдущей статье и напишите прямо:

Как вставить дракона в редактор? 》

«Практика работы с редактором Quill Rich Text»

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 * 40
export 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
      );
    }
  }
}

Шаг 3. Зарегистрируйте пользовательский блот в Quill

Чтобы использовать SnakeBlot, вам также необходимо зарегистрировать его в Quill:

import SnakeBlot from './formats/snake';
Quill.register('formats/snake', SnakeBlot);

Шаг 4. Вызов API Quill для вставки пользовательского содержимого

После вызова 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',
          });
        },
      },
    }
  },
});

Изображение эффекта:

贪吃蛇.gif

Всегда сохраняйте свою детскую невинность и сохраняйте свое любопытство к миру, вы лучший подарок, который Бог принес миру.

С Днем защиты детей вас, кто когда-то был ребенком!

Добро пожаловать в DevUI Assistant WeChat: devui-official для совместного обсуждения технологий текстового редактора и интерфейсных технологий.

Добро пожаловать, чтобы следовать за намиDevUIБиблиотека компонентов, зажечь нашу маленькую звезду 🌟:

GitHub.com/Облако разработки Fe/…

Также добро пожаловать в недавно выпущенный DevUIDevUI AdminСистема из коробки строит красивую и атмосферную систему управления фоном за 10 минут!

Присоединяйтесь к нам

Мы команда DevUI, приглашаем вас сюда, чтобы вместе с нами создать элегантную и эффективную систему человеко-машинного дизайна/исследований и разработок. Электронная почта для приема на работу:muyang2@huawei.com.

Текст/DevUI Кагол

Рекомендуемые статьи в прошлом

Как вставить дракона в редактор? 》

«Практика работы с редактором Quill Rich Text»

«StepsGuide: компонент как последователь»

"Как решить проблему с ошибкой данных, вызванную неравномерной скоростью запросов асинхронного интерфейса? 》

«Вне прозвища! Выпущен DevUI Admin V1.0! 》