2019-12-19 20:44:14 +08:00
|
|
|
import { DoricContext } from "../DoricContext";
|
|
|
|
import { acquireViewNode } from "../DoricRegistry";
|
|
|
|
|
2019-12-20 16:46:25 +08:00
|
|
|
export enum LayoutSpec {
|
2019-12-19 21:12:41 +08:00
|
|
|
EXACTLY = 0,
|
|
|
|
WRAP_CONTENT = 1,
|
|
|
|
AT_MOST = 2,
|
|
|
|
}
|
|
|
|
|
2019-12-20 16:46:25 +08:00
|
|
|
const SPECIFIED = 1
|
|
|
|
const START = 1 << 1
|
|
|
|
const END = 1 << 2
|
|
|
|
|
|
|
|
const SHIFT_X = 0
|
|
|
|
const SHIFT_Y = 4
|
|
|
|
|
|
|
|
export const LEFT = (START | SPECIFIED) << SHIFT_X
|
|
|
|
export const RIGHT = (END | SPECIFIED) << SHIFT_X
|
|
|
|
|
|
|
|
export const TOP = (START | SPECIFIED) << SHIFT_Y
|
|
|
|
export const BOTTOM = (END | SPECIFIED) << SHIFT_Y
|
|
|
|
|
|
|
|
export const CENTER_X = SPECIFIED << SHIFT_X
|
|
|
|
export const CENTER_Y = SPECIFIED << SHIFT_Y
|
|
|
|
|
|
|
|
export const CENTER = CENTER_X | CENTER_Y
|
|
|
|
|
|
|
|
export type FrameSize = {
|
|
|
|
width: number,
|
|
|
|
height: number,
|
|
|
|
}
|
2019-12-20 17:56:01 +08:00
|
|
|
export function toPixelString(v: number) {
|
2019-12-20 15:37:56 +08:00
|
|
|
return `${v}px`
|
2019-12-19 21:12:41 +08:00
|
|
|
}
|
2019-12-20 15:37:56 +08:00
|
|
|
|
2021-04-15 18:34:06 +08:00
|
|
|
export function pixelString2Number(v: string) {
|
|
|
|
return parseFloat(v.substring(0, v.indexOf("px")))
|
|
|
|
}
|
|
|
|
|
2019-12-20 17:56:01 +08:00
|
|
|
export function toRGBAString(color: number) {
|
2019-12-20 15:37:56 +08:00
|
|
|
let strs = []
|
|
|
|
for (let i = 0; i < 32; i += 8) {
|
2021-04-19 19:56:00 +08:00
|
|
|
strs.push(((color >> i) & 0xff))
|
2019-12-19 21:12:41 +08:00
|
|
|
}
|
2021-04-19 19:56:00 +08:00
|
|
|
strs = strs.reverse()
|
|
|
|
/// RGBAd
|
|
|
|
return `rgba(${strs[1]},${strs[2]},${strs[3]},${strs[0] / 255})`
|
2019-12-19 21:12:41 +08:00
|
|
|
}
|
|
|
|
|
2019-12-20 15:37:56 +08:00
|
|
|
|
2019-12-19 20:44:14 +08:00
|
|
|
export type DoricViewNodeClass = { new(...args: any[]): {} }
|
|
|
|
|
|
|
|
export interface DVModel {
|
|
|
|
id: string,
|
|
|
|
type: string,
|
|
|
|
props: {
|
|
|
|
[index: string]: any
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
export abstract class DoricViewNode {
|
|
|
|
viewId = ""
|
|
|
|
viewType = "View"
|
|
|
|
context: DoricContext
|
2019-12-26 19:15:54 +08:00
|
|
|
superNode?: DoricSuperNode
|
2019-12-19 21:12:41 +08:00
|
|
|
layoutConfig = {
|
2019-12-19 20:44:14 +08:00
|
|
|
widthSpec: LayoutSpec.EXACTLY,
|
|
|
|
heightSpec: LayoutSpec.EXACTLY,
|
2019-12-19 21:12:41 +08:00
|
|
|
alignment: 0,
|
2019-12-19 20:44:14 +08:00
|
|
|
weight: 0,
|
|
|
|
margin: {
|
|
|
|
left: 0,
|
|
|
|
right: 0,
|
|
|
|
top: 0,
|
|
|
|
bottom: 0
|
|
|
|
}
|
|
|
|
}
|
2019-12-20 13:14:48 +08:00
|
|
|
padding = {
|
|
|
|
left: 0,
|
|
|
|
right: 0,
|
|
|
|
top: 0,
|
|
|
|
bottom: 0,
|
|
|
|
}
|
2019-12-20 15:37:56 +08:00
|
|
|
|
|
|
|
border?: {
|
|
|
|
width: number,
|
|
|
|
color: number,
|
|
|
|
}
|
|
|
|
|
2019-12-20 13:14:48 +08:00
|
|
|
frameWidth = 0
|
2019-12-20 15:37:56 +08:00
|
|
|
|
2019-12-20 13:14:48 +08:00
|
|
|
frameHeight = 0
|
|
|
|
|
2019-12-20 15:37:56 +08:00
|
|
|
offsetX = 0
|
|
|
|
|
|
|
|
offsetY = 0
|
|
|
|
|
2019-12-19 20:44:14 +08:00
|
|
|
view!: HTMLElement
|
2019-12-20 15:37:56 +08:00
|
|
|
|
2021-04-16 14:34:16 +08:00
|
|
|
_originDisplay: string = ""
|
|
|
|
|
2021-04-16 13:20:03 +08:00
|
|
|
transform: {
|
|
|
|
translateX?: number,
|
|
|
|
translateY?: number,
|
|
|
|
scaleX?: number,
|
|
|
|
scaleY?: number,
|
|
|
|
rotation?: number,
|
|
|
|
rotationX?: number,
|
|
|
|
rotationY?: number
|
|
|
|
} = {}
|
|
|
|
|
|
|
|
transformOrigin: { x: number, y: number } | undefined
|
|
|
|
|
2019-12-19 20:44:14 +08:00
|
|
|
constructor(context: DoricContext) {
|
|
|
|
this.context = context
|
|
|
|
}
|
|
|
|
|
2019-12-26 19:15:54 +08:00
|
|
|
init(superNode?: DoricSuperNode) {
|
2019-12-26 19:01:28 +08:00
|
|
|
if (superNode) {
|
|
|
|
this.superNode = superNode
|
2019-12-26 19:15:54 +08:00
|
|
|
if (this instanceof DoricSuperNode) {
|
2019-12-26 19:01:28 +08:00
|
|
|
this.reusable = superNode.reusable
|
|
|
|
}
|
2019-12-19 20:44:14 +08:00
|
|
|
}
|
|
|
|
this.view = this.build()
|
2021-04-16 14:34:16 +08:00
|
|
|
this._originDisplay = this.view.style.display
|
2019-12-19 20:44:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
abstract build(): HTMLElement
|
|
|
|
|
2019-12-20 15:37:56 +08:00
|
|
|
get paddingLeft() {
|
|
|
|
return this.padding.left || 0
|
|
|
|
}
|
|
|
|
|
|
|
|
get paddingRight() {
|
|
|
|
return this.padding.right || 0
|
|
|
|
}
|
|
|
|
|
|
|
|
get paddingTop() {
|
|
|
|
return this.padding.top || 0
|
|
|
|
}
|
|
|
|
|
|
|
|
get paddingBottom() {
|
|
|
|
return this.padding.bottom || 0
|
|
|
|
}
|
|
|
|
|
|
|
|
get borderWidth() {
|
|
|
|
return this.border?.width || 0
|
|
|
|
}
|
|
|
|
|
2019-12-19 20:44:14 +08:00
|
|
|
blend(props: { [index: string]: any }) {
|
2019-12-26 17:09:03 +08:00
|
|
|
this.view.id = `${this.viewId}`
|
2019-12-19 20:44:14 +08:00
|
|
|
for (let key in props) {
|
|
|
|
this.blendProps(this.view, key, props[key])
|
|
|
|
}
|
2021-04-14 17:46:58 +08:00
|
|
|
this.onBlending()
|
2019-12-21 11:09:27 +08:00
|
|
|
this.layout()
|
|
|
|
}
|
2021-04-14 17:46:58 +08:00
|
|
|
|
|
|
|
onBlending() {
|
2021-04-16 13:20:03 +08:00
|
|
|
this.updateTransform()
|
2021-04-14 17:46:58 +08:00
|
|
|
}
|
|
|
|
|
2019-12-21 11:09:27 +08:00
|
|
|
onBlended() {
|
|
|
|
}
|
2021-04-14 17:46:58 +08:00
|
|
|
|
2019-12-21 11:09:27 +08:00
|
|
|
configBorder() {
|
2019-12-20 15:37:56 +08:00
|
|
|
if (this.border) {
|
2021-04-16 17:19:02 +08:00
|
|
|
this.applyCSSStyle({
|
|
|
|
borderStyle: "solid",
|
|
|
|
borderWidth: toPixelString(this.border.width),
|
|
|
|
borderColor: toRGBAString(this.border.color),
|
|
|
|
})
|
2019-12-20 15:37:56 +08:00
|
|
|
}
|
2019-12-21 11:09:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
configWidth() {
|
2021-04-16 17:19:02 +08:00
|
|
|
let width: string
|
2019-12-21 11:09:27 +08:00
|
|
|
switch (this.layoutConfig.widthSpec) {
|
|
|
|
case LayoutSpec.WRAP_CONTENT:
|
2021-04-16 17:19:02 +08:00
|
|
|
width = "max-content"
|
2019-12-21 11:09:27 +08:00
|
|
|
break
|
|
|
|
|
|
|
|
case LayoutSpec.AT_MOST:
|
2021-04-16 17:19:02 +08:00
|
|
|
width = "100%"
|
2019-12-21 11:09:27 +08:00
|
|
|
break
|
|
|
|
|
|
|
|
case LayoutSpec.EXACTLY:
|
|
|
|
default:
|
2021-04-16 17:19:02 +08:00
|
|
|
width = toPixelString(this.frameWidth
|
2019-12-21 11:09:27 +08:00
|
|
|
- this.paddingLeft - this.paddingRight
|
|
|
|
- this.borderWidth * 2)
|
|
|
|
break
|
|
|
|
}
|
2021-04-16 17:19:02 +08:00
|
|
|
this.applyCSSStyle({ width })
|
2019-12-21 11:09:27 +08:00
|
|
|
}
|
2021-04-16 17:19:02 +08:00
|
|
|
|
2019-12-21 11:09:27 +08:00
|
|
|
configHeight() {
|
2021-04-16 17:19:02 +08:00
|
|
|
let height
|
2019-12-21 11:09:27 +08:00
|
|
|
switch (this.layoutConfig.heightSpec) {
|
|
|
|
case LayoutSpec.WRAP_CONTENT:
|
2021-04-16 17:19:02 +08:00
|
|
|
height = "max-content"
|
2019-12-21 11:09:27 +08:00
|
|
|
break
|
|
|
|
|
|
|
|
case LayoutSpec.AT_MOST:
|
2021-04-16 17:19:02 +08:00
|
|
|
height = "100%"
|
2019-12-21 11:09:27 +08:00
|
|
|
break
|
|
|
|
|
|
|
|
case LayoutSpec.EXACTLY:
|
|
|
|
default:
|
2021-04-16 17:19:02 +08:00
|
|
|
height = toPixelString(this.frameHeight
|
2019-12-21 11:09:27 +08:00
|
|
|
- this.paddingTop - this.paddingBottom
|
|
|
|
- this.borderWidth * 2)
|
|
|
|
break
|
|
|
|
}
|
2021-04-16 17:19:02 +08:00
|
|
|
this.applyCSSStyle({ height })
|
2019-12-21 11:09:27 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
configMargin() {
|
|
|
|
if (this.layoutConfig.margin) {
|
2021-04-16 17:19:02 +08:00
|
|
|
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),
|
|
|
|
})
|
2019-12-21 11:09:27 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
configPadding() {
|
2019-12-20 15:37:56 +08:00
|
|
|
if (this.padding) {
|
2021-04-16 17:19:02 +08:00
|
|
|
this.applyCSSStyle({
|
|
|
|
paddingLeft: toPixelString(this.paddingLeft),
|
|
|
|
paddingRight: toPixelString(this.paddingRight),
|
|
|
|
paddingTop: toPixelString(this.paddingTop),
|
|
|
|
paddingBottom: toPixelString(this.paddingBottom),
|
|
|
|
})
|
2019-12-20 15:37:56 +08:00
|
|
|
}
|
2019-12-19 20:44:14 +08:00
|
|
|
}
|
|
|
|
|
2019-12-20 16:46:25 +08:00
|
|
|
layout() {
|
2019-12-21 11:09:27 +08:00
|
|
|
this.configMargin()
|
|
|
|
this.configBorder()
|
|
|
|
this.configPadding()
|
|
|
|
this.configWidth()
|
|
|
|
this.configHeight()
|
2019-12-20 16:46:25 +08:00
|
|
|
}
|
|
|
|
|
2019-12-19 20:44:14 +08:00
|
|
|
blendProps(v: HTMLElement, propName: string, prop: any) {
|
|
|
|
switch (propName) {
|
2019-12-20 15:37:56 +08:00
|
|
|
case "border":
|
|
|
|
this.border = prop
|
|
|
|
break
|
|
|
|
case "padding":
|
|
|
|
this.padding = prop
|
|
|
|
break
|
|
|
|
case 'width':
|
|
|
|
this.frameWidth = prop as number
|
|
|
|
break
|
|
|
|
case 'height':
|
|
|
|
this.frameHeight = prop as number
|
|
|
|
break
|
2019-12-19 20:44:14 +08:00
|
|
|
case 'backgroundColor':
|
|
|
|
this.backgroundColor = prop as number
|
|
|
|
break
|
|
|
|
case 'layoutConfig':
|
2019-12-19 21:12:41 +08:00
|
|
|
const layoutConfig = prop
|
2019-12-19 20:44:14 +08:00
|
|
|
for (let key in layoutConfig) {
|
|
|
|
Reflect.set(this.layoutConfig, key, Reflect.get(layoutConfig, key, layoutConfig))
|
|
|
|
}
|
|
|
|
break
|
2019-12-20 13:14:48 +08:00
|
|
|
case 'x':
|
2019-12-20 15:37:56 +08:00
|
|
|
this.offsetX = prop as number
|
2019-12-20 13:14:48 +08:00
|
|
|
break
|
|
|
|
case 'y':
|
2019-12-20 15:37:56 +08:00
|
|
|
this.offsetY = prop as number
|
2019-12-20 13:14:48 +08:00
|
|
|
break
|
2019-12-20 21:26:10 +08:00
|
|
|
case 'onClick':
|
2019-12-26 19:01:28 +08:00
|
|
|
this.view.onclick = (event: Event) => {
|
2019-12-20 21:26:10 +08:00
|
|
|
this.callJSResponse(prop as string)
|
2019-12-26 19:01:28 +08:00
|
|
|
event.stopPropagation()
|
2019-12-20 21:26:10 +08:00
|
|
|
}
|
|
|
|
break
|
2019-12-21 17:49:34 +08:00
|
|
|
case 'corners':
|
|
|
|
if (typeof prop === 'object') {
|
2021-04-16 17:19:02 +08:00
|
|
|
this.applyCSSStyle({
|
|
|
|
borderTopLeftRadius: toPixelString(prop.leftTop),
|
|
|
|
borderTopRightRadius: toPixelString(prop.rightTop),
|
|
|
|
borderBottomRightRadius: toPixelString(prop.rightBottom),
|
|
|
|
borderBottomLeftRadius: toPixelString(prop.leftBottom),
|
|
|
|
})
|
2019-12-21 17:49:34 +08:00
|
|
|
} else {
|
2021-04-16 17:19:02 +08:00
|
|
|
this.applyCSSStyle({ borderRadius: toPixelString(prop) })
|
2019-12-21 17:49:34 +08:00
|
|
|
}
|
|
|
|
break
|
2019-12-21 18:04:12 +08:00
|
|
|
case 'shadow':
|
|
|
|
const opacity = prop.opacity || 0
|
2021-04-16 17:19:02 +08:00
|
|
|
let boxShadow
|
2019-12-21 18:04:12 +08:00
|
|
|
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
|
2021-04-16 17:19:02 +08:00
|
|
|
boxShadow = `${toPixelString(offsetX)} ${toPixelString(offsetY)} ${toPixelString(shadowRadius)} ${toRGBAString((shadowColor & 0xffffff) | ((alpha & 0xff) << 24))} `
|
2019-12-21 18:04:12 +08:00
|
|
|
} else {
|
2021-04-16 17:19:02 +08:00
|
|
|
boxShadow = ""
|
2019-12-21 18:04:12 +08:00
|
|
|
}
|
2021-04-16 17:19:02 +08:00
|
|
|
this.applyCSSStyle({
|
|
|
|
boxShadow,
|
|
|
|
})
|
2019-12-21 18:04:12 +08:00
|
|
|
break
|
2021-04-15 15:51:24 +08:00
|
|
|
case 'alpha':
|
2021-04-16 17:19:02 +08:00
|
|
|
this.applyCSSStyle({
|
|
|
|
opacity: `${prop}`,
|
|
|
|
})
|
2021-04-15 15:51:24 +08:00
|
|
|
break
|
2021-04-16 13:20:03 +08:00
|
|
|
case 'rotation':
|
|
|
|
this.transform.rotation = prop
|
|
|
|
break
|
2021-04-16 17:19:02 +08:00
|
|
|
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
|
2021-04-16 13:20:03 +08:00
|
|
|
case 'pivotX':
|
|
|
|
if (this.transformOrigin) {
|
|
|
|
this.transformOrigin.x = prop
|
|
|
|
} else {
|
|
|
|
this.transformOrigin = {
|
|
|
|
x: prop,
|
|
|
|
y: 0.5,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break
|
|
|
|
case 'pivotY':
|
|
|
|
if (this.transformOrigin) {
|
|
|
|
this.transformOrigin.y = prop
|
|
|
|
} else {
|
|
|
|
this.transformOrigin = {
|
|
|
|
x: 0.5,
|
|
|
|
y: prop,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break
|
2021-04-16 14:34:16 +08:00
|
|
|
case 'hidden':
|
2021-04-16 17:19:02 +08:00
|
|
|
this.applyCSSStyle({
|
|
|
|
display: prop === true ? "none" : this._originDisplay
|
|
|
|
})
|
2021-04-16 14:34:16 +08:00
|
|
|
break
|
2021-04-16 13:20:03 +08:00
|
|
|
default:
|
|
|
|
console.error(`Cannot blend prop for ${propName}`)
|
|
|
|
break
|
2019-12-19 20:44:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
set backgroundColor(v: number) {
|
2021-04-16 17:19:02 +08:00
|
|
|
this.applyCSSStyle({ backgroundColor: toRGBAString(v) })
|
2019-12-19 20:44:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static create(context: DoricContext, type: string) {
|
|
|
|
const viewNodeClass = acquireViewNode(type)
|
|
|
|
if (viewNodeClass === undefined) {
|
|
|
|
console.error(`Cannot find ViewNode for ${type}`)
|
|
|
|
return undefined
|
|
|
|
}
|
|
|
|
const ret = new viewNodeClass(context) as DoricViewNode
|
|
|
|
ret.viewType = type
|
|
|
|
return ret
|
|
|
|
}
|
2019-12-20 16:46:25 +08:00
|
|
|
|
2019-12-20 21:26:10 +08:00
|
|
|
getIdList() {
|
|
|
|
const ids: string[] = []
|
|
|
|
let viewNode: DoricViewNode | undefined = this
|
|
|
|
do {
|
|
|
|
ids.push(viewNode.viewId)
|
|
|
|
viewNode = viewNode.superNode
|
|
|
|
} while (viewNode)
|
|
|
|
return ids.reverse()
|
|
|
|
}
|
|
|
|
|
|
|
|
callJSResponse(funcId: string, ...args: any) {
|
|
|
|
const argumentsList: any = ['__response__', this.getIdList(), funcId]
|
|
|
|
for (let i = 1; i < arguments.length; i++) {
|
|
|
|
argumentsList.push(arguments[i])
|
|
|
|
}
|
2019-12-28 14:25:03 +08:00
|
|
|
return Reflect.apply(this.context.invokeEntityMethod, this.context, argumentsList)
|
2019-12-20 21:26:10 +08:00
|
|
|
}
|
2021-04-14 16:53:38 +08:00
|
|
|
|
|
|
|
pureCallJSResponse(funcId: string, ...args: any) {
|
|
|
|
const argumentsList: any = ['__response__', this.getIdList(), funcId]
|
|
|
|
for (let i = 1; i < arguments.length; i++) {
|
|
|
|
argumentsList.push(arguments[i])
|
|
|
|
}
|
|
|
|
return Reflect.apply(this.context.pureInvokeEntityMethod, this.context, argumentsList)
|
|
|
|
}
|
2021-04-15 15:51:24 +08:00
|
|
|
|
2021-04-16 13:20:03 +08:00
|
|
|
|
|
|
|
updateTransform() {
|
2021-04-16 17:19:02 +08:00
|
|
|
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(" ")
|
|
|
|
})
|
2021-04-16 13:20:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
updateTransformOrigin() {
|
|
|
|
if (this.transformOrigin) {
|
2021-04-16 17:19:02 +08:00
|
|
|
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])
|
|
|
|
}
|
2021-04-16 13:20:03 +08:00
|
|
|
}
|
|
|
|
}
|
2021-04-15 15:51:24 +08:00
|
|
|
/** ++++++++++call from doric ++++++++++*/
|
|
|
|
getWidth() {
|
|
|
|
return this.view.offsetWidth
|
|
|
|
}
|
|
|
|
|
|
|
|
getHeight() {
|
|
|
|
return this.view.offsetHeight
|
|
|
|
}
|
|
|
|
|
|
|
|
setWidth(v: number) {
|
|
|
|
this.view.style.width = toPixelString(v)
|
|
|
|
}
|
|
|
|
|
|
|
|
setHeight(v: number) {
|
|
|
|
this.view.style.height = toPixelString(v)
|
|
|
|
}
|
|
|
|
|
|
|
|
getX() {
|
|
|
|
return this.view.offsetLeft
|
|
|
|
}
|
|
|
|
|
|
|
|
getY() {
|
|
|
|
return this.view.offsetTop
|
|
|
|
}
|
|
|
|
|
|
|
|
setX(v: number) {
|
|
|
|
this.view.style.left = toPixelString(v)
|
|
|
|
}
|
|
|
|
|
|
|
|
setY(v: number) {
|
|
|
|
this.view.style.top = toPixelString(v)
|
|
|
|
}
|
|
|
|
|
|
|
|
getBackgroundColor() {
|
|
|
|
return this.view.style.backgroundColor
|
|
|
|
}
|
|
|
|
|
|
|
|
setBackgroundColor(v: number) {
|
|
|
|
this.backgroundColor = v
|
|
|
|
}
|
|
|
|
|
|
|
|
getAlpha() {
|
2021-04-15 18:34:06 +08:00
|
|
|
return parseFloat(this.view.style.opacity)
|
2021-04-15 15:51:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
setAlpha(v: number) {
|
|
|
|
this.view.style.opacity = `${v}`
|
|
|
|
}
|
|
|
|
|
|
|
|
getCorners() {
|
2021-04-15 18:34:06 +08:00
|
|
|
return parseFloat(this.view.style.borderRadius)
|
2021-04-15 15:51:24 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
setCorners(v: number) {
|
|
|
|
this.view.style.borderRadius = toPixelString(v)
|
|
|
|
}
|
|
|
|
|
|
|
|
getLocationOnScreen() {
|
|
|
|
const rect = this.view.getClientRects()[0]
|
|
|
|
return {
|
|
|
|
x: rect.left,
|
|
|
|
y: rect.top,
|
|
|
|
}
|
|
|
|
}
|
2021-04-16 13:20:03 +08:00
|
|
|
|
|
|
|
getRotation() {
|
|
|
|
return this.transform.rotation
|
|
|
|
}
|
|
|
|
|
|
|
|
setRotation(v: number) {
|
|
|
|
this.transform.rotation = v
|
|
|
|
this.updateTransform()
|
|
|
|
}
|
|
|
|
|
|
|
|
getRotationX() {
|
|
|
|
return this.transform.rotationX
|
|
|
|
}
|
|
|
|
|
|
|
|
setRotationX(v: number) {
|
|
|
|
this.transform.rotationX = v
|
|
|
|
this.updateTransform()
|
|
|
|
}
|
|
|
|
|
|
|
|
getRotationY() {
|
|
|
|
return this.transform.rotationY
|
|
|
|
}
|
|
|
|
|
|
|
|
setRotationY(v: number) {
|
|
|
|
this.transform.rotationY = v
|
|
|
|
this.updateTransform()
|
|
|
|
}
|
|
|
|
|
|
|
|
getTranslationX() {
|
|
|
|
return this.transform.translateX
|
|
|
|
}
|
|
|
|
|
|
|
|
setTranslationX(v: number) {
|
|
|
|
this.transform.translateX = v
|
|
|
|
this.updateTransform()
|
|
|
|
}
|
|
|
|
|
|
|
|
getTranslationY() {
|
|
|
|
return this.transform.translateY
|
|
|
|
}
|
|
|
|
|
|
|
|
setTranslationY(v: number) {
|
|
|
|
this.transform.translateY = v
|
|
|
|
this.updateTransform()
|
|
|
|
}
|
|
|
|
|
|
|
|
getScaleX() {
|
|
|
|
return this.transform.scaleX
|
|
|
|
}
|
|
|
|
|
|
|
|
setScaleX(v: number) {
|
|
|
|
this.transform.scaleX = v
|
|
|
|
this.updateTransform()
|
|
|
|
}
|
|
|
|
|
|
|
|
getScaleY() {
|
|
|
|
return this.transform.scaleY
|
|
|
|
}
|
|
|
|
|
|
|
|
setScaleY(v: number) {
|
|
|
|
this.transform.scaleY = v
|
|
|
|
this.updateTransform()
|
|
|
|
}
|
|
|
|
|
|
|
|
getPivotX() {
|
|
|
|
return this.transformOrigin?.x || 0.5
|
|
|
|
}
|
|
|
|
|
|
|
|
setPivotX(v: number) {
|
|
|
|
if (this.transformOrigin) {
|
|
|
|
this.transformOrigin.x = v
|
|
|
|
} else {
|
|
|
|
this.transformOrigin = {
|
|
|
|
x: v,
|
|
|
|
y: 0.5,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.updateTransform()
|
|
|
|
}
|
|
|
|
|
|
|
|
getPivotY() {
|
|
|
|
return this.transformOrigin?.y || 0.5
|
|
|
|
}
|
|
|
|
|
|
|
|
setPivotY(v: number) {
|
|
|
|
if (this.transformOrigin) {
|
|
|
|
this.transformOrigin.y = v
|
|
|
|
} else {
|
|
|
|
this.transformOrigin = {
|
|
|
|
x: 0.5,
|
|
|
|
y: v,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
this.updateTransform()
|
|
|
|
}
|
2021-04-15 15:51:24 +08:00
|
|
|
/** ----------call from doric ----------*/
|
2019-12-19 20:44:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-12-26 19:15:54 +08:00
|
|
|
export abstract class DoricSuperNode extends DoricViewNode {
|
2019-12-19 20:44:14 +08:00
|
|
|
reusable = false
|
|
|
|
|
|
|
|
subModels: Map<String, DVModel> = new Map
|
|
|
|
|
|
|
|
blendProps(v: HTMLElement, propName: string, prop: any) {
|
|
|
|
if (propName === 'subviews') {
|
|
|
|
if (prop instanceof Array) {
|
|
|
|
prop.forEach((e: DVModel) => {
|
|
|
|
this.mixinSubModel(e)
|
|
|
|
this.blendSubNode(e)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
super.blendProps(v, propName, prop)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mixinSubModel(subNode: DVModel) {
|
|
|
|
const oldValue = this.getSubModel(subNode.id)
|
|
|
|
if (oldValue) {
|
|
|
|
this.mixin(subNode, oldValue)
|
|
|
|
} else {
|
|
|
|
this.subModels.set(subNode.id, subNode)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
getSubModel(id: string) {
|
|
|
|
return this.subModels.get(id)
|
|
|
|
}
|
|
|
|
|
|
|
|
mixin(src: DVModel, target: DVModel) {
|
|
|
|
for (let key in src.props) {
|
|
|
|
if (key === "subviews") {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
Reflect.set(target.props, key, Reflect.get(src.props, key))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
clearSubModels() {
|
|
|
|
this.subModels.clear()
|
|
|
|
}
|
|
|
|
|
|
|
|
removeSubModel(id: string) {
|
|
|
|
this.subModels.delete(id)
|
|
|
|
}
|
|
|
|
|
|
|
|
abstract blendSubNode(model: DVModel): void
|
|
|
|
|
2019-12-21 16:57:07 +08:00
|
|
|
abstract getSubNodeById(viewId: string): DoricViewNode | undefined
|
2019-12-19 20:44:14 +08:00
|
|
|
}
|
|
|
|
|
2019-12-26 19:15:54 +08:00
|
|
|
export abstract class DoricGroupViewNode extends DoricSuperNode {
|
2019-12-19 20:44:14 +08:00
|
|
|
childNodes: DoricViewNode[] = []
|
|
|
|
childViewIds: string[] = []
|
|
|
|
|
2019-12-26 19:15:54 +08:00
|
|
|
init(superNode?: DoricSuperNode) {
|
|
|
|
super.init(superNode)
|
|
|
|
this.view.style.overflow = "hidden"
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-12-19 20:44:14 +08:00
|
|
|
blendProps(v: HTMLElement, propName: string, prop: any) {
|
|
|
|
if (propName === 'children') {
|
|
|
|
if (prop instanceof Array) {
|
|
|
|
this.childViewIds = prop
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
super.blendProps(v, propName, prop)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
blend(props: { [index: string]: any }) {
|
|
|
|
super.blend(props)
|
2019-12-21 11:09:27 +08:00
|
|
|
}
|
2021-04-15 11:35:58 +08:00
|
|
|
|
2021-04-14 17:46:58 +08:00
|
|
|
onBlending() {
|
|
|
|
super.onBlending()
|
2019-12-19 20:44:14 +08:00
|
|
|
this.configChildNode()
|
|
|
|
}
|
2021-04-15 11:35:58 +08:00
|
|
|
|
|
|
|
onBlended() {
|
|
|
|
super.onBlended()
|
|
|
|
this.childNodes.forEach(e => e.onBlended())
|
|
|
|
}
|
|
|
|
|
2019-12-19 20:44:14 +08:00
|
|
|
configChildNode() {
|
|
|
|
this.childViewIds.forEach((childViewId, index) => {
|
|
|
|
const model = this.getSubModel(childViewId)
|
|
|
|
if (model === undefined) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if (index < this.childNodes.length) {
|
|
|
|
const oldNode = this.childNodes[index]
|
|
|
|
if (oldNode.viewId === childViewId) {
|
|
|
|
//The same,skip
|
|
|
|
} else {
|
|
|
|
if (this.reusable) {
|
|
|
|
if (oldNode.viewType === model.type) {
|
|
|
|
//Same type,can be reused
|
|
|
|
oldNode.viewId = childViewId
|
|
|
|
oldNode.blend(model.props)
|
|
|
|
} else {
|
|
|
|
//Replace this view
|
|
|
|
this.view.removeChild(oldNode.view)
|
|
|
|
const newNode = DoricViewNode.create(this.context, model.type)
|
|
|
|
if (newNode === undefined) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
newNode.viewId = childViewId
|
|
|
|
newNode.init(this)
|
|
|
|
newNode.blend(model.props)
|
|
|
|
this.childNodes[index] = newNode
|
|
|
|
this.view.replaceChild(newNode.view, oldNode.view)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//Find in remain nodes
|
|
|
|
let position = -1
|
|
|
|
for (let start = index + 1; start < this.childNodes.length; start++) {
|
|
|
|
if (childViewId === this.childNodes[start].viewId) {
|
|
|
|
//Found
|
|
|
|
position = start
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (position >= 0) {
|
|
|
|
//Found swap idx,position
|
|
|
|
const reused = this.childNodes[position]
|
|
|
|
const abandoned = this.childNodes[index]
|
|
|
|
this.childNodes[index] = reused
|
|
|
|
this.childNodes[position] = abandoned
|
|
|
|
this.view.removeChild(reused.view)
|
|
|
|
this.view.insertBefore(reused.view, abandoned.view)
|
|
|
|
this.view.removeChild(abandoned.view)
|
|
|
|
if (position === this.view.childElementCount - 1) {
|
|
|
|
this.view.appendChild(abandoned.view)
|
|
|
|
} else {
|
|
|
|
this.view.insertBefore(abandoned.view, this.view.children[position])
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//Not found,insert
|
|
|
|
const newNode = DoricViewNode.create(this.context, model.type)
|
|
|
|
if (newNode === undefined) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
newNode.viewId = childViewId
|
|
|
|
newNode.init(this)
|
|
|
|
newNode.blend(model.props)
|
|
|
|
this.childNodes[index] = newNode
|
|
|
|
this.view.insertBefore(newNode.view, this.view.children[index])
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
//Insert
|
|
|
|
const newNode = DoricViewNode.create(this.context, model.type)
|
|
|
|
if (newNode === undefined) {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
newNode.viewId = childViewId
|
|
|
|
newNode.init(this)
|
|
|
|
newNode.blend(model.props)
|
|
|
|
this.childNodes.push(newNode)
|
|
|
|
this.view.appendChild(newNode.view)
|
|
|
|
}
|
|
|
|
})
|
|
|
|
let size = this.childNodes.length
|
|
|
|
for (let idx = this.childViewIds.length; idx < size; idx++) {
|
|
|
|
this.view.removeChild(this.childNodes[idx].view)
|
|
|
|
}
|
|
|
|
this.childNodes = this.childNodes.slice(0, this.childViewIds.length)
|
|
|
|
}
|
|
|
|
|
|
|
|
blendSubNode(model: DVModel) {
|
2019-12-20 13:14:48 +08:00
|
|
|
this.getSubNodeById(model.id)?.blend(model.props)
|
2019-12-19 20:44:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
getSubNodeById(viewId: string) {
|
|
|
|
return this.childNodes.filter(e => e.viewId === viewId)[0]
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|