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