diff --git a/CHANGELOG.md b/CHANGELOG.md index 0c11db4..2174ba5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,3 +10,4 @@ - add blur - add contaienr - add SlotStyleHandler +- edit container folder animation diff --git a/src/components/grid-container/container.ts b/src/components/grid-container/container.ts index 996310b..e36ebdf 100644 --- a/src/components/grid-container/container.ts +++ b/src/components/grid-container/container.ts @@ -5,6 +5,7 @@ import GestureManager from './gesture-manager' import GaiaContainerPage from './gaia-container-page' import GaiaContainerFolder from './gaia-container-folder' import {DragAndDrop, STATUS, ChildElementInfo} from './contianer-interface' +import slotStyleHandler from '../../utils/SlotStyleHandler' /** * 想法: * 1. 用 grid 布局的特性排列应用图标(1×1)和小组件(n×m) @@ -188,14 +189,19 @@ class GaiaContainer extends LitElement { super() this.row = row this.column = column + ;(window as any).con = this // this.attachShadow({ mode: "open" }); // this.shadowRoot && (this.shadowRoot.innerHTML = this.template); } firstUpdated() { + slotStyleHandler.injectGlobalCss( + this, + GaiaContainer.styles.cssText, + this.name + ) let dndObserverCallback = () => { - console.log(this._dnd.enabled, this.dragAndDrop) if (this._dnd.enabled !== this.dragAndDrop) { this._dnd.enabled = this.dragAndDrop if (this._dnd.enabled) { @@ -517,6 +523,7 @@ class GaiaContainer extends LitElement { * @returns 被添加的元素 */ realAppendChild(pagination = 0, ...args: HTMLElement[]) { + if (pagination < 0) return let page = this.pages[pagination] if (!page) { page = this.addPage() @@ -1028,14 +1035,10 @@ class GaiaContainer extends LitElement { for (let i = 0, iLen = children.length; i < iLen; i++) { let child = children[i] as GaiaContainerChild if ( - childX >= (child._lastMasterLeft as number) && - childY >= (child._lastMasterTop as number) && - childX < - (child._lastMasterLeft as number) + - (child._lastElementWidth as number) && - childY < - (child._lastMasterTop as number) + - (child._lastElementHeight as number) + childX >= child._lastMasterLeft! && + childY >= child._lastMasterTop! && + childX < child._lastMasterLeft! + this.gridWidth * child.column && + childY < child._lastMasterTop! + this.gridHeight * child.row ) { if (child.pagination !== this._dnd.child.pagination) { // 当被选中元素与被移动元素页码不一致时,该次移动属于跨页移动 @@ -1120,11 +1123,14 @@ class GaiaContainer extends LitElement { this._dnd.lastDropChild = null } - this._dnd.child?.container.style.setProperty( + this._dnd.child?.container?.style.setProperty( '--offset-position-left', '0px' ) - this._dnd.child?.container.style.setProperty('--offset-position-top', '0px') + this._dnd.child?.container?.style.setProperty( + '--offset-position-top', + '0px' + ) this._dnd.child = null this._dnd.isSpanning = false this.status &= ~STATUS.DRAG @@ -1351,6 +1357,7 @@ class GaiaContainer extends LitElement { // 图标悬浮于另一个图标正上方 dropChild.master.classList.add('merging') this.mergeTimer = setTimeout(() => { + if (!this._dnd.child) return this.mergeFolder( (dropChild as GaiaContainerChild).master, this._dnd.child.master @@ -1364,10 +1371,10 @@ class GaiaContainer extends LitElement { !dropChild.isWidget && !this._dnd.child.isWidget ) { - // 图标悬浮于文件夹正上方 this.clearMergeTimer() dropChild.master.classList.add('merging') this.mergeTimer = setTimeout(() => { + if (!this._dnd.child) return ;(dropChild as GaiaContainerFolder).open() this.mergeFolder( (dropChild as GaiaContainerFolder).master, @@ -1437,8 +1444,8 @@ class GaiaContainer extends LitElement { } } - folder.addAppIcon(appMaster) appendFolder(referenceNode as HTMLElement) + folder.addAppIcon(appMaster) this._staticElements = this._staticElements.filter( (el) => el !== folder.element @@ -1505,7 +1512,7 @@ class GaiaContainer extends LitElement { ) } else if (this._dnd.timeout !== null && this._dnd.child?.isFolder) { this._dnd.child.open() - } else if (this._dnd.timeout !== null) { + } else if (this._dnd.timeout !== null && this._dnd.child?.element) { let handled = !this.dispatchEvent( new CustomEvent('activate', { cancelable: true, @@ -1760,6 +1767,9 @@ class GaiaContainer extends LitElement { child.synchroniseMaster() // } } + if (Object.keys(this.folders).length) { + // debugger + } for (let i = 0; i < children.length; i++) { child = children[i] @@ -1840,6 +1850,10 @@ class GaiaContainer extends LitElement { transform: opacity 0.1s; } + ::slotted(.gaia-container-child) { + width: 100%; + } + ::slotted(.gaia-container-child.dragging) { z-index: 1; will-change: transform; @@ -1867,6 +1881,9 @@ class GaiaContainer extends LitElement { } ` + editStyle(moduleName: string, style: string) { + this.shadowStyles[moduleName] = style + } @state() shadowStyles: {[subModule: string]: string} = new Proxy( {}, @@ -1874,14 +1891,11 @@ class GaiaContainer extends LitElement { set: ( target: {[module: string]: string}, prop: string, - value + value: string ): boolean => { - console.log('prop', prop) - console.log('value', value) - console.log('target[prop]', target[prop]) - if (!target[prop] || target[prop] !== value) { target[prop] = value + slotStyleHandler.injectGlobalCss(this, value, this.name, prop) this.requestUpdate() } diff --git a/src/components/grid-container/gaia-container-child.ts b/src/components/grid-container/gaia-container-child.ts index 01caca1..02b3a6e 100644 --- a/src/components/grid-container/gaia-container-child.ts +++ b/src/components/grid-container/gaia-container-child.ts @@ -28,7 +28,6 @@ export default class GaiaContainerChild { column: number manager: GaiaContainer _isStatic: boolean | string - folderName: string = '' anchorCoordinate: Coordinate // 静态位置 _lastMasterTop: number | null = null @@ -89,6 +88,11 @@ export default class GaiaContainerChild { return inPage ? 'page' : 'folder' } + get folderName() { + const name = this.master.parentElement?.dataset.name + return name || '' + } + get element() { return this._element } diff --git a/src/components/grid-container/gaia-container-folder.ts b/src/components/grid-container/gaia-container-folder.ts index 183d6b1..5bada67 100644 --- a/src/components/grid-container/gaia-container-folder.ts +++ b/src/components/grid-container/gaia-container-folder.ts @@ -34,10 +34,14 @@ export default class GaiaContainerFolder extends GaiaContainerChild { } init() { - this.addAnimationStyle() + this.manager.editStyle( + 'GaiaContainerFolder', + GaiaContainerFolder.shadowStyle() + ) this.container.addEventListener('touchstart', this) this.container.addEventListener('touchmove', this) this.container.addEventListener('touchend', this) + this.element.dataset.name = this.name this.master.className = 'folder initializing' this.master.id = this._id this.master.addEventListener( @@ -53,7 +57,6 @@ export default class GaiaContainerFolder extends GaiaContainerChild { ) this.container.appendChild(this.title) this.container.style.width = this.manager.gridWidth + 'px' - // this.manager.injectGlobalCss(this.shadowStyle, this.manager.name, 'gaia-container-folder'); } get element() { @@ -109,16 +112,11 @@ export default class GaiaContainerFolder extends GaiaContainerChild { } showIconsSubtitle(element: HTMLElement) { - const icon = element.querySelector(this.iconName) - icon && - icon.attributes.hasOwnProperty(this.hideAttrName) && - icon.attributes.removeNamedItem(this.hideAttrName) + element.removeAttribute(this.hideAttrName) } hideIconsSubtitle(element: HTMLElement) { - const icon = element.querySelector(this.iconName) - const attr = document.createAttribute(this.hideAttrName) - icon && icon.attributes.setNamedItem(attr) + element.setAttribute(this.hideAttrName, '') } /** @@ -156,9 +154,9 @@ export default class GaiaContainerFolder extends GaiaContainerChild { this._children.push(child!) if (!this._status) { - this.hideIconsSubtitle(element) + this.hideIconsSubtitle(child?.element!) } else { - this.showIconsSubtitle(element) + this.showIconsSubtitle(child?.element!) } if (!this._status && shouldOpen) { @@ -168,7 +166,6 @@ export default class GaiaContainerFolder extends GaiaContainerChild { this.movein(element) this.element.appendChild(element) } - child!.folderName = this.name } removeAppIcon(node: GaiaContainerChild | HTMLElement) { @@ -183,8 +180,7 @@ export default class GaiaContainerFolder extends GaiaContainerChild { }) } if (!removeChild) return null - ;(removeChild as GaiaContainerChild).folderName = '' - this.showIconsSubtitle((removeChild as GaiaContainerChild).container) + this.showIconsSubtitle((removeChild as GaiaContainerChild).element!) this.manager._children.push(removeChild as GaiaContainerChild) return removeChild } @@ -213,10 +209,9 @@ export default class GaiaContainerFolder extends GaiaContainerChild { */ open() { if (this._status) return - const self = this this._status = 1 this.master.classList.add('openning') - this._children.forEach((child) => this.showIconsSubtitle(child.master)) + this._children.forEach((child) => this.showIconsSubtitle(child.element!)) this.manager.status |= 16 this.manager.openedFolder = this this.container.style.height = '100%' @@ -225,30 +220,35 @@ export default class GaiaContainerFolder extends GaiaContainerChild { this.container.style.removeProperty('--grid-height') this.container.style.removeProperty('--grid-width') - this.element.addEventListener('transitionend', function transitionend(evt) { - if (evt.target == self.element && evt.propertyName == 'height') { - self._children.forEach((child) => child.synchroniseContainer()) + this.element.addEventListener('transitionend', this.openTransition) + } + + openTransition = (evt: TransitionEvent) => { + if (evt.target == this.element && evt.propertyName == 'height') { + this._children.forEach((child) => child.synchroniseContainer()) + } + + if ( + this._children[this._children.length - 1].master.compareDocumentPosition( + evt.target as HTMLElement + ) & 16 + ) { + return + } + this.container.style.setProperty('--folder-element-left', '0px') + this.container.style.setProperty('--folder-element-top', '0px') + this.openTimer = setTimeout(() => { + this.master.classList.remove('openning') + this.master.classList.add('opened') + let element = this.suspendElement.shift() + while (element) { + this.movein(element) + this.element.appendChild(element) + element = this.suspendElement.shift() } - self.container.style.setProperty('--folder-element-left', '0px') - self.container.style.setProperty('--folder-element-top', '0px') - if ( - self._children[self._children.length].master.compareDocumentPosition( - evt.target as HTMLElement - ) & 16 - ) - return - self.openTimer = setTimeout(() => { - self.master.classList.remove('openning') - self.master.classList.add('opened') - let element = self.suspendElement.shift() - while (element) { - self.movein(element) - self.element.appendChild(element) - element = self.suspendElement.shift() - } - }, 200) - self.element.removeEventListener('transitionend', transitionend) }) + + this.element.removeEventListener('transitionend', this.openTransition) } /** @@ -263,12 +263,16 @@ export default class GaiaContainerFolder extends GaiaContainerChild { this.removeAppIcon(child) return false } else { - this.hideIconsSubtitle(child.master) + this.hideIconsSubtitle(child.element!) return true } }) - this.master.classList.add('closing') + + this.master.classList.remove('openning') this.master.classList.remove('opened') + this.master.classList.add('closing') + this.element.removeEventListener('transitionend', this.openTransition) + this.manager.status &= ~16 this.manager.openedFolder = null this.container.style.height = this.manager.gridHeight + 'px' @@ -297,10 +301,9 @@ export default class GaiaContainerFolder extends GaiaContainerChild { } destroy() { - if (this._children.length > 1) { + if (this._children.length > 0) { return } - const {height: originHeight, width: originWidth} = this.element.getBoundingClientRect() const {height: targetHeight, width: targetWidth} = @@ -308,6 +311,7 @@ export default class GaiaContainerFolder extends GaiaContainerChild { const child = this._children[0] const master = this._children[0].master + this.manager._children.push(child) const childContainer = master.querySelector( '.gaia-container-child' ) as HTMLElement @@ -319,7 +323,7 @@ export default class GaiaContainerFolder extends GaiaContainerChild { // nextTick,用以配合 originXXX 形成动画 setTimeout(() => { - this.showIconsSubtitle(master) + this.showIconsSubtitle(this._children[0].element!) this.element.style.height = targetHeight + 'px' this.element.style.width = targetWidth + 'px' }) @@ -360,57 +364,23 @@ export default class GaiaContainerFolder extends GaiaContainerChild { this._status && (evt.target as HTMLElement).tagName !== 'GAIA-APP-ICON' ) { - evt.preventDefault() - evt.stopImmediatePropagation() + // evt.preventDefault() + // evt.stopImmediatePropagation() } break } } - addAnimationStyle() { - const styleArr = document.head.querySelectorAll('style') - let styleNode - styleArr.forEach((item) => { - try { - if (item.dataset?.name === 'gaia') { - styleNode = item - } - } catch (error) {} - }) - if (!styleNode) { - styleNode = document.createElement('style') - styleNode.dataset.name = 'gaia' - document.head.appendChild(styleNode) - } - styleNode.innerHTML += ` - @keyframes folder-fadein { - 0% { - opacity: 0; - } - - 100% { - opacity: 1; - } - } - - ` - } - - get shadowStyle() { + static shadowStyle() { return ` ::slotted(.gaia-container-child) { box-sizing: content-box; } - ::slotted(.gaia-container-folder) { - transition: transform 0.2s, box-shadow 0.2s, height 0.2s, width 0.2s !important; - } - ::slotted(.folder:not(.opened)) .gaia-container-container { - } ::slotted(.gaia-container-folder) { display: grid; position: unset; - grid-template-rows: repeat(3, 33.3%); - grid-template-columns: repeat(3, 33.3%); + grid-template-rows: repeat(4, 25%); + grid-template-columns: repeat(4, 25%); grid-auto-flow: row dense; height: 100%; width: 100%; @@ -432,8 +402,8 @@ export default class GaiaContainerFolder extends GaiaContainerChild { } ::slotted(.gaia-container-folder) .gaia-container-child { - height: 16.5% !important; - width: 16.5% !important; + height: 12.5% !important; + width: 12.5% !important; } ::slotted(.gaia-container-folder) gaia-app-icon { @@ -465,6 +435,9 @@ export default class GaiaContainerFolder extends GaiaContainerChild { transform: unset !important; transition: unset !important; } + ::slotted(.folder.initializing) .container-master { + position: relative; + } ::slotted(.folder.initializing) { animation: folder-fadein 0.3s cubic-bezier(.08,.82,.17,1); @@ -497,7 +470,6 @@ export default class GaiaContainerFolder extends GaiaContainerChild { ::slotted(.folder-title) { font-size: 1rem; - color: #fff; line-height: 1rem; margin-top: 3px; opacity: 1; @@ -521,6 +493,9 @@ export default class GaiaContainerFolder extends GaiaContainerChild { ::slotted(.folder.openning) > .gaia-container-child { transform: translate(0) !important; } + ::slotted(.folder.openning) .gaia-container-child { + transition: left 0.2s, height 0.2s, width 0.2s !important; + } ::slotted(.folder.openning) > .gaia-container-child, ::slotted(.folder.closing) > .gaia-container-child { @@ -548,6 +523,16 @@ export default class GaiaContainerFolder extends GaiaContainerChild { ::slotted(.folder.merging) .folder-title { opacity: 0; } + + @keyframes folder-fadein { + 0% { + opacity: 0; + } + + 100% { + opacity: 1; + } + } ` } } diff --git a/src/test/panels/container/asserts/icon.png b/src/test/panels/container/asserts/icon.png new file mode 100644 index 0000000..00b88f7 Binary files /dev/null and b/src/test/panels/container/asserts/icon.png differ diff --git a/src/test/panels/container/homescreen-container.ts b/src/test/panels/container/homescreen-container.ts index 155bc21..c817464 100644 --- a/src/test/panels/container/homescreen-container.ts +++ b/src/test/panels/container/homescreen-container.ts @@ -1,11 +1,15 @@ import {html, css, LitElement, TemplateResult} from 'lit' import {customElement, query, state} from 'lit/decorators.js' import GaiaContainer from '../../../components/grid-container/container' +import GaiaContainerChild from '../../../components/grid-container/gaia-container-child' +import homescreenStyle from './homescreen-style' + import './icon' @customElement('panel-container') export class PanelContainer extends LitElement { container!: GaiaContainer + icons: {[prop: string]: GaiaContainerChild} = {} @query('.reset') resetBtn!: HTMLElement createRandomColor() { function randomInt(min: number, max: number) { @@ -23,35 +27,73 @@ export class PanelContainer extends LitElement { } addAppIcon() { ;(window as any).panel = this - console.log(this.container) - console.log('add') const icon = document.createElement('site-icon') icon.setAttribute('color', this.createRandomColor()) this.container.appendContainerChild(icon) + this.icons[icon.name] = this.container.getChildByElement(icon)! } - reset() { - console.log('reset') - } + reset() {} firstUpdated() { this.container = new GaiaContainer() this.container.setAttribute('dragAndDrop', 'true') this.shadowRoot?.appendChild(this.container) ;(window as any).container = this.container + ;(window as any).home = this + this.addAppIcon() } + @state() test!: string + @state() + protected styles: {[subModule: string]: string} = new Proxy( + {}, + { + set: ( + target: {[module: string]: string}, + prop: string, + value + ): boolean => { + if (!target[prop] || target[prop] !== value) { + target[prop] = value + this.requestUpdate() + } + + return true + }, + } + ) render() { + let style = '' + for (const subModule in this.styles) { + const str = this.styles[subModule] + style += str + style += '\n' + } return html` - - + +
+ + +
` } - static styles = css` - star-container { - height: 90vh; - width: 100vw; - } - ` + static styles = [ + css` + star-container { + height: 90vh; + width: 100vw; + } + .btns { + position: fixed; + top: 0; + left: 0; + z-index: 1; + } + `, + homescreenStyle, + ] } declare global { diff --git a/src/test/panels/container/homescreen-style.ts b/src/test/panels/container/homescreen-style.ts new file mode 100644 index 0000000..81ec1c3 --- /dev/null +++ b/src/test/panels/container/homescreen-style.ts @@ -0,0 +1,9 @@ +import {css} from 'lit' + +export default css` + /* 图标归位时的动画 */ + star-container:not(.loading) + .gaia-container-child:not(.added):not(.dragging) { + transition: transform 0.2s; + } +` diff --git a/src/test/panels/container/icon-style.ts b/src/test/panels/container/icon-style.ts index a1b49f8..71dd248 100644 --- a/src/test/panels/container/icon-style.ts +++ b/src/test/panels/container/icon-style.ts @@ -1,16 +1,19 @@ import {css} from 'lit' export default css` - :host() { - position: absolute; + :host([color]) { + position: var(--width, absolute); top: 50%; left: 50%; - transform: translate(-50%, -50%); + width: 50%; + height: 50%; + max-width: var(--width); + max-height: var(--width); + border-radius: 50%; } #display { --background-color: #fff; - width: 20vw; - height: 12.5vh; + width: 100%; } :host([hide-subtitle]) #subtitle { opacity: 0; @@ -23,12 +26,13 @@ export default css` #image-container { position: relative; transition: visibility 0.2s, opacity 0.2s; - border: 50%; + border-radius: 50%; background-color: var(--background-color); } #image-container img { display: block; + opacity: 0; } #image-container.initial-load { @@ -51,10 +55,11 @@ export default css` animation: rotate 2s infinite linear; } - #image-container, + /* #image-container, #image-container > div { width: 100%; - } + height: 100%; + } */ #subtitle { white-space: nowrap; diff --git a/src/test/panels/container/icon.ts b/src/test/panels/container/icon.ts index 1971f53..173fe62 100644 --- a/src/test/panels/container/icon.ts +++ b/src/test/panels/container/icon.ts @@ -3,15 +3,25 @@ import {customElement, property, query} from 'lit/decorators.js' import style from './icon-style' let count = 0 +let imgBlob!: Blob +const getImage = (): Promise => { + return new Promise((res, rej) => { + if (imgBlob) return res(imgBlob) + const canvas = document.createElement('canvas') + canvas.width = 100 + canvas.height = 100 + canvas.toBlob((blob) => (blob ? res(blob) : rej())) + }) +} @customElement('site-icon') export default class SiteIcon extends LitElement { static defaultColor = '#fff' _color = '#fff' - + name: string = `图标${++count}` @query('#subtitle') subtitle!: HTMLElement @query('#image-container') imgContainer!: HTMLElement - @query('#display') displayImg!: HTMLElement + @query('#display') displayImg!: HTMLImageElement set color(value: string) { this._color = value @@ -31,17 +41,22 @@ export default class SiteIcon extends LitElement { this.style.setProperty('--background-color', color) } firstUpdated() { - console.log('firstUpdated', this.displayImg) + const {width} = this.getBoundingClientRect() + this.style.setProperty('--width', width + 'px') + getImage().then((blob: Blob) => { + let url = URL.createObjectURL(blob) + this.displayImg.src = url + }) } render() { this.changeColor(this.color) return html`
- +
-
图标${++count}
+
${this.name}
` } diff --git a/src/utils/SlotStyleHandler.ts b/src/utils/SlotStyleHandler.ts index 4349134..7f5ab04 100644 --- a/src/utils/SlotStyleHandler.ts +++ b/src/utils/SlotStyleHandler.ts @@ -28,7 +28,6 @@ class SlotStyleHandler { processCss(style: string, name: string) { let globalCss = '' style.replace(this.regex.keyframes, (match) => { - console.log('=====', match) globalCss += match return '' })