TASK: #103599 - edit container folder animation

This commit is contained in:
luojiahao 2022-08-30 13:51:10 +08:00
parent 4a46290727
commit eab57a3d92
10 changed files with 206 additions and 132 deletions

View File

@ -10,3 +10,4 @@
- add blur
- add contaienr
- add SlotStyleHandler
- edit container folder animation

View File

@ -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×1n×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()
}

View File

@ -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
}

View File

@ -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;
}
}
`
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -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`
<button class="add" @click=${this.addAppIcon}></button>
<button class="reset" @click=${this.reset}></button>
<style>
${style}
</style>
<div class="btns">
<button class="add" @click=${this.addAppIcon}></button>
<button class="reset" @click=${this.reset}></button>
</div>
`
}
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 {

View File

@ -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;
}
`

View File

@ -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;

View File

@ -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<Blob> => {
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`
<div id="image-container">
<div id="spinner"></div>
<img id="display" />
<img id="display" src="./asserts/icon.png" />
</div>
<div>
<div dir="auto" id="subtitle">${++count}</div>
<div dir="auto" id="subtitle">${this.name}</div>
</div>
`
}

View File

@ -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 ''
})