import { MathUtils } from '@uino/base-thing';
import { BaseCSSComponent } from './BaseCSSComponent';
let _mat4 = MathUtils.createMat4();
/**
* @class CSS3DComponent
* CSS 3D 组件。
* @memberof THING
* @extends THING..BaseCSSComponent
* @public
*/
class CSS3DComponent extends BaseCSSComponent {
constructor(options) {
super(options);
this._renderType = 'Sprite';
this._pivot = null;
this._offset = [0, 0, 0];
this._css3DNode = null;
this._cssFactor = 1 / 30;
this._waitingCommands = [];
this._loadingTimer = null
// 临时变量用于修复scrollWidth/Height不包含border尺寸的问题
// 默认值为false用于兼容,后续可考虑废弃此变量
this.fixBorder = true;
}
// #region Private
// Update CSS 3D object scale.
_updateCSS3DObjectScale() {
let cssFactor = this._cssFactor;
this._css3DNode.scale = [cssFactor, cssFactor, 1];
}
_updateCSS3DObjectQuaternion() {
}
_getDomElementWidth() {
const domElement = this._domElement;
let width = this.fixBorder ? domElement.offsetWidth : domElement.scrollWidth;
return width || domElement.scrollWidth;
}
_getDomElementHeight() {
const domElement = this._domElement;
let height = this.fixBorder ? domElement.offsetHeight : domElement.scrollHeight;
return height || domElement.scrollHeight;
}
_updateCSS3DObjectPositionByPivot(pivot, domWidth, domHeight) {
let domElement = this._domElement;
let css3DNode = this._css3DNode;
// let scale = css3DNode.scale;
let scaledWidth = domWidth;
let scaledHeight = domHeight;
let offsetX = (0.5 - pivot[0]) * scaledWidth;
let offsetY = (0.5 - pivot[1]) * scaledHeight;
domElement._setLeft(0);
domElement._setTop(0);
css3DNode.setAnchorMatrix(offsetX, offsetY);
}
_processWaitingCommands() {
let waitingCommands = this._waitingCommands;
if (!waitingCommands.length) {
return;
}
if (!this._getDomElementWidth() || !this._getDomElementHeight()) {
return;
}
waitingCommands.forEach(command => {
command.onProcess();
});
waitingCommands.length = 0;
}
// #endregion
// #region Overrides
onRemove() {
if (this._css3DNode) {
this.onDisposeCSS3DNode(this._css3DNode);
this._css3DNode = null;
}
super.onRemove();
}
onVisibleChange(value) {
let css3DNode = this._css3DNode;
if (!css3DNode) {
return;
}
css3DNode.visible = value;
}
onRender() {
let css3DNode = this._css3DNode;
if (!css3DNode) {
return;
}
// Get current visible state
let visible = this._visible !== null ? this._visible : true;
// Sync CSS 3D renderable node visible state
css3DNode.visible = visible;
// Auto update visible state
if (this.updateDomElementVisibility(visible)) {
let matrixWorld = this._object.node.getMatrixWorld(_mat4);
matrixWorld[12] += this._offset[0];
matrixWorld[13] += this._offset[1];
matrixWorld[14] += this._offset[2];
// Update parent matrix
css3DNode.parentMatrix = matrixWorld;
// Update CSS 3D object transform
let domWidth = this._getDomElementWidth();
let domHeight = this._getDomElementHeight();
if (domWidth && domHeight) {
// 姑且认为dom准备就绪后,50毫秒,node完成加载
if (css3DNode.loading) {
this._loadingTimer = setTimeout(() => {
css3DNode.loading = false;
}, 50)
}
else {
if (this._loadingTimer) {
clearTimeout(this._loadingTimer)
this._loadingTimer = null
}
}
this._updateCSS3DObjectScale();
this._updateCSS3DObjectQuaternion();
let pivot = this._pivot;
if (pivot) {
this._updateCSS3DObjectPositionByPivot(pivot, domWidth, domHeight);
}
}
else {
// Here we must prevent element is still in loading state
if (css3DNode.visible) {
const scale = 0.00000001;
this._css3DNode.scale = [scale, scale, scale];
}
}
}
// Update waiting commands
this._processWaitingCommands();
}
onCreateCSS3DNode() {
let css3DNode = this._delegate.createCSS3DNode(this._renderType);
css3DNode.loading = true;
return css3DNode;
}
onDisposeCSS3DNode(node) {
this._delegate.disposeCSS3DNode(node);
}
// #endregion
/**
* 获取CSS3D节点。
* @type {THING.DOM.CSS3DNode}
* @public
*/
get css3DNode() {
return this._css3DNode;
}
/**
* 获取/设置DOM元素。
* @type {object}
* @public
*/
get domElement() {
return super.domElement;
}
set domElement(value) {
super.domElement = value;
if (this._css3DNode) {
if (this._css3DNode.domElement != value) {
this._css3DNode.domElement = value;
this._delegate.css3DRenderer.removeCache(this._css3DNode);
}
}
else {
this._css3DNode = this.onCreateCSS3DNode();
this._css3DNode.visible = this._object.visible;
this._css3DNode.domElement = value;
}
}
/**
* 获取/设置从左下角计算的轴心点。
* @type {Array<number>}
* @public
*/
get pivot() {
return this._pivot ? this._pivot : [0.5, 0.5];
}
set pivot(value) {
if (value) {
this._pivot = value.slice(0);
}
else {
this._pivot = null;
let domWidth = this._getDomElementWidth();
let domHeight = this._getDomElementHeight();
if (domWidth && domHeight) {
this._updateCSS3DObjectPositionByPivot([0.5, 0.5], domWidth, domHeight);
}
}
}
/**
* 获取/设置偏移量(以像素为单位)。
* @type {Array<number>}
* @public
*/
get offset() {
return this._offset;
}
set offset(value) {
if (value) {
this._offset = value.slice(0);
}
else {
this._offset = [0, 0, 0];
}
}
/**
* 获取/设置缩放因子。
* @type {number}
* @public
*/
get factor() {
return this._cssFactor;
}
set factor(value) {
this._cssFactor = value;
}
/**
* 获取/设置渲染类型。
* @type {THING.RenderType}
* @public
*/
get renderType() {
let css3DNode = this._css3DNode;
if (css3DNode) {
return css3DNode.renderType;
}
return this._renderType;
}
set renderType(value) {
this._renderType = value;
// Check whether finished load DOM element
let domElement = this.domElement;
if (!domElement) {
this._waitingCommands.push({
onProcess: () => {
this.renderType = value;
}
});
return;
}
// Update render type
this._css3DNode.renderType = value;
// We hide it first until element scroll width and height loaded
let domWidth = this._getDomElementWidth();
let domHeight = this._getDomElementHeight();
if (!domWidth || !domHeight) {
this._css3DNode.scale = [Infinity, Infinity, Infinity];
}
}
}
export { CSS3DComponent }