TASK: #103599 - optimize strategy of container

This commit is contained in:
luojiahao 2022-08-31 11:14:29 +08:00
parent eab57a3d92
commit 35dcb012a2
8 changed files with 269 additions and 98 deletions

View File

@ -11,3 +11,4 @@
- add contaienr - add contaienr
- add SlotStyleHandler - add SlotStyleHandler
- edit container folder animation - edit container folder animation
- optimize strategy of container

View File

@ -0,0 +1,70 @@
import {css} from 'lit'
export default css`
:host {
position: relative;
display: block;
width: 100%;
height: 100%;
}
:host #container {
width: 100%;
height: 100%;
display: grid;
/* 设置三列每列33.33%宽也可以用repeat重复 */
/* grid-template-columns: 33.33% 33.33% 33.33%; */
/* grid-template-columns: repeat(3, 33.33%); */
grid-template-rows: repeat(1, 100%);
grid-template-columns: repeat(auto-fit, 100%);
/* 间距,格式为 row column单值时表示行列间距一致 */
gap: 0px;
/* 排列格式,此处意为先列后行,不在意元素顺序,尽量塞满每列 */
grid-auto-flow: column dense;
/* 隐式网格宽度,即 grid-template-rows 属性规定列数外的网格列宽 */
grid-auto-columns: 100vw;
}
::slotted(.gaia-container-page) {
display: grid;
grid-template-columns: repeat(4, 25%);
grid-template-rows: repeat(6, 16.66%);
grid-auto-flow: row dense;
grid-auto-rows: 33%;
height: 100%;
transform: opacity 0.1s;
}
::slotted(.gaia-container-child) {
width: 100%;
}
::slotted(.gaia-container-child.dragging) {
z-index: 1;
will-change: transform;
}
::slotted(.container-master) {
opacity: 1;
transform: opacity 0.15s;
}
::slotted(.gaia-container-page) > .container-master.merging {
opacity: 0.5;
}
::slotted(.gaia-container-child) {
height: calc(var(--grid-height) * var(--rows, 1));
width: calc(var(--grid-width) * var(--columns, 1));
display: flex !important;
flex-direction: column;
align-items: center;
justify-content: center;
}
::slotted(.folder) > .gaia-container-child {
transition: transform 0.2s, height 0.2s, width 0.2s;
}
`

View File

