Коммит b69ee706 создал по автору Anton Medvedev's avatar Anton Medvedev
Просмотр файлов

Merge branch 'main' into 'main'

#25 Для доски добавлено модальное окно

See merge request teknokomo/universo-monorepo!22
владельцы e01bee79 14d05b02
...@@ -4543,9 +4543,9 @@ ...@@ -4543,9 +4543,9 @@
} }
}, },
"node_modules/phaser3-rex-plugins": { "node_modules/phaser3-rex-plugins": {
"version": "1.60.4", "version": "1.60.5",
"resolved": "https://registry.npmjs.org/phaser3-rex-plugins/-/phaser3-rex-plugins-1.60.4.tgz", "resolved": "https://registry.npmjs.org/phaser3-rex-plugins/-/phaser3-rex-plugins-1.60.5.tgz",
"integrity": "sha512-EzcwHnjmGgnFTZxTeyTTZDqTXdOevxujttpZjgzJGJXfGKKPK1vBtRP2P52DcEaAyKWdn5srbP53yOir+zE98g==", "integrity": "sha512-4H01cj2HdN9aUlSlsFncENOV1nk+LZEdMJJ5fDch4cJqNwE0xFpetBdCJ+0H9I3hK1rvHxw58QABmmT5aiHl+g==",
"dependencies": { "dependencies": {
"eventemitter3": "^3.1.2", "eventemitter3": "^3.1.2",
"handlebars": "^4.7.7", "handlebars": "^4.7.7",
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
"graphql": "^15.8.0", "graphql": "^15.8.0",
"graphql-tag": "^2.12.6", "graphql-tag": "^2.12.6",
"phaser": "^3.60.0", "phaser": "^3.60.0",
"phaser3-rex-plugins": "^1.60.4", "phaser3-rex-plugins": "^1.60.5",
"pinia": "^2.1.6", "pinia": "^2.1.6",
"quasar": "^2.6.0", "quasar": "^2.6.0",
"vue": "^3.0.0", "vue": "^3.0.0",
......
...@@ -91,6 +91,7 @@ const linksList = [ ...@@ -91,6 +91,7 @@ const linksList = [
}, },
]; ];
export default defineComponent({ export default defineComponent({
name: 'MainLayout', name: 'MainLayout',
......
...@@ -31,58 +31,53 @@ class Demo extends Phaser.Scene { ...@@ -31,58 +31,53 @@ class Demo extends Phaser.Scene {
} }
create(): void { create(): void {
const scrollablePanel = this.rexUI.add.scrollablePanel({
x: 400, const scrollablePanel = this.rexUI.add.scrollablePanel({
y: 300, x: 400,
width: 400, y: 300,
height: 400, width: 400,
scrollMode: 2, // Добавлено для активации обеих полос прокрутки height: 400,
background: this.rexUI.add.roundRectangle(0, 0, 2, 2, 10, COLOR_PRIMARY).setStrokeStyle(2, COLOR_DARK), scrollMode: 2,
panel: { background: this.rexUI.add.roundRectangle(0, 0, 2, 2, 10, COLOR_PRIMARY).setStrokeStyle(1, COLOR_DARK),
child: CreatePanel(this), panel: {
mask: { child: CreatePanel(this),
padding: 1 mask: {
}, padding: 1
},
sliderX: { // Добавлено для горизонтальной полосы прокрутки
track: this.rexUI.add.roundRectangle(0, 0, 20, 10, 10, COLOR_DARK),
thumb: this.rexUI.add.roundRectangle(0, 0, 0, 0, 13, COLOR_LIGHT),
hideUnscrollableSlider: true,
input: 'click',
},
sliderY: {
track: this.rexUI.add.roundRectangle(0, 0, 20, 10, 10, COLOR_DARK),
thumb: this.rexUI.add.roundRectangle(0, 0, 0, 0, 13, COLOR_LIGHT),
hideUnscrollableSlider: true,
input: 'click',
},
header: this.rexUI.add.label({
height: 30,
background: this.rexUI.add.roundRectangle(0, 0, 20, 20, 0, COLOR_DARK),
text: this.add.text(0, 0, 'Header'),
}),
space: {
left: 10,
right: 10,
top: 10,
bottom: 10,
panel: 10,
sliderX: 10, // Добавлено пространство для горизонтальной полосы прокрутки
sliderY: 10
}, },
scrollerX: false, },
scrollerY: false, sliderX: {
track: this.rexUI.add.roundRectangle(0, 0, 20, 10, 10, COLOR_DARK),
thumb: this.rexUI.add.roundRectangle(0, 0, 0, 0, 13, COLOR_LIGHT),
hideUnscrollableSlider: true,
input: 'click',
},
sliderY: {
track: this.rexUI.add.roundRectangle(0, 0, 20, 10, 10, COLOR_DARK),
thumb: this.rexUI.add.roundRectangle(0, 0, 0, 0, 13, COLOR_LIGHT),
hideUnscrollableSlider: true,
input: 'click',
},
header: CreateHeader(this),
space: {
left: 10,
right: 10,
top: 10,
bottom: 10,
panel: 10,
sliderX: 10,
sliderY: 10
},
scrollerX: false,
scrollerY: false,
}) })
.setDraggable('header') .setDraggable('header') // Draggable-header
.layout(); .layout();
AddResizeController(scrollablePanel); AddResizeController(scrollablePanel);
const options = ['A', 'BB', 'CCC', 'DDDD']; this.add.text(0, 580, 'Drag-drop any item to any sizer');
const dropDownList = CreateDropDownList(this, scrollablePanel.width - -150, 125, options);
dropDownList.layout();
scrollablePanel.add(dropDownList);
} }
...@@ -138,6 +133,8 @@ const CreateLabel = (scene: Phaser.Scene, text: string): Phaser.GameObjects.Cont ...@@ -138,6 +133,8 @@ const CreateLabel = (scene: Phaser.Scene, text: string): Phaser.GameObjects.Cont
background: scene.rexUI.add.roundRectangle(0, 0, 0, 0, 10, COLOR_LIGHT), background: scene.rexUI.add.roundRectangle(0, 0, 0, 0, 10, COLOR_LIGHT),
text: scene.add.text(0, 0, text, { text: scene.add.text(0, 0, text, {
fontSize: 18 fontSize: 18
}), }),
align: 'center', align: 'center',
space: { space: {
...@@ -226,12 +223,36 @@ bottomRighterController ...@@ -226,12 +223,36 @@ bottomRighterController
sizer.pin(bottomRighterController); sizer.pin(bottomRighterController);
} }
const CreateDropDownList = (scene: Phaser.Scene, x: number, y: number, options: string[]): Phaser.GameObjects.Container => { const CreateHeader = function (scene: Phaser.Scene): Phaser.GameObjects.Container {
const sizer = scene.rexUI.add.sizer({
orientation: 'x'
})
.addBackground(scene.rexUI.add.roundRectangle(0, 0, 20, 20, 0, COLOR_DARK))
const headerLabel = scene.rexUI.add.label({
text: scene.add.text(0, 0, 'Header'),
})
const dropDownButton = CreateDropDownList(scene)
sizer
.add(
headerLabel,
{ proportion: 1, expand: true }
)
.add(
dropDownButton,
{ proportion: 0, expand: true }
)
return sizer;
}
const CreateDropDownList = function (scene: Phaser.Scene): Phaser.GameObjects.Text {
const options = ['Добавить колонку', 'Переименовать доску'];
const maxTextSize = GetMaxTextObjectSize(scene, options); const maxTextSize = GetMaxTextObjectSize(scene, options);
const label = scene.rexUI.add.label({ const label = scene.rexUI.add.label({
x: x, y: y,
background: scene.rexUI.add.roundRectangle(0, 0, 2, 2, 0, COLOR_PRIMARY).setAlpha(0), background: scene.rexUI.add.roundRectangle(0, 0, 2, 2, 0, COLOR_PRIMARY).setAlpha(0),
icon: scene.rexUI.add.roundRectangle(0, 0, 20, 20, 10, COLOR_LIGHT),
text: CreateTextObject(scene, '...').setFixedSize(maxTextSize.width, maxTextSize.height), text: CreateTextObject(scene, '...').setFixedSize(maxTextSize.width, maxTextSize.height),
space: { space: {
left: 10, left: 10,
...@@ -245,11 +266,16 @@ const CreateDropDownList = (scene: Phaser.Scene, x: number, y: number, options: ...@@ -245,11 +266,16 @@ const CreateDropDownList = (scene: Phaser.Scene, x: number, y: number, options:
let menu: Phaser.GameObjects.Container | undefined; let menu: Phaser.GameObjects.Container | undefined;
scene.rexUI.add.click(label).on('click', () => { scene.rexUI.add.click(label).on('click', () => {
if (!menu) { if (!menu || !menu.scene) {
const menuX = label.getElement('text').getTopLeft().x; const menuX = label.getElement('text').getTopLeft().x;
const menuY = label.bottom; const menuY = label.bottom;
menu = CreatePopupList(scene, menuX, menuY, options, (button: Phaser.GameObjects.Container) => { menu = CreatePopupList(scene, menuX, menuY, options, function (button) {
label.setData('value', button.text); console.log('Click', button.text); // Добавленный console.log
if (button.text === 'Добавить колонку') {
// Ваш код для добавления новой колонки
} else if (button.text === 'Переименовать доску') {
// Ваш код для переименования доски
}
menu?.collapse(); menu?.collapse();
menu = undefined; menu = undefined;
}); });
...@@ -262,7 +288,261 @@ const CreateDropDownList = (scene: Phaser.Scene, x: number, y: number, options: ...@@ -262,7 +288,261 @@ const CreateDropDownList = (scene: Phaser.Scene, x: number, y: number, options:
return label; return label;
}; };
const CreatePopupList = (scene: Phaser.Scene, x: number, y: number, options: string[], onClick: (button: Phaser.GameObjects.Container) => void): Phaser.GameObjects.Container => { const CreateModalLabel = (scene: Phaser.Scene, text: string): Phaser.GameObjects.Container => {
const normalBackground = scene.rexUI.add.roundRectangle(0, 0, 0, 0, 10, 0xffffff, 0.0);
normalBackground.setStrokeStyle(2, 0x000000); // Чёрная обводка
const hoverBackground = scene.rexUI.add.roundRectangle(0, 0, 0, 0, 10, 0xffffff, 0.0);
hoverBackground.setStrokeStyle(2, 0x000000); // Чёрная обводка
const label = scene.rexUI.add.label({
background: normalBackground,
text: scene.add.text(0, 0, text, {
fontSize: 18,
color: '#000000' // Чёрный текст
}),
align: 'center',
space: {
left: 5,
right: 5,
top: 5,
bottom: 5,
},
})
.on('button.over', function (button, groupName, index, pointer, event) {
button.getElement('background').destroy();
button.setElement('background', hoverBackground);
})
.on('button.out', function (button, groupName, index, pointer, event) {
button.getElement('background').destroy();
button.setElement('background', normalBackground);
});
return label;
}
const CreateModalBoard = (scene: Phaser.Scene): any => {
const content = '1';
const textArea = scene.rexUI.add.textAreaInput({
x: 400,
y: 300,
width: 280,
height: 20,
background: scene.rexUI.add.roundRectangle(0, 0, 2, 2, 0, COLOR_PRIMARY),
text: {
background: {
stroke: 'black',
// 'focus.stroke': 'red',
},
style: {
fontSize: 20,
backgroundBottomY: 1,
backgroundHeight: 20,
'cursor.color': 'black',
'cursor.backgroundColor': 'white',
color: '#000000'
},
},
space: {
left: 0,
right: 0,
top: 0,
bottom: 0,
text: 10,
header: 0,
footer: 0,
},
mouseWheelScroller: {
focus: false,
speed: 0.1,
},
content: content,
})
.layout()
.on('textchange', function (text: string) {
console.log(`Content: '${text}'`);
});
textArea.setDepth(2);
const separator = scene.add.rectangle(0, 0, 280, 2, 0x000000);
const dialog = scene.rexUI.add.dialog({
background: scene.rexUI.add.roundRectangle(0, 0, 100, 100, 20, 0xffffff).setStrokeStyle(2, 0x000000),
title: scene.rexUI.add.label({
text: scene.add.text(0, 0, 'Редактирование объекта', {
fontSize: '24px',
color: '#000000'
}),
space: {
left: 15,
right: 15,
top: 10,
bottom: 10
}
}),
content: textArea,
actions: [
CreateModalLabel(scene, 'Отменить'),
CreateModalLabel(scene, 'Сохранить')
],
space: {
title: 25,
content: 25,
action: 15,
left: 20,
right: 20,
top: 20,
bottom: 20,
},
align: {
actions: 'right',
},
expand: {
content: false,
}
})
.on('button.over', function (button, groupName, index, pointer, event) {
button.getElement('background').setStrokeStyle(1, 0xffffff);
})
.on('button.out', function (button, groupName, index, pointer, event) {
button.getElement('background').setStrokeStyle();
});
return dialog;
}
const CreateModalColumn = (scene: Phaser.Scene): any => {
const content = '1';
const textArea = scene.rexUI.add.textAreaInput({
x: 400,
y: 300,
width: 280,
height: 20,
background: scene.rexUI.add.roundRectangle(0, 0, 2, 2, 0, COLOR_PRIMARY),
text: {
background: {
stroke: 'black',
// 'focus.stroke': 'red',
},
style: {
fontSize: 20,
backgroundBottomY: 1,
backgroundHeight: 20,
'cursor.color': 'black',
'cursor.backgroundColor': 'white',
color: '#000000'
},
},
space: {
left: 0,
right: 0,
top: 0,
bottom: 0,
text: 10,
header: 0,
footer: 0,
},
mouseWheelScroller: {
focus: false,
speed: 0.1,
},
content: content,
})
.layout()
.on('textchange', function (text: string) {
console.log(`Content: '${text}'`);
});
textArea.setDepth(2);
const separator = scene.add.rectangle(0, 0, 280, 2, 0x000000);
const dialog = scene.rexUI.add.dialog({
background: scene.rexUI.add.roundRectangle(0, 0, 100, 100, 20, 0xffffff).setStrokeStyle(2, 0x000000),
title: scene.rexUI.add.label({
text: scene.add.text(0, 0, 'Редактирование объекта', {
fontSize: '24px',
color: '#000000'
}),
space: {
left: 15,
right: 15,
top: 10,
bottom: 10
}
}),
content: textArea,
actions: [
CreateModalLabel(scene, 'Отменить'),
CreateModalLabel(scene, 'Сохранить')
],
space: {
title: 25,
content: 25,
action: 15,
left: 20,
right: 20,
top: 20,
bottom: 20,
},
align: {
actions: 'right',
},
expand: {
content: false,
}
})
.on('button.over', function (button, groupName, index, pointer, event) {
button.getElement('background').setStrokeStyle(1, 0xffffff);
})
.on('button.out', function (button, groupName, index, pointer, event) {
button.getElement('background').setStrokeStyle();
});
return dialog;
}
const CreatePopupList = function (scene: Phaser.Scene, x: number, y: number, options: string[], onClick: (button: Phaser.GameObjects.Text) => void): Phaser.GameObjects.Container {
const items = options.map(option => ({ label: option })); const items = options.map(option => ({ label: option }));
const menu = scene.rexUI.add.menu({ const menu = scene.rexUI.add.menu({
x: x, x: x,
...@@ -292,12 +572,43 @@ const CreatePopupList = (scene: Phaser.Scene, x: number, y: number, options: str ...@@ -292,12 +572,43 @@ const CreatePopupList = (scene: Phaser.Scene, x: number, y: number, options: str
} }
}); });
menu.on('button.click', onClick); menu.on('button.click', function(button) {
if (button.text === 'Добавить колонку') {
CreateModalColumn(scene)
.setPosition(400, 300)
.layout()
.modalPromise({
manaulClose: true,
duration: {
in: 500,
out: 500
}
})
.then(function (data) {
console.log(data);
});
} else if (button.text === 'Переименовать доску') {
CreateModalBoard(scene)
.setPosition(400, 300)
.layout()
.modalPromise({
manaulClose: true,
duration: {
in: 500,
out: 500
}
})
.then(function (data) {
console.log(data);
});
}
menu.collapse();
})
return menu; return menu;
}; };
const GetMaxTextObjectSize = (scene: Phaser.Scene, contentArray: string[]): { width: number, height: number } => { const GetMaxTextObjectSize = function (scene: Phaser.Scene, contentArray: string[]): { width: number, height: number } {
const textObject = CreateTextObject(scene, ''); const textObject = CreateTextObject(scene, '');
let width = 0, height = 0; let width = 0, height = 0;
for (const content of contentArray) { for (const content of contentArray) {
...@@ -309,7 +620,7 @@ const GetMaxTextObjectSize = (scene: Phaser.Scene, contentArray: string[]): { wi ...@@ -309,7 +620,7 @@ const GetMaxTextObjectSize = (scene: Phaser.Scene, contentArray: string[]): { wi
return { width, height }; return { width, height };
}; };
const CreateTextObject = (scene: Phaser.Scene, text: string): Phaser.GameObjects.Text => { const CreateTextObject = function (scene: Phaser.Scene, text: string): Phaser.GameObjects.Text {
return scene.add.text(0, 0, text, { return scene.add.text(0, 0, text, {
fontSize: '20px' fontSize: '20px'
}); });
......
<template>
<q-layout view="hHh lpR fFf">
<!-- Шапка страницы -->
<q-header elevated>
<q-toolbar>
<!-- Кнопка для открытия/закрытия бокового меню -->
<q-btn
dense
flat
round
@click="drawer = !drawer"
icon="menu"
/>
<q-toolbar-title>
Бесконечный холст
</q-toolbar-title>
</q-toolbar>
</q-header>
<!-- Боковое меню -->
<q-drawer
v-model="drawer"
show-if-above
>
<!-- Кнопка для добавления новой доски -->
<q-btn
color="Standard"
text-color="#00000000"
@click="addBoard"
label="Добавить доску"
/>
</q-drawer>
<!-- Контейнер для основного содержимого страницы -->
<q-page-container class="grid-center">
<!-- Контейнер для игры Phaser -->
<div ref="gameRef"></div>
<!-- Скрытое поле ввода для редактирования заголовков досок -->
<input
ref="inputRef"
v-model="editingText"
@input="updateTitle"
@blur="stopEditing"
style="position: absolute; display: none"
/>
<!-- Цикл для создания элементов для каждой доски -->
<div v-for="(board, index) in boards" :key="index" class="board-inputs">
<input
v-model="board.userTitle"
placeholder="Board title"
:class="{ 'highlighted': board.isHighlighted }"
@click="toggleHighlight(board)"
/>
<div v-for="(column, columnIndex) in board.userColumns" :key="columnIndex" class="column-inputs">
<input v-model="column.title" placeholder="Column title" />
<textarea v-model="column.text" placeholder="Column text"></textarea>
</div>
</div>
</q-page-container>
</q-layout>
</template>
<script lang="ts">
// Импортируем необходимые функции и объекты из библиотеки Vue и Phaser.
import { ref, onMounted } from 'vue';
import * as Phaser from 'phaser';
import { defineComponent } from 'vue';
// Определение класса Board, который представляет собой доску.
class Board {
// Объявляем свойства класса Board.
// Прямоугольник, представляющий доску.
public board: Phaser.GameObjects.Rectangle;
// Область, в которой можно перетаскивать доску.
public draggableArea: Phaser.GameObjects.Rectangle;
// Текстовый объект для отображения заголовка доски.
public title: Phaser.GameObjects.Text;
// Двумерный массив прямоугольников, представляющих колонки на доске.
public columns: Phaser.GameObjects.Rectangle[][] = [];
// Массив прямоугольников, представляющих карточки.
public cards: Phaser.GameObjects.Rectangle[] = [];
// Выбранная карточка для перетаскивания.
public selectedCard: Phaser.GameObjects.Rectangle | null = null;
// Смещение X и Y для выбранной карточки.
public cardOffsetX = 0;
public cardOffsetY = 0;
// Статический массив всех досок.
static boards: Board[] = [];
// Смещение X и Y для доски.
public offsetX = 0;
public offsetY = 0;
// Позиция, в которую следует переместить карточку.
public targetCardPosition: { x: number, y: number } | null = null;
// Массив линий на доске (не используется в предоставленном коде).
private lines: Phaser.GameObjects.Rectangle[] = [];
// Выделенная колонка.
public highlightedColumn: Phaser.GameObjects.Rectangle | null = null;
// Массив текстовых объектов для отображения заголовков колонок.
public columnTitles: Phaser.GameObjects.Text[] = [];
// Массив пользовательских колонок (тип не указан).
// eslint-disable-next-line @typescript-eslint/no-explicit-any
public userColumns: any[] = [];
// Исходная позиция карточки перед перетаскиванием.
public originalCardPosition: { x: number, y: number } | null = null;
// Массив временно занятых позиций.
public temporarilyOccupied: { x: number, y: number }[] = [];
// Массив стикеров на доске.
public stickers: Phaser.GameObjects.Rectangle[] = [];
// Заголовок пользователя.
userTitle = '';
// Флаг, указывающий, выделен ли объект.
isHighlighted = false;
// Конструктор класса Board.
constructor(private scene: Phaser.Scene, x: number, y: number, title: string) {
// Создаем прямоугольник для доски.
this.board = scene.add.rectangle(x, y + 200, 600, 400, 0xBDBDBD).setStrokeStyle(1, 0x000000);
// Создаем область для перетаскивания доски.
this.draggableArea = scene.add.rectangle(x, y + 25, 600, 50, 0xff0000, 0).setInteractive().setStrokeStyle(1, 0x000000);
// Создаем текстовый объект для заголовка доски.
this.title = scene.add.text(x - 282, y + 15, title, { color: '#000000' , fontSize: '20px' }).setInteractive();
// Добавляем обработчик события на нажатие на заголовок.
this.title.on('pointerdown', () => {
// Переключаем флаг выделения.
this.isHighlighted = !this.isHighlighted;
// Устанавливаем цвет заголовка в зависимости от флага выделения.
this.title.setColor(this.isHighlighted ? '#ffff00' : '#000000');
});
// Цикл для создания трех колонок на доске.
for (let i = 0; i < 3; i++) {
// Инициализация массива для текущей колонки.
this.columns[i] = [];
// Создание текстового объекта для заголовка колонки.
const columnTitle = scene.add.text(x - 282 + i * 200, y + 70, `Column ${i + 1}`, { color: '#000000' });
// Добавление заголовка колонки в массив columnTitles.
this.columnTitles.push(columnTitle);
// Цикл для создания трех ячеек в текущей колонке.
for (let j = 0; j < 3; j++) {
// Создание прямоугольника для ячейки.
const column = scene.add.rectangle(x - 200 + i * 200, y + 140 + j * 100, 170, 90, 0x000000).setInteractive();
// Добавление ячейки в текущую колонку.
this.columns[i].push(column);
}
}
if (i === 0 || i === 2) {
const line = scene.add.rectangle((i === 0 ? column.x + 100 : column.x - 100), y + 238, 2, 300, 0x000000);
this.lines.push(line);
}
}
this.scene.input.on('pointermove', (pointer: Phaser.Input.Pointer) => {
if (this.selectedCard) {
// Установка позиции выбранной карточки в соответствии с позицией указателя.
this.selectedCard.setPosition(pointer.x - this.cardOffsetX, pointer.y - this.cardOffsetY);
// Проверка пересечения выбранной карточки с каждой ячейкой каждой колонки.
this.columns.forEach((columnGroup) => {
columnGroup.forEach((column) => {
// Сброс подсветки у всех ячеек.
column.setStrokeStyle(1, 0x000000);
// Проверка, занята ли текущая ячейка.
const isTemporarilyOccupied = this.temporarilyOccupied.some(pos => pos.x === column.x && pos.y === column.y);
const isOccupied = this.cards.some(card => card.x === column.x && card.y === column.y) || isTemporarilyOccupied;
// Если выбранная карточка пересекается с текущей ячейкой.
if (column && this.selectedCard && Phaser.Geom.Intersects.RectangleToRectangle(this.selectedCard.getBounds(), column.getBounds())) {
// Установка позиции для перемещения выбранной карточки.
this.targetCardPosition = { x: column.x, y: column.y };
// Подсветка текущей ячейки.
column.setStrokeStyle(2, 0xff0000);
this.highlightedColumn = column;
}
});
});
// Если указатель вышел за пределы доски и карточка выбрана.
if (!this.contains(pointer.x, pointer.y) && this.selectedCard) {
// Поиск первой свободной ячейки на доске.
for (let i = 0; i < this.columns.length; i++) {
for (let j = 0; j < this.columns[i].length; j++) {
const column = this.columns[i][j];
const isOccupied = this.cards.some(card => card.x === column.x && card.y === column.y);
if (!isOccupied) {
// Подсветка найденной свободной ячейки.
column.setStrokeStyle(2, 0xff0000);
this.highlightedColumn = column;
this.targetCardPosition = { x: column.x, y: column.y };
break;
}
}
if (this.highlightedColumn) {
break;
}
}
}
// Перемещение выбранной карточки на другую доску, если указатель находится над ней.
Board.boards.forEach((board) => {
if (board !== this && board.contains(pointer.x, pointer.y)) {
this.cards = this.cards.filter(card => card !== this.selectedCard);
if (this.selectedCard) {
board.cards.push(this.selectedCard);
}
}
});
}
});
}
addSticker(x: number, y: number) {
const sticker = this.scene.add.rectangle(x, y, 170, 90, 0xFFD966).setInteractive().setStrokeStyle(1, 0x000000);
this.stickers.push(sticker);
let offsetX = 0;
let offsetY = 0;
let isDragging = false; // Добавляем эту переменную
sticker.on('pointerdown', (pointer: Phaser.Input.Pointer) => {
offsetX = pointer.x - sticker.x;
offsetY = pointer.y - sticker.y;
sticker.setDepth(9999); // Устанавливаем глубину стикера на максимальное значение
isDragging = true; // Устанавливаем значение в true при нажатии на стикер
});
sticker.on('pointerup', () => {
sticker.setDepth(0); // Возвращаем глубину стикера к исходному значению
isDragging = false; // Устанавливаем значение в false при отпускании кнопки мыши
if (this.contains(sticker.x, sticker.y)) {
// Удаляем стикер
this.stickers = this.stickers.filter(s => s !== sticker);
sticker.destroy();
// Добавляем карточку на самую верхнюю незанятую ячейку
for (let i = 0; i < this.columns.length; i++) {
for (let j = 0; j < this.columns[i].length; j++) {
const column = this.columns[i][j];
const isOccupied = this.cards.some(card => card.x === column.x && card.y === column.y);
if (!isOccupied) {
const card = this.scene.add.rectangle(column.x, column.y, column.width, column.height, 0xFFFFFF).setInteractive().setStrokeStyle(1, 0x000000);
this.cards.push(card);
// Добавляем обработчики событий для новой карточки
card.on('pointerdown', (pointer: Phaser.Input.Pointer) => {
this.selectedCard = card;
this.cardOffsetX = pointer.x - card.x;
this.cardOffsetY = pointer.y - card.y;
card.setDepth(9999); // Устанавливаем глубину карточки на максимальное значение
this.originalCardPosition = { x: card.x, y: card.y };
this.temporarilyOccupied.push({ x: card.x, y: card.y });
});
card.on('pointerup', () => {
// Здесь добавьте логику для обработки события pointerup карточки
// (аналогично тому, что у вас уже есть для других карточек)
});
return; // Завершаем цикл после добавления карточки
}
}
}
}
});
this.scene.input.on('pointermove', (pointer: Phaser.Input.Pointer) => {
if (isDragging) { // Проверяем значение переменной перед перемещением стикера
sticker.setPosition(pointer.x - offsetX, pointer.y - offsetY);
}
});
this.stickers.push(sticker);
return sticker;
}
reorderCards(columnIndex: number, startingSubColumnIndex: number) {
for (let j = startingSubColumnIndex + 1; j < 3; j++) {
const currentCard = this.cards.find(card => card.x === this.columns[columnIndex][j].x && card.y === this.columns[columnIndex][j].y);
if (currentCard) {
// Перемещаем следующую карточку на позицию выше
currentCard.setPosition(this.columns[columnIndex][j - 1].x, this.columns[columnIndex][j - 1].y);
}
}
}
addCard(columnIndex: number, subColumnIndex: number) {
if (this.columns.length > columnIndex && this.columns[columnIndex].length > subColumnIndex) {
const targetColumn = this.columns[columnIndex][subColumnIndex];
const isOccupied = this.cards.some(card => card.x === targetColumn.x && card.y === targetColumn.y);
// Если ячейка уже занята, не добавляем карточку
if (isOccupied) {
return;
}
const card = this.scene.add.rectangle(targetColumn.x, targetColumn.y, targetColumn.width, targetColumn.height, 0xFFFFFF).setInteractive().setStrokeStyle(1, 0x000000);
this.cards.push(card)
card.on('pointerdown', (pointer: Phaser.Input.Pointer) => {
this.selectedCard = card;
this.cardOffsetX = pointer.x - card.x;
this.cardOffsetY = pointer.y - card.y;
card.setDepth(9999); // Устанавливаем глубину карточки на максимальное значение
// Сохраняем исходное положение карточки
this.originalCardPosition = { x: card.x, y: card.y };
this.temporarilyOccupied.push({ x: card.x, y: card.y });
});
card.on('pointerup', () => {
if (this.selectedCard) {
if (!this.contains(this.selectedCard.x, this.selectedCard.y)) {
// Если карточка за пределами доски, преобразуем её в стикер
const sticker = this.addSticker(this.selectedCard.x, this.selectedCard.y);
// Удаляем исходную карточку
this.selectedCard.destroy();
this.cards = this.cards.filter(card => card !== this.selectedCard);
// Переупорядочиваем карточки
if (this.originalCardPosition) {
const originalColumnIndex = this.columns.findIndex(columnGroup => columnGroup.some(column => column.x === this.originalCardPosition.x));
const originalSubColumnIndex = this.columns[originalColumnIndex].findIndex(column => column.y === this.originalCardPosition.y);
this.reorderCards(originalColumnIndex, originalSubColumnIndex);
}
} else {
if (this.targetCardPosition && this.selectedCard) {
// Определение индекса колонки, в которой находится карточка
const columnIndex = this.highlightedColumn ? this.columns.findIndex(columnGroup => columnGroup.includes(this.highlightedColumn)) : -1;
if (columnIndex !== -1) {
// Находим самую верхнюю незанятую подколонку
for (let j = 0; j < 3; j++) {
const targetSubColumn = this.columns[columnIndex][j];
const isOccupied = this.cards.some(card => card.x === targetSubColumn.x && card.y === targetSubColumn.y);
if (!isOccupied) {
this.targetCardPosition = { x: targetSubColumn.x, y: targetSubColumn.y };
break;
}
}
}
this.selectedCard.setPosition(this.targetCardPosition.x, this.targetCardPosition.y);
this.targetCardPosition = null;
}
if (this.highlightedColumn) {
this.highlightedColumn.setStrokeStyle(1, 0x000000);
this.highlightedColumn = null;
}
if (this.originalCardPosition) {
// Определите индекс колонки и подколонки исходной позиции карточки
const originalColumnIndex = this.columns.findIndex(columnGroup => columnGroup.some(column => column.x === this.originalCardPosition.x));
const originalSubColumnIndex = this.columns[originalColumnIndex].findIndex(column => column.y === this.originalCardPosition.y);
// Переупорядочивание карточек в исходной колонке
for (let j = originalSubColumnIndex + 1; j < 3; j++) {
const currentCard = this.cards.find(card => card.x === this.columns[originalColumnIndex][j].x && card.y === this.columns[originalColumnIndex][j].y);
if (currentCard) {
// Перемещаем следующую карточку на позицию выше
currentCard.setPosition(this.columns[originalColumnIndex][j - 1].x, this.columns[originalColumnIndex][j - 1].y);
}
}
}
}
this.selectedCard = null;
this.originalCardPosition = null;
card.setDepth(0); // Возвращаем глубину карточки к исходному значению
}
});
}
}
contains(x: number, y: number) {
return this.board.getBounds().contains(x, y);
}
setPosition(x: number, y: number) {
const dx = x - this.board.x;
const dy = y - this.board.y;
this.board.setPosition(x, y);
this.title.setPosition(this.title.x + dx, this.title.y + dy);
this.draggableArea.setPosition(this.draggableArea.x + dx, this.draggableArea.y + dy);
this.columns.forEach((columnGroup) => {
columnGroup.forEach((column) => {
column.setPosition(column.x + dx, column.y + dy);
});
});
this.columnTitles.forEach((columnTitle) => {
columnTitle.setPosition(columnTitle.x + dx, columnTitle.y + dy);
});
this.cards.forEach((card) => {
if (card === this.selectedCard) {
card.setPosition(x - this.cardOffsetX, y - this.cardOffsetY);
} else {
card.setPosition(card.x + dx, card.y + dy);
}
});
}
}
export default defineComponent({
setup() {
const gameRef = ref<HTMLDivElement | null>(null);
const drawer = ref(false);
let game: Phaser.Game | null = null;
let selectedBoard: Board | null = null;
const inputRef = ref<HTMLInputElement | null>(null);
const editingText = ref('');
let editingBoard: Board | null = null;
const updateTitle = () => {
if (editingBoard) {
editingBoard.userTitle = editingText.value;
editingBoard.title.setText(editingText.value);
}
};
const stopEditing = () => {
if (inputRef.value) {
inputRef.value.style.display = 'none';
}
if (editingBoard) {
editingBoard.isHighlighted = false;
editingBoard.title.setColor('#000000');
editingBoard = null;
}
};
const addBoard = () => {
if (game && game.scene.scenes[0]) {
const board = new Board(game.scene.scenes[0], 200, 200, 'Board Title');
// Добавляем по одной карточке в каждую ячейку первой колонки
for (let j = 0; j < 3; j++) {
board.addCard(0, j);
}
// Добавляем карточки в верхнюю и центральную ячейки второго столбца
board.addCard(1, 0);
board.addCard(1, 1);
Board.boards.push(board);
}
};
const toggleHighlight = (board: Board) => {
board.isHighlighted = !board.isHighlighted;
};
onMounted(() => {
if (gameRef.value) {
const config: Phaser.Types.Core.GameConfig = {
type: Phaser.AUTO,
backgroundColor: '#ffffff',
parent: gameRef.value,
scene: {
create: function () {
this.input.on('pointerdown', (pointer: Phaser.Input.Pointer) => {
Board.boards.forEach(board => {
if (board.draggableArea.getBounds().contains(pointer.x, pointer.y)) {
selectedBoard = board;
board.offsetX = pointer.x - board.board.x;
board.offsetY = pointer.y - board.board.y;
this.events.on('editTitle', (board: Board) => {
if (inputRef.value) {
editingBoard = board;
editingText.value = board.userTitle;
const rect = board.title.getBounds();
inputRef.value.style.left = `${rect.x}px`;
inputRef.value.style.top = `${rect.y}px`;
inputRef.value.style.width = `${rect.width}px`;
inputRef.value.style.display = 'block';
inputRef.value.focus();
}
});
}
});
});
this.input.on('pointerup', () => {
selectedBoard = null;
});
this.input.on('pointermove', (pointer: Phaser.Input.Pointer) => {
if (selectedBoard) {
selectedBoard.setPosition(pointer.x - selectedBoard.offsetX, pointer.y - selectedBoard.offsetY);
if (selectedBoard.selectedCard) {
selectedBoard.selectedCard.setPosition(pointer.x - selectedBoard.cardOffsetX, pointer.y - selectedBoard.cardOffsetY);
}
}
});
}
}
};
game = new Phaser.Game(config);
}
});
return {
gameRef,
drawer,
addBoard,
toggleHighlight,
inputRef,
editingText,
updateTitle,
stopEditing,
boards: Board.boards
}
}
});
</script>
<style scoped>
.highlighted {
background-color: yellow; /* Измените на нужный цвет выделения */
}
</style>
Поддерживает Markdown
0% или .
You are about to add 0 people to the discussion. Proceed with caution.
Сначала завершите редактирование этого сообщения!
Пожалуйста, зарегистрируйтесь или чтобы прокомментировать