Все взрослые когда-то были детьми, но лишь немногие помнят. --"маленький принц"
DevUIЭто интерфейсное решение с открытым исходным кодом для корпоративных продуктов среднего и внутреннего уровня.沉浸
,灵活
,至简
Разрабатывайте ценности, призывайте дизайнеров удовлетворять реальные потребности, проектируйте для большинства людей и отказывайтесь от броских и привлекательных дизайнов. если вы развиваетесьToB
из工具类产品
, DevUI будет очень хорошим выбором!
введение
Сегодня День защиты детей, я вдруг вспомнил, что видел некоторое время назад对半
Игра про змей, написанная одноклассниками, говорят, что对半
Одноклассникам понадобился всего час, чтобы написать его.
«Canvas 300 строк кода для реализации змеи»
Я лежал на кровати и чистил телефон, когда увидел对半
Статья однокурсника сразу подсела, а прочитав внимательно, сразу поставил лайк и прокомментировал.
Хоть эта статья и не загорелась, но это не влияет на то, что это хорошая статья.
Так что я думал
Можно ли эту игру про змейку вставить в редактор?
Сталкиваетесь ли вы в своей повседневной работе со странным контентом, вставленным в редактор?
Добро пожаловать для обсуждения в области комментариев.
Все еще вставляется пользовательский контент
Без лишних слов, просто обратитесь к предыдущей статье:
«Обитый текстовый редактор тренировок»
Просто выполните следующие четыре шага:
- Шаг 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',
});
},
},
}
},
});
Изображение эффекта:
Всегда сохраняйте свою детскую невинность и сохраняйте свое любопытство к миру, вы лучший подарок, который Бог принес миру.
С Днем защиты детей вас, кто когда-то был ребенком!
Добро пожаловать в DevUI Assistant WeChat: devui-official для совместного обсуждения технологий текстового редактора и интерфейсных технологий.
Добро пожаловать, чтобы следовать за намиDevUIБиблиотека компонентов, зажечь нашу маленькую звезду 🌟:
GitHub.com/Облако разработки Fe/…
Также добро пожаловать в недавно выпущенный DevUIDevUI AdminСистема из коробки строит красивую и атмосферную систему управления фоном за 10 минут!
Присоединяйтесь к нам
Мы команда DevUI, приглашаем вас сюда, чтобы вместе с нами создать элегантную и эффективную систему человеко-машинного дизайна/исследований и разработок. Электронная почта для приема на работу:muyang2@huawei.com.
Текст/DevUI Кагол
Рекомендуемые статьи в прошлом
Как вставить дракона в редактор? 》
«Практика работы с редактором Quill Rich Text»