use css flex and box to implement stack and vlayout and hlayout

This commit is contained in:
pengfei.zhou
2019-12-21 11:09:27 +08:00
parent ef88d9c092
commit c8f3b5db00
11 changed files with 729 additions and 793 deletions

View File

@@ -5,6 +5,5 @@ export class ShaderPlugin extends DoricPlugin {
render(ret: DVModel) {
this.context.rootNode.viewId = ret.id
this.context.rootNode.blend(ret.props)
this.context.rootNode.layout()
}
}

View File

@@ -1,122 +1,53 @@
import { DoricGroupViewNode, LayoutSpec, FrameSize, LEFT, RIGHT, CENTER_X, CENTER_Y, TOP, BOTTOM } from "./DoricViewNode";
import { DoricGroupViewNode, LEFT, RIGHT, CENTER_X, CENTER_Y, TOP, BOTTOM, toPixelString } from "./DoricViewNode";
export class DoricHLayoutNode extends DoricGroupViewNode {
space = 0
gravity = 0
contentSize = {
width: 0,
height: 0,
weight: 0,
}
build() {
return document.createElement('div')
const ret = document.createElement('div')
ret.style.display = "flex"
ret.style.flexDirection = "row"
ret.style.flexWrap = "nowrap"
return ret
}
blendProps(v: HTMLElement, propName: string, prop: any) {
if (propName === 'space') {
this.space = prop
} else if (propName === 'gravity') {
this.gravity = prop
this.gravity = prop
if ((this.gravity & LEFT) === LEFT) {
this.view.style.justifyContent = "flex-start"
} else if ((this.gravity & RIGHT) === RIGHT) {
this.view.style.justifyContent = "flex-end"
} else if ((this.gravity & CENTER_X) === CENTER_X) {
this.view.style.justifyContent = "center"
}
if ((this.gravity & TOP) === TOP) {
this.view.style.alignItems = "flex-start"
} else if ((this.gravity & BOTTOM) === BOTTOM) {
this.view.style.alignItems = "flex-end"
} else if ((this.gravity & CENTER_Y) === CENTER_Y) {
this.view.style.alignItems = "center"
}
} else {
super.blendProps(v, propName, prop)
}
}
blend(props: { [index: string]: any }) {
super.blend(props)
this.childNodes.forEach(e => {
e.view.style.position = "absolute"
})
}
measureContentSize(targetSize: { width: number, height: number }) {
let width = this.frameWidth
let height = this.frameHeight
let contentSize = { width: 0, height: 0, weight: 0 }
let limitSize = {
width: targetSize.width - this.paddingLeft - this.paddingRight,
height: targetSize.height - this.paddingTop - this.paddingBottom,
}
contentSize = this.childNodes.reduce((prev, current) => {
const size = current.measureContentSize(limitSize)
return {
width: prev.width + size.width + this.space
+ current.layoutConfig?.margin?.left || 0
+ current.layoutConfig?.margin?.right || 0,
height: Math.max(prev.height, size.height),
weight: prev.weight + current.layoutConfig?.weight || 0
}
}, contentSize)
contentSize.width -= this.space
switch (this.layoutConfig.widthSpec) {
case LayoutSpec.AT_MOST:
width = targetSize.width
break
case LayoutSpec.WRAP_CONTENT:
width = contentSize.width
break
default:
break
}
switch (this.layoutConfig.heightSpec) {
case LayoutSpec.AT_MOST:
height = targetSize.height
break
case LayoutSpec.WRAP_CONTENT:
height = contentSize.height
break
default:
break
}
if (contentSize.weight > 0) {
contentSize.width = targetSize.width
}
this.contentSize = contentSize
return { width, height }
}
layoutSelf(targetSize: FrameSize) {
const { width, height } = this.measureContentSize(targetSize)
this.width = width
this.height = height
let xStart = this.paddingLeft;
if ((this.gravity & LEFT) == LEFT) {
xStart = this.paddingLeft
} else if ((this.gravity & RIGHT) == RIGHT) {
xStart = targetSize.width - this.contentSize.width - this.paddingRight;
} else if ((this.gravity & CENTER_X) == CENTER_X) {
xStart = (targetSize.width - this.contentSize.width - this.paddingLeft - this.paddingRight) / 2 + this.paddingLeft
}
let remain = targetSize.width - this.contentSize.width - this.paddingLeft - this.paddingRight
this.childNodes.forEach(e => {
const childTargetSize = {
width: width - xStart - this.paddingRight,
height: height - this.paddingTop - this.paddingBottom,
}
if (e.layoutConfig?.weight > 0) {
childTargetSize.width += remain / this.contentSize.weight * e.layoutConfig.weight
}
e.layoutSelf(childTargetSize)
let gravity = e.layoutConfig?.alignment | this.gravity
if ((gravity & TOP) === TOP) {
e.y = 0
} else if ((gravity & BOTTOM) === BOTTOM) {
e.y = height - e.height + this.paddingTop - this.paddingBottom
} else if ((gravity & CENTER_Y) === CENTER_Y) {
e.x = height / 2 - e.height / 2 - this.paddingTop
} else {
if (e.layoutConfig.margin?.left) {
e.x = e.layoutConfig.margin?.left
} else if (e.layoutConfig.margin?.right) {
e.x = width - e.width + this.paddingLeft - this.paddingRight - e.layoutConfig.margin?.right
}
}
if (e.layoutConfig.margin?.left !== undefined) {
xStart += e.layoutConfig.margin.left
}
e.x = xStart - this.paddingLeft
xStart += e.width + this.space
if (e.layoutConfig.margin?.right !== undefined) {
xStart += e.layoutConfig.margin.right
layout() {
super.layout()
this.childNodes.forEach((e, idx) => {
e.view.style.flexShrink = "0"
if (e.layoutConfig?.weight) {
e.view.style.flex = `${e.layoutConfig?.weight}`
}
e.view.style.marginLeft = toPixelString(e.layoutConfig?.margin?.left || 0)
e.view.style.marginRight = toPixelString(
(idx === this.childNodes.length - 1) ? 0 : this.space
+ (e.layoutConfig?.margin?.right || 0))
e.view.style.marginTop = toPixelString(e.layoutConfig?.margin?.top || 0)
e.view.style.marginBottom = toPixelString(e.layoutConfig?.margin?.bottom || 0)
})
}
}

View File

@@ -1,4 +1,4 @@
import { DoricGroupViewNode, LayoutSpec, FrameSize, LEFT, RIGHT, CENTER_X, CENTER_Y, TOP, BOTTOM } from "./DoricViewNode";
import { DoricGroupViewNode, LayoutSpec, FrameSize, LEFT, RIGHT, CENTER_X, CENTER_Y, TOP, BOTTOM, toPixelString } from "./DoricViewNode";
export class DoricStackNode extends DoricGroupViewNode {
@@ -6,92 +6,16 @@ export class DoricStackNode extends DoricGroupViewNode {
return document.createElement('div')
}
blend(props: { [index: string]: any }) {
super.blend(props)
layout() {
super.layout()
this.configOffset()
}
configOffset() {
this.childNodes.forEach(e => {
e.view.style.position = "absolute"
})
}
measureContentSize(targetSize: { width: number, height: number }) {
let width = this.frameWidth
let height = this.frameHeight
let contentSize = { width: 0, height: 0 }
let limitSize = {
width: targetSize.width - this.paddingLeft - this.paddingRight,
height: targetSize.height - this.paddingTop - this.paddingBottom,
}
if (this.layoutConfig.widthSpec === LayoutSpec.WRAP_CONTENT
|| this.layoutConfig.heightSpec === LayoutSpec.WRAP_CONTENT) {
contentSize = this.childNodes.reduce((prev, current) => {
const size = current.measureContentSize(limitSize)
return {
width: Math.max(prev.width, size.width),
height: Math.max(prev.height, size.height),
}
}, contentSize)
}
switch (this.layoutConfig.widthSpec) {
case LayoutSpec.AT_MOST:
width = targetSize.width
break
case LayoutSpec.WRAP_CONTENT:
width = contentSize.width
break
default:
break
}
switch (this.layoutConfig.heightSpec) {
case LayoutSpec.AT_MOST:
height = targetSize.height
break
case LayoutSpec.WRAP_CONTENT:
height = contentSize.height
break
default:
break
}
return { width, height }
}
layoutSelf(targetSize: FrameSize) {
const { width, height } = this.measureContentSize(targetSize)
this.width = width
this.height = height
const limitSize = {
width: width - this.paddingLeft - this.paddingRight,
height: height - this.paddingTop - this.paddingBottom,
}
this.childNodes.forEach(e => {
e.layoutSelf(limitSize)
let gravity = e.layoutConfig?.alignment || 0
if ((gravity & LEFT) === LEFT) {
e.x = 0
} else if ((gravity & RIGHT) === RIGHT) {
e.x = width - e.width + this.paddingLeft - this.paddingRight
} else if ((gravity & CENTER_X) === CENTER_X) {
e.x = width / 2 - e.width / 2 - this.paddingLeft
} else {
if (e.layoutConfig.margin?.left) {
e.x = e.layoutConfig.margin?.left
} else if (e.layoutConfig.margin?.right) {
e.x = width - e.width + this.paddingLeft - this.paddingRight - e.layoutConfig.margin?.right
}
}
if ((gravity & TOP) === TOP) {
e.y = 0
} else if ((gravity & BOTTOM) === BOTTOM) {
e.y = height - e.height + this.paddingTop - this.paddingBottom
} else if ((gravity & CENTER_Y) === CENTER_Y) {
e.y = height / 2 - e.height / 2 - this.paddingTop
} else {
if (e.layoutConfig.margin?.top) {
e.y = e.layoutConfig.margin?.top
} else if (e.layoutConfig.margin?.bottom) {
e.y = height - e.height + this.paddingTop - this.paddingBottom - e.layoutConfig.margin?.bottom
}
}
e.view.style.left = toPixelString(e.offsetX + this.paddingLeft)
e.view.style.top = toPixelString(e.offsetY + this.paddingTop)
})
}
}

View File

@@ -6,9 +6,6 @@ export class DoricTextNode extends DoricViewNode {
return document.createElement('p')
}
measureContentSize(targetSize: FrameSize) {
return targetSize
}
blendProps(v: HTMLParagraphElement, propName: string, prop: any) {
switch (propName) {
case 'text':

View File

@@ -1,13 +1,9 @@
import { DoricGroupViewNode, LayoutSpec, FrameSize, LEFT, RIGHT, CENTER_X, CENTER_Y, TOP, BOTTOM } from "./DoricViewNode";
import { DoricGroupViewNode, LayoutSpec, FrameSize, LEFT, RIGHT, CENTER_X, CENTER_Y, TOP, BOTTOM, toPixelString } from "./DoricViewNode";
export class DoricVLayoutNode extends DoricGroupViewNode {
space = 0
gravity = 0
contentSize = {
width: 0,
height: 0,
weight: 0,
}
build() {
const ret = document.createElement('div')
ret.style.display = "flex"
@@ -21,73 +17,37 @@ export class DoricVLayoutNode extends DoricGroupViewNode {
} else if (propName === 'gravity') {
this.gravity = prop
if ((this.gravity & LEFT) === LEFT) {
this.view.style.justifyContent = "flex-start"
this.view.style.alignItems = "flex-start"
} else if ((this.gravity & RIGHT) === RIGHT) {
this.view.style.justifyContent = "flex-end"
this.view.style.alignItems = "flex-end"
} else if ((this.gravity & CENTER_X) === CENTER_X) {
this.view.style.justifyContent = "center"
this.view.style.alignItems = "center"
}
if ((this.gravity & TOP) === TOP) {
this.view.style.alignItems = "flex-start"
this.view.style.justifyContent = "flex-start"
} else if ((this.gravity & BOTTOM) === BOTTOM) {
this.view.style.alignItems = "flex-end"
this.view.style.justifyContent = "flex-end"
} else if ((this.gravity & CENTER_Y) === CENTER_Y) {
this.view.style.alignItems = "center"
this.view.style.justifyContent = "center"
}
} else {
super.blendProps(v, propName, prop)
}
}
measureContentSize(targetSize: { width: number, height: number }) {
let width = this.frameWidth
let height = this.frameHeight
let contentSize = { width: 0, height: 0, weight: 0 }
let limitSize = {
width: targetSize.width - this.paddingLeft - this.paddingRight,
height: targetSize.height - this.paddingTop - this.paddingBottom,
}
contentSize = this.childNodes.reduce((prev, current) => {
const size = current.measureContentSize(limitSize)
return {
width: Math.max(prev.width, size.width),
height: prev.height + size.height + this.space
+ current.layoutConfig?.margin?.top || 0
+ current.layoutConfig?.margin?.bottom || 0,
weight: prev.weight + current.layoutConfig?.weight || 0
layout() {
super.layout()
this.childNodes.forEach((e, idx) => {
e.view.style.flexShrink = "0"
if (e.layoutConfig?.weight) {
e.view.style.flex = `${e.layoutConfig?.weight}`
}
}, contentSize)
contentSize.height -= this.space
switch (this.layoutConfig.widthSpec) {
case LayoutSpec.AT_MOST:
width = targetSize.width
break
case LayoutSpec.WRAP_CONTENT:
width = contentSize.width
break
default:
break
}
switch (this.layoutConfig.heightSpec) {
case LayoutSpec.AT_MOST:
height = targetSize.height
break
case LayoutSpec.WRAP_CONTENT:
height = contentSize.height
break
default:
break
}
if (contentSize.weight > 0) {
contentSize.height = targetSize.height
}
this.contentSize = contentSize
return { width, height }
}
layoutSelf(targetSize: FrameSize) {
const { width, height } = this.measureContentSize(targetSize)
this.width = width
this.height = height
e.view.style.marginTop = toPixelString(e.layoutConfig?.margin?.top || 0)
e.view.style.marginBottom = toPixelString(
(idx === this.childNodes.length - 1) ? 0 : this.space
+ (e.layoutConfig?.margin?.bottom || 0))
e.view.style.marginLeft = toPixelString(e.layoutConfig?.margin?.left || 0)
e.view.style.marginRight = toPixelString(e.layoutConfig?.margin?.right || 0)
})
}
}

View File

@@ -138,28 +138,81 @@ export abstract class DoricViewNode {
for (let key in props) {
this.blendProps(this.view, key, props[key])
}
this.onBlended()
this.layout()
}
onBlended() {
}
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)
}
}
configWidth() {
switch (this.layoutConfig.widthSpec) {
case LayoutSpec.WRAP_CONTENT:
this.view.style.width = "auto"
break
case LayoutSpec.AT_MOST:
this.view.style.width = "100%"
break
case LayoutSpec.EXACTLY:
default:
this.view.style.width = toPixelString(this.frameWidth
- this.paddingLeft - this.paddingRight
- this.borderWidth * 2)
break
}
}
configHeight() {
switch (this.layoutConfig.heightSpec) {
case LayoutSpec.WRAP_CONTENT:
this.view.style.height = "auto"
break
case LayoutSpec.AT_MOST:
this.view.style.height = "100%"
break
case LayoutSpec.EXACTLY:
default:
this.view.style.height = toPixelString(this.frameHeight
- this.paddingTop - this.paddingBottom
- this.borderWidth * 2)
break
}
}
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)
}
}
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.x = this.offsetX
this.y = this.offsetY
}
layout() {
this.layoutSelf({ width: this.frameWidth, height: this.frameHeight })
}
layoutSelf(targetSize: FrameSize) {
this.width = targetSize.width
this.height = targetSize.height
this.configMargin()
this.configBorder()
this.configPadding()
this.configWidth()
this.configHeight()
}
blendProps(v: HTMLElement, propName: string, prop: any) {
@@ -199,31 +252,6 @@ export abstract class DoricViewNode {
}
}
set width(v: number) {
this.view.style.width = toPixelString(v - this.paddingLeft - this.paddingRight - this.borderWidth * 2)
}
get width() {
return this.view.offsetWidth
}
set height(v: number) {
this.view.style.height = toPixelString(v - this.paddingTop - this.paddingBottom - this.borderWidth * 2)
}
get height() {
return this.view.offsetHeight
}
set x(v: number) {
this.view.style.left = toPixelString(v + (this.superNode?.paddingLeft || 0))
}
get x() {
return this.view.offsetLeft
}
set y(v: number) {
this.view.style.top = toPixelString(v + (this.superNode?.paddingTop || 0))
}
get y() {
return this.view.offsetTop
}
set backgroundColor(v: number) {
this.view.style.backgroundColor = toRGBAString(v)
}
@@ -239,8 +267,6 @@ export abstract class DoricViewNode {
return ret
}
abstract measureContentSize(targetSize: FrameSize): FrameSize
getIdList() {
const ids: string[] = []
let viewNode: DoricViewNode | undefined = this
@@ -329,9 +355,11 @@ export abstract class DoricGroupViewNode extends DoricSuperViewNode {
blend(props: { [index: string]: any }) {
super.blend(props)
}
onBlended() {
super.onBlended()
this.configChildNode()
}
configChildNode() {
this.childViewIds.forEach((childViewId, index) => {
const model = this.getSubModel(childViewId)