123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 |
- import computeLayout from "./css-layout";
- import { getDefaultStyle, scalableStyles, layoutAffectedStyles } from "./style";
- type LayoutData = {
- left: number,
- top: number,
- width: number,
- height: number
- };
- type LayoutNode = {
- id: number,
- style: Object,
- children: LayoutNode[],
- layout?: LayoutData
- };
- let uuid = 0;
- class Element {
- public static uuid(): number {
- return uuid++;
- }
- public parent: Element | null = null;
- public id: number = Element.uuid();
- public style: { [key: string]: any } = {};
- public computedStyle: { [key: string]: any } = {};
- public lastComputedStyle: { [key: string]: any } = {};
- public children: { [key: string]: Element } = {};
- public layoutBox: LayoutData = { left: 0, top: 0, width: 0, height: 0 };
- constructor(style: { [key: string]: any } = {}) {
- // 拷贝一份,防止被外部逻辑修改
- style = Object.assign(getDefaultStyle(), style);
- this.computedStyle = Object.assign(getDefaultStyle(), style);
- this.lastComputedStyle = Object.assign(getDefaultStyle(), style);
- Object.keys(style).forEach(key => {
- Object.defineProperty(this.style, key, {
- configurable: true,
- enumerable: true,
- get: () => style[key],
- set: (value: any) => {
- if (value === style[key] || value === undefined) {
- return;
- }
- this.lastComputedStyle = this.computedStyle[key]
- style[key] = value
- this.computedStyle[key] = value
- // 如果设置的是一个可缩放的属性, 计算自己
- if (scalableStyles.includes(key) && this.style.scale) {
- this.computedStyle[key] = value * this.style.scale
- }
- // 如果设置的是 scale, 则把所有可缩放的属性计算
- if (key === "scale") {
- scalableStyles.forEach(prop => {
- if (style[prop]) {
- this.computedStyle[prop] = style[prop] * value
- }
- })
- }
- if (key === "hidden") {
- if (value) {
- layoutAffectedStyles.forEach((key: string) => {
- this.computedStyle[key] = 0;
- });
- } else {
- layoutAffectedStyles.forEach((key: string) => {
- this.computedStyle[key] = this.lastComputedStyle[key];
- });
- }
- }
- }
- })
- })
- if (this.style.scale) {
- scalableStyles.forEach((key: string) => {
- if (this.style[key]) {
- const computedValue = this.style[key] * this.style.scale;
- this.computedStyle[key] = computedValue;
- }
- });
- }
- if (style.hidden) {
- layoutAffectedStyles.forEach((key: string) => {
- this.computedStyle[key] = 0;
- });
- }
- }
- getAbsolutePosition(element: Element) {
- if (!element) {
- return this.getAbsolutePosition(this)
- }
- if (!element.parent) {
- return {
- left: 0,
- top: 0
- }
- }
- const {left, top} = this.getAbsolutePosition(element.parent)
- return {
- left: left + element.layoutBox.left,
- top: top + element.layoutBox.top
- }
- }
- public add(element: Element) {
- element.parent = this;
- this.children[element.id] = element;
- }
- public remove(element?: Element) {
- // 删除自己
- if (!element) {
- Object.keys(this.children).forEach(id => {
- const child = this.children[id]
- child.remove()
- delete this.children[id]
- })
- } else if (this.children[element.id]) {
- // 是自己的子节点才删除
- element.remove()
- delete this.children[element.id];
- }
- }
- public getNodeTree(): LayoutNode {
- return {
- id: this.id,
- style: this.computedStyle,
- children: Object.keys(this.children).map((id: string) => {
- const child = this.children[id];
- return child.getNodeTree();
- })
- }
- }
- public applyLayout(layoutNode: LayoutNode) {
- ["left", "top", "width", "height"].forEach((key: string) => {
- if (layoutNode.layout && typeof layoutNode.layout[key] === "number") {
- this.layoutBox[key] = layoutNode.layout[key];
- if (this.parent && (key === "left" || key === "top")) {
- this.layoutBox[key] += this.parent.layoutBox[key];
- }
- }
- });
- layoutNode.children.forEach((child: LayoutNode) => {
- this.children[child.id].applyLayout(child);
- });
- }
- layout() {
- const nodeTree = this.getNodeTree();
- computeLayout(nodeTree);
- this.applyLayout(nodeTree);
- }
- }
- export default Element;
|