UNPKG

infamous

Version:

A CSS3D/WebGL UI library.

175 lines (147 loc) 6.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _lowclass = _interopRequireDefault(require("lowclass")); var _native = require("lowclass/native"); var _Node = _interopRequireDefault(require("./Node")); var _Motor = _interopRequireDefault(require("./Motor")); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } // fallback to experimental CSS transform if browser doesn't have it (fix for Safari 9) if (typeof document.createElement('div').style.transform == 'undefined') { if (typeof CSSStyleDeclaration !== 'undefined') { // doesn't exist in Jest+@skatejs/ssr environment Object.defineProperty(CSSStyleDeclaration.prototype, 'transform', { set(value) { this.webkitTransform = value; }, get() { return this.webkitTransform; }, enumerable: true }); } } /** * Manages a DOM element. Exposes a set of recommended APIs for working with * DOM efficiently. Currently doesn't do much yet... */ var _default = (0, _lowclass.default)('ElementOperations', { element: null, constructor(element) { this.element = element; }, /** * @param {Array.string} classes An array of class names to add to the * managed element. * * Note: updating class names with `el.classList.add()` won't thrash the * layout. See: http://www.html5rocks.com/en/tutorials/speed/animations */ setClasses(...classes) { if (classes.length) this.element.classList.add(...classes); return this; }, /** * Apply a style property to the element. * * @private * @param {string} property The CSS property we will a apply. * @param {string} value The value the CSS property wil have. */ applyStyle(property, value) { this.element.style[property] = value; }, add(child) { this.element.appendChild(child); }, remove(child) { // This conditional check is needed incase the element was already // removed from the HTML-API side. if (child.parentNode === this.element) this.element.removeChild(child); }, connectChildElement(child) { if ( // When using the imperative API, this statement is // true, so the DOM elements need to be connected. !child.parentNode // This condition is irrelevant when strictly using the // imperative API. However, it is possible that when // using the HTML API that the HTML-API node can be placed // somewhere that isn't another HTML-API node, and the // imperative Node can be gotten and used to add the // node to another imperative Node. In this case, the // HTML-API node will be added to the proper HTMLparent. || child.parentElement && child.parentElement !== this.element // When an HTML-API node is already child of the // relevant parent, or it is child of a shadow root of // the relevant parent, there there's nothing to do, // everything is already as expected, so the following // conditional body is skipped. ) { this.add(child); } }, disconnectChildElement(child) { // If DeclarativeBase#remove was called first, we don't need to // call this again. if (!child.parentNode) return; this.remove(child); }, /** * Apply the DOMMatrix value to the style of this Node's element. */ applyTransform(domMatrix) { // for now, template strings need to be on one line, otherwise Meteor // users will have bugs from Meteor's injected line numbers. See: // https://github.com/meteor/meteor/issues/9160 // // THREE-COORDS-TO-DOM-COORDS // -- We negate the 13th matrix value to make the DOM's positive Y // direction downward again because we first negated the value in // Transformable when calculating world transforms so that // Three.js positive Y would go downward like DOM. // -- We also translate the DOM element into the middle of the view // (similar to align and mountPoint values of 0.5) so that the DOM // element is aligned with the Three mesh in the middle of the view, // then in Transformable#_calculateMatrix we adjust the world matrix // back into DOM coordinates at the top/left. // -- We apply opposite X rotation to counter the negated X rotation in // Transformable for the Three.js objects. // // TODO #66: moving _calcSize to a render task affets this code const el = this.element; const elSize = el._calculatedSize; const parentSize = el.parent._calculatedSize; // THREE-COORDS-TO-DOM-COORDS: moves DOM elements to the Three.js // coordinate space (align and mountPoint are in the middle of the // view). The threeJsPostAdjustment in Transformable moves both the // pre-adjusted DOM element and the Three objects into the top/left // coordinate space. const threeJsPreAdjustment = `translate3d(calc(${parentSize.x / 2}px - ${elSize.x / 2}px), calc(${parentSize.y / 2}px - ${elSize.y / 2}px), 0px)`; const cssMatrixString = `${threeJsPreAdjustment} matrix3d( ${domMatrix.m11}, ${domMatrix.m12}, ${domMatrix.m13}, ${domMatrix.m14}, ${domMatrix.m21}, ${domMatrix.m22}, ${domMatrix.m23}, ${domMatrix.m24}, ${domMatrix.m31}, ${domMatrix.m32}, ${domMatrix.m33}, ${domMatrix.m34}, ${domMatrix.m41}, ${-domMatrix.m42}, ${domMatrix.m43}, ${domMatrix.m44})`; // THREE-COORDS-TO-DOM-COORDS: rotate X and Z the opposite direction for Three.js domMatrix.rotateAxisAngleSelf(0, 0, 1, -2 * el.rotation.z); domMatrix.rotateAxisAngleSelf(1, 0, 0, -2 * el.rotation.x); this.applyStyle('transform', cssMatrixString); }, /** * [applySize description] */ applySize(size) { const { x, y } = size; this.applyStyle('width', `${x}px`); this.applyStyle('height', `${y}px`); // NOTE: we ignore the Z axis on elements, since they are flat. }, applyOpacity(opacity) { this.applyStyle('opacity', opacity); }, applyImperativeNodeProperties(node) { // Only Node is Transformable if (node instanceof _Node.default) { this.applyOpacity(node._properties.opacity); this.applyTransform(node._properties.transform); } // But both Node and Scene are Sizeable this.applySize(node._calculatedSize); } }); exports.default = _default;