TASK: #103600 - add delay exchange feature
This commit is contained in:
parent
30f071d0cf
commit
fe5a857be2
|
@ -12,3 +12,4 @@
|
|||
- add SlotStyleHandler
|
||||
- edit container folder animation
|
||||
- optimize strategy of container
|
||||
- add delay exchange feature
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
# star-container
|
||||
|
||||
容器组件提供了一个便捷易用的拖拽容器组件,可以根据传参不同,将在指定位置生成不同的子节点
|
||||
|
||||
## 组件结构
|
||||
|
||||
1. `container-page`: 分页组件,用于创建、管理、删除容器分页
|
||||
2. `container-child`: 子节点组件,通过传参可以指定节点大小、位置,也用于给图标、组件定位
|
||||
3. `container-folder`: 文件夹组件,用于创建、管理、删除文件夹
|
||||
4. `gesture-manager`: 手势管理,用于提供多指手势判断
|
||||
5. `element-exchange-strategy`: 子节点之间的交换规则
|
||||
|
||||
## 基本用法
|
||||
|
||||
在`HTML`文件下添加该`container`文件即可使用:
|
||||
|
||||
```html
|
||||
<head>
|
||||
<script src="grid-container/container.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<star-container drag-and-drop></star-container>
|
||||
</body>
|
||||
```
|
||||
|
||||
之后可以在`js`中添加子节点:
|
||||
|
||||
```js
|
||||
const container = document.querySelector('star-container')
|
||||
container.appendContainerChild(element)
|
||||
```
|
||||
|
||||
方法`appendContainerChild`可以接收第二个参数`options`,用以定制子节点的位置、大小、添加完成回调、文件名:
|
||||
|
||||
```ts
|
||||
interface options {
|
||||
row: number // 节点占的行数
|
||||
column: number // 节点占的列数
|
||||
anchorCoordinate: {
|
||||
portrait: [number, number] // 竖屏时,节点左上角占据的位置
|
||||
landscape: [number, number] // 横屏时,节点左上角占据的位置
|
||||
}
|
||||
callback: Function // 添加完成回调
|
||||
folderName: string // 该图标所在的文件夹,若文件夹不存在则该图标所在位置自动生成一个文件夹
|
||||
}
|
||||
```
|
||||
|
||||
## 拖拽换位规则
|
||||
|
||||
交换、放置、移动规则,情景重合时,以越下方的规则为准:
|
||||
|
||||
1. 优先级高的可以挤占优先级低的子节点位置(gridId)
|
||||
2. 图标节点、拖拽中的子节点可以挤占同优先级的子节点位置
|
||||
3. 因位置被挤占而需要寻位的元素依照优先级(priority)的降序,依次寻位
|
||||
4. 被挤占节点(A)的寻位时的方向,与挤占节点(B)的 GridID 对比获得:
|
||||
1. A.gridId < B.gridId: 向上
|
||||
2. A.gridId % container.column < B.gridId % container.column:
|
||||
向左
|
||||
3. 以上两条,若其一的条件相反,相对应的方向也取反
|
||||
5. 寻位时,优先尝试水平方向移动,后尝试垂直方向移动
|
||||
6. 寻位方向并非绝对方向,以移动网格数量最短为优先条件
|
||||
7. 为满足规则 5,规则 6 的寻位方向变化以顺时针方向转变,如:上左 → 上右 → 下右 → 下左
|
||||
8. 图标节点被另一个图标节点挤占位置时,仅能再去挤占相邻网格的图标的位置(即不允许挤占上下相邻图标)
|
|
@ -57,6 +57,11 @@ const MERGE_FORDER_TIME = 700
|
|||
*/
|
||||
const FORDER_OPERATE_TIME = 500
|
||||
|
||||
/**
|
||||
* 悬停交换时长
|
||||
*/
|
||||
const EXCHANGETIMEOUT = 300
|
||||
|
||||
/**
|
||||
* The minimum time between sending move events during drag-and-drop.
|
||||
*/
|
||||
|
@ -196,6 +201,10 @@ class GaiaContainer extends LitElement {
|
|||
childCoordinate: {[gridId: number]: GaiaContainerChild | undefined}[] = []
|
||||
// 交换策略
|
||||
exchangeStratege: ExchangeStrategy
|
||||
// 正在交换的网格
|
||||
exchangeGridId: number | undefined
|
||||
// 交换计时器
|
||||
exchangeTimer: number | undefined
|
||||
constructor(row = 6, column = 4) {
|
||||
super()
|
||||
this.row = row
|
||||
|
@ -210,8 +219,8 @@ class GaiaContainer extends LitElement {
|
|||
firstUpdated() {
|
||||
slotStyleHandler.injectGlobalCss(this, containerStyle.cssText, this.name)
|
||||
let dndObserverCallback = () => {
|
||||
if (this._dnd.enabled !== this.dragAndDrop) {
|
||||
this._dnd.enabled = this.dragAndDrop
|
||||
if (this._dnd.enabled !== this['drag-and-drop']) {
|
||||
this._dnd.enabled = this['drag-and-drop']
|
||||
if (this._dnd.enabled) {
|
||||
this.addEventListener('touchstart', this)
|
||||
this.addEventListener('touchmove', this)
|
||||
|
@ -256,10 +265,10 @@ class GaiaContainer extends LitElement {
|
|||
}
|
||||
}
|
||||
this.dndObserver = new MutationObserver(dndObserverCallback)
|
||||
this.dndObserver.observe(this, {
|
||||
attributes: true,
|
||||
attributeFilter: ['drag-and-drop'],
|
||||
})
|
||||
// this.dndObserver.observe(this, {
|
||||
// attributes: true,
|
||||
// attributeFilter: ['drag-and-drop'],
|
||||
// })
|
||||
|
||||
dndObserverCallback()
|
||||
this.changeLayout()
|
||||
|
@ -437,7 +446,7 @@ class GaiaContainer extends LitElement {
|
|||
}
|
||||
|
||||
// TODO:用于适应旋转屏幕时,改变行数与列数,需要配合row、column属性
|
||||
changeLayout() {
|
||||
changeLayout = () => {
|
||||
// this.styleDom.innerHTML = this.shadowStyle;
|
||||
const {height, width} = this.getBoundingClientRect()
|
||||
this.height = height
|
||||
|
@ -452,7 +461,8 @@ class GaiaContainer extends LitElement {
|
|||
this.style.setProperty('--grid-height', this.gridHeight + 'px')
|
||||
this.style.setProperty('--grid-width', this.gridWidth + 'px')
|
||||
|
||||
this._children.forEach((child) => child.changeSize())
|
||||
// this._children.forEach((child) => child.changeSize())
|
||||
this.synchronise()
|
||||
}
|
||||
|
||||
get containerChildren(): HTMLElement[] {
|
||||
|
@ -576,7 +586,7 @@ class GaiaContainer extends LitElement {
|
|||
return length ? this._children[length - 1].element : null
|
||||
}
|
||||
|
||||
@property({type: Boolean}) dragAndDrop!: boolean
|
||||
@property({type: Boolean}) 'drag-and-drop'!: boolean
|
||||
|
||||
get dragAndDropTimeout() {
|
||||
return this._dnd.delay
|
||||
|
@ -986,25 +996,8 @@ class GaiaContainer extends LitElement {
|
|||
childToInsert.gridId = this.getGridIdByCoordinate(
|
||||
childToInsert._lastMasterLeft!,
|
||||
childToInsert._lastMasterTop!,
|
||||
pagination
|
||||
childToInsert.pagination
|
||||
)
|
||||
this.recordChildGridId(childToInsert)
|
||||
}
|
||||
|
||||
recordChildGridId = (
|
||||
child: GaiaContainerChild,
|
||||
{gridId, pagination}: {gridId: number; pagination: number} = {
|
||||
gridId: child.gridId,
|
||||
pagination: this.pagination,
|
||||
}
|
||||
) => {
|
||||
const recorder = this.childCoordinate[pagination]
|
||||
const {row, column} = child
|
||||
for (let i = 0; i < row; i++) {
|
||||
for (let j = 0; j < column; j++) {
|
||||
recorder[gridId + i + j * this.column] = child
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1453,18 +1446,21 @@ class GaiaContainer extends LitElement {
|
|||
}
|
||||
}
|
||||
|
||||
dropElement() {
|
||||
let {dropTarget, dropChild, isPage, pagination, gridId} =
|
||||
this.getChildFromPoint(
|
||||
this._dnd.gridPosition.x +
|
||||
(this._dnd.last.pageX - this._dnd.start.pageX),
|
||||
this._dnd.gridPosition.y +
|
||||
(this._dnd.last.pageY - this._dnd.start.pageY)
|
||||
)
|
||||
|
||||
// 此次交换是否向前挤压
|
||||
pushAhead: boolean = true
|
||||
// 此次交换是否向上挤压
|
||||
pushUp: boolean = true
|
||||
dropElement(mode: 'delay' | 'immediately' = 'delay') {
|
||||
const distanceX =
|
||||
this._dnd.gridPosition.x + this._dnd.last.pageX - this._dnd.start.pageX
|
||||
const distanceY =
|
||||
this._dnd.gridPosition.y + this._dnd.last.pageY - this._dnd.start.pageY
|
||||
const {dropTarget, dropChild, isPage, pagination, gridId} =
|
||||
this.getChildFromPoint(distanceX, distanceY)
|
||||
const child = this._dnd.child
|
||||
if (
|
||||
this._staticElements.includes(dropTarget) &&
|
||||
(this._dnd.child !== dropChild || gridId == dropChild.gridId)
|
||||
(child !== dropChild || gridId == dropChild.gridId)
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
@ -1472,7 +1468,7 @@ class GaiaContainer extends LitElement {
|
|||
this._dnd.dropTarget = dropTarget
|
||||
// 拖拽元素悬浮页面默认为当前页面
|
||||
const suspendingPage = isPage ? dropTarget! : this.pages[this.pagination]
|
||||
if (this._dnd.child.isTail && isPage) {
|
||||
if (child.isTail && isPage) {
|
||||
if (this._dnd.lastDropChild) {
|
||||
this._dnd.lastDropChild.master.classList.remove('merging')
|
||||
this._dnd.lastDropChild = null
|
||||
|
@ -1480,36 +1476,73 @@ class GaiaContainer extends LitElement {
|
|||
this.clearMergeTimer()
|
||||
// return
|
||||
}
|
||||
if (dropChild?.priority && dropChild.priority > this._dnd.child.priority) {
|
||||
if (dropChild?.priority && dropChild.priority > child.priority) {
|
||||
return this._staticElements.push(dropTarget)
|
||||
}
|
||||
// TODO: 图标与图标之间不应该立即进行尝试交换,而是给予一定时间,一定时间内没有进入
|
||||
// 合并文件夹状态才进行尝试,同时判断图标间的挤占方向
|
||||
let {canPlace, placedRecorder, childCoordinate} =
|
||||
this.exchangeStratege.tryToPlace(
|
||||
this._dnd.child,
|
||||
gridId,
|
||||
this.pagination,
|
||||
'place'
|
||||
)
|
||||
|
||||
if (canPlace) {
|
||||
this._dnd.child.gridId = gridId
|
||||
childCoordinate && (this.childCoordinate = childCoordinate)
|
||||
if (placedRecorder) {
|
||||
for (const pagination in placedRecorder) {
|
||||
for (const gridId in placedRecorder[pagination]) {
|
||||
const child = placedRecorder[pagination][gridId]
|
||||
if (child.pagination != +pagination) {
|
||||
this.pages[pagination].appendChild(child.master)
|
||||
const exchange = (gridId: number) => {
|
||||
// TODO: 图标与图标之间不应该立即进行尝试交换,而是给予一定时间,一定时间内没有进入
|
||||
// 合并文件夹状态才进行尝试,同时判断图标间的挤占方向
|
||||
let {canPlace, placedRecorder, childCoordinate} =
|
||||
this.exchangeStratege.tryToPlace(
|
||||
child,
|
||||
gridId,
|
||||
this.pagination,
|
||||
'place',
|
||||
[this.pushAhead, this.pushUp]
|
||||
)
|
||||
|
||||
if (canPlace) {
|
||||
child.gridId = gridId
|
||||
childCoordinate && (this.childCoordinate = childCoordinate)
|
||||
if (placedRecorder) {
|
||||
for (const pagination in placedRecorder) {
|
||||
for (const gridId in placedRecorder[pagination]) {
|
||||
const child = placedRecorder[pagination][gridId]
|
||||
if (child.pagination != +pagination) {
|
||||
this.pages[pagination].appendChild(child.master)
|
||||
}
|
||||
child.gridId = +gridId
|
||||
}
|
||||
child.gridId = +gridId
|
||||
}
|
||||
this.synchronise()
|
||||
}
|
||||
this.synchronise()
|
||||
}
|
||||
}
|
||||
|
||||
const openChangeTimer = (gridId: number) => {
|
||||
if (mode !== 'immediately' && gridId == this.exchangeGridId) {
|
||||
return
|
||||
}
|
||||
const change = () => {
|
||||
exchange(gridId)
|
||||
this.exchangeTimer = undefined
|
||||
this.exchangeGridId = undefined
|
||||
}
|
||||
this.exchangeGridId = gridId
|
||||
clearTimeout(this.exchangeTimer)
|
||||
if (mode === 'immediately') {
|
||||
change()
|
||||
} else {
|
||||
this.exchangeTimer = setTimeout(change, EXCHANGETIMEOUT)
|
||||
}
|
||||
}
|
||||
|
||||
// 落点网格的中心位置
|
||||
const gridCenter = {
|
||||
x: ((gridId % this.column) + 0.5) * this.gridWidth,
|
||||
y: (Math.floor(gridId / this.column) + 0.5) * this.gridHeight,
|
||||
}
|
||||
|
||||
if (
|
||||
Math.abs(distanceX - gridCenter.x) < 0.5 * this.gridWidth &&
|
||||
Math.abs(distanceY - gridCenter.y) < 0.5 * this.gridHeight
|
||||
) {
|
||||
this.pushAhead = distanceX > gridCenter.x
|
||||
this.pushUp = distanceY > gridCenter.y
|
||||
openChangeTimer(gridId)
|
||||
}
|
||||
|
||||
// let children = this._children
|
||||
// let folderPosition = {left: 0, top: 0}
|
||||
// if (this.openedFolder) {
|
||||
|
@ -1518,9 +1551,9 @@ class GaiaContainer extends LitElement {
|
|||
// folderPosition.top = this.openedFolder.element.offsetTop
|
||||
// }
|
||||
// if (isPage) {
|
||||
// this._dnd.child.isWidget && (this._dnd.child.isStatic = false)
|
||||
// this.realAppendChild(pagination, this._dnd.child.master)
|
||||
// this._dnd.child.isWidget && (this._dnd.child.isStatic = 'current')
|
||||
// child.isWidget && (child.isStatic = false)
|
||||
// this.realAppendChild(pagination, child.master)
|
||||
// child.isWidget && (child.isStatic = 'current')
|
||||
// return this.synchronise()
|
||||
// }
|
||||
// if (dropTarget) {
|
||||
|
@ -1532,7 +1565,7 @@ class GaiaContainer extends LitElement {
|
|||
// this._dnd.last.pageX + this.pages[this.pagination].offsetLeft
|
||||
// const lastY = this._dnd.last.pageY
|
||||
// for (let i = 0, iLen = children.length; i < iLen; i++) {
|
||||
// if (children[i] === this._dnd.child) {
|
||||
// if (children[i] === child) {
|
||||
// childIndex = i
|
||||
// }
|
||||
|
||||
|
@ -1585,35 +1618,35 @@ class GaiaContainer extends LitElement {
|
|||
// !this.mergeTimer &&
|
||||
// dropChild.position === 'page' &&
|
||||
// !dropChild.isFolder &&
|
||||
// !this._dnd.child.isFolder &&
|
||||
// !child.isFolder &&
|
||||
// !dropChild.isWidget &&
|
||||
// !this._dnd.child.isWidget
|
||||
// !child.isWidget
|
||||
// ) {
|
||||
// // 图标悬浮于另一个图标正上方
|
||||
// dropChild.master.classList.add('merging')
|
||||
// this.mergeTimer = setTimeout(() => {
|
||||
// if (!this._dnd.child) return
|
||||
// if (!child) return
|
||||
// this.mergeFolder(
|
||||
// (dropChild as GaiaContainerChild).master,
|
||||
// this._dnd.child.master
|
||||
// child.master
|
||||
// )
|
||||
// this.clearMergeTimer()
|
||||
// }, MERGE_FORDER_TIME)
|
||||
// } else if (
|
||||
// dropChild.position === 'page' &&
|
||||
// dropChild.isFolder &&
|
||||
// !this._dnd.child.isFolder &&
|
||||
// !child.isFolder &&
|
||||
// !dropChild.isWidget &&
|
||||
// !this._dnd.child.isWidget
|
||||
// !child.isWidget
|
||||
// ) {
|
||||
// this.clearMergeTimer()
|
||||
// dropChild.master.classList.add('merging')
|
||||
// this.mergeTimer = setTimeout(() => {
|
||||
// if (!this._dnd.child) return
|
||||
// if (!child) return
|
||||
// ;(dropChild as GaiaContainerFolder).open()
|
||||
// this.mergeFolder(
|
||||
// (dropChild as GaiaContainerFolder).master,
|
||||
// this._dnd.child.master
|
||||
// child.master
|
||||
// )
|
||||
// this.clearMergeTimer()
|
||||
// }, MERGE_FORDER_TIME)
|
||||
|
@ -1744,7 +1777,7 @@ class GaiaContainer extends LitElement {
|
|||
|
||||
endDrag(event: Event) {
|
||||
if (this._dnd.active) {
|
||||
this.dropElement()
|
||||
this.dropElement('immediately')
|
||||
this.dispatchEvent(
|
||||
new CustomEvent('drag-end', {
|
||||
cancelable: true,
|
||||
|
|
|
@ -87,7 +87,8 @@ export default class ExchangeStrategy {
|
|||
pickChildren(childrenArr: Set<GaiaContainerChild>[], pagination: number) {
|
||||
const recorder = this.coordinate[pagination]
|
||||
for (const children of childrenArr) {
|
||||
for (const child of children) {
|
||||
if (!children) continue
|
||||
for (const child of [...children]) {
|
||||
if (!this.pickChild(child, recorder)) {
|
||||
return false
|
||||
}
|
||||
|
@ -115,28 +116,42 @@ export default class ExchangeStrategy {
|
|||
}
|
||||
|
||||
/**
|
||||
* 获取交换元素的移动方向
|
||||
* @param child
|
||||
* @param dropChild
|
||||
* 获取交换节点的移动方向
|
||||
* @param child 交换节点
|
||||
* @param dropChild 落点节点
|
||||
* @param directed 与拖拽节点直接接触节点的移动方向,暂时仅用于图标间的交换
|
||||
* @returns
|
||||
*/
|
||||
getExchangeDirection(
|
||||
child: GaiaContainerChild,
|
||||
dropChild: GaiaContainerChild | undefined
|
||||
dropChild: GaiaContainerChild | undefined,
|
||||
directed?: [boolean, boolean]
|
||||
): [Directions, Directions] {
|
||||
if (!dropChild) return [0, 0]
|
||||
const mColumn = this.manager.column
|
||||
const horizontalDir =
|
||||
child.gridId % mColumn <= dropChild.gridId % mColumn
|
||||
let horizontalDir, verticalDir
|
||||
if (child.priority + dropChild.priority == 2) {
|
||||
// 两个图标在进行交换
|
||||
horizontalDir = directed
|
||||
? directed[0]
|
||||
? Directions.FORWARD
|
||||
: Directions.BACKWARD
|
||||
: child.gridId < dropChild.gridId
|
||||
? Directions.BACKWARD
|
||||
: Directions.FORWARD
|
||||
// 当垂直方向为0时,表示为图标间的交换,比起上下移动,更优先横向、换行移动
|
||||
const verticalDir =
|
||||
child.priority + dropChild.priority == 2
|
||||
? 0
|
||||
: child.gridId > dropChild.gridId
|
||||
? Directions.UPWARD
|
||||
: Directions.DOWN
|
||||
|
||||
// 当垂直方向为0时,比起上下移动,更优先横向、换行移动
|
||||
verticalDir = 0
|
||||
} else {
|
||||
// 小组件参与交换
|
||||
horizontalDir =
|
||||
child.gridId % mColumn <= dropChild.gridId % mColumn
|
||||
? Directions.BACKWARD
|
||||
: Directions.FORWARD
|
||||
verticalDir =
|
||||
child.gridId > dropChild.gridId ? Directions.UPWARD : Directions.DOWN
|
||||
}
|
||||
|
||||
return [horizontalDir, verticalDir]
|
||||
}
|
||||
|
||||
|
@ -172,8 +187,9 @@ export default class ExchangeStrategy {
|
|||
if (!this.suspendChild[priority]) {
|
||||
this.suspendChild[priority] = new Set(children)
|
||||
} else {
|
||||
const suspending = [...this.suspendChild[priority]]
|
||||
this.suspendChild[priority] = new Set([...suspending, ...children])
|
||||
children.forEach((child) => {
|
||||
this.suspendChild[priority].add(child)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -235,6 +251,7 @@ export default class ExchangeStrategy {
|
|||
// 优先级足以放置的网格
|
||||
let access: Set<number> = new Set()
|
||||
const recorder = this.coordinate[pagination]
|
||||
const mColumn = this.manager.column
|
||||
this.pickChild(dropChild, recorder)
|
||||
// 以二维网格的方式遍历格子
|
||||
const BFS = (
|
||||
|
@ -242,7 +259,6 @@ export default class ExchangeStrategy {
|
|||
[horizontalDir, verticalDir]: [Directions, Directions],
|
||||
callback: () => number[][] | undefined
|
||||
) => {
|
||||
const mColumn = this.manager.column
|
||||
const threshold = origin % mColumn
|
||||
// 象限旋转方法,是否为0来决定方向 direction 逆时针变换时采用的变换矩阵顺序
|
||||
const quadrantType = horizontalDir + verticalDir
|
||||
|
@ -262,10 +278,10 @@ export default class ExchangeStrategy {
|
|||
const hdir = dir[0] * convert[0]
|
||||
const vdir = dir[1] * convert[1]
|
||||
dir = [hdir, vdir]
|
||||
// 沿着列
|
||||
for (let j = 0; j <= step; j++) {
|
||||
// 沿着行
|
||||
for (let i = 0; i > -1; i++) {
|
||||
// 沿着行
|
||||
for (let i = 0; i <= step; i++) {
|
||||
// 沿着列
|
||||
for (let j = 0; j > -1; j++) {
|
||||
if (i + j > step) break
|
||||
if (i + j !== step) continue
|
||||
|
||||
|
@ -277,8 +293,12 @@ export default class ExchangeStrategy {
|
|||
continue
|
||||
|
||||
if (
|
||||
((hdir > 0 && testId % mColumn >= threshold) ||
|
||||
(hdir < 0 && testId % mColumn <= threshold)) &&
|
||||
((hdir > 0 &&
|
||||
testId > dropChild.gridId &&
|
||||
testId % mColumn >= threshold) || // 向后移动,测试网格应大于原网格,且取余后也比原处取余大
|
||||
(hdir < 0 &&
|
||||
testId < dropChild.gridId &&
|
||||
testId % mColumn <= threshold)) && // 向前移动,测试网格应小于原网格,且取余后也比原处取余小
|
||||
testId >= 0 &&
|
||||
testId <= 23
|
||||
) {
|
||||
|
@ -293,20 +313,20 @@ export default class ExchangeStrategy {
|
|||
return methods?.length ? methods : undefined
|
||||
}
|
||||
// 以一维轴的方式遍历格子
|
||||
const DFS = (origin: number, direction: number) => {
|
||||
let gridId
|
||||
const BFSinAxios = (origin: number, direction: number) => {
|
||||
let gridId: number | undefined
|
||||
const ergodic = (direction: number) => {
|
||||
for (
|
||||
let i = 1, testId = origin + i * direction;
|
||||
testId < 24 && testId > -1;
|
||||
i++
|
||||
i++, testId = origin + i * direction
|
||||
) {
|
||||
testId = origin + i * direction
|
||||
|
||||
// 获取测试网格之中的子节点
|
||||
const dropGrid = this.coordinate[pagination][testId]
|
||||
|
||||
if (
|
||||
!dropGrid ||
|
||||
(!this.placedChild.has(dropChild) && dropGrid.priority < 2)
|
||||
!dropGrid || // 测试网格为空格子时
|
||||
(!this.placedChild.has(dropGrid) && dropGrid.priority < 2) // 子节点为图标节点,且未移动过
|
||||
) {
|
||||
gridId = testId
|
||||
break
|
||||
|
@ -332,18 +352,19 @@ export default class ExchangeStrategy {
|
|||
preserve,
|
||||
access
|
||||
)
|
||||
console.log('gridMethods', gridMethods)
|
||||
|
||||
if (gridMethods.length) {
|
||||
const dropChildren = this.getChildrenByGridArea(
|
||||
grid,
|
||||
dropChild.row,
|
||||
dropChild.column,
|
||||
pagination,
|
||||
dropChild.priority
|
||||
)
|
||||
if (dropChildren.canPlace) {
|
||||
methods.push(...gridMethods)
|
||||
for (let grids of gridMethods) {
|
||||
const dropChildren = this.getChildrenByGridArea(
|
||||
grids[0],
|
||||
dropChild.row,
|
||||
dropChild.column,
|
||||
pagination,
|
||||
dropChild.priority
|
||||
)
|
||||
if (dropChildren.canPlace) {
|
||||
methods.push(...gridMethods)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -352,20 +373,28 @@ export default class ExchangeStrategy {
|
|||
return methods
|
||||
}
|
||||
|
||||
// TODO:需要测试是否不需要选择,直接使用第一个方法?
|
||||
const chooseMethod = (methods: number[][]) => {
|
||||
let gridId: number | undefined
|
||||
let step = 9
|
||||
|
||||
if (methods) {
|
||||
for (const method of methods) {
|
||||
const diff = Math.abs(method[0] - dropChild.gridId)
|
||||
const methodStep = (diff % mColumn) + Math.floor(diff / mColumn)
|
||||
// 保证取所有方法中移动步数最少的
|
||||
if (step >= methodStep) {
|
||||
step = methodStep
|
||||
} else {
|
||||
continue
|
||||
}
|
||||
if (
|
||||
Math.floor(method[0] / this.manager.column) ==
|
||||
Math.floor(dropChild.gridId / this.manager.column) // 优先选择同行位移(gridId 左右移动)
|
||||
Math.floor(method[0] / mColumn) ==
|
||||
Math.floor(dropChild.gridId / mColumn) // 优先选择同行位移(gridId 左右移动)
|
||||
) {
|
||||
gridId = method[0]
|
||||
break
|
||||
// break
|
||||
} else if (
|
||||
Math.abs(method[0] - dropChild.gridId) % this.manager.column ==
|
||||
Math.abs(method[0] - dropChild.gridId) % mColumn ==
|
||||
0 // 其次选择同列位移(gridId 上下移动)
|
||||
) {
|
||||
gridId = method[0]
|
||||
|
@ -378,7 +407,7 @@ export default class ExchangeStrategy {
|
|||
}
|
||||
const methods = direction[1]
|
||||
? BFS(dropChild.gridId, direction, place)
|
||||
: DFS(dropChild.gridId, direction[0])
|
||||
: BFSinAxios(dropChild.gridId, direction[0])
|
||||
const gridId = methods ? chooseMethod(methods) : undefined
|
||||
if (typeof gridId == 'number') {
|
||||
if (this.placedRecorder[pagination]) {
|
||||
|
@ -393,7 +422,7 @@ export default class ExchangeStrategy {
|
|||
pagination,
|
||||
dropChild.priority
|
||||
)
|
||||
if (!conflictChildren) return false
|
||||
this.placedChild.add(dropChild)
|
||||
this.pickChildren(conflictChildren.dropChildren, pagination)
|
||||
this.concatSuspendingChild(conflictChildren.dropChildren)
|
||||
this.placeChild(dropChild, gridId, pagination)
|
||||
|
@ -496,7 +525,8 @@ export default class ExchangeStrategy {
|
|||
child: GaiaContainerChild,
|
||||
gridId: number,
|
||||
pagination: number,
|
||||
mode: 'place' | 'move'
|
||||
mode: 'place' | 'move',
|
||||
direction: [boolean, boolean]
|
||||
): {
|
||||
canPlace: boolean
|
||||
placedRecorder?: PlacedRecorder
|
||||
|
@ -515,9 +545,10 @@ export default class ExchangeStrategy {
|
|||
return {canPlace: false}
|
||||
}
|
||||
|
||||
const recorder = this.coordinate[pagination]
|
||||
this.placedRecorder = {}
|
||||
this.placedChild.add(child)
|
||||
const recorder = this.coordinate[pagination]
|
||||
this.pickChild(child, recorder)
|
||||
const dropChild = recorder[gridId]
|
||||
// 获取被挤占位置的元素,按照优先级排序
|
||||
const pickChildren = this.getChildrenByGridArea(
|
||||
|
@ -530,7 +561,7 @@ export default class ExchangeStrategy {
|
|||
)!
|
||||
// 检查该子节点与被挤占位置的元素之间的优先级,若存在优先级高的元素,该次放置失败
|
||||
const canPlace = this.allowPlace(child, pickChildren.dropChildren, mode)
|
||||
if (!canPlace) {
|
||||
if (!canPlace || !pickChildren.canPlace) {
|
||||
return {canPlace: false}
|
||||
}
|
||||
|
||||
|
@ -539,27 +570,32 @@ export default class ExchangeStrategy {
|
|||
this.pickChildren(this.suspendChild, pagination)
|
||||
|
||||
// 放置该子节点
|
||||
this.pickChild(child, recorder)
|
||||
this.placeChild(child, gridId, pagination)
|
||||
|
||||
// 尝试移动并放置被挤出去的子节点
|
||||
for (let i = 0; i < this.suspendChild.length; i++) {
|
||||
let j = this.suspendChild.length - i
|
||||
let suspendArr = this.suspendChild[j]
|
||||
if (suspendArr?.size) {
|
||||
for (let suspendChild of [...suspendArr]) {
|
||||
const exchangeDirection = this.getExchangeDirection(
|
||||
child,
|
||||
suspendChild
|
||||
)
|
||||
if (!this.moveChild(suspendChild, exchangeDirection, pagination)) {
|
||||
// 移动失败
|
||||
|
||||
return {canPlace: false}
|
||||
}
|
||||
try {
|
||||
for (let i = 0; i < this.suspendChild.length; i++) {
|
||||
let j = this.suspendChild.length - i
|
||||
let suspendArr = this.suspendChild[j]
|
||||
if (suspendArr?.size) {
|
||||
suspendArr.forEach((suspendChild) => {
|
||||
const exchangeDirection = this.getExchangeDirection(
|
||||
child,
|
||||
suspendChild,
|
||||
direction
|
||||
)
|
||||
if (!this.moveChild(suspendChild, exchangeDirection, pagination)) {
|
||||
// 移动失败
|
||||
throw suspendChild
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
} catch (placeFaild) {
|
||||
console.info('try to place failed!')
|
||||
return {canPlace: false}
|
||||
}
|
||||
|
||||
return {
|
||||
canPlace: true,
|
||||
placedRecorder: this.placedRecorder,
|
||||
|
@ -584,18 +620,20 @@ export default class ExchangeStrategy {
|
|||
let children: Set<GaiaContainerChild>[] = []
|
||||
let canPlace = true
|
||||
for (let i = 0; i < row; i++) {
|
||||
for (let j = 0; j < column; j++) {
|
||||
for (let j = 0; j < column && canPlace; j++) {
|
||||
const targetGrid = gridId + j + i * this.manager.column
|
||||
const child = recorder[targetGrid]
|
||||
|
||||
if (
|
||||
child &&
|
||||
(child.priority < priority ||
|
||||
(mode == 'place' && child.priority == priority))
|
||||
) {
|
||||
if (this.placedChild.has(child)) {
|
||||
if (child) {
|
||||
if (
|
||||
this.placedChild.has(child) ||
|
||||
child.priority > priority ||
|
||||
(mode == 'move' &&
|
||||
child.priority == priority &&
|
||||
child.priority != 1)
|
||||
) {
|
||||
canPlace = false
|
||||
continue
|
||||
break
|
||||
}
|
||||
if (children[child.priority]) {
|
||||
children[child.priority].add(child)
|
||||
|
|
|
@ -158,11 +158,14 @@ export default class GaiaContainerChild {
|
|||
const area = this.getArea(type)
|
||||
if (!area) return
|
||||
|
||||
const [rowStart, columStart] = area
|
||||
const [rowStart, columnStart] = area
|
||||
|
||||
const rowEnd = rowStart + this.row
|
||||
const columEnd = columStart + this.column
|
||||
this.master.style.gridArea = `${rowStart} / ${columStart} / ${rowEnd} / ${columEnd}`
|
||||
const columnEnd = columnStart + this.column
|
||||
this.center.x = columnStart + this.column / 2 - 1
|
||||
this.center.y = rowStart + this.row / 2 - 1
|
||||
|
||||
this.master.style.gridArea = `${rowStart} / ${columnStart} / ${rowEnd} / ${columnEnd}`
|
||||
;(this._element as HTMLElement).dataset.static = type
|
||||
}
|
||||
|
||||
|
@ -333,21 +336,6 @@ export default class GaiaContainerChild {
|
|||
this.curGridId = gridId
|
||||
}
|
||||
|
||||
// for (let i = 0; i < this.row; i++) {
|
||||
// for (let j = 0; j < this.column; j++) {
|
||||
// if (position == 'page') {
|
||||
// this.manager.childCoordinate[this.pagination][
|
||||
// gridId + i + j * this.manager.column
|
||||
// ] = this
|
||||
// } else {
|
||||
// // TODO:文件夹分页
|
||||
// this.manager.folders[this.folderName].childCoordinate[0][
|
||||
// gridId + i + j
|
||||
// ] = this
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
if (this._lastMasterTop !== top || this._lastMasterLeft !== left) {
|
||||
this._lastMasterTop = top
|
||||
this._lastMasterLeft = left
|
||||
|
|
|
@ -32,8 +32,6 @@ export class PanelContainer extends LitElement {
|
|||
x =
|
||||
typeof x == 'number' ? x : this.rowInput.value ? +this.rowInput.value : 1
|
||||
y = y ? y : this.columnInput.value ? +this.columnInput.value : 1
|
||||
console.log(x, y)
|
||||
;(window as any).panel = this
|
||||
const icon = document.createElement('site-icon')
|
||||
|
||||
icon.setAttribute('color', this.createRandomColor())
|
||||
|
@ -53,7 +51,7 @@ export class PanelContainer extends LitElement {
|
|||
this.indicator.appendChild(index)
|
||||
}
|
||||
this.container = new GaiaContainer()
|
||||
this.container.setAttribute('dragAndDrop', 'true')
|
||||
this.container.setAttribute('drag-and-drop', '')
|
||||
this.shadowRoot?.appendChild(this.container)
|
||||
this.container.sortMode = true
|
||||
;(window as any).container = this.container
|
||||
|
@ -95,7 +93,7 @@ export class PanelContainer extends LitElement {
|
|||
<button class="add" @click=${this.addAppIcon}>添加</button>
|
||||
<button class="reset" @click=${() => this.addAppIcon(1, 1)}>1×1</button>
|
||||
<button class="reset" @click=${() => this.addAppIcon(2, 2)}>2×2</button>
|
||||
<input id="row" placeholder="row" value="2" />
|
||||
<input id="row" placeholder="row" value="3" />
|
||||
<input id="column" placeholder="column" value="2" />
|
||||
<div id="indicator"></div>
|
||||
</div>
|
||||
|
|
|
@ -53,7 +53,7 @@ export default class SiteIcon extends LitElement {
|
|||
return html`
|
||||
<div id="image-container">
|
||||
<div id="spinner"></div>
|
||||
<img id="display" src="./asserts/icon.png" />
|
||||
<img id="display" />
|
||||
</div>
|
||||
<div>
|
||||
<div dir="auto" id="subtitle">${this.name}</div>
|
||||
|
|
Loading…
Reference in New Issue