web: add AnimatePlugin

This commit is contained in:
pengfei.zhou
2021-04-16 17:19:02 +08:00
committed by osborn
parent 900bccb98a
commit d1cc17c7a0
7 changed files with 345 additions and 124 deletions

View File

@@ -20,6 +20,7 @@ export class DoricContext {
pluginInstances: Map<string, DoricPlugin> = new Map
rootNode: DoricStackNode
headNodes: Map<string, Map<string, DoricViewNode>> = new Map
animationSet?: { viewNode: DoricViewNode, keyFrame: Partial<CSSStyleDeclaration> }[]
constructor(content: string) {
createContext(this.contextId, content)
@@ -84,6 +85,18 @@ export class DoricContext {
}) {
this.invokeEntityMethod("__build__", frame)
}
inAnimation() {
return !!this.animationSet
}
addAnimation(viewNode: DoricViewNode, keyFrame: Partial<CSSStyleDeclaration>) {
this.animationSet?.push({
viewNode,
keyFrame
})
}
teardown() {
for (let plugin of this.pluginInstances.values()) {
plugin.onTearDown()

View File

@@ -15,6 +15,7 @@ import { DoricListItemNode } from "./shader/DoricListItemNode"
import { DoricListNode } from "./shader/DoricListNode"
import { DoricDraggableNode } from "./shader/DoricDraggableNode"
import { DoricRefreshableNode } from "./shader/DoricRefreshableNode"
import { AnimatePlugin } from "./plugins/AnimatePlugin"
const bundles: Map<string, string> = new Map
@@ -54,6 +55,7 @@ registerPlugin('modal', ModalPlugin)
registerPlugin('storage', StoragePlugin)
registerPlugin('navigator', NavigatorPlugin)
registerPlugin('popover', PopoverPlugin)
registerPlugin('animate', AnimatePlugin)
registerViewNode('Stack', DoricStackNode)
registerViewNode('VLayout', DoricVLayoutNode)

View File

@@ -0,0 +1,47 @@
import { DoricPlugin } from "../DoricPlugin";
export class AnimatePlugin extends DoricPlugin {
submit() {
return Promise.resolve()
}
animateRender(args: {
duration: number,
id: string,
props: any
}) {
this.context.animationSet = []
if (this.context.rootNode.viewId?.length > 0) {
const viewNode = this.context.targetViewNode(args.id)
viewNode?.blend(args.props)
viewNode?.onBlended()
} else {
this.context.rootNode.viewId = args.id
this.context.rootNode.blend(args.props)
this.context.rootNode.onBlended()
}
return new Promise(resolve => {
Promise.resolve().then(() => {
Promise.all(
this.context.animationSet?.map(e => {
return new Promise(resolve => {
const animation = e.viewNode.view.animate(
[e.keyFrame as Keyframe],
{
duration: args.duration,
fill: "forwards"
})
animation.onfinish = () => {
resolve(true)
}
})
}) || [])
.then(() => {
resolve(0)
})
.finally(() => {
this.context.animationSet = undefined
})
})
})
}
}

View File

@@ -39,24 +39,29 @@ export class DoricStackNode extends DoricGroupViewNode {
configOffset() {
this.childNodes.forEach(e => {
e.view.style.position = "absolute"
e.view.style.left = toPixelString(e.offsetX + this.paddingLeft)
e.view.style.top = toPixelString(e.offsetY + this.paddingTop)
const position = "absolute"
let left = toPixelString(e.offsetX + this.paddingLeft)
let top = toPixelString(e.offsetY + this.paddingTop)
const gravity = e.layoutConfig.alignment
if ((gravity & LEFT) === LEFT) {
e.view.style.left = toPixelString(0)
left = toPixelString(0)
} else if ((gravity & RIGHT) === RIGHT) {
e.view.style.left = toPixelString(this.view.offsetWidth - e.view.offsetWidth)
left = toPixelString(this.view.offsetWidth - e.view.offsetWidth)
} else if ((gravity & CENTER_X) === CENTER_X) {
e.view.style.left = toPixelString(this.view.offsetWidth / 2 - e.view.offsetWidth / 2)
left = toPixelString(this.view.offsetWidth / 2 - e.view.offsetWidth / 2)
}
if ((gravity & TOP) === TOP) {
e.view.style.top = toPixelString(0)
top = toPixelString(0)
} else if ((gravity & BOTTOM) === BOTTOM) {
e.view.style.top = toPixelString(this.view.offsetHeight - e.view.offsetHeight)
top = toPixelString(this.view.offsetHeight - e.view.offsetHeight)
} else if ((gravity & CENTER_Y) === CENTER_Y) {
e.view.style.top = toPixelString(this.view.offsetHeight / 2 - e.view.offsetHeight / 2)
top = toPixelString(this.view.offsetHeight / 2 - e.view.offsetHeight / 2)
}
e.applyCSSStyle({
position,
left,
top,
})
})
}
}

View File

@@ -172,64 +172,75 @@ export abstract class DoricViewNode {
configBorder() {
if (this.border) {
this.view.style.borderStyle = "solid"
this.view.style.borderWidth = toPixelString(this.border.width)
this.view.style.borderColor = toRGBAString(this.border.color)
this.applyCSSStyle({
borderStyle: "solid",
borderWidth: toPixelString(this.border.width),
borderColor: toRGBAString(this.border.color),
})
}
}
configWidth() {
let width: string
switch (this.layoutConfig.widthSpec) {
case LayoutSpec.WRAP_CONTENT:
this.view.style.width = "max-content"
width = "max-content"
break
case LayoutSpec.AT_MOST:
this.view.style.width = "100%"
width = "100%"
break
case LayoutSpec.EXACTLY:
default:
this.view.style.width = toPixelString(this.frameWidth
width = toPixelString(this.frameWidth
- this.paddingLeft - this.paddingRight
- this.borderWidth * 2)
break
}
this.applyCSSStyle({ width })
}
configHeight() {
let height
switch (this.layoutConfig.heightSpec) {
case LayoutSpec.WRAP_CONTENT:
this.view.style.height = "max-content"
height = "max-content"
break
case LayoutSpec.AT_MOST:
this.view.style.height = "100%"
height = "100%"
break
case LayoutSpec.EXACTLY:
default:
this.view.style.height = toPixelString(this.frameHeight
height = toPixelString(this.frameHeight
- this.paddingTop - this.paddingBottom
- this.borderWidth * 2)
break
}
this.applyCSSStyle({ height })
}
configMargin() {
if (this.layoutConfig.margin) {
this.view.style.marginLeft = toPixelString(this.layoutConfig.margin.left || 0)
this.view.style.marginRight = toPixelString(this.layoutConfig.margin.right || 0)
this.view.style.marginTop = toPixelString(this.layoutConfig.margin.top || 0)
this.view.style.marginBottom = toPixelString(this.layoutConfig.margin.bottom || 0)
this.applyCSSStyle({
marginLeft: toPixelString(this.layoutConfig.margin.left || 0),
marginRight: toPixelString(this.layoutConfig.margin.right || 0),
marginTop: toPixelString(this.layoutConfig.margin.top || 0),
marginBottom: toPixelString(this.layoutConfig.margin.bottom || 0),
})
}
}
configPadding() {
if (this.padding) {
this.view.style.paddingLeft = toPixelString(this.paddingLeft)
this.view.style.paddingRight = toPixelString(this.paddingRight)
this.view.style.paddingTop = toPixelString(this.paddingTop)
this.view.style.paddingBottom = toPixelString(this.paddingBottom)
this.applyCSSStyle({
paddingLeft: toPixelString(this.paddingLeft),
paddingRight: toPixelString(this.paddingRight),
paddingTop: toPixelString(this.paddingTop),
paddingBottom: toPixelString(this.paddingBottom),
})
}
}
@@ -278,33 +289,59 @@ export abstract class DoricViewNode {
break
case 'corners':
if (typeof prop === 'object') {
this.view.style.borderTopLeftRadius = toPixelString(prop.leftTop)
this.view.style.borderTopRightRadius = toPixelString(prop.rightTop)
this.view.style.borderBottomRightRadius = toPixelString(prop.rightBottom)
this.view.style.borderBottomLeftRadius = toPixelString(prop.leftBottom)
this.applyCSSStyle({
borderTopLeftRadius: toPixelString(prop.leftTop),
borderTopRightRadius: toPixelString(prop.rightTop),
borderBottomRightRadius: toPixelString(prop.rightBottom),
borderBottomLeftRadius: toPixelString(prop.leftBottom),
})
} else {
this.view.style.borderRadius = toPixelString(prop)
this.applyCSSStyle({ borderRadius: toPixelString(prop) })
}
break
case 'shadow':
const opacity = prop.opacity || 0
let boxShadow
if (opacity > 0) {
const offsetX = prop.offsetX || 0
const offsetY = prop.offsetY || 0
const shadowColor = prop.color || 0xff000000
const shadowRadius = prop.radius
const alpha = opacity * 255
this.view.style.boxShadow = `${toPixelString(offsetX)} ${toPixelString(offsetY)} ${toPixelString(shadowRadius)} ${toRGBAString((shadowColor & 0xffffff) | ((alpha & 0xff) << 24))} `
boxShadow = `${toPixelString(offsetX)} ${toPixelString(offsetY)} ${toPixelString(shadowRadius)} ${toRGBAString((shadowColor & 0xffffff) | ((alpha & 0xff) << 24))} `
} else {
this.view.style.boxShadow = ""
boxShadow = ""
}
this.applyCSSStyle({
boxShadow,
})
break
case 'alpha':
this.view.style.opacity = `${prop}`
this.applyCSSStyle({
opacity: `${prop}`,
})
break
case 'rotation':
this.transform.rotation = prop
break
case 'rotationX':
this.transform.rotationX = prop
break
case 'rotationY':
this.transform.rotationY = prop
break
case 'scaleX':
this.transform.scaleX = prop
break
case 'scaleY':
this.transform.scaleY = prop
break
case 'translationX':
this.transform.translateX = prop
break
case 'translationY':
this.transform.translateY = prop
break
case 'pivotX':
if (this.transformOrigin) {
this.transformOrigin.x = prop
@@ -326,11 +363,9 @@ export abstract class DoricViewNode {
}
break
case 'hidden':
if (prop === true) {
this.view.style.display = "none"
} else {
this.view.style.display = this._originDisplay
}
this.applyCSSStyle({
display: prop === true ? "none" : this._originDisplay
})
break
default:
console.error(`Cannot blend prop for ${propName}`)
@@ -339,7 +374,7 @@ export abstract class DoricViewNode {
}
set backgroundColor(v: number) {
this.view.style.backgroundColor = toRGBAString(v)
this.applyCSSStyle({ backgroundColor: toRGBAString(v) })
}
static create(context: DoricContext, type: string) {
@@ -381,31 +416,45 @@ export abstract class DoricViewNode {
updateTransform() {
this.view.style.transform = Object.entries(this.transform).filter((e: [string, number?]) => !!e[1]).map((e: [string, number?]) => {
const v = e[1] || 0
switch (e[0]) {
case "translateX":
return `translateX(${v}px)`
case "scaleX":
return `scaleX(${v})`
case "scaleY":
return `scaleY(${v})`
case "rotation":
return `rotate(${v / 2}turn)`
case "rotateX":
return `rotateX(${v / 2}turn)`
case "rotateY":
return `rotateY(${v / 2}turn)`
default:
console.error(`Do not support transform ${e[0]}`)
return ""
}
}).join(" ")
this.applyCSSStyle({
transform: Object.entries(this.transform).filter((e: [string, number?]) => !!e[1]).map((e: [string, number?]) => {
const v = e[1] || 0
switch (e[0]) {
case "translateX":
return `translateX(${v}px)`
case "scaleX":
return `scaleX(${v})`
case "scaleY":
return `scaleY(${v})`
case "rotation":
return `rotate(${v / 2}turn)`
case "rotationX":
return `rotateX(${v / 2}turn)`
case "rotationY":
return `rotateY(${v / 2}turn)`
default:
console.error(`Do not support transform ${e[0]}`)
return ""
}
}).join(" ")
})
}
updateTransformOrigin() {
if (this.transformOrigin) {
this.view.style.transformOrigin = `${Math.round(this.transformOrigin.x * 100)}% ${Math.round(this.transformOrigin.y * 100)}%`
this.applyCSSStyle({
transformOrigin: `${Math.round(this.transformOrigin.x * 100)}% ${Math.round(this.transformOrigin.y * 100)}%`
})
}
}
applyCSSStyle(cssStyle: Partial<CSSStyleDeclaration>) {
if (this.context.inAnimation()) {
this.context.addAnimation(this, cssStyle)
} else {
for (let v in cssStyle) {
Reflect.set(this.view.style, v, cssStyle[v])
}
}
}
/** ++++++++++call from doric ++++++++++*/