@ -6,6 +6,8 @@ import GaiaContainerPage from './gaia-container-page'
import GaiaContainerFolder from './gaia-container-folder' import GaiaContainerFolder from './gaia-container-folder'
import {DragAndDrop, STATUS, ChildElementInfo} from './contianer-interface' import {DragAndDrop, STATUS, ChildElementInfo} from './contianer-interface'
import slotStyleHandler from '../../utils/SlotStyleHandler' import slotStyleHandler from '../../utils/SlotStyleHandler'
import varStyle from './style-variables'
import containerStyle from './container-style'
/** /**
* *
* 1. grid 1×1n×m * 1. grid 1×1n×m
@ -179,11 +181,13 @@ class GaiaContainer extends LitElement {
openedFolder: GaiaContainerFolder | null = null openedFolder: GaiaContainerFolder | null = null
gesture: GestureManager | null = null gesture: GestureManager | null = null
dndObserver: MutationObserver | null = null dndObserver: MutationObserver | null = null
pageHeight: number = 0 pageHeight: number = 1
pageWidth: number = 0 pageWidth: number = 1
gridHeight: number = 0 gridHeight: number = 1
gridWidth: number = 0 gridWidth: number = 1
istouching: boolean = false istouching: boolean = false
// 组件坐标
childCoordinate: {[gridId: number]: GaiaContainerChild}[] = []
constructor(row = 6, column = 4) { constructor(row = 6, column = 4) {
super() super()
@ -196,11 +200,7 @@ class GaiaContainer extends LitElement {
} }
firstUpdated() { firstUpdated() {
slotStyleHandler.injectGlobalCss( slotStyleHandler.injectGlobalCss(this, containerStyle.cssText, this.name)
this,
GaiaContainer.styles.cssText,
this.name
)
let dndObserverCallback = () => { let dndObserverCallback = () => {
if (this._dnd.enabled !== this.dragAndDrop) { if (this._dnd.enabled !== this.dragAndDrop) {
this._dnd.enabled = this.dragAndDrop this._dnd.enabled = this.dragAndDrop
@ -434,8 +434,8 @@ class GaiaContainer extends LitElement {
this.width = width this.width = width
this.pageWidth = this.pages.length ? this.pages[0].offsetWidth : 0 this.pageWidth = this.pages.length ? this.pages[0].offsetWidth : 0
this.pageHeight = this.pages.length ? this.pages[0].offsetHeight : 0 this.pageHeight = this.pages.length ? this.pages[0].offsetHeight : 0
this.gridHeight = this.pageHeight / this.row this.gridHeight = Math.floor(this.pageHeight / this.row)
this.gridWidth = this.pageWidth / this.column this.gridWidth = Math.floor(this.pageWidth / this.column)
this.style.setProperty('--page-width', this.pageWidth + 'px') this.style.setProperty('--page-width', this.pageWidth + 'px')
this.style.setProperty('--page-height', this.pageHeight + 'px') this.style.setProperty('--page-height', this.pageHeight + 'px')
@ -493,6 +493,67 @@ class GaiaContainer extends LitElement {
return null return null
} }
/**
* x y ID
* |---- Page 0 (landscape) -----|
* |----+----+----+----+----+----|
* | 0 | 1 | 2 | 3 | 4 | 5 |
* |----+----+----+----+----+----|
* | 6 | 7 | 8 | 9 | 10 | 11 |
* |----+----+----+----+----+----| ......
* | 12 | 13 | 14 | 15 | 16 | 17 |
* |----+----+----+----+----+----|
* | 18 | 19 | 20 | 21 | 22 | 23 |
* |----+----+----+----+----+----|
*
* | Page 0 (portrait) |
* |----+----+----+----|
* | 0 | 1 | 2 | 3 |
* |----+----+----+----|
* | 4 | 5 | 6 | 7 |
* |----+----+----+----| ......
* | 8 | 9 | 10 | 11 |
* |----+----+----+----|
* | 12 | 13 | 14 | 15 |
* |----+----+----+----|
* | 16 | 17 | 18 | 19 |
* |----+----+----+----|
* | 20 | 21 | 22 | 23 |
* |----+----+----+----|
* @param x
* @param y
* @returns
*/
getGridIdByCoordinate(
x: number,
y: number,
pagination: number = this.pagination
) {
let page = this.pages[pagination]
x += page.scrollLeft - page.offsetLeft
y += page.scrollTop - page.offsetTop
const coordinateX = parseInt(String(x / this.gridWidth))
const coordinateY = parseInt(String(y / this.gridHeight))
const gridId = coordinateX + coordinateY * this.column
return gridId
}
getFolderGridIdByCoordinate(x: number, y: number, pagination: number = 0) {
if (!this.openedFolder || !this.openedFolder._status) return -1
let page = this.pages[this.openedFolder.pagination]
x += page.scrollLeft - page.offsetLeft - this.openedFolder._lastMasterLeft!
y += page.scrollTop - page.offsetTop - this.openedFolder._lastMasterTop!
const coordinateX = parseInt(String(x / this.openedFolder.gridWidth))
const coordinateY = parseInt(String(y / this.openedFolder.gridHeight))
const gridId = coordinateX + coordinateY * 4
return gridId
}
get firstChild() { get firstChild() {
return this._children.length ? this._children[0].element : null return this._children.length ? this._children[0].element : null
} }
@ -765,8 +826,8 @@ class GaiaContainer extends LitElement {
if (!this.pageHeight || !this.pageWidth) { if (!this.pageHeight || !this.pageWidth) {
this.pageWidth = page.offsetWidth this.pageWidth = page.offsetWidth
this.pageHeight = page.offsetHeight this.pageHeight = page.offsetHeight
this.gridHeight = this.pageHeight / this.row this.gridHeight = Math.floor(this.pageHeight / this.row)
this.gridWidth = this.pageWidth / this.column this.gridWidth = Math.floor(this.pageWidth / this.column)
this.style.setProperty('--page-width', this.pageWidth + 'px') this.style.setProperty('--page-width', this.pageWidth + 'px')
this.style.setProperty('--page-height', this.pageHeight + 'px') this.style.setProperty('--page-height', this.pageHeight + 'px')
@ -998,7 +1059,7 @@ class GaiaContainer extends LitElement {
throw 'getChildOffsetRect called on unknown child' throw 'getChildOffsetRect called on unknown child'
} }
getChildFromPoint(x: number, y: number) { getChildFromPoint_bak(x: number, y: number) {
// 是否与主屏文件夹进行交互中 // 是否与主屏文件夹进行交互中
const interactWithFolder = !!this.openedFolder const interactWithFolder = !!this.openedFolder
const {offsetHeight, offsetWidth, offsetX, offsetY} = this._dnd.last const {offsetHeight, offsetWidth, offsetX, offsetY} = this._dnd.last
@ -1095,6 +1156,40 @@ class GaiaContainer extends LitElement {
} }
} }
getChildFromPoint(x: number, y: number) {
// 是否与主屏文件夹进行交互中
const interactWithFolder = !!this.openedFolder
let children, child: GaiaContainerChild, childIndex, element
if (interactWithFolder && this.openedFolder) {
childIndex = this.getFolderGridIdByCoordinate(x, y, this.pagination)
children = this.openedFolder?.childCoordinate[this.pagination]
child = children[childIndex]
element = child?.element ?? this.openedFolder.container
} else {
childIndex = this.getGridIdByCoordinate(x, y, this.pagination)
children = this.childCoordinate[this.pagination]
child = children[childIndex]
element = child?.element ?? this.pages[this.pagination]
}
// TODO还需要考虑跨文件夹移动
if (
typeof child?.pagination == 'number' &&
child.pagination !== this._dnd.child.pagination
) {
// 当被选中元素与被移动元素页码不一致时,+
this._dnd.isSpanning = true
} else if (element.dataset.page != this._dnd.child.pagination) {
// 放置页面与原应用图标页面不一致,属于跨页移动
this._dnd.isSpanning = true
}
return {
dropTarget: element,
isPage: !interactWithFolder && !child,
pagination: this.pagination,
}
}
cancelDrag() { cancelDrag() {
if (this._dnd.timeout !== null) { if (this._dnd.timeout !== null) {
clearTimeout(this._dnd.timeout) clearTimeout(this._dnd.timeout)
@ -1116,7 +1211,6 @@ class GaiaContainer extends LitElement {
this._dnd.clickCapture = true this._dnd.clickCapture = true
this.dispatchEvent(new CustomEvent('drag-finish')) this.dispatchEvent(new CustomEvent('drag-finish'))
} }
this.synchronise()
if (this._dnd.lastDropChild) { if (this._dnd.lastDropChild) {
this._dnd.lastDropChild.master.classList.remove('merging') this._dnd.lastDropChild.master.classList.remove('merging')
@ -1134,6 +1228,7 @@ class GaiaContainer extends LitElement {
this._dnd.child = null this._dnd.child = null
this._dnd.isSpanning = false this._dnd.isSpanning = false
this.status &= ~STATUS.DRAG this.status &= ~STATUS.DRAG
this.synchronise()
} }
startDrag() { startDrag() {
@ -1244,7 +1339,7 @@ class GaiaContainer extends LitElement {
) )
this._dnd.dropTarget = dropTarget this._dnd.dropTarget = dropTarget
// 拖拽元素悬浮页面默认为当前页面 // 拖拽元素悬浮页面默认为当前页面
const suspendingPage = isPage ? dropTarget : this.pages[this.pagination] const suspendingPage = isPage ? dropTarget! : this.pages[this.pagination]
if ( if (
dropTarget === this._dnd.child.element || // 悬浮在自身容器之上 dropTarget === this._dnd.child.element || // 悬浮在自身容器之上
(this._dnd.child.isTail && isPage) (this._dnd.child.isTail && isPage)
@ -1481,10 +1576,10 @@ class GaiaContainer extends LitElement {
// if (!insertBefore && !dropChild.master.nextSibling) { // if (!insertBefore && !dropChild.master.nextSibling) {
if (operator === 'insertAfter' && dropChild) { if (operator === 'insertAfter' && dropChild) {
this.realInsertAfter(this._dnd.child.master, dropChild.master) this.realInsertAfter(this._dnd.child.master, dropChild.master)
this._staticElements.push(dropChild.element as HTMLElement) // this._staticElements.push(dropChild.element as HTMLElement)
} else if (operator === 'insertBefore' && dropChild) { } else if (operator === 'insertBefore' && dropChild) {
this.realInsertBefore(this._dnd.child.master, dropChild.master) this.realInsertBefore(this._dnd.child.master, dropChild.master)
this._staticElements.push(dropChild.element as HTMLElement) // this._staticElements.push(dropChild.element as HTMLElement)
} else { } else {
// TODO: 此处为合并为文件夹方法 // TODO: 此处为合并为文件夹方法
// this.mergeForder(); // this.mergeForder();
@ -1531,7 +1626,7 @@ class GaiaContainer extends LitElement {
} }
handleTransformRatio(x: number) { handleTransformRatio(x: number) {
if (this._status & STATUS.DRAG) return x if (this._status & STATUS.DRAG) return 1
const percentage = Math.abs(x) / this.pageHeight const percentage = Math.abs(x) / this.pageHeight
this.ratio = 1 this.ratio = 1
@ -1812,74 +1907,7 @@ class GaiaContainer extends LitElement {
} }
} }
static styles = css` static styles = [containerStyle, varStyle]
:host {
position: relative;
display: block;
width: 100%;
height: 100%;
}
:host #container {
width: 100%;
height: 100%;
display: grid;
/* 设置三列每列33.33%宽也可以用repeat重复 */
/* grid-template-columns: 33.33% 33.33% 33.33%; */
/* grid-template-columns: repeat(3, 33.33%); */
grid-template-rows: repeat(1, 100%);
grid-template-columns: repeat(auto-fit, 100%);
/* 间距,格式为 row column单值时表示行列间距一致 */
gap: 0px;
/* 排列格式,此处意为先列后行,不在意元素顺序,尽量塞满每列 */
grid-auto-flow: column dense;
/* 隐式网格宽度,即 grid-template-rows 属性规定列数外的网格列宽 */
grid-auto-columns: 100vw;
}
::slotted(.gaia-container-page) {
display: grid;
grid-template-columns: repeat(4, 25%);
grid-template-rows: repeat(6, 16.66%);
grid-auto-flow: row dense;
grid-auto-rows: 33%;
height: 100%;
transform: opacity 0.1s;
}
::slotted(.gaia-container-child) {
width: 100%;
}
::slotted(.gaia-container-child.dragging) {
z-index: 1;
will-change: transform;
}
::slotted(.container-master) {
opacity: 1;
transform: opacity 0.15s;
}
::slotted(.gaia-container-page) > .container-master.merging {
opacity: 0.5;
}
::slotted(.gaia-container-child) {
height: calc(var(--grid-height) * var(--rows, 1));
width: calc(var(--grid-width) * var(--columns, 1));
display: flex !important;
flex-direction: column;
align-items: center;
justify-content: center;
}
::slotted(.folder) > .gaia-container-child {
transition: transform 0.2s, height 0.2s, width 0.2s;
}
`
editStyle(moduleName: string, style: string) { editStyle(moduleName: string, style: string) {
this.shadowStyles[moduleName] = style this.shadowStyles[moduleName] = style

View File

@ -129,6 +129,7 @@ export default class GaiaContainerChild {
if (!area) return if (!area) return
const [rowStart, columStart] = area const [rowStart, columStart] = area
const rowEnd = rowStart + +this.row const rowEnd = rowStart + +this.row
const columEnd = columStart + +this.column const columEnd = columStart + +this.column
this.master.style.gridArea = `${rowStart} / ${columStart} / ${rowEnd} / ${columEnd}` this.master.style.gridArea = `${rowStart} / ${columStart} / ${rowEnd} / ${columEnd}`
@ -287,6 +288,26 @@ export default class GaiaContainerChild {
let container = this.container let container = this.container
let top = master.offsetTop let top = master.offsetTop
let left = master.offsetLeft let left = master.offsetLeft
const position = this.position
// 左上角的 GridID
const gridId =
position == 'page'
? this.manager.getGridIdByCoordinate(left, top, this.pagination)
: this.manager.getFolderGridIdByCoordinate(left, top, this.pagination)
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
} else {
// TODO文件夹分页
this.manager.folders[this.folderName].childCoordinate[0][
gridId + i + j
] = this
}
}
}
if (this._lastMasterTop !== top || this._lastMasterLeft !== left) { if (this._lastMasterTop !== top || this._lastMasterLeft !== left) {
this._lastMasterTop = top this._lastMasterTop = top

View File

@ -26,6 +26,13 @@ export default class GaiaContainerFolder extends GaiaContainerChild {
_title: HTMLElement | null = null _title: HTMLElement | null = null
// 开启文件夹动画计时器 // 开启文件夹动画计时器
openTimer: number | undefined = undefined openTimer: number | undefined = undefined
// 子节点坐标
childCoordinate: {[gridId: number]: GaiaContainerChild}[] = [{}]
gridHeight: number = 0
gridWidth: number = 0
_lastElementTop!: number
_lastElementLeft!: number
constructor(manager: GaiaContainer, name?: string) { constructor(manager: GaiaContainer, name?: string) {
super(null, 1, 1, null, manager) super(null, 1, 1, null, manager)
this.name = this.checkAndGetFolderName(name) this.name = this.checkAndGetFolderName(name)
@ -59,6 +66,15 @@ export default class GaiaContainerFolder extends GaiaContainerChild {
this.container.style.width = this.manager.gridWidth + 'px' this.container.style.width = this.manager.gridWidth + 'px'
} }
resize() {
let element = this.element
this._lastElementTop = element?.offsetTop!
this._lastElementLeft = element?.offsetLeft!
this.gridHeight = this._children[0]._lastElementHeight!
this.gridWidth = this._children[0]._lastElementWidth!
}
get element() { get element() {
if ( if (
!this._element || !this._element ||
@ -226,6 +242,11 @@ export default class GaiaContainerFolder extends GaiaContainerChild {
openTransition = (evt: TransitionEvent) => { openTransition = (evt: TransitionEvent) => {
if (evt.target == this.element && evt.propertyName == 'height') { if (evt.target == this.element && evt.propertyName == 'height') {
this._children.forEach((child) => child.synchroniseContainer()) this._children.forEach((child) => child.synchroniseContainer())
if (!this._lastElementLeft || !this._lastElementTop) {
let element = this.element
this._lastElementTop = element?.offsetTop!
this._lastElementLeft = element?.offsetLeft!
}
} }
if ( if (
@ -246,6 +267,8 @@ export default class GaiaContainerFolder extends GaiaContainerChild {
this.element.appendChild(element) this.element.appendChild(element)
element = this.suspendElement.shift() element = this.suspendElement.shift()
} }
this.gridHeight = this._children[0]._lastElementHeight!
this.gridWidth = this._children[0]._lastElementWidth!
}) })
this.element.removeEventListener('transitionend', this.openTransition) this.element.removeEventListener('transitionend', this.openTransition)
@ -295,6 +318,8 @@ export default class GaiaContainerFolder extends GaiaContainerChild {
if (this._children.length <= 1) { if (this._children.length <= 1) {
this.destroy() this.destroy()
} }
this.gridHeight = this._children[0]._lastElementHeight!
this.gridWidth = this._children[0]._lastElementWidth!
}, },
{once: true} {once: true}
) )
@ -362,10 +387,10 @@ export default class GaiaContainerFolder extends GaiaContainerChild {
case 'touchmove': case 'touchmove':
if ( if (
this._status && this._status &&
(evt.target as HTMLElement).tagName !== 'GAIA-APP-ICON' (evt.target as HTMLElement).tagName !== 'SITE-ICON'
) { ) {
// evt.preventDefault() evt.preventDefault()
// evt.stopImmediatePropagation() evt.stopImmediatePropagation()
} }
break break
} }

View File

@ -1,10 +1,12 @@
import GaiaContainer from './container'
class GaiaContainerPage { class GaiaContainerPage {
_pages: HTMLElement[] = [] // 存储所有添加进 gaia-container 的页面 _pages: HTMLElement[] = [] // 存储所有添加进 gaia-container 的页面
// 等待被移除的页面,仅在编辑、拖拽时出现,若结束前两种状态时仍然没有子节点,则删除 // 等待被移除的页面,仅在编辑、拖拽时出现,若结束前两种状态时仍然没有子节点,则删除
_suspending: HTMLElement | null = null _suspending: HTMLElement | null = null
_manager _manager: GaiaContainer
observerCallback: MutationCallback observerCallback: MutationCallback
constructor(manager: any) { constructor(manager: GaiaContainer) {
this._manager = manager this._manager = manager
this._manager.addEventListener('statuschange', () => { this._manager.addEventListener('statuschange', () => {
@ -61,6 +63,7 @@ class GaiaContainerPage {
this._pages.push(div) this._pages.push(div)
div.dataset.page = `${this._pages.length - 1}` div.dataset.page = `${this._pages.length - 1}`
this.observe(div) this.observe(div)
this._manager.childCoordinate[this._pages.length - 1] = {}
return div return div
} }
@ -90,14 +93,17 @@ class GaiaContainerPage {
if (index > -1) { if (index > -1) {
page?.remove?.() page?.remove?.()
let flag = false let flag = false
let coordinates: GaiaContainer['childCoordinate'] = []
this._pages = this._pages.filter((page, i) => { this._pages = this._pages.filter((page, i) => {
if (flag) { if (flag) {
;(page.dataset.page as any) = --i ;(page.dataset.page as any) = --i
page.style.setProperty('--pagination', String(i)) page.style.setProperty('--pagination', String(i))
} }
if (i == index) flag = true if (i == index) flag = true
coordinates[i] = this._manager.childCoordinate[i - +flag]
return return
}) })
this._manager.childCoordinate = coordinates
} }
} }

View File

@ -0,0 +1,8 @@
import {css} from 'lit'
export default css`
:host {
/* 图标大小 */
--icon-size: 108px;
}
`

View File

@ -5,8 +5,8 @@ export default css`
position: var(--width, absolute); position: var(--width, absolute);
top: 50%; top: 50%;
left: 50%; left: 50%;
width: 50%; width: var(--icon-size, 50%);
height: 50%; height: var(--icon-size, 50%);
max-width: var(--width); max-width: var(--width);
max-height: var(--width); max-height: var(--width);
border-radius: 50%; border-radius: 50%;
@ -62,11 +62,23 @@ export default css`
} */ } */
#subtitle { #subtitle {
white-space: nowrap; /* 小黑 文字 中 */
overflow: hidden; margin-top: 6px;
text-overflow: ellipsis; height: 26px;
/* 小正文-浅-居中 */
font-family: 'OPPOSans';
font-style: normal;
font-weight: 400;
font-size: 20px;
line-height: 26px;
/* identical to box height, or 130% */
text-align: center; text-align: center;
line-height: 1rem;
/* 字体/ 高亮白 */
color: #fafafa;
text-shadow: 0px 2px 4px rgba(0, 0, 0, 0.28);
} }
@keyframes rotate { @keyframes rotate {