You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
6867 lines
295 KiB
6867 lines
295 KiB
/******/ (() => { // webpackBootstrap
|
|
/******/ var __webpack_modules__ = ([
|
|
/* 0 */,
|
|
/* 1 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
|
|
/* harmony export */ });
|
|
if (typeof GameGlobal !== 'undefined') {
|
|
GameGlobal.__env = GameGlobal.wx || GameGlobal.tt || GameGlobal.swan;
|
|
}
|
|
var domEventMap = {
|
|
touchstart: 'mousedown',
|
|
touchmove: 'mousemove',
|
|
touchend: 'mouseup',
|
|
touchcancel: 'mouseleave',
|
|
};
|
|
var eventType;
|
|
(function (eventType) {
|
|
eventType["on"] = "on";
|
|
eventType["off"] = "off";
|
|
})(eventType || (eventType = {}));
|
|
function genDomTouchEvent(event, type) {
|
|
if (typeof document !== 'undefined') {
|
|
return function (listener) {
|
|
type === eventType.on ?
|
|
document.addEventListener(event, listener, false)
|
|
: document.removeEventListener(event, listener, false);
|
|
};
|
|
}
|
|
}
|
|
function getOnTouchHandler(event, type) {
|
|
if (typeof GameGlobal !== 'undefined') {
|
|
return GameGlobal.__env["".concat(type).concat(event)];
|
|
}
|
|
else {
|
|
return genDomTouchEvent(domEventMap[event.toLowerCase()], type);
|
|
}
|
|
}
|
|
/**
|
|
* Layout 可能用在不用的平台,而Layout会依赖平台下面的一些方法来实现具体的功能,比如创建图片
|
|
* 为了更好做平台适配,统一封装 env 模块,不同平台要做适配,替换 env 的具体方法即可
|
|
*/
|
|
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = ({
|
|
// 监听触摸相关事件
|
|
onTouchStart: getOnTouchHandler('TouchStart', eventType.on),
|
|
onTouchMove: getOnTouchHandler('TouchMove', eventType.on),
|
|
onTouchEnd: getOnTouchHandler('TouchEnd', eventType.on),
|
|
onTouchCancel: getOnTouchHandler('TouchCancel', eventType.on),
|
|
// 取消监听触摸相关事件
|
|
offTouchStart: getOnTouchHandler('TouchStart', eventType.off),
|
|
offTouchMove: getOnTouchHandler('TouchMove', eventType.off),
|
|
offTouchEnd: getOnTouchHandler('TouchEnd', eventType.off),
|
|
offTouchCancel: getOnTouchHandler('TouchCancel', eventType.off),
|
|
// Layout 支持百分比样式,如果根节点样式设置为 100%,直接取 Canvas 的尺寸,不同平台的取法不一样,因此单独提供函数
|
|
getRootCanvasSize: function () {
|
|
if (typeof __env !== 'undefined' && __env.getSharedCanvas) {
|
|
var cvs = __env.getSharedCanvas();
|
|
return {
|
|
width: cvs.width,
|
|
height: cvs.height,
|
|
};
|
|
}
|
|
else {
|
|
return {
|
|
width: 300,
|
|
height: 150,
|
|
};
|
|
}
|
|
},
|
|
// 取当前设备的 devicePixelRatio,不同平台的取法不一样
|
|
getDevicePixelRatio: function () {
|
|
if (typeof __env !== 'undefined' && __env.getSystemInfoSync) {
|
|
return __env.getSystemInfoSync().devicePixelRatio;
|
|
}
|
|
else if (window.devicePixelRatio) {
|
|
return window.devicePixelRatio;
|
|
}
|
|
else {
|
|
return 1;
|
|
}
|
|
},
|
|
// 创建Canvas
|
|
createCanvas: function () {
|
|
if (typeof __env !== 'undefined') {
|
|
return __env.createCanvas();
|
|
}
|
|
return document.createElement('canvas');
|
|
},
|
|
// 创建图片
|
|
createImage: function () {
|
|
if (typeof __env !== 'undefined') {
|
|
return __env.createImage();
|
|
}
|
|
return document.createElement('img');
|
|
}
|
|
});
|
|
|
|
|
|
/***/ }),
|
|
/* 2 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ StyleOpType: () => (/* binding */ StyleOpType),
|
|
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__),
|
|
/* harmony export */ getElementById: () => (/* binding */ getElementById),
|
|
/* harmony export */ getElementsByClassName: () => (/* binding */ getElementsByClassName),
|
|
/* harmony export */ getElementsById: () => (/* binding */ getElementsById),
|
|
/* harmony export */ setDirty: () => (/* binding */ setDirty)
|
|
/* harmony export */ });
|
|
/* harmony import */ var _style__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(3);
|
|
/* harmony import */ var _common_rect__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4);
|
|
/* harmony import */ var _common_imageManager__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(5);
|
|
/* harmony import */ var tiny_emitter__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(8);
|
|
/* harmony import */ var tiny_emitter__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(tiny_emitter__WEBPACK_IMPORTED_MODULE_3__);
|
|
/* harmony import */ var _styleParser__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(9);
|
|
/* harmony import */ var _common_util__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(7);
|
|
/* harmony import */ var _env__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(1);
|
|
var __spreadArray = (undefined && undefined.__spreadArray) || function (to, from, pack) {
|
|
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
if (ar || !(i in from)) {
|
|
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
ar[i] = from[i];
|
|
}
|
|
}
|
|
return to.concat(ar || Array.prototype.slice.call(from));
|
|
};
|
|
/* eslint-disable no-param-reassign */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function getElementsById(tree, list, id) {
|
|
if (list === void 0) { list = []; }
|
|
tree.children.forEach(function (child) {
|
|
if (child.idName === id) {
|
|
list.push(child);
|
|
}
|
|
if (child.children.length) {
|
|
getElementsById(child, list, id);
|
|
}
|
|
});
|
|
return list;
|
|
}
|
|
function getElementById(tree, id) {
|
|
var list = getElementsById(tree, [], id);
|
|
return (list === null || list === void 0 ? void 0 : list[0]) || null;
|
|
}
|
|
function getElementsByClassName(tree, list, className) {
|
|
if (list === void 0) { list = []; }
|
|
tree.children.forEach(function (child) {
|
|
if (child.classList.contains(className)) {
|
|
list.push(child);
|
|
}
|
|
if (child.children.length) {
|
|
getElementsByClassName(child, list, className);
|
|
}
|
|
});
|
|
return list;
|
|
}
|
|
/**
|
|
* 将当前节点置脏,Layout 的 ticker 会根据这个标记位执行 reflow
|
|
*/
|
|
function setDirty(ele, reason) {
|
|
// for debug
|
|
// console.log('[Layout] trigger reflow cause', ele, reason);
|
|
ele.isDirty = true;
|
|
var parent = ele.parent;
|
|
while (parent) {
|
|
parent.isDirty = true;
|
|
parent = parent.parent;
|
|
}
|
|
}
|
|
// 全局事件管道
|
|
var EE = new (tiny_emitter__WEBPACK_IMPORTED_MODULE_3___default().TinyEmitter)();
|
|
var uuid = 0;
|
|
var toEventName = function (event, id) {
|
|
var elementEvent = [
|
|
'click',
|
|
'touchstart',
|
|
'touchmove',
|
|
'touchend',
|
|
'touchcancel',
|
|
];
|
|
if (elementEvent.indexOf(event) !== -1) {
|
|
return "element-".concat(id, "-").concat(event);
|
|
}
|
|
return "element-".concat(id, "-").concat(event);
|
|
};
|
|
;
|
|
var StyleOpType;
|
|
(function (StyleOpType) {
|
|
StyleOpType[StyleOpType["Set"] = 0] = "Set";
|
|
StyleOpType[StyleOpType["Delete"] = 1] = "Delete";
|
|
})(StyleOpType || (StyleOpType = {}));
|
|
var ElementClassList = /** @class */ (function () {
|
|
function ElementClassList(ele, initialTokens) {
|
|
this.element = ele;
|
|
this.tokens = new Set(initialTokens || []);
|
|
}
|
|
Object.defineProperty(ElementClassList.prototype, "length", {
|
|
get: function () {
|
|
return this.tokens.size;
|
|
},
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
Object.defineProperty(ElementClassList.prototype, "value", {
|
|
get: function () {
|
|
return Array.from(this.tokens).join(' ');
|
|
},
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
ElementClassList.prototype.changeHandler = function () {
|
|
var ele = this.element;
|
|
var oldStyle = Object.assign({}, ele.style);
|
|
// 根据 className 从样式表中算出当前应该作用的样式
|
|
// @ts-ignore
|
|
var newStyle = Array.from(this.tokens).reduce(function (res, oneClass) { return Object.assign(res, ele.root.styleSheet[oneClass]); }, {});
|
|
// console.log('newStyle', newStyle)
|
|
var parentStyle;
|
|
if (ele.parent) {
|
|
parentStyle = ele.parent.style;
|
|
}
|
|
else {
|
|
parentStyle = _env__WEBPACK_IMPORTED_MODULE_6__["default"].getRootCanvasSize();
|
|
}
|
|
if (typeof newStyle.opacity === 'undefined') {
|
|
newStyle.opacity = 1;
|
|
}
|
|
Object.keys(newStyle).concat(Object.keys(oldStyle)).forEach(function (key) {
|
|
// 手动通过this.style设置过的样式认为是内联样式,优先级最高,也就是 className 的属性不影响
|
|
if (!Reflect.has(ele.innerStyle, key)) {
|
|
// 根据 className 计算出来的新增或者修改的样式
|
|
if (Reflect.has(newStyle, key)) {
|
|
/**
|
|
* 新增的样式,需要区分出是 className 导致的还是开发者手动修改的
|
|
* 临时占位,因为后续的 reflow 和 repaint 逻辑在 style Proxy 处理,这样做在 style Proxy 也不会认为是开发者手动设置的样式
|
|
*/
|
|
if (!Reflect.has(oldStyle, key)) {
|
|
Reflect.set(ele.originStyle, key, undefined);
|
|
}
|
|
// @ts-ignore
|
|
var newValue = newStyle[key];
|
|
if (key === 'width') {
|
|
newValue = parentStyle.width ? (0,_common_util__WEBPACK_IMPORTED_MODULE_5__.convertPercent)(newValue, parentStyle.width) : 0;
|
|
}
|
|
if (key === 'height') {
|
|
newValue = parentStyle.height ? (0,_common_util__WEBPACK_IMPORTED_MODULE_5__.convertPercent)(newValue, parentStyle.height) : 0;
|
|
}
|
|
if (key === 'opacity' && parentStyle && parentStyle.opacity !== 1 && typeof parentStyle.opacity === 'number') {
|
|
newValue = parentStyle.opacity * newValue;
|
|
}
|
|
// @ts-ignore
|
|
// 根据 className 计算出的样式覆盖当前样式
|
|
ele.style[key] = newValue;
|
|
}
|
|
else {
|
|
// console.log('del', key)
|
|
// 不在内联样式,根据 className 计算出的样式又没有,认为这些样式都应该删除了
|
|
delete ele.style[key];
|
|
}
|
|
}
|
|
});
|
|
};
|
|
ElementClassList.prototype.add = function (className) {
|
|
if (!this.contains(className)) {
|
|
this.tokens.add(className);
|
|
this.changeHandler();
|
|
}
|
|
};
|
|
// 检查列表中是否存在指定的令牌
|
|
ElementClassList.prototype.contains = function (className) {
|
|
return this.tokens.has(className);
|
|
};
|
|
ElementClassList.prototype.remove = function (className) {
|
|
if (this.contains(className)) {
|
|
this.tokens.delete(className);
|
|
this.changeHandler();
|
|
}
|
|
};
|
|
return ElementClassList;
|
|
}());
|
|
var Element = /** @class */ (function () {
|
|
function Element(_a) {
|
|
var _b = _a.style, style = _b === void 0 ? {} : _b, _c = _a.idName, idName = _c === void 0 ? '' : _c, _d = _a.className, className = _d === void 0 ? '' : _d, _e = _a.id, id = _e === void 0 ? uuid += 1 : _e, _f = _a.dataset, dataset = _f === void 0 ? {} : _f;
|
|
var _this = this;
|
|
/**
|
|
* 子节点列表
|
|
*/
|
|
this.children = [];
|
|
/**
|
|
* 当前节点的父节点
|
|
*/
|
|
this.parent = null;
|
|
/**
|
|
* 当前节点所在节点树的根节点,指向 Layout
|
|
*/
|
|
this.root = null;
|
|
// public EE: any;
|
|
/**
|
|
* 用于标识当前节点是否已经执行销毁逻辑,销毁之后原先的功能都会异常,一般业务侧不用关心这个
|
|
*/
|
|
this.isDestroyed = false;
|
|
this.ctx = null;
|
|
/**
|
|
* 置脏标记位,目前当修改会影响布局属性的时候,会自动置脏
|
|
*/
|
|
this.isDirty = false;
|
|
/**
|
|
* css-layout 节点属性,业务侧无需关心
|
|
*/
|
|
this.shouldUpdate = false;
|
|
/**
|
|
* 有些 style 属性并不能直接用来渲染,需要经过解析之后才能进行渲染,这些值不会存储在 style 上
|
|
* 比如 style.transform,如果每次都解析性能太差了
|
|
*/
|
|
this.renderForLayout = {};
|
|
this.innerStyle = {};
|
|
this.id = id;
|
|
this.idName = idName;
|
|
this.className = className;
|
|
this.layoutBox = {
|
|
left: 0,
|
|
top: 0,
|
|
width: 0,
|
|
height: 0,
|
|
absoluteX: 0,
|
|
absoluteY: 0,
|
|
originalAbsoluteX: 0,
|
|
originalAbsoluteY: 0,
|
|
};
|
|
this.dataset = dataset;
|
|
_style__WEBPACK_IMPORTED_MODULE_0__.renderAffectStyles.forEach(function (prop) {
|
|
var val = style[prop];
|
|
if (typeof val !== 'undefined') {
|
|
_this.calculateRenderForLayout(true, prop, StyleOpType.Set, val);
|
|
}
|
|
});
|
|
this.originStyle = style;
|
|
this.style = style;
|
|
this.rect = null;
|
|
var initialTokens = className.split(/\s+/).filter(function (item) { return !!item; });
|
|
initialTokens.push(idName);
|
|
this.classList = new ElementClassList(this, initialTokens);
|
|
}
|
|
Element.prototype.styleChangeHandler = function (prop, styleOpType, val) {
|
|
};
|
|
Element.prototype.calculateRenderForLayout = function (init, prop, styleOpType, val) {
|
|
var _this = this;
|
|
var _a;
|
|
if (!init) {
|
|
this.styleChangeHandler(prop, styleOpType, val);
|
|
}
|
|
if (styleOpType === StyleOpType.Set) {
|
|
switch (prop) {
|
|
case 'backgroundImage':
|
|
var url = (0,_styleParser__WEBPACK_IMPORTED_MODULE_4__.backgroundImageParser)(val);
|
|
if (url) {
|
|
_common_imageManager__WEBPACK_IMPORTED_MODULE_2__["default"].loadImage(url, function (img) {
|
|
var _a;
|
|
if (!_this.isDestroyed) {
|
|
_this.renderForLayout.backgroundImage = img;
|
|
// 当图片加载完成,实例可能已经被销毁了
|
|
(_a = _this.root) === null || _a === void 0 ? void 0 : _a.emit('repaint');
|
|
}
|
|
});
|
|
}
|
|
break;
|
|
case 'transform':
|
|
delete this.renderForLayout.scaleX;
|
|
delete this.renderForLayout.scaleY;
|
|
delete this.renderForLayout.rotate;
|
|
Object.assign(this.renderForLayout, (0,_styleParser__WEBPACK_IMPORTED_MODULE_4__.parseTransform)(val));
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
switch (prop) {
|
|
case 'backgroundImage':
|
|
this.renderForLayout.backgroundImage = undefined;
|
|
break;
|
|
case 'transform':
|
|
delete this.renderForLayout.scaleX;
|
|
delete this.renderForLayout.scaleY;
|
|
delete this.renderForLayout.rotate;
|
|
break;
|
|
}
|
|
}
|
|
// 初始化的逻辑不需要做这些判断
|
|
if (!init) {
|
|
if (_style__WEBPACK_IMPORTED_MODULE_0__.reflowAffectedStyles.indexOf(prop) > -1) {
|
|
// setDirty(this, `change prop ${prop} from ${oldVal} to ${val}`);
|
|
setDirty(this);
|
|
}
|
|
else if (_style__WEBPACK_IMPORTED_MODULE_0__.repaintAffectedStyles.indexOf(prop) > -1) {
|
|
(_a = this.root) === null || _a === void 0 ? void 0 : _a.emit('repaint');
|
|
}
|
|
}
|
|
};
|
|
Element.prototype.observeStyleAndEvent = function () {
|
|
var _this = this;
|
|
if (typeof Proxy === 'function') {
|
|
var ele_1 = this;
|
|
var innerStyle_1 = this.innerStyle;
|
|
this.style = new Proxy(this.originStyle, {
|
|
get: function (target, prop, receiver) {
|
|
return Reflect.get(target, prop, receiver);
|
|
},
|
|
set: function (target, prop, val, receiver) {
|
|
// 判断初始化的className是否包含该属性
|
|
var isSetForInnerStyle = !Reflect.has(target, prop);
|
|
var oldVal = Reflect.get(target, prop, receiver);
|
|
if (typeof prop === 'string' && oldVal !== val) {
|
|
// console.log('set', prop, oldVal, val)
|
|
ele_1.calculateRenderForLayout(false, prop, StyleOpType.Set, val);
|
|
}
|
|
if (isSetForInnerStyle) {
|
|
// console.log('set innerStyle', prop, val)
|
|
// 将私有属性同步一份到 innerStyle
|
|
Reflect.set(innerStyle_1, prop, val);
|
|
}
|
|
return Reflect.set(target, prop, val, receiver);
|
|
},
|
|
deleteProperty: function (target, prop) {
|
|
ele_1.calculateRenderForLayout(false, prop, StyleOpType.Delete);
|
|
return Reflect.deleteProperty(target, prop);
|
|
},
|
|
});
|
|
}
|
|
// 事件冒泡逻辑
|
|
['touchstart', 'touchmove', 'touchcancel', 'touchend', 'click'].forEach(function (eventName) {
|
|
_this.on(eventName, function (e, touchMsg) {
|
|
// 处理伪类逻辑
|
|
if (eventName === 'touchstart') {
|
|
_this.activeHandler(e);
|
|
if (_this !== _this.root) {
|
|
// @ts-ignore
|
|
_this.root.activeElements.push(_this);
|
|
}
|
|
}
|
|
else if (eventName === 'touchend' || eventName === 'touchcancel') {
|
|
_this.deactiveHandler(e);
|
|
// @ts-ignore
|
|
var index = _this.root.activeElements.indexOf(_this);
|
|
if (index > -1) {
|
|
// @ts-ignore
|
|
_this.root.activeElements.splice(index, 1);
|
|
}
|
|
}
|
|
_this.parent && _this.parent.emit(eventName, e, touchMsg);
|
|
});
|
|
});
|
|
// this.classNameList = this.innerClassName.split(/\s+/);
|
|
};
|
|
Element.prototype.activeHandler = function (e) {
|
|
var activeStyle = this.style[':active'];
|
|
if (activeStyle) {
|
|
// 将当前的style缓存起来,在 active 取消的时候重置回去
|
|
this.cacheStyle = Object.assign({}, this.style);
|
|
Object.assign(this.style, activeStyle);
|
|
}
|
|
};
|
|
Element.prototype.deactiveHandler = function (e) {
|
|
var _this = this;
|
|
var activeStyle = this.style[':active'];
|
|
if (activeStyle) {
|
|
Object.keys(activeStyle).forEach(function (key) {
|
|
if (!_this.cacheStyle) {
|
|
return;
|
|
}
|
|
if (_this.cacheStyle[key]) {
|
|
// @ts-ignore
|
|
_this.style[key] = _this.cacheStyle[key];
|
|
}
|
|
else {
|
|
delete _this.style[key];
|
|
}
|
|
});
|
|
}
|
|
};
|
|
/**
|
|
* 节点重绘接口,子类填充实现
|
|
*/
|
|
Element.prototype.repaint = function () { };
|
|
/**
|
|
* 节点渲染接口子类填充实现
|
|
*/
|
|
Element.prototype.render = function () { };
|
|
/**
|
|
* 节点构造函数初始化后调用的方法,子类填充实现
|
|
*/
|
|
Element.prototype.afterCreate = function () { };
|
|
/**
|
|
* 参照 Web 规范:https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect
|
|
*/
|
|
Element.prototype.getBoundingClientRect = function () {
|
|
if (!this.rect) {
|
|
this.rect = new _common_rect__WEBPACK_IMPORTED_MODULE_1__["default"](this.layoutBox.absoluteX, this.layoutBox.absoluteY, this.layoutBox.width, this.layoutBox.height);
|
|
}
|
|
this.rect.set(this.layoutBox.absoluteX, this.layoutBox.absoluteY, this.layoutBox.width, this.layoutBox.height);
|
|
return this.rect;
|
|
};
|
|
/**
|
|
* 查询当前节点树下,idName 为给定参数的的节点
|
|
* 节点的 id 唯一性 Layout 并不保证,但这里只返回符合条件的第一个节点
|
|
*/
|
|
Element.prototype.getElementById = function (id) {
|
|
return getElementById(this, id);
|
|
};
|
|
/**
|
|
* 查询当前节点树下,idName 为给定参数的的节点
|
|
* 节点的 id 唯一性 Layout 并不保证,这里返回符合条件的节点集合
|
|
*/
|
|
Element.prototype.getElementsById = function (id) {
|
|
return getElementsById(this, [], id);
|
|
};
|
|
/**
|
|
* 查询当前节点树下,className 包含给定参数的的节点集合
|
|
*/
|
|
Element.prototype.getElementsByClassName = function (className) {
|
|
return getElementsByClassName(this, [], className);
|
|
};
|
|
/**
|
|
* 布局计算完成,准备执行渲染之前执行的操作,不同的子类有不同的行为
|
|
* 比如 ScrollView 在渲染之前还需要初始化滚动相关的能力
|
|
*
|
|
*/
|
|
Element.prototype.insert = function (ctx, needRender) {
|
|
this.shouldUpdate = false;
|
|
this.ctx = ctx;
|
|
if (needRender) {
|
|
this.render();
|
|
}
|
|
};
|
|
/**
|
|
* 节点解除事件绑定
|
|
*/
|
|
Element.prototype.unBindEvent = function () {
|
|
var _this = this;
|
|
[
|
|
'touchstart',
|
|
'touchmove',
|
|
'touchcancel',
|
|
'touchend',
|
|
'click',
|
|
'repaint',
|
|
].forEach(function (eventName) {
|
|
_this.off(eventName);
|
|
});
|
|
};
|
|
/**
|
|
* 将节点从当前节点树中删除
|
|
*/
|
|
Element.prototype.remove = function () {
|
|
var parent = this.parent;
|
|
if (!parent) {
|
|
return;
|
|
}
|
|
var index = parent.children.indexOf(this);
|
|
if (index !== -1) {
|
|
parent.children.splice(index, 1);
|
|
this.unBindEvent();
|
|
setDirty(this, "remove");
|
|
this.parent = null;
|
|
this.ctx = null;
|
|
}
|
|
else {
|
|
console.warn('[Layout] this element has been removed');
|
|
}
|
|
};
|
|
Element.prototype.setDirty = function () {
|
|
setDirty(this);
|
|
};
|
|
// 子类填充实现
|
|
Element.prototype.destroySelf = function () {
|
|
};
|
|
// 子类填充实现
|
|
Element.prototype.destroy = function () {
|
|
this.unBindEvent();
|
|
this.isDestroyed = true;
|
|
// this.EE = null;
|
|
this.parent = null;
|
|
this.ctx = null;
|
|
this.classList = null;
|
|
};
|
|
Element.prototype.add = function (element) {
|
|
element.parent = this;
|
|
this.children.push(element);
|
|
};
|
|
/**
|
|
* 将一个节点添加作为当前节点的子节点
|
|
*/
|
|
Element.prototype.appendChild = function (element) {
|
|
this.add(element);
|
|
setDirty(this, "appendChild ".concat(element));
|
|
};
|
|
/**
|
|
* 移除给定的子节点,只有一级节点能够移除
|
|
*/
|
|
Element.prototype.removeChild = function (element) {
|
|
var index = this.children.indexOf(element);
|
|
if (index !== -1) {
|
|
element.remove();
|
|
setDirty(this, "removeChild ".concat(element));
|
|
}
|
|
else {
|
|
console.warn('[Layout] the element to be removed is not a child of this element');
|
|
}
|
|
};
|
|
Element.prototype.emit = function (event) {
|
|
var theArgs = [];
|
|
for (var _i = 1; _i < arguments.length; _i++) {
|
|
theArgs[_i - 1] = arguments[_i];
|
|
}
|
|
EE.emit.apply(EE, __spreadArray([toEventName(event, this.id)], theArgs, false));
|
|
};
|
|
Element.prototype.on = function (event, callback) {
|
|
EE.on(toEventName(event, this.id), callback);
|
|
};
|
|
Element.prototype.once = function (event, callback) {
|
|
EE.once(toEventName(event, this.id), callback);
|
|
};
|
|
Element.prototype.off = function (event, callback) {
|
|
EE.off(toEventName(event, this.id), callback);
|
|
};
|
|
/**
|
|
* 渲染 border 相关能力抽象,子类可按需调用
|
|
* 由于支持了rotate特性,所以所有的渲染都需要方向减去transform的中间点
|
|
*/
|
|
Element.prototype.renderBorder = function (ctx, originX, originY) {
|
|
if (originX === void 0) { originX = 0; }
|
|
if (originY === void 0) { originY = 0; }
|
|
var style = this.style || {};
|
|
var radius = style.borderRadius || 0;
|
|
var _a = style.borderWidth, borderWidth = _a === void 0 ? 0 : _a;
|
|
var tlr = style.borderTopLeftRadius || radius;
|
|
var trr = style.borderTopRightRadius || radius;
|
|
var bbr = style.borderBottomLeftRadius || radius;
|
|
var brr = style.borderBottomRightRadius || radius;
|
|
var box = this.layoutBox;
|
|
var _b = style.borderColor, borderColor = _b === void 0 ? '' : _b;
|
|
var x = box.absoluteX;
|
|
var y = box.absoluteY;
|
|
var width = box.width, height = box.height;
|
|
var hasRadius = radius || tlr || trr || bbr || brr;
|
|
// borderWidth 和 radius 都没有,不需要执行后续逻辑,提升性能
|
|
if (!borderWidth && !hasRadius) {
|
|
return { needClip: false, needStroke: false };
|
|
}
|
|
ctx.lineWidth = borderWidth;
|
|
ctx.strokeStyle = borderColor;
|
|
// 左上角的点
|
|
ctx.beginPath();
|
|
ctx.moveTo(x + tlr - originX, y - originY);
|
|
ctx.lineTo(x + width - trr - originX, y - originY);
|
|
// 右上角的圆角
|
|
ctx.arcTo(x + width - originX, y - originY, x + width - originX, y + trr - originY, trr);
|
|
// 右下角的点
|
|
ctx.lineTo(x + width - originX, y + height - brr - originY);
|
|
// 右下角的圆角
|
|
ctx.arcTo(x + width - originX, y + height - originY, x + width - brr - originX, y + height - originY, brr);
|
|
// 左下角的点
|
|
ctx.lineTo(x + bbr - originX, y + height - originY);
|
|
// 左下角的圆角
|
|
ctx.arcTo(x - originX, y + height - originY, x - originX, y + height - bbr - originY, bbr);
|
|
// 左上角的点
|
|
ctx.lineTo(x - originX, y + tlr - originY);
|
|
// 左上角的圆角
|
|
ctx.arcTo(x - originX, y - originY, x + tlr - originX, y - originY, tlr);
|
|
ctx.closePath();
|
|
return { needClip: !!hasRadius, needStroke: !!borderWidth };
|
|
};
|
|
/**
|
|
* 每个子类都会有自己的渲染逻辑,但他们都有些通用的处理,比如透明度、旋转和border的处理,baseRender 用于处理通用的渲染逻辑
|
|
*/
|
|
Element.prototype.baseRender = function (type) {
|
|
var ctx = this.ctx;
|
|
var style = this.style;
|
|
var box = this.layoutBox;
|
|
var drawX = box.absoluteX, drawY = box.absoluteY, width = box.width, height = box.height;
|
|
if (style.opacity !== undefined) {
|
|
ctx.globalAlpha = style.opacity;
|
|
}
|
|
var originX = 0;
|
|
var originY = 0;
|
|
if (this.renderForLayout.rotate !== undefined || this.renderForLayout.scaleX !== undefined || this.renderForLayout.scaleY !== undefined) {
|
|
originX = drawX + box.width / 2;
|
|
originY = drawY + box.height / 2;
|
|
ctx.translate(originX, originY);
|
|
}
|
|
/**
|
|
* 请注意,这里暂时仅支持没有子节点的元素发生旋转,如果父节点旋转了子节点并不会跟着旋转
|
|
* 要实现父节点带动子节点旋转的能力,需要引入矩阵库,对代码改动也比较大,暂时不做改造。
|
|
*/
|
|
if (this.renderForLayout.rotate !== undefined) {
|
|
ctx.rotate(this.renderForLayout.rotate);
|
|
}
|
|
if (this.renderForLayout.scaleX !== undefined || this.renderForLayout.scaleY !== undefined) {
|
|
ctx.scale(this.renderForLayout.scaleX !== undefined ? this.renderForLayout.scaleX : 1, this.renderForLayout.scaleY !== undefined ? this.renderForLayout.scaleY : 1);
|
|
}
|
|
if (style.borderColor) {
|
|
ctx.strokeStyle = style.borderColor;
|
|
}
|
|
ctx.lineWidth = style.borderWidth || 0;
|
|
// for clip
|
|
var _a = this.renderBorder(ctx, originX, originY), needClip = _a.needClip, needStroke = _a.needStroke;
|
|
if (needClip) {
|
|
ctx.clip();
|
|
}
|
|
if (style.backgroundColor) {
|
|
ctx.fillStyle = style.backgroundColor;
|
|
ctx.fillRect(drawX - originX, drawY - originY, box.width, box.height);
|
|
}
|
|
if (this.renderForLayout.backgroundImage) {
|
|
ctx.drawImage(this.renderForLayout.backgroundImage, drawX - originX, drawY - originY, box.width, box.height);
|
|
}
|
|
return { needStroke: needStroke, needClip: needClip, originX: originX, originY: originY, drawX: drawX, drawY: drawY, width: width, height: height };
|
|
};
|
|
return Element;
|
|
}());
|
|
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Element);
|
|
|
|
|
|
/***/ }),
|
|
/* 3 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ reflowAffectedStyles: () => (/* binding */ reflowAffectedStyles),
|
|
/* harmony export */ renderAffectStyles: () => (/* binding */ renderAffectStyles),
|
|
/* harmony export */ repaintAffectedStyles: () => (/* binding */ repaintAffectedStyles)
|
|
/* harmony export */ });
|
|
var reflowAffectedStyles = [
|
|
'width', 'height',
|
|
'minWidth', 'minHeight',
|
|
'maxWidth', 'maxHeight',
|
|
'left', 'right', 'top', 'bottom',
|
|
'margin', 'marginLeft', 'marginRight', 'marginTop', 'marginBottom',
|
|
'padding', 'paddingLeft', 'paddingRight', 'paddingTop', 'paddingBottom',
|
|
'borderWidth', 'borderLeftWidth', 'borderRightWidth', 'borderTopWidth', 'borderBottomWidth',
|
|
'flexDirection',
|
|
'flexShrink',
|
|
'flexGrow',
|
|
'justifyContent',
|
|
'alignItems', 'alignSelf',
|
|
'flex',
|
|
'flexWrap',
|
|
'position',
|
|
'fontWeight',
|
|
];
|
|
var repaintAffectedStyles = [
|
|
'fontSize',
|
|
'lineHeight',
|
|
'textAlign',
|
|
'verticalAlign',
|
|
'color',
|
|
'backgroundColor',
|
|
'textOverflow',
|
|
'letterSpacing',
|
|
'borderRadius',
|
|
'borderColor',
|
|
'opacity',
|
|
'transform',
|
|
'textStrokeColor',
|
|
'textStrokeWidth',
|
|
'textShadow',
|
|
];
|
|
var renderAffectStyles = [
|
|
'textShadow',
|
|
'transform',
|
|
'backgroundImage',
|
|
];
|
|
|
|
|
|
|
|
/***/ }),
|
|
/* 4 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
|
|
/* harmony export */ });
|
|
var Rect = /** @class */ (function () {
|
|
function Rect(left, top, width, height) {
|
|
if (left === void 0) { left = 0; }
|
|
if (top === void 0) { top = 0; }
|
|
if (width === void 0) { width = 0; }
|
|
if (height === void 0) { height = 0; }
|
|
this.width = 0;
|
|
this.height = 0;
|
|
this.left = 0;
|
|
this.right = 0;
|
|
this.top = 0;
|
|
this.bottom = 0;
|
|
this.set(left, top, width, height);
|
|
}
|
|
Rect.prototype.set = function (left, top, width, height) {
|
|
if (left === void 0) { left = 0; }
|
|
if (top === void 0) { top = 0; }
|
|
if (width === void 0) { width = 0; }
|
|
if (height === void 0) { height = 0; }
|
|
this.left = left;
|
|
this.top = top;
|
|
this.width = width;
|
|
this.height = height;
|
|
this.right = this.left + this.width;
|
|
this.bottom = this.top + this.height;
|
|
};
|
|
/**
|
|
* 判断两个矩形是否相交
|
|
* 原理可见: https://zhuanlan.zhihu.com/p/29704064
|
|
*/
|
|
Rect.prototype.intersects = function (rect) {
|
|
return !(this.right < rect.left || rect.right < this.left || this.bottom < rect.top || rect.bottom < this.top);
|
|
};
|
|
return Rect;
|
|
}());
|
|
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Rect);
|
|
|
|
|
|
/***/ }),
|
|
/* 5 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
|
|
/* harmony export */ });
|
|
/* harmony import */ var _pool__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(6);
|
|
/* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(7);
|
|
/* harmony import */ var _env__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(1);
|
|
|
|
|
|
|
|
var ImageManager = /** @class */ (function () {
|
|
function ImageManager() {
|
|
this.imgPool = new _pool__WEBPACK_IMPORTED_MODULE_0__["default"]('imgPool');
|
|
}
|
|
ImageManager.prototype.getRes = function (src) {
|
|
return this.imgPool.get(src);
|
|
};
|
|
ImageManager.prototype.loadImagePromise = function (src) {
|
|
var _this = this;
|
|
return new Promise(function (resolve, reject) {
|
|
_this.loadImage(src, resolve, reject);
|
|
});
|
|
};
|
|
ImageManager.prototype.loadImage = function (src, success, fail) {
|
|
if (success === void 0) { success = _util__WEBPACK_IMPORTED_MODULE_1__.none; }
|
|
if (fail === void 0) { fail = _util__WEBPACK_IMPORTED_MODULE_1__.none; }
|
|
if (!src) {
|
|
return null;
|
|
}
|
|
var img;
|
|
var cache = this.getRes(src);
|
|
// 图片已经被加载过,直接返回图片并且执行回调
|
|
if (cache && cache.loadDone) {
|
|
img = cache.img;
|
|
success(img, true);
|
|
}
|
|
else if (cache && !cache.loadDone) {
|
|
// 图片正在加载过程中,返回图片并且等待图片加载完成执行回调
|
|
img = cache.img;
|
|
cache.onloadcbks.push(success);
|
|
cache.onerrorcbks.push(fail);
|
|
}
|
|
else {
|
|
// 创建图片,将回调函数推入回调函数栈
|
|
img = _env__WEBPACK_IMPORTED_MODULE_2__["default"].createImage();
|
|
var newCache_1 = {
|
|
img: img,
|
|
loadDone: false,
|
|
onloadcbks: [success],
|
|
onerrorcbks: [fail],
|
|
};
|
|
this.imgPool.set(src, newCache_1);
|
|
img.onload = function () {
|
|
newCache_1.loadDone = true;
|
|
newCache_1.onloadcbks.forEach(function (fn) { return fn(img, false); });
|
|
newCache_1.onloadcbks = [];
|
|
newCache_1.onerrorcbks = [];
|
|
};
|
|
img.onerror = function () {
|
|
newCache_1.onerrorcbks.forEach(function (fn) { return fn(img, false); });
|
|
newCache_1.onerrorcbks = [];
|
|
newCache_1.onloadcbks = [];
|
|
};
|
|
img.src = src;
|
|
}
|
|
return img;
|
|
};
|
|
return ImageManager;
|
|
}());
|
|
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (new ImageManager());
|
|
|
|
|
|
/***/ }),
|
|
/* 6 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
|
|
/* harmony export */ });
|
|
var pools = [];
|
|
var Pool = /** @class */ (function () {
|
|
function Pool(name) {
|
|
if (name === void 0) { name = 'pool'; }
|
|
this.name = 'pool';
|
|
this.pool = {};
|
|
var curr = pools.find(function (item) { return item.name === name; });
|
|
if (curr) {
|
|
return curr;
|
|
}
|
|
this.name = name;
|
|
this.pool = {};
|
|
pools.push(this);
|
|
}
|
|
Pool.prototype.get = function (key) {
|
|
return this.pool[key];
|
|
};
|
|
Pool.prototype.set = function (key, value) {
|
|
this.pool[key] = value;
|
|
};
|
|
Pool.prototype.clear = function () {
|
|
this.pool = {};
|
|
};
|
|
Pool.prototype.getList = function () {
|
|
return Object.values(this.pool);
|
|
};
|
|
return Pool;
|
|
}());
|
|
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Pool);
|
|
|
|
|
|
/***/ }),
|
|
/* 7 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ STATE: () => (/* binding */ STATE),
|
|
/* harmony export */ clamp: () => (/* binding */ clamp),
|
|
/* harmony export */ clearCanvas: () => (/* binding */ clearCanvas),
|
|
/* harmony export */ convertPercent: () => (/* binding */ convertPercent),
|
|
/* harmony export */ copyTouchArray: () => (/* binding */ copyTouchArray),
|
|
/* harmony export */ isClick: () => (/* binding */ isClick),
|
|
/* harmony export */ isGameTouchEvent: () => (/* binding */ isGameTouchEvent),
|
|
/* harmony export */ isPercent: () => (/* binding */ isPercent),
|
|
/* harmony export */ lerp: () => (/* binding */ lerp),
|
|
/* harmony export */ none: () => (/* binding */ none)
|
|
/* harmony export */ });
|
|
/* istanbul ignore next */
|
|
function none() { }
|
|
/**
|
|
* 根据触摸时长和触摸位置变化来判断是否属于点击事件
|
|
*/
|
|
function isClick(touchMsg) {
|
|
var start = touchMsg.touchstart;
|
|
var end = touchMsg.touchend;
|
|
if (!start
|
|
|| !end
|
|
|| !start.timeStamp
|
|
|| !end.timeStamp
|
|
|| start.pageX === undefined
|
|
|| start.pageY === undefined
|
|
|| end.pageX === undefined
|
|
|| end.pageY === undefined) {
|
|
return false;
|
|
}
|
|
var startPosX = start.pageX;
|
|
var startPosY = start.pageY;
|
|
var endPosX = end.pageX;
|
|
var endPosY = end.pageY;
|
|
var touchTimes = end.timeStamp - start.timeStamp;
|
|
return !!(Math.abs(endPosY - startPosY) < 30
|
|
&& Math.abs(endPosX - startPosX) < 30
|
|
&& touchTimes < 300);
|
|
}
|
|
var STATE;
|
|
(function (STATE) {
|
|
STATE["UNINIT"] = "UNINIT";
|
|
STATE["INITED"] = "INITED";
|
|
STATE["RENDERED"] = "RENDERED";
|
|
STATE["CLEAR"] = "CLEAR";
|
|
})(STATE || (STATE = {}));
|
|
;
|
|
function clearCanvas(ctx) {
|
|
ctx && ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
|
}
|
|
function copyTouchArray(touches) {
|
|
return touches.map(function (touch) { return ({
|
|
identifier: touch.identifier,
|
|
pageX: touch.pageX,
|
|
pageY: touch.pageY,
|
|
clientX: touch.clientX,
|
|
clientY: touch.clientY,
|
|
}); });
|
|
}
|
|
function isGameTouchEvent(e) {
|
|
return 'touches' in e;
|
|
}
|
|
/**
|
|
* 取最小值和最大值之间的区间限定值
|
|
* @param {number} number 需要被处理的数字
|
|
* @param {number} min 最小值
|
|
* @param {number} max 最大值
|
|
*/
|
|
function clamp(number, min, max) {
|
|
return Math.max(min, Math.min(number, max));
|
|
}
|
|
/**
|
|
* 两个数之间的线性插值。
|
|
*/
|
|
function lerp(from, to, ratio) {
|
|
return from + (to - from) * ratio;
|
|
}
|
|
function convertPercent(data, parentData) {
|
|
if (typeof data === 'number' || data === null) {
|
|
return data;
|
|
}
|
|
var matchData = data.match(/(\d+(?:\.\d+)?)%/);
|
|
if (matchData && matchData[1]) {
|
|
return parentData * parseFloat(matchData[1]) * 0.01;
|
|
}
|
|
}
|
|
function isPercent(data) {
|
|
return typeof data === 'string' && /\d+(?:\.\d+)?%/.test(data);
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
/* 8 */
|
|
/***/ ((module) => {
|
|
|
|
function E () {
|
|
// Keep this empty so it's easier to inherit from
|
|
// (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)
|
|
}
|
|
|
|
E.prototype = {
|
|
on: function (name, callback, ctx) {
|
|
var e = this.e || (this.e = {});
|
|
|
|
(e[name] || (e[name] = [])).push({
|
|
fn: callback,
|
|
ctx: ctx
|
|
});
|
|
|
|
return this;
|
|
},
|
|
|
|
once: function (name, callback, ctx) {
|
|
var self = this;
|
|
function listener () {
|
|
self.off(name, listener);
|
|
callback.apply(ctx, arguments);
|
|
};
|
|
|
|
listener._ = callback
|
|
return this.on(name, listener, ctx);
|
|
},
|
|
|
|
emit: function (name) {
|
|
var data = [].slice.call(arguments, 1);
|
|
var evtArr = ((this.e || (this.e = {}))[name] || []).slice();
|
|
var i = 0;
|
|
var len = evtArr.length;
|
|
|
|
for (i; i < len; i++) {
|
|
evtArr[i].fn.apply(evtArr[i].ctx, data);
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
off: function (name, callback) {
|
|
var e = this.e || (this.e = {});
|
|
var evts = e[name];
|
|
var liveEvents = [];
|
|
|
|
if (evts && callback) {
|
|
for (var i = 0, len = evts.length; i < len; i++) {
|
|
if (evts[i].fn !== callback && evts[i].fn._ !== callback)
|
|
liveEvents.push(evts[i]);
|
|
}
|
|
}
|
|
|
|
// Remove event from queue to prevent memory leak
|
|
// Suggested by https://github.com/lazd
|
|
// Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910
|
|
|
|
(liveEvents.length)
|
|
? e[name] = liveEvents
|
|
: delete e[name];
|
|
|
|
return this;
|
|
}
|
|
};
|
|
|
|
module.exports = E;
|
|
module.exports.TinyEmitter = E;
|
|
|
|
|
|
/***/ }),
|
|
/* 9 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ backgroundImageParser: () => (/* binding */ backgroundImageParser),
|
|
/* harmony export */ isValidTextShadow: () => (/* binding */ isValidTextShadow),
|
|
/* harmony export */ parseTransform: () => (/* binding */ parseTransform)
|
|
/* harmony export */ });
|
|
function degreesToRadians(degrees) {
|
|
return degrees * Math.PI / 180;
|
|
}
|
|
// 背景图正则表达式
|
|
var isValidUrlPropReg = /\s*url\((.*?)\)\s*/;
|
|
// 解析背景图片
|
|
function backgroundImageParser(val) {
|
|
if (typeof val === 'string') {
|
|
var list = val.match(isValidUrlPropReg);
|
|
if (list) {
|
|
var url = list[1].replace(/('|")/g, '');
|
|
return url;
|
|
}
|
|
}
|
|
console.error("[Layout]: ".concat(val, " is not a valid backgroundImage"));
|
|
return null;
|
|
}
|
|
var textShadowReg = /^(\d+px\s){2}\d+px\s(?:[a-zA-Z]+|#[0-9a-fA-F]{3,6})(,\s*(\d+px\s){2}\d+px\s(?:[a-zA-Z]+|#[0-9a-fA-F]{3,6}))*$/;
|
|
function isValidTextShadow(textShadow) {
|
|
return textShadowReg.test(textShadow);
|
|
}
|
|
function isValidTransformValue(value) {
|
|
// 使用正则表达式验证数字或逗号分隔的数字,后面可以跟可选的角度单位(deg)
|
|
return /^(-?\d+(\.\d+)?)(deg)?(,\s*(-?\d+(\.\d+)?)(deg)?)*$/.test(value);
|
|
// return /^(-?\d+(\.\d+)?)(deg)?(,\s*(-?\d+(\.\d+)?)(deg)?)*(,\s*(true|false))?$/.test(value);
|
|
}
|
|
var transformRegex = /(\w+)\(([^)]+)\)/g;
|
|
function parseTransform(transform) {
|
|
var result = {};
|
|
var match;
|
|
while ((match = transformRegex.exec(transform))) {
|
|
var name_1 = match[1], value = match[2];
|
|
if (!isValidTransformValue(value)) {
|
|
throw new Error("[Layout]: invalid value for ".concat(name_1, ": ").concat(value));
|
|
}
|
|
var values = value
|
|
.split(',')
|
|
.map(function (val) { return val.trim().replace('deg', ''); })
|
|
.map(Number);
|
|
switch (name_1) {
|
|
case 'rotate':
|
|
result.rotate = degreesToRadians(values[0]);
|
|
break;
|
|
case 'scale':
|
|
result.scaleX = values[0];
|
|
result.scaleY = values[1] || values[0];
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
/* 10 */
|
|
/***/ (function(module, exports) {
|
|
|
|
var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;// UMD (Universal Module Definition)
|
|
// See https://github.com/umdjs/umd for reference
|
|
//
|
|
// This file uses the following specific UMD implementation:
|
|
// https://github.com/umdjs/umd/blob/master/returnExports.js
|
|
(function(root, factory) {
|
|
if (true) {
|
|
// AMD. Register as an anonymous module.
|
|
!(__WEBPACK_AMD_DEFINE_ARRAY__ = [], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory),
|
|
__WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ?
|
|
(__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__),
|
|
__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
|
|
} else {}
|
|
}(this, function() {
|
|
/**
|
|
* Copyright (c) 2014, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*/
|
|
|
|
var computeLayout = (function() {
|
|
|
|
var CSS_UNDEFINED;
|
|
|
|
var CSS_DIRECTION_INHERIT = 'inherit';
|
|
var CSS_DIRECTION_LTR = 'ltr';
|
|
var CSS_DIRECTION_RTL = 'rtl';
|
|
|
|
var CSS_FLEX_DIRECTION_ROW = 'row';
|
|
var CSS_FLEX_DIRECTION_ROW_REVERSE = 'row-reverse';
|
|
var CSS_FLEX_DIRECTION_COLUMN = 'column';
|
|
var CSS_FLEX_DIRECTION_COLUMN_REVERSE = 'column-reverse';
|
|
|
|
var CSS_JUSTIFY_FLEX_START = 'flex-start';
|
|
var CSS_JUSTIFY_CENTER = 'center';
|
|
var CSS_JUSTIFY_FLEX_END = 'flex-end';
|
|
var CSS_JUSTIFY_SPACE_BETWEEN = 'space-between';
|
|
var CSS_JUSTIFY_SPACE_AROUND = 'space-around';
|
|
|
|
var CSS_ALIGN_FLEX_START = 'flex-start';
|
|
var CSS_ALIGN_CENTER = 'center';
|
|
var CSS_ALIGN_FLEX_END = 'flex-end';
|
|
var CSS_ALIGN_STRETCH = 'stretch';
|
|
|
|
var CSS_POSITION_RELATIVE = 'relative';
|
|
var CSS_POSITION_ABSOLUTE = 'absolute';
|
|
|
|
var leading = {
|
|
'row': 'left',
|
|
'row-reverse': 'right',
|
|
'column': 'top',
|
|
'column-reverse': 'bottom'
|
|
};
|
|
var trailing = {
|
|
'row': 'right',
|
|
'row-reverse': 'left',
|
|
'column': 'bottom',
|
|
'column-reverse': 'top'
|
|
};
|
|
var pos = {
|
|
'row': 'left',
|
|
'row-reverse': 'right',
|
|
'column': 'top',
|
|
'column-reverse': 'bottom'
|
|
};
|
|
var dim = {
|
|
'row': 'width',
|
|
'row-reverse': 'width',
|
|
'column': 'height',
|
|
'column-reverse': 'height'
|
|
};
|
|
|
|
// When transpiled to Java / C the node type has layout, children and style
|
|
// properties. For the JavaScript version this function adds these properties
|
|
// if they don't already exist.
|
|
function fillNodes(node) {
|
|
if (!node.layout || node.isDirty) {
|
|
node.layout = {
|
|
width: undefined,
|
|
height: undefined,
|
|
top: 0,
|
|
left: 0,
|
|
right: 0,
|
|
bottom: 0
|
|
};
|
|
}
|
|
|
|
if (!node.style) {
|
|
node.style = {};
|
|
}
|
|
|
|
if (!node.children) {
|
|
node.children = [];
|
|
}
|
|
node.children.forEach(fillNodes);
|
|
return node;
|
|
}
|
|
|
|
function isUndefined(value) {
|
|
return value === undefined;
|
|
}
|
|
|
|
function isRowDirection(flexDirection) {
|
|
return flexDirection === CSS_FLEX_DIRECTION_ROW ||
|
|
flexDirection === CSS_FLEX_DIRECTION_ROW_REVERSE;
|
|
}
|
|
|
|
function isColumnDirection(flexDirection) {
|
|
return flexDirection === CSS_FLEX_DIRECTION_COLUMN ||
|
|
flexDirection === CSS_FLEX_DIRECTION_COLUMN_REVERSE;
|
|
}
|
|
|
|
function getLeadingMargin(node, axis) {
|
|
if (node.style.marginStart !== undefined && isRowDirection(axis)) {
|
|
return node.style.marginStart;
|
|
}
|
|
|
|
var value = null;
|
|
switch (axis) {
|
|
case 'row': value = node.style.marginLeft; break;
|
|
case 'row-reverse': value = node.style.marginRight; break;
|
|
case 'column': value = node.style.marginTop; break;
|
|
case 'column-reverse': value = node.style.marginBottom; break;
|
|
}
|
|
|
|
if (value !== undefined) {
|
|
return value;
|
|
}
|
|
|
|
if (node.style.margin !== undefined) {
|
|
return node.style.margin;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
function getTrailingMargin(node, axis) {
|
|
if (node.style.marginEnd !== undefined && isRowDirection(axis)) {
|
|
return node.style.marginEnd;
|
|
}
|
|
|
|
var value = null;
|
|
switch (axis) {
|
|
case 'row': value = node.style.marginRight; break;
|
|
case 'row-reverse': value = node.style.marginLeft; break;
|
|
case 'column': value = node.style.marginBottom; break;
|
|
case 'column-reverse': value = node.style.marginTop; break;
|
|
}
|
|
|
|
if (value != null) {
|
|
return value;
|
|
}
|
|
|
|
if (node.style.margin !== undefined) {
|
|
return node.style.margin;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
function getLeadingPadding(node, axis) {
|
|
if (node.style.paddingStart !== undefined && node.style.paddingStart >= 0
|
|
&& isRowDirection(axis)) {
|
|
return node.style.paddingStart;
|
|
}
|
|
|
|
var value = null;
|
|
switch (axis) {
|
|
case 'row': value = node.style.paddingLeft; break;
|
|
case 'row-reverse': value = node.style.paddingRight; break;
|
|
case 'column': value = node.style.paddingTop; break;
|
|
case 'column-reverse': value = node.style.paddingBottom; break;
|
|
}
|
|
|
|
if (value != null && value >= 0) {
|
|
return value;
|
|
}
|
|
|
|
if (node.style.padding !== undefined && node.style.padding >= 0) {
|
|
return node.style.padding;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
function getTrailingPadding(node, axis) {
|
|
if (node.style.paddingEnd !== undefined && node.style.paddingEnd >= 0
|
|
&& isRowDirection(axis)) {
|
|
return node.style.paddingEnd;
|
|
}
|
|
|
|
var value = null;
|
|
switch (axis) {
|
|
case 'row': value = node.style.paddingRight; break;
|
|
case 'row-reverse': value = node.style.paddingLeft; break;
|
|
case 'column': value = node.style.paddingBottom; break;
|
|
case 'column-reverse': value = node.style.paddingTop; break;
|
|
}
|
|
|
|
if (value != null && value >= 0) {
|
|
return value;
|
|
}
|
|
|
|
if (node.style.padding !== undefined && node.style.padding >= 0) {
|
|
return node.style.padding;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
function getLeadingBorder(node, axis) {
|
|
if (node.style.borderStartWidth !== undefined && node.style.borderStartWidth >= 0
|
|
&& isRowDirection(axis)) {
|
|
return node.style.borderStartWidth;
|
|
}
|
|
|
|
var value = null;
|
|
switch (axis) {
|
|
case 'row': value = node.style.borderLeftWidth; break;
|
|
case 'row-reverse': value = node.style.borderRightWidth; break;
|
|
case 'column': value = node.style.borderTopWidth; break;
|
|
case 'column-reverse': value = node.style.borderBottomWidth; break;
|
|
}
|
|
|
|
if (value != null && value >= 0) {
|
|
return value;
|
|
}
|
|
|
|
if (node.style.borderWidth !== undefined && node.style.borderWidth >= 0) {
|
|
return node.style.borderWidth;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
function getTrailingBorder(node, axis) {
|
|
if (node.style.borderEndWidth !== undefined && node.style.borderEndWidth >= 0
|
|
&& isRowDirection(axis)) {
|
|
return node.style.borderEndWidth;
|
|
}
|
|
|
|
var value = null;
|
|
switch (axis) {
|
|
case 'row': value = node.style.borderRightWidth; break;
|
|
case 'row-reverse': value = node.style.borderLeftWidth; break;
|
|
case 'column': value = node.style.borderBottomWidth; break;
|
|
case 'column-reverse': value = node.style.borderTopWidth; break;
|
|
}
|
|
|
|
if (value != null && value >= 0) {
|
|
return value;
|
|
}
|
|
|
|
if (node.style.borderWidth !== undefined && node.style.borderWidth >= 0) {
|
|
return node.style.borderWidth;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
function getLeadingPaddingAndBorder(node, axis) {
|
|
return getLeadingPadding(node, axis) + getLeadingBorder(node, axis);
|
|
}
|
|
|
|
function getTrailingPaddingAndBorder(node, axis) {
|
|
return getTrailingPadding(node, axis) + getTrailingBorder(node, axis);
|
|
}
|
|
|
|
function getBorderAxis(node, axis) {
|
|
return getLeadingBorder(node, axis) + getTrailingBorder(node, axis);
|
|
}
|
|
|
|
function getMarginAxis(node, axis) {
|
|
return getLeadingMargin(node, axis) + getTrailingMargin(node, axis);
|
|
}
|
|
|
|
function getPaddingAndBorderAxis(node, axis) {
|
|
return getLeadingPaddingAndBorder(node, axis) +
|
|
getTrailingPaddingAndBorder(node, axis);
|
|
}
|
|
|
|
function getJustifyContent(node) {
|
|
if (node.style.justifyContent) {
|
|
return node.style.justifyContent;
|
|
}
|
|
return 'flex-start';
|
|
}
|
|
|
|
function getAlignContent(node) {
|
|
if (node.style.alignContent) {
|
|
return node.style.alignContent;
|
|
}
|
|
return 'flex-start';
|
|
}
|
|
|
|
function getAlignItem(node, child) {
|
|
if (child.style.alignSelf) {
|
|
return child.style.alignSelf;
|
|
}
|
|
if (node.style.alignItems) {
|
|
return node.style.alignItems;
|
|
}
|
|
return 'stretch';
|
|
}
|
|
|
|
function resolveAxis(axis, direction) {
|
|
if (direction === CSS_DIRECTION_RTL) {
|
|
if (axis === CSS_FLEX_DIRECTION_ROW) {
|
|
return CSS_FLEX_DIRECTION_ROW_REVERSE;
|
|
} else if (axis === CSS_FLEX_DIRECTION_ROW_REVERSE) {
|
|
return CSS_FLEX_DIRECTION_ROW;
|
|
}
|
|
}
|
|
|
|
return axis;
|
|
}
|
|
|
|
function resolveDirection(node, parentDirection) {
|
|
var direction;
|
|
if (node.style.direction) {
|
|
direction = node.style.direction;
|
|
} else {
|
|
direction = CSS_DIRECTION_INHERIT;
|
|
}
|
|
|
|
if (direction === CSS_DIRECTION_INHERIT) {
|
|
direction = (parentDirection === undefined ? CSS_DIRECTION_LTR : parentDirection);
|
|
}
|
|
|
|
return direction;
|
|
}
|
|
|
|
function getFlexDirection(node) {
|
|
if (node.style.flexDirection) {
|
|
return node.style.flexDirection;
|
|
}
|
|
return CSS_FLEX_DIRECTION_COLUMN;
|
|
}
|
|
|
|
function getCrossFlexDirection(flexDirection, direction) {
|
|
if (isColumnDirection(flexDirection)) {
|
|
return resolveAxis(CSS_FLEX_DIRECTION_ROW, direction);
|
|
} else {
|
|
return CSS_FLEX_DIRECTION_COLUMN;
|
|
}
|
|
}
|
|
|
|
function getPositionType(node) {
|
|
if (node.style.position) {
|
|
return node.style.position;
|
|
}
|
|
return 'relative';
|
|
}
|
|
|
|
function isFlex(node) {
|
|
return (
|
|
getPositionType(node) === CSS_POSITION_RELATIVE &&
|
|
node.style.flex > 0
|
|
);
|
|
}
|
|
|
|
function isFlexWrap(node) {
|
|
return node.style.flexWrap === 'wrap';
|
|
}
|
|
|
|
function getDimWithMargin(node, axis) {
|
|
return node.layout[dim[axis]] + getMarginAxis(node, axis);
|
|
}
|
|
|
|
function isDimDefined(node, axis) {
|
|
return node.style[dim[axis]] !== undefined && node.style[dim[axis]] >= 0;
|
|
}
|
|
|
|
function isPosDefined(node, pos) {
|
|
return node.style[pos] !== undefined;
|
|
}
|
|
|
|
function isMeasureDefined(node) {
|
|
return node.style.measure !== undefined;
|
|
}
|
|
|
|
function getPosition(node, pos) {
|
|
if (node.style[pos] !== undefined) {
|
|
return node.style[pos];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
function boundAxis(node, axis, value) {
|
|
var min = {
|
|
'row': node.style.minWidth,
|
|
'row-reverse': node.style.minWidth,
|
|
'column': node.style.minHeight,
|
|
'column-reverse': node.style.minHeight
|
|
}[axis];
|
|
|
|
var max = {
|
|
'row': node.style.maxWidth,
|
|
'row-reverse': node.style.maxWidth,
|
|
'column': node.style.maxHeight,
|
|
'column-reverse': node.style.maxHeight
|
|
}[axis];
|
|
|
|
var boundValue = value;
|
|
if (max !== undefined && max >= 0 && boundValue > max) {
|
|
boundValue = max;
|
|
}
|
|
if (min !== undefined && min >= 0 && boundValue < min) {
|
|
boundValue = min;
|
|
}
|
|
return boundValue;
|
|
}
|
|
|
|
function fmaxf(a, b) {
|
|
if (a > b) {
|
|
return a;
|
|
}
|
|
return b;
|
|
}
|
|
|
|
// When the user specifically sets a value for width or height
|
|
function setDimensionFromStyle(node, axis) {
|
|
// The parent already computed us a width or height. We just skip it
|
|
if (node.layout[dim[axis]] !== undefined) {
|
|
return;
|
|
}
|
|
// We only run if there's a width or height defined
|
|
if (!isDimDefined(node, axis)) {
|
|
return;
|
|
}
|
|
|
|
// The dimensions can never be smaller than the padding and border
|
|
node.layout[dim[axis]] = fmaxf(
|
|
boundAxis(node, axis, node.style[dim[axis]]),
|
|
getPaddingAndBorderAxis(node, axis)
|
|
);
|
|
}
|
|
|
|
function setTrailingPosition(node, child, axis) {
|
|
child.layout[trailing[axis]] = node.layout[dim[axis]] -
|
|
child.layout[dim[axis]] - child.layout[pos[axis]];
|
|
}
|
|
|
|
// If both left and right are defined, then use left. Otherwise return
|
|
// +left or -right depending on which is defined.
|
|
function getRelativePosition(node, axis) {
|
|
if (node.style[leading[axis]] !== undefined) {
|
|
return getPosition(node, leading[axis]);
|
|
}
|
|
return -getPosition(node, trailing[axis]);
|
|
}
|
|
|
|
function layoutNodeImpl(node, parentMaxWidth, /*css_direction_t*/parentDirection) {
|
|
var/*css_direction_t*/ direction = resolveDirection(node, parentDirection);
|
|
var/*(c)!css_flex_direction_t*//*(java)!int*/ mainAxis = resolveAxis(getFlexDirection(node), direction);
|
|
var/*(c)!css_flex_direction_t*//*(java)!int*/ crossAxis = getCrossFlexDirection(mainAxis, direction);
|
|
var/*(c)!css_flex_direction_t*//*(java)!int*/ resolvedRowAxis = resolveAxis(CSS_FLEX_DIRECTION_ROW, direction);
|
|
|
|
// Handle width and height style attributes
|
|
setDimensionFromStyle(node, mainAxis);
|
|
setDimensionFromStyle(node, crossAxis);
|
|
|
|
// Set the resolved resolution in the node's layout
|
|
node.layout.direction = direction;
|
|
|
|
// The position is set by the parent, but we need to complete it with a
|
|
// delta composed of the margin and left/top/right/bottom
|
|
node.layout[leading[mainAxis]] += getLeadingMargin(node, mainAxis) +
|
|
getRelativePosition(node, mainAxis);
|
|
node.layout[trailing[mainAxis]] += getTrailingMargin(node, mainAxis) +
|
|
getRelativePosition(node, mainAxis);
|
|
node.layout[leading[crossAxis]] += getLeadingMargin(node, crossAxis) +
|
|
getRelativePosition(node, crossAxis);
|
|
node.layout[trailing[crossAxis]] += getTrailingMargin(node, crossAxis) +
|
|
getRelativePosition(node, crossAxis);
|
|
|
|
// Inline immutable values from the target node to avoid excessive method
|
|
// invocations during the layout calculation.
|
|
var/*int*/ childCount = node.children.length;
|
|
var/*float*/ paddingAndBorderAxisResolvedRow = getPaddingAndBorderAxis(node, resolvedRowAxis);
|
|
|
|
if (isMeasureDefined(node)) {
|
|
var/*bool*/ isResolvedRowDimDefined = !isUndefined(node.layout[dim[resolvedRowAxis]]);
|
|
|
|
var/*float*/ width = CSS_UNDEFINED;
|
|
if (isDimDefined(node, resolvedRowAxis)) {
|
|
width = node.style.width;
|
|
} else if (isResolvedRowDimDefined) {
|
|
width = node.layout[dim[resolvedRowAxis]];
|
|
} else {
|
|
width = parentMaxWidth -
|
|
getMarginAxis(node, resolvedRowAxis);
|
|
}
|
|
width -= paddingAndBorderAxisResolvedRow;
|
|
|
|
// We only need to give a dimension for the text if we haven't got any
|
|
// for it computed yet. It can either be from the style attribute or because
|
|
// the element is flexible.
|
|
var/*bool*/ isRowUndefined = !isDimDefined(node, resolvedRowAxis) && !isResolvedRowDimDefined;
|
|
var/*bool*/ isColumnUndefined = !isDimDefined(node, CSS_FLEX_DIRECTION_COLUMN) &&
|
|
isUndefined(node.layout[dim[CSS_FLEX_DIRECTION_COLUMN]]);
|
|
|
|
// Let's not measure the text if we already know both dimensions
|
|
if (isRowUndefined || isColumnUndefined) {
|
|
var/*css_dim_t*/ measureDim = node.style.measure(
|
|
/*(c)!node->context,*/
|
|
/*(java)!layoutContext.measureOutput,*/
|
|
width
|
|
);
|
|
if (isRowUndefined) {
|
|
node.layout.width = measureDim.width +
|
|
paddingAndBorderAxisResolvedRow;
|
|
}
|
|
if (isColumnUndefined) {
|
|
node.layout.height = measureDim.height +
|
|
getPaddingAndBorderAxis(node, CSS_FLEX_DIRECTION_COLUMN);
|
|
}
|
|
}
|
|
if (childCount === 0) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
var/*bool*/ isNodeFlexWrap = isFlexWrap(node);
|
|
|
|
var/*css_justify_t*/ justifyContent = getJustifyContent(node);
|
|
|
|
var/*float*/ leadingPaddingAndBorderMain = getLeadingPaddingAndBorder(node, mainAxis);
|
|
var/*float*/ leadingPaddingAndBorderCross = getLeadingPaddingAndBorder(node, crossAxis);
|
|
var/*float*/ paddingAndBorderAxisMain = getPaddingAndBorderAxis(node, mainAxis);
|
|
var/*float*/ paddingAndBorderAxisCross = getPaddingAndBorderAxis(node, crossAxis);
|
|
|
|
var/*bool*/ isMainDimDefined = !isUndefined(node.layout[dim[mainAxis]]);
|
|
var/*bool*/ isCrossDimDefined = !isUndefined(node.layout[dim[crossAxis]]);
|
|
var/*bool*/ isMainRowDirection = isRowDirection(mainAxis);
|
|
|
|
var/*int*/ i;
|
|
var/*int*/ ii;
|
|
var/*css_node_t**/ child;
|
|
var/*(c)!css_flex_direction_t*//*(java)!int*/ axis;
|
|
|
|
var/*css_node_t**/ firstAbsoluteChild = null;
|
|
var/*css_node_t**/ currentAbsoluteChild = null;
|
|
|
|
var/*float*/ definedMainDim = CSS_UNDEFINED;
|
|
if (isMainDimDefined) {
|
|
definedMainDim = node.layout[dim[mainAxis]] - paddingAndBorderAxisMain;
|
|
}
|
|
|
|
// We want to execute the next two loops one per line with flex-wrap
|
|
var/*int*/ startLine = 0;
|
|
var/*int*/ endLine = 0;
|
|
// var/*int*/ nextOffset = 0;
|
|
var/*int*/ alreadyComputedNextLayout = 0;
|
|
// We aggregate the total dimensions of the container in those two variables
|
|
var/*float*/ linesCrossDim = 0;
|
|
var/*float*/ linesMainDim = 0;
|
|
var/*int*/ linesCount = 0;
|
|
while (endLine < childCount) {
|
|
// <Loop A> Layout non flexible children and count children by type
|
|
|
|
// mainContentDim is accumulation of the dimensions and margin of all the
|
|
// non flexible children. This will be used in order to either set the
|
|
// dimensions of the node if none already exist, or to compute the
|
|
// remaining space left for the flexible children.
|
|
var/*float*/ mainContentDim = 0;
|
|
|
|
// There are three kind of children, non flexible, flexible and absolute.
|
|
// We need to know how many there are in order to distribute the space.
|
|
var/*int*/ flexibleChildrenCount = 0;
|
|
var/*float*/ totalFlexible = 0;
|
|
var/*int*/ nonFlexibleChildrenCount = 0;
|
|
|
|
// Use the line loop to position children in the main axis for as long
|
|
// as they are using a simple stacking behaviour. Children that are
|
|
// immediately stacked in the initial loop will not be touched again
|
|
// in <Loop C>.
|
|
var/*bool*/ isSimpleStackMain =
|
|
(isMainDimDefined && justifyContent === CSS_JUSTIFY_FLEX_START) ||
|
|
(!isMainDimDefined && justifyContent !== CSS_JUSTIFY_CENTER);
|
|
var/*int*/ firstComplexMain = (isSimpleStackMain ? childCount : startLine);
|
|
|
|
// Use the initial line loop to position children in the cross axis for
|
|
// as long as they are relatively positioned with alignment STRETCH or
|
|
// FLEX_START. Children that are immediately stacked in the initial loop
|
|
// will not be touched again in <Loop D>.
|
|
var/*bool*/ isSimpleStackCross = true;
|
|
var/*int*/ firstComplexCross = childCount;
|
|
|
|
var/*css_node_t**/ firstFlexChild = null;
|
|
var/*css_node_t**/ currentFlexChild = null;
|
|
|
|
var/*float*/ mainDim = leadingPaddingAndBorderMain;
|
|
var/*float*/ crossDim = 0;
|
|
|
|
var/*float*/ maxWidth;
|
|
for (i = startLine; i < childCount; ++i) {
|
|
child = node.children[i];
|
|
child.lineIndex = linesCount;
|
|
|
|
child.nextAbsoluteChild = null;
|
|
child.nextFlexChild = null;
|
|
|
|
var/*css_align_t*/ alignItem = getAlignItem(node, child);
|
|
|
|
// Pre-fill cross axis dimensions when the child is using stretch before
|
|
// we call the recursive layout pass
|
|
if (alignItem === CSS_ALIGN_STRETCH &&
|
|
getPositionType(child) === CSS_POSITION_RELATIVE &&
|
|
isCrossDimDefined &&
|
|
!isDimDefined(child, crossAxis)) {
|
|
child.layout[dim[crossAxis]] = fmaxf(
|
|
boundAxis(child, crossAxis, node.layout[dim[crossAxis]] -
|
|
paddingAndBorderAxisCross - getMarginAxis(child, crossAxis)),
|
|
// You never want to go smaller than padding
|
|
getPaddingAndBorderAxis(child, crossAxis)
|
|
);
|
|
} else if (getPositionType(child) === CSS_POSITION_ABSOLUTE) {
|
|
// Store a private linked list of absolutely positioned children
|
|
// so that we can efficiently traverse them later.
|
|
if (firstAbsoluteChild === null) {
|
|
firstAbsoluteChild = child;
|
|
}
|
|
if (currentAbsoluteChild !== null) {
|
|
currentAbsoluteChild.nextAbsoluteChild = child;
|
|
}
|
|
currentAbsoluteChild = child;
|
|
|
|
// Pre-fill dimensions when using absolute position and both offsets for the axis are defined (either both
|
|
// left and right or top and bottom).
|
|
for (ii = 0; ii < 2; ii++) {
|
|
axis = (ii !== 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
|
if (!isUndefined(node.layout[dim[axis]]) &&
|
|
!isDimDefined(child, axis) &&
|
|
isPosDefined(child, leading[axis]) &&
|
|
isPosDefined(child, trailing[axis])) {
|
|
child.layout[dim[axis]] = fmaxf(
|
|
boundAxis(child, axis, node.layout[dim[axis]] -
|
|
getPaddingAndBorderAxis(node, axis) -
|
|
getMarginAxis(child, axis) -
|
|
getPosition(child, leading[axis]) -
|
|
getPosition(child, trailing[axis])),
|
|
// You never want to go smaller than padding
|
|
getPaddingAndBorderAxis(child, axis)
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
var/*float*/ nextContentDim = 0;
|
|
|
|
// It only makes sense to consider a child flexible if we have a computed
|
|
// dimension for the node.
|
|
if (isMainDimDefined && isFlex(child)) {
|
|
flexibleChildrenCount++;
|
|
totalFlexible += child.style.flex;
|
|
|
|
// Store a private linked list of flexible children so that we can
|
|
// efficiently traverse them later.
|
|
if (firstFlexChild === null) {
|
|
firstFlexChild = child;
|
|
}
|
|
if (currentFlexChild !== null) {
|
|
currentFlexChild.nextFlexChild = child;
|
|
}
|
|
currentFlexChild = child;
|
|
|
|
// Even if we don't know its exact size yet, we already know the padding,
|
|
// border and margin. We'll use this partial information, which represents
|
|
// the smallest possible size for the child, to compute the remaining
|
|
// available space.
|
|
nextContentDim = getPaddingAndBorderAxis(child, mainAxis) +
|
|
getMarginAxis(child, mainAxis);
|
|
|
|
} else {
|
|
maxWidth = CSS_UNDEFINED;
|
|
if (!isMainRowDirection) {
|
|
if (isDimDefined(node, resolvedRowAxis)) {
|
|
maxWidth = node.layout[dim[resolvedRowAxis]] -
|
|
paddingAndBorderAxisResolvedRow;
|
|
} else {
|
|
maxWidth = parentMaxWidth -
|
|
getMarginAxis(node, resolvedRowAxis) -
|
|
paddingAndBorderAxisResolvedRow;
|
|
}
|
|
}
|
|
|
|
// This is the main recursive call. We layout non flexible children.
|
|
if (alreadyComputedNextLayout === 0) {
|
|
layoutNode(/*(java)!layoutContext, */child, maxWidth, direction);
|
|
}
|
|
|
|
// Absolute positioned elements do not take part of the layout, so we
|
|
// don't use them to compute mainContentDim
|
|
if (getPositionType(child) === CSS_POSITION_RELATIVE) {
|
|
nonFlexibleChildrenCount++;
|
|
// At this point we know the final size and margin of the element.
|
|
nextContentDim = getDimWithMargin(child, mainAxis);
|
|
}
|
|
}
|
|
|
|
// The element we are about to add would make us go to the next line
|
|
if (isNodeFlexWrap &&
|
|
isMainDimDefined &&
|
|
mainContentDim + nextContentDim > definedMainDim &&
|
|
// If there's only one element, then it's bigger than the content
|
|
// and needs its own line
|
|
i !== startLine) {
|
|
nonFlexibleChildrenCount--;
|
|
alreadyComputedNextLayout = 1;
|
|
break;
|
|
}
|
|
|
|
// Disable simple stacking in the main axis for the current line as
|
|
// we found a non-trivial child. The remaining children will be laid out
|
|
// in <Loop C>.
|
|
if (isSimpleStackMain &&
|
|
(getPositionType(child) !== CSS_POSITION_RELATIVE || isFlex(child))) {
|
|
isSimpleStackMain = false;
|
|
firstComplexMain = i;
|
|
}
|
|
|
|
// Disable simple stacking in the cross axis for the current line as
|
|
// we found a non-trivial child. The remaining children will be laid out
|
|
// in <Loop D>.
|
|
if (isSimpleStackCross &&
|
|
(getPositionType(child) !== CSS_POSITION_RELATIVE ||
|
|
(alignItem !== CSS_ALIGN_STRETCH && alignItem !== CSS_ALIGN_FLEX_START) ||
|
|
isUndefined(child.layout[dim[crossAxis]]))) {
|
|
isSimpleStackCross = false;
|
|
firstComplexCross = i;
|
|
}
|
|
|
|
if (isSimpleStackMain) {
|
|
child.layout[pos[mainAxis]] += mainDim;
|
|
if (isMainDimDefined) {
|
|
setTrailingPosition(node, child, mainAxis);
|
|
}
|
|
|
|
mainDim += getDimWithMargin(child, mainAxis);
|
|
crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis)));
|
|
}
|
|
|
|
if (isSimpleStackCross) {
|
|
child.layout[pos[crossAxis]] += linesCrossDim + leadingPaddingAndBorderCross;
|
|
if (isCrossDimDefined) {
|
|
setTrailingPosition(node, child, crossAxis);
|
|
}
|
|
}
|
|
|
|
alreadyComputedNextLayout = 0;
|
|
mainContentDim += nextContentDim;
|
|
endLine = i + 1;
|
|
}
|
|
|
|
// <Loop B> Layout flexible children and allocate empty space
|
|
|
|
// In order to position the elements in the main axis, we have two
|
|
// controls. The space between the beginning and the first element
|
|
// and the space between each two elements.
|
|
var/*float*/ leadingMainDim = 0;
|
|
var/*float*/ betweenMainDim = 0;
|
|
|
|
// The remaining available space that needs to be allocated
|
|
var/*float*/ remainingMainDim = 0;
|
|
if (isMainDimDefined) {
|
|
remainingMainDim = definedMainDim - mainContentDim;
|
|
} else {
|
|
remainingMainDim = fmaxf(mainContentDim, 0) - mainContentDim;
|
|
}
|
|
|
|
// If there are flexible children in the mix, they are going to fill the
|
|
// remaining space
|
|
if (flexibleChildrenCount !== 0) {
|
|
var/*float*/ flexibleMainDim = remainingMainDim / totalFlexible;
|
|
var/*float*/ baseMainDim;
|
|
var/*float*/ boundMainDim;
|
|
|
|
// If the flex share of remaining space doesn't meet min/max bounds,
|
|
// remove this child from flex calculations.
|
|
currentFlexChild = firstFlexChild;
|
|
while (currentFlexChild !== null) {
|
|
baseMainDim = flexibleMainDim * currentFlexChild.style.flex +
|
|
getPaddingAndBorderAxis(currentFlexChild, mainAxis);
|
|
boundMainDim = boundAxis(currentFlexChild, mainAxis, baseMainDim);
|
|
|
|
if (baseMainDim !== boundMainDim) {
|
|
remainingMainDim -= boundMainDim;
|
|
totalFlexible -= currentFlexChild.style.flex;
|
|
}
|
|
|
|
currentFlexChild = currentFlexChild.nextFlexChild;
|
|
}
|
|
flexibleMainDim = remainingMainDim / totalFlexible;
|
|
|
|
// The non flexible children can overflow the container, in this case
|
|
// we should just assume that there is no space available.
|
|
if (flexibleMainDim < 0) {
|
|
flexibleMainDim = 0;
|
|
}
|
|
|
|
currentFlexChild = firstFlexChild;
|
|
while (currentFlexChild !== null) {
|
|
// At this point we know the final size of the element in the main
|
|
// dimension
|
|
currentFlexChild.layout[dim[mainAxis]] = boundAxis(currentFlexChild, mainAxis,
|
|
flexibleMainDim * currentFlexChild.style.flex +
|
|
getPaddingAndBorderAxis(currentFlexChild, mainAxis)
|
|
);
|
|
|
|
maxWidth = CSS_UNDEFINED;
|
|
if (isDimDefined(node, resolvedRowAxis)) {
|
|
maxWidth = node.layout[dim[resolvedRowAxis]] -
|
|
paddingAndBorderAxisResolvedRow;
|
|
} else if (!isMainRowDirection) {
|
|
maxWidth = parentMaxWidth -
|
|
getMarginAxis(node, resolvedRowAxis) -
|
|
paddingAndBorderAxisResolvedRow;
|
|
}
|
|
|
|
// And we recursively call the layout algorithm for this child
|
|
layoutNode(/*(java)!layoutContext, */currentFlexChild, maxWidth, direction);
|
|
|
|
child = currentFlexChild;
|
|
currentFlexChild = currentFlexChild.nextFlexChild;
|
|
child.nextFlexChild = null;
|
|
}
|
|
|
|
// We use justifyContent to figure out how to allocate the remaining
|
|
// space available
|
|
} else if (justifyContent !== CSS_JUSTIFY_FLEX_START) {
|
|
if (justifyContent === CSS_JUSTIFY_CENTER) {
|
|
leadingMainDim = remainingMainDim / 2;
|
|
} else if (justifyContent === CSS_JUSTIFY_FLEX_END) {
|
|
leadingMainDim = remainingMainDim;
|
|
} else if (justifyContent === CSS_JUSTIFY_SPACE_BETWEEN) {
|
|
remainingMainDim = fmaxf(remainingMainDim, 0);
|
|
if (flexibleChildrenCount + nonFlexibleChildrenCount - 1 !== 0) {
|
|
betweenMainDim = remainingMainDim /
|
|
(flexibleChildrenCount + nonFlexibleChildrenCount - 1);
|
|
} else {
|
|
betweenMainDim = 0;
|
|
}
|
|
} else if (justifyContent === CSS_JUSTIFY_SPACE_AROUND) {
|
|
// Space on the edges is half of the space between elements
|
|
betweenMainDim = remainingMainDim /
|
|
(flexibleChildrenCount + nonFlexibleChildrenCount);
|
|
leadingMainDim = betweenMainDim / 2;
|
|
}
|
|
}
|
|
|
|
// <Loop C> Position elements in the main axis and compute dimensions
|
|
|
|
// At this point, all the children have their dimensions set. We need to
|
|
// find their position. In order to do that, we accumulate data in
|
|
// variables that are also useful to compute the total dimensions of the
|
|
// container!
|
|
mainDim += leadingMainDim;
|
|
|
|
for (i = firstComplexMain; i < endLine; ++i) {
|
|
child = node.children[i];
|
|
|
|
if (getPositionType(child) === CSS_POSITION_ABSOLUTE &&
|
|
isPosDefined(child, leading[mainAxis])) {
|
|
// In case the child is position absolute and has left/top being
|
|
// defined, we override the position to whatever the user said
|
|
// (and margin/border).
|
|
child.layout[pos[mainAxis]] = getPosition(child, leading[mainAxis]) +
|
|
getLeadingBorder(node, mainAxis) +
|
|
getLeadingMargin(child, mainAxis);
|
|
} else {
|
|
// If the child is position absolute (without top/left) or relative,
|
|
// we put it at the current accumulated offset.
|
|
child.layout[pos[mainAxis]] += mainDim;
|
|
|
|
// Define the trailing position accordingly.
|
|
if (isMainDimDefined) {
|
|
setTrailingPosition(node, child, mainAxis);
|
|
}
|
|
|
|
// Now that we placed the element, we need to update the variables
|
|
// We only need to do that for relative elements. Absolute elements
|
|
// do not take part in that phase.
|
|
if (getPositionType(child) === CSS_POSITION_RELATIVE) {
|
|
// The main dimension is the sum of all the elements dimension plus
|
|
// the spacing.
|
|
mainDim += betweenMainDim + getDimWithMargin(child, mainAxis);
|
|
// The cross dimension is the max of the elements dimension since there
|
|
// can only be one element in that cross dimension.
|
|
crossDim = fmaxf(crossDim, boundAxis(child, crossAxis, getDimWithMargin(child, crossAxis)));
|
|
}
|
|
}
|
|
}
|
|
|
|
var/*float*/ containerCrossAxis = node.layout[dim[crossAxis]];
|
|
if (!isCrossDimDefined) {
|
|
containerCrossAxis = fmaxf(
|
|
// For the cross dim, we add both sides at the end because the value
|
|
// is aggregate via a max function. Intermediate negative values
|
|
// can mess this computation otherwise
|
|
boundAxis(node, crossAxis, crossDim + paddingAndBorderAxisCross),
|
|
paddingAndBorderAxisCross
|
|
);
|
|
}
|
|
|
|
// <Loop D> Position elements in the cross axis
|
|
for (i = firstComplexCross; i < endLine; ++i) {
|
|
child = node.children[i];
|
|
|
|
if (getPositionType(child) === CSS_POSITION_ABSOLUTE &&
|
|
isPosDefined(child, leading[crossAxis])) {
|
|
// In case the child is absolutely positionned and has a
|
|
// top/left/bottom/right being set, we override all the previously
|
|
// computed positions to set it correctly.
|
|
child.layout[pos[crossAxis]] = getPosition(child, leading[crossAxis]) +
|
|
getLeadingBorder(node, crossAxis) +
|
|
getLeadingMargin(child, crossAxis);
|
|
|
|
} else {
|
|
var/*float*/ leadingCrossDim = leadingPaddingAndBorderCross;
|
|
|
|
// For a relative children, we're either using alignItems (parent) or
|
|
// alignSelf (child) in order to determine the position in the cross axis
|
|
if (getPositionType(child) === CSS_POSITION_RELATIVE) {
|
|
/*eslint-disable */
|
|
// This variable is intentionally re-defined as the code is transpiled to a block scope language
|
|
var/*css_align_t*/ alignItem = getAlignItem(node, child);
|
|
/*eslint-enable */
|
|
if (alignItem === CSS_ALIGN_STRETCH) {
|
|
// You can only stretch if the dimension has not already been set
|
|
// previously.
|
|
if (isUndefined(child.layout[dim[crossAxis]])) {
|
|
child.layout[dim[crossAxis]] = fmaxf(
|
|
boundAxis(child, crossAxis, containerCrossAxis -
|
|
paddingAndBorderAxisCross - getMarginAxis(child, crossAxis)),
|
|
// You never want to go smaller than padding
|
|
getPaddingAndBorderAxis(child, crossAxis)
|
|
);
|
|
}
|
|
} else if (alignItem !== CSS_ALIGN_FLEX_START) {
|
|
// The remaining space between the parent dimensions+padding and child
|
|
// dimensions+margin.
|
|
var/*float*/ remainingCrossDim = containerCrossAxis -
|
|
paddingAndBorderAxisCross - getDimWithMargin(child, crossAxis);
|
|
|
|
if (alignItem === CSS_ALIGN_CENTER) {
|
|
leadingCrossDim += remainingCrossDim / 2;
|
|
} else { // CSS_ALIGN_FLEX_END
|
|
leadingCrossDim += remainingCrossDim;
|
|
}
|
|
}
|
|
}
|
|
|
|
// And we apply the position
|
|
child.layout[pos[crossAxis]] += linesCrossDim + leadingCrossDim;
|
|
|
|
// Define the trailing position accordingly.
|
|
if (isCrossDimDefined) {
|
|
setTrailingPosition(node, child, crossAxis);
|
|
}
|
|
}
|
|
}
|
|
|
|
linesCrossDim += crossDim;
|
|
linesMainDim = fmaxf(linesMainDim, mainDim);
|
|
linesCount += 1;
|
|
startLine = endLine;
|
|
}
|
|
|
|
// <Loop E>
|
|
//
|
|
// Note(prenaux): More than one line, we need to layout the crossAxis
|
|
// according to alignContent.
|
|
//
|
|
// Note that we could probably remove <Loop D> and handle the one line case
|
|
// here too, but for the moment this is safer since it won't interfere with
|
|
// previously working code.
|
|
//
|
|
// See specs:
|
|
// http://www.w3.org/TR/2012/CR-css3-flexbox-20120918/#layout-algorithm
|
|
// section 9.4
|
|
//
|
|
if (linesCount > 1 && isCrossDimDefined) {
|
|
var/*float*/ nodeCrossAxisInnerSize = node.layout[dim[crossAxis]] -
|
|
paddingAndBorderAxisCross;
|
|
var/*float*/ remainingAlignContentDim = nodeCrossAxisInnerSize - linesCrossDim;
|
|
|
|
var/*float*/ crossDimLead = 0;
|
|
var/*float*/ currentLead = leadingPaddingAndBorderCross;
|
|
|
|
var/*css_align_t*/ alignContent = getAlignContent(node);
|
|
if (alignContent === CSS_ALIGN_FLEX_END) {
|
|
currentLead += remainingAlignContentDim;
|
|
} else if (alignContent === CSS_ALIGN_CENTER) {
|
|
currentLead += remainingAlignContentDim / 2;
|
|
} else if (alignContent === CSS_ALIGN_STRETCH) {
|
|
if (nodeCrossAxisInnerSize > linesCrossDim) {
|
|
crossDimLead = (remainingAlignContentDim / linesCount);
|
|
}
|
|
}
|
|
|
|
var/*int*/ endIndex = 0;
|
|
for (i = 0; i < linesCount; ++i) {
|
|
var/*int*/ startIndex = endIndex;
|
|
|
|
// compute the line's height and find the endIndex
|
|
var/*float*/ lineHeight = 0;
|
|
for (ii = startIndex; ii < childCount; ++ii) {
|
|
child = node.children[ii];
|
|
if (getPositionType(child) !== CSS_POSITION_RELATIVE) {
|
|
continue;
|
|
}
|
|
if (child.lineIndex !== i) {
|
|
break;
|
|
}
|
|
if (!isUndefined(child.layout[dim[crossAxis]])) {
|
|
lineHeight = fmaxf(
|
|
lineHeight,
|
|
child.layout[dim[crossAxis]] + getMarginAxis(child, crossAxis)
|
|
);
|
|
}
|
|
}
|
|
endIndex = ii;
|
|
lineHeight += crossDimLead;
|
|
|
|
for (ii = startIndex; ii < endIndex; ++ii) {
|
|
child = node.children[ii];
|
|
if (getPositionType(child) !== CSS_POSITION_RELATIVE) {
|
|
continue;
|
|
}
|
|
|
|
var/*css_align_t*/ alignContentAlignItem = getAlignItem(node, child);
|
|
if (alignContentAlignItem === CSS_ALIGN_FLEX_START) {
|
|
child.layout[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis);
|
|
} else if (alignContentAlignItem === CSS_ALIGN_FLEX_END) {
|
|
child.layout[pos[crossAxis]] = currentLead + lineHeight - getTrailingMargin(child, crossAxis) - child.layout[dim[crossAxis]];
|
|
} else if (alignContentAlignItem === CSS_ALIGN_CENTER) {
|
|
var/*float*/ childHeight = child.layout[dim[crossAxis]];
|
|
child.layout[pos[crossAxis]] = currentLead + (lineHeight - childHeight) / 2;
|
|
} else if (alignContentAlignItem === CSS_ALIGN_STRETCH) {
|
|
child.layout[pos[crossAxis]] = currentLead + getLeadingMargin(child, crossAxis);
|
|
// TODO(prenaux): Correctly set the height of items with undefined
|
|
// (auto) crossAxis dimension.
|
|
}
|
|
}
|
|
|
|
currentLead += lineHeight;
|
|
}
|
|
}
|
|
|
|
var/*bool*/ needsMainTrailingPos = false;
|
|
var/*bool*/ needsCrossTrailingPos = false;
|
|
|
|
// If the user didn't specify a width or height, and it has not been set
|
|
// by the container, then we set it via the children.
|
|
if (!isMainDimDefined) {
|
|
node.layout[dim[mainAxis]] = fmaxf(
|
|
// We're missing the last padding at this point to get the final
|
|
// dimension
|
|
boundAxis(node, mainAxis, linesMainDim + getTrailingPaddingAndBorder(node, mainAxis)),
|
|
// We can never assign a width smaller than the padding and borders
|
|
paddingAndBorderAxisMain
|
|
);
|
|
|
|
if (mainAxis === CSS_FLEX_DIRECTION_ROW_REVERSE ||
|
|
mainAxis === CSS_FLEX_DIRECTION_COLUMN_REVERSE) {
|
|
needsMainTrailingPos = true;
|
|
}
|
|
}
|
|
|
|
if (!isCrossDimDefined) {
|
|
node.layout[dim[crossAxis]] = fmaxf(
|
|
// For the cross dim, we add both sides at the end because the value
|
|
// is aggregate via a max function. Intermediate negative values
|
|
// can mess this computation otherwise
|
|
boundAxis(node, crossAxis, linesCrossDim + paddingAndBorderAxisCross),
|
|
paddingAndBorderAxisCross
|
|
);
|
|
|
|
if (crossAxis === CSS_FLEX_DIRECTION_ROW_REVERSE ||
|
|
crossAxis === CSS_FLEX_DIRECTION_COLUMN_REVERSE) {
|
|
needsCrossTrailingPos = true;
|
|
}
|
|
}
|
|
|
|
// <Loop F> Set trailing position if necessary
|
|
if (needsMainTrailingPos || needsCrossTrailingPos) {
|
|
for (i = 0; i < childCount; ++i) {
|
|
child = node.children[i];
|
|
|
|
if (needsMainTrailingPos) {
|
|
setTrailingPosition(node, child, mainAxis);
|
|
}
|
|
|
|
if (needsCrossTrailingPos) {
|
|
setTrailingPosition(node, child, crossAxis);
|
|
}
|
|
}
|
|
}
|
|
|
|
// <Loop G> Calculate dimensions for absolutely positioned elements
|
|
currentAbsoluteChild = firstAbsoluteChild;
|
|
while (currentAbsoluteChild !== null) {
|
|
// Pre-fill dimensions when using absolute position and both offsets for
|
|
// the axis are defined (either both left and right or top and bottom).
|
|
for (ii = 0; ii < 2; ii++) {
|
|
axis = (ii !== 0) ? CSS_FLEX_DIRECTION_ROW : CSS_FLEX_DIRECTION_COLUMN;
|
|
|
|
if (!isUndefined(node.layout[dim[axis]]) &&
|
|
!isDimDefined(currentAbsoluteChild, axis) &&
|
|
isPosDefined(currentAbsoluteChild, leading[axis]) &&
|
|
isPosDefined(currentAbsoluteChild, trailing[axis])) {
|
|
currentAbsoluteChild.layout[dim[axis]] = fmaxf(
|
|
boundAxis(currentAbsoluteChild, axis, node.layout[dim[axis]] -
|
|
getBorderAxis(node, axis) -
|
|
getMarginAxis(currentAbsoluteChild, axis) -
|
|
getPosition(currentAbsoluteChild, leading[axis]) -
|
|
getPosition(currentAbsoluteChild, trailing[axis])
|
|
),
|
|
// You never want to go smaller than padding
|
|
getPaddingAndBorderAxis(currentAbsoluteChild, axis)
|
|
);
|
|
}
|
|
|
|
if (isPosDefined(currentAbsoluteChild, trailing[axis]) &&
|
|
!isPosDefined(currentAbsoluteChild, leading[axis])) {
|
|
currentAbsoluteChild.layout[leading[axis]] =
|
|
node.layout[dim[axis]] -
|
|
currentAbsoluteChild.layout[dim[axis]] -
|
|
getPosition(currentAbsoluteChild, trailing[axis]);
|
|
}
|
|
}
|
|
|
|
child = currentAbsoluteChild;
|
|
currentAbsoluteChild = currentAbsoluteChild.nextAbsoluteChild;
|
|
child.nextAbsoluteChild = null;
|
|
}
|
|
}
|
|
|
|
function layoutNode(node, parentMaxWidth, parentDirection) {
|
|
node.shouldUpdate = true;
|
|
|
|
var direction = node.style.direction || CSS_DIRECTION_LTR;
|
|
var skipLayout =
|
|
!node.isDirty &&
|
|
node.lastLayout &&
|
|
node.lastLayout.requestedHeight === node.layout.height &&
|
|
node.lastLayout.requestedWidth === node.layout.width &&
|
|
node.lastLayout.parentMaxWidth === parentMaxWidth &&
|
|
node.lastLayout.direction === direction;
|
|
|
|
if (skipLayout) {
|
|
node.layout.width = node.lastLayout.width;
|
|
node.layout.height = node.lastLayout.height;
|
|
node.layout.top = node.lastLayout.top;
|
|
node.layout.left = node.lastLayout.left;
|
|
} else {
|
|
if (!node.lastLayout) {
|
|
node.lastLayout = {};
|
|
}
|
|
|
|
node.lastLayout.requestedWidth = node.layout.width;
|
|
node.lastLayout.requestedHeight = node.layout.height;
|
|
node.lastLayout.parentMaxWidth = parentMaxWidth;
|
|
node.lastLayout.direction = direction;
|
|
|
|
// Reset child layouts
|
|
node.children.forEach(function(child) {
|
|
child.layout.width = undefined;
|
|
child.layout.height = undefined;
|
|
child.layout.top = 0;
|
|
child.layout.left = 0;
|
|
});
|
|
|
|
layoutNodeImpl(node, parentMaxWidth, parentDirection);
|
|
|
|
node.lastLayout.width = node.layout.width;
|
|
node.lastLayout.height = node.layout.height;
|
|
node.lastLayout.top = node.layout.top;
|
|
node.lastLayout.left = node.layout.left;
|
|
}
|
|
}
|
|
|
|
return {
|
|
layoutNodeImpl: layoutNodeImpl,
|
|
computeLayout: layoutNode,
|
|
fillNodes: fillNodes
|
|
};
|
|
})();
|
|
|
|
// This module export is only used for the purposes of unit testing this file. When
|
|
// the library is packaged this file is included within css-layout.js which forms
|
|
// the public API.
|
|
if (true) {
|
|
module.exports = computeLayout;
|
|
}
|
|
|
|
|
|
return function(node) {
|
|
/*eslint-disable */
|
|
// disabling ESLint because this code relies on the above include
|
|
computeLayout.fillNodes(node);
|
|
computeLayout.computeLayout(node);
|
|
/*eslint-enable */
|
|
};
|
|
}));
|
|
|
|
|
|
/***/ }),
|
|
/* 11 */
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
var nodeToJson = __webpack_require__(12);
|
|
var xmlToNodeobj = __webpack_require__(14);
|
|
var x2xmlnode = __webpack_require__(14);
|
|
var buildOptions = (__webpack_require__(13).buildOptions);
|
|
var validator = __webpack_require__(16);
|
|
exports.parse = function (xmlData, options, validationOption) {
|
|
if (validationOption) {
|
|
if (validationOption === true)
|
|
validationOption = {};
|
|
var result = validator.validate(xmlData, validationOption);
|
|
if (result !== true) {
|
|
throw Error(result.err.msg);
|
|
}
|
|
}
|
|
options = buildOptions(options, x2xmlnode.defaultOptions, x2xmlnode.props);
|
|
return nodeToJson.convertToJson(xmlToNodeobj.getTraversalObj(xmlData, options), options);
|
|
};
|
|
|
|
|
|
/***/ }),
|
|
/* 12 */
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
var util = __webpack_require__(13);
|
|
var convertToJson = function (node, options) {
|
|
var jObj = {
|
|
name: node.tagname
|
|
};
|
|
//when no child node or attr is present
|
|
if ((!node.child || util.isEmptyObject(node.child)) && (!node.attrsMap || util.isEmptyObject(node.attrsMap))) {
|
|
return util.isExist(node.val) && !!node.val ? node.val : jObj;
|
|
}
|
|
else {
|
|
//otherwise create a textnode if node has some text
|
|
if (util.isExist(node.val)) {
|
|
if (!(typeof node.val === 'string' && (node.val === '' || node.val === options.cdataPositionChar))) {
|
|
if (options.arrayMode === "strict") {
|
|
jObj[options.textNodeName] = [node.val];
|
|
}
|
|
else {
|
|
jObj[options.textNodeName] = node.val;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
util.merge(jObj, node.attrsMap, options.arrayMode);
|
|
jObj.children = [];
|
|
node.children.forEach(function (child) {
|
|
jObj.children.push(convertToJson(child, options));
|
|
});
|
|
//add value
|
|
return jObj;
|
|
};
|
|
exports.convertToJson = convertToJson;
|
|
|
|
|
|
/***/ }),
|
|
/* 13 */
|
|
/***/ ((__unused_webpack_module, exports) => {
|
|
|
|
"use strict";
|
|
|
|
var getAllMatches = function (string, regex) {
|
|
var matches = [];
|
|
var match = regex.exec(string);
|
|
while (match) {
|
|
var allmatches = [];
|
|
var len = match.length;
|
|
for (var index = 0; index < len; index++) {
|
|
allmatches.push(match[index]);
|
|
}
|
|
matches.push(allmatches);
|
|
match = regex.exec(string);
|
|
}
|
|
return matches;
|
|
};
|
|
var doesMatch = function (string, regex) {
|
|
var match = regex.exec(string);
|
|
return !(match === null || typeof match === 'undefined');
|
|
};
|
|
var doesNotMatch = function (string, regex) {
|
|
return !doesMatch(string, regex);
|
|
};
|
|
exports.isExist = function (v) {
|
|
return typeof v !== 'undefined';
|
|
};
|
|
exports.isEmptyObject = function (obj) {
|
|
return Object.keys(obj).length === 0;
|
|
};
|
|
/**
|
|
* Copy all the properties of a into b.
|
|
* @param {*} target
|
|
* @param {*} a
|
|
*/
|
|
exports.merge = function (target, a, arrayMode) {
|
|
if (a) {
|
|
var keys = Object.keys(a); // will return an array of own properties
|
|
var len = keys.length; //don't make it inline
|
|
for (var i = 0; i < len; i++) {
|
|
if (arrayMode === 'strict') {
|
|
target[keys[i]] = [a[keys[i]]];
|
|
}
|
|
else {
|
|
target[keys[i]] = a[keys[i]];
|
|
}
|
|
}
|
|
}
|
|
};
|
|
/* exports.merge =function (b,a){
|
|
return Object.assign(b,a);
|
|
} */
|
|
exports.getValue = function (v) {
|
|
if (exports.isExist(v)) {
|
|
return v;
|
|
}
|
|
else {
|
|
return '';
|
|
}
|
|
};
|
|
// const fakeCall = function(a) {return a;};
|
|
// const fakeCallNoReturn = function() {};
|
|
exports.buildOptions = function (options, defaultOptions, props) {
|
|
var newOptions = {};
|
|
if (!options) {
|
|
return defaultOptions; //if there are not options
|
|
}
|
|
for (var i = 0; i < props.length; i++) {
|
|
if (options[props[i]] !== undefined) {
|
|
newOptions[props[i]] = options[props[i]];
|
|
}
|
|
else {
|
|
newOptions[props[i]] = defaultOptions[props[i]];
|
|
}
|
|
}
|
|
return newOptions;
|
|
};
|
|
exports.doesMatch = doesMatch;
|
|
exports.doesNotMatch = doesNotMatch;
|
|
exports.getAllMatches = getAllMatches;
|
|
|
|
|
|
/***/ }),
|
|
/* 14 */
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
var util = __webpack_require__(13);
|
|
var buildOptions = (__webpack_require__(13).buildOptions);
|
|
var xmlNode = __webpack_require__(15);
|
|
var TagType = { OPENING: 1, CLOSING: 2, SELF: 3, CDATA: 4 };
|
|
var regx = '<((!\\[CDATA\\[([\\s\\S]*?)(]]>))|(([\\w:\\-._]*:)?([\\w:\\-._]+))([^>]*)>|((\\/)(([\\w:\\-._]*:)?([\\w:\\-._]+))\\s*>))([^<]*)';
|
|
//const tagsRegx = new RegExp("<(\\/?[\\w:\\-\._]+)([^>]*)>(\\s*"+cdataRegx+")*([^<]+)?","g");
|
|
//const tagsRegx = new RegExp("<(\\/?)((\\w*:)?([\\w:\\-\._]+))([^>]*)>([^<]*)("+cdataRegx+"([^<]*))*([^<]+)?","g");
|
|
//polyfill
|
|
if (!Number.parseInt && window.parseInt) {
|
|
Number.parseInt = window.parseInt;
|
|
}
|
|
if (!Number.parseFloat && window.parseFloat) {
|
|
Number.parseFloat = window.parseFloat;
|
|
}
|
|
var defaultOptions = {
|
|
attributeNamePrefix: '@_',
|
|
attrNodeName: false,
|
|
textNodeName: '#text',
|
|
ignoreAttributes: true,
|
|
ignoreNameSpace: false,
|
|
allowBooleanAttributes: false, //a tag can have attributes without any value
|
|
//ignoreRootElement : false,
|
|
parseNodeValue: true,
|
|
parseAttributeValue: false,
|
|
arrayMode: false,
|
|
trimValues: true, //Trim string values of tag and attributes
|
|
cdataTagName: false,
|
|
cdataPositionChar: '\\c',
|
|
localeRange: '',
|
|
tagValueProcessor: function (a) {
|
|
return a;
|
|
},
|
|
attrValueProcessor: function (a) {
|
|
return a;
|
|
},
|
|
stopNodes: []
|
|
//decodeStrict: false,
|
|
};
|
|
exports.defaultOptions = defaultOptions;
|
|
var props = [
|
|
'attributeNamePrefix',
|
|
'attrNodeName',
|
|
'textNodeName',
|
|
'ignoreAttributes',
|
|
'ignoreNameSpace',
|
|
'allowBooleanAttributes',
|
|
'parseNodeValue',
|
|
'parseAttributeValue',
|
|
'arrayMode',
|
|
'trimValues',
|
|
'cdataTagName',
|
|
'cdataPositionChar',
|
|
'localeRange',
|
|
'tagValueProcessor',
|
|
'attrValueProcessor',
|
|
'parseTrueNumberOnly',
|
|
'stopNodes'
|
|
];
|
|
exports.props = props;
|
|
var getTraversalObj = function (xmlData, options) {
|
|
options = buildOptions(options, defaultOptions, props);
|
|
//xmlData = xmlData.replace(/\r?\n/g, " ");//make it single line
|
|
xmlData = xmlData.replace(/<!--[\s\S]*?-->/g, ''); //Remove comments
|
|
var xmlObj = new xmlNode('!xml');
|
|
var currentNode = xmlObj;
|
|
regx = regx.replace(/\[\\w/g, '[' + options.localeRange + '\\w');
|
|
var tagsRegx = new RegExp(regx, 'g');
|
|
var tag = tagsRegx.exec(xmlData);
|
|
var nextTag = tagsRegx.exec(xmlData);
|
|
while (tag) {
|
|
var tagType = checkForTagType(tag);
|
|
if (tagType === TagType.CLOSING) {
|
|
//add parsed data to parent node
|
|
if (currentNode.parent && tag[14]) {
|
|
currentNode.parent.val = util.getValue(currentNode.parent.val) + '' + processTagValue(tag, options, currentNode.parent.tagname);
|
|
}
|
|
if (options.stopNodes.length && options.stopNodes.includes(currentNode.tagname)) {
|
|
currentNode.child = [];
|
|
if (currentNode.attrsMap == undefined) {
|
|
currentNode.attrsMap = {};
|
|
}
|
|
currentNode.val = xmlData.substr(currentNode.startIndex + 1, tag.index - currentNode.startIndex - 1);
|
|
}
|
|
currentNode = currentNode.parent;
|
|
}
|
|
else if (tagType === TagType.CDATA) {
|
|
if (options.cdataTagName) {
|
|
//add cdata node
|
|
var childNode = new xmlNode(options.cdataTagName, currentNode, tag[3]);
|
|
childNode.attrsMap = buildAttributesMap(tag[8], options);
|
|
currentNode.addChild(childNode);
|
|
//for backtracking
|
|
currentNode.val = util.getValue(currentNode.val) + options.cdataPositionChar;
|
|
//add rest value to parent node
|
|
if (tag[14]) {
|
|
currentNode.val += processTagValue(tag, options);
|
|
}
|
|
}
|
|
else {
|
|
currentNode.val = (currentNode.val || '') + (tag[3] || '') + processTagValue(tag, options);
|
|
}
|
|
}
|
|
else if (tagType === TagType.SELF) {
|
|
if (currentNode && tag[14]) {
|
|
currentNode.val = util.getValue(currentNode.val) + '' + processTagValue(tag, options);
|
|
}
|
|
var childNode = new xmlNode(options.ignoreNameSpace ? tag[7] : tag[5], currentNode, '');
|
|
if (tag[8] && tag[8].length > 0) {
|
|
tag[8] = tag[8].substr(0, tag[8].length - 1);
|
|
}
|
|
childNode.attrsMap = buildAttributesMap(tag[8], options);
|
|
currentNode.addChild(childNode);
|
|
}
|
|
else {
|
|
//TagType.OPENING
|
|
var childNode = new xmlNode(options.ignoreNameSpace ? tag[7] : tag[5], currentNode, processTagValue(tag, options));
|
|
if (options.stopNodes.length && options.stopNodes.includes(childNode.tagname)) {
|
|
childNode.startIndex = tag.index + tag[1].length;
|
|
}
|
|
childNode.attrsMap = buildAttributesMap(tag[8], options);
|
|
currentNode.addChild(childNode);
|
|
currentNode = childNode;
|
|
}
|
|
tag = nextTag;
|
|
nextTag = tagsRegx.exec(xmlData);
|
|
}
|
|
return xmlObj;
|
|
};
|
|
function processTagValue(parsedTags, options, parentTagName) {
|
|
var tagName = parsedTags[7] || parentTagName;
|
|
var val = parsedTags[14];
|
|
if (val) {
|
|
if (options.trimValues) {
|
|
val = val.trim();
|
|
}
|
|
val = options.tagValueProcessor(val, tagName);
|
|
val = parseValue(val, options.parseNodeValue, options.parseTrueNumberOnly);
|
|
}
|
|
return val;
|
|
}
|
|
function checkForTagType(match) {
|
|
if (match[4] === ']]>') {
|
|
return TagType.CDATA;
|
|
}
|
|
else if (match[10] === '/') {
|
|
return TagType.CLOSING;
|
|
}
|
|
else if (typeof match[8] !== 'undefined' && match[8].substr(match[8].length - 1) === '/') {
|
|
return TagType.SELF;
|
|
}
|
|
else {
|
|
return TagType.OPENING;
|
|
}
|
|
}
|
|
function resolveNameSpace(tagname, options) {
|
|
if (options.ignoreNameSpace) {
|
|
var tags = tagname.split(':');
|
|
var prefix = tagname.charAt(0) === '/' ? '/' : '';
|
|
if (tags[0] === 'xmlns') {
|
|
return '';
|
|
}
|
|
if (tags.length === 2) {
|
|
tagname = prefix + tags[1];
|
|
}
|
|
}
|
|
return tagname;
|
|
}
|
|
function parseValue(val, shouldParse, parseTrueNumberOnly) {
|
|
if (shouldParse && typeof val === 'string') {
|
|
var parsed = void 0;
|
|
if (val.trim() === '' || isNaN(val)) {
|
|
parsed = val === 'true' ? true : val === 'false' ? false : val;
|
|
}
|
|
else {
|
|
if (val.indexOf('0x') !== -1) {
|
|
//support hexa decimal
|
|
parsed = Number.parseInt(val, 16);
|
|
}
|
|
else if (val.indexOf('.') !== -1) {
|
|
parsed = Number.parseFloat(val);
|
|
}
|
|
else {
|
|
parsed = Number.parseInt(val, 10);
|
|
}
|
|
if (parseTrueNumberOnly) {
|
|
parsed = String(parsed) === val ? parsed : val;
|
|
}
|
|
}
|
|
return parsed;
|
|
}
|
|
else {
|
|
if (util.isExist(val)) {
|
|
return val;
|
|
}
|
|
else {
|
|
return '';
|
|
}
|
|
}
|
|
}
|
|
//TODO: change regex to capture NS
|
|
//const attrsRegx = new RegExp("([\\w\\-\\.\\:]+)\\s*=\\s*(['\"])((.|\n)*?)\\2","gm");
|
|
var attrsRegx = new RegExp('([^\\s=]+)\\s*(=\\s*([\'"])(.*?)\\3)?', 'g');
|
|
function buildAttributesMap(attrStr, options) {
|
|
if (!options.ignoreAttributes && typeof attrStr === 'string') {
|
|
attrStr = attrStr.replace(/\r?\n/g, ' ');
|
|
//attrStr = attrStr || attrStr.trim();
|
|
var matches = util.getAllMatches(attrStr, attrsRegx);
|
|
var len = matches.length; //don't make it inline
|
|
var attrs = {};
|
|
for (var i = 0; i < len; i++) {
|
|
var attrName = resolveNameSpace(matches[i][1], options);
|
|
if (attrName.length) {
|
|
if (matches[i][4] !== undefined) {
|
|
if (options.trimValues) {
|
|
matches[i][4] = matches[i][4].trim();
|
|
}
|
|
matches[i][4] = options.attrValueProcessor(matches[i][4], attrName);
|
|
attrs[options.attributeNamePrefix + attrName] = parseValue(matches[i][4], options.parseAttributeValue, options.parseTrueNumberOnly);
|
|
}
|
|
else if (options.allowBooleanAttributes) {
|
|
attrs[options.attributeNamePrefix + attrName] = true;
|
|
}
|
|
}
|
|
}
|
|
if (!Object.keys(attrs).length) {
|
|
return;
|
|
}
|
|
if (options.attrNodeName) {
|
|
var attrCollection = {};
|
|
attrCollection[options.attrNodeName] = attrs;
|
|
return attrCollection;
|
|
}
|
|
return attrs;
|
|
}
|
|
}
|
|
exports.getTraversalObj = getTraversalObj;
|
|
|
|
|
|
/***/ }),
|
|
/* 15 */
|
|
/***/ ((module) => {
|
|
|
|
"use strict";
|
|
|
|
module.exports = function (tagname, parent, val) {
|
|
this.tagname = tagname;
|
|
this.parent = parent;
|
|
this.child = {}; //child tags
|
|
this.attrsMap = {}; //attributes map
|
|
this.children = [];
|
|
this.val = val; //text only
|
|
this.addChild = function (child) {
|
|
this.children.push(child);
|
|
if (Array.isArray(this.child[child.tagname])) {
|
|
//already presents
|
|
this.child[child.tagname].push(child);
|
|
}
|
|
else {
|
|
this.child[child.tagname] = [child];
|
|
}
|
|
};
|
|
};
|
|
|
|
|
|
/***/ }),
|
|
/* 16 */
|
|
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
|
|
var util = __webpack_require__(13);
|
|
var defaultOptions = {
|
|
allowBooleanAttributes: false, //A tag can have attributes without any value
|
|
localeRange: 'a-zA-Z',
|
|
};
|
|
var props = ['allowBooleanAttributes', 'localeRange'];
|
|
//const tagsPattern = new RegExp("<\\/?([\\w:\\-_\.]+)\\s*\/?>","g");
|
|
exports.validate = function (xmlData, options) {
|
|
options = util.buildOptions(options, defaultOptions, props);
|
|
//xmlData = xmlData.replace(/(\r\n|\n|\r)/gm,"");//make it single line
|
|
//xmlData = xmlData.replace(/(^\s*<\?xml.*?\?>)/g,"");//Remove XML starting tag
|
|
//xmlData = xmlData.replace(/(<!DOCTYPE[\s\w\"\.\/\-\:]+(\[.*\])*\s*>)/g,"");//Remove DOCTYPE
|
|
var tags = [];
|
|
var tagFound = false;
|
|
if (xmlData[0] === '\ufeff') {
|
|
// check for byte order mark (BOM)
|
|
xmlData = xmlData.substr(1);
|
|
}
|
|
var regxAttrName = new RegExp('^[_w][\\w\\-.:]*$'.replace('_w', '_' + options.localeRange));
|
|
var regxTagName = new RegExp('^([w]|_)[\\w.\\-_:]*'.replace('([w', '([' + options.localeRange));
|
|
for (var i = 0; i < xmlData.length; i++) {
|
|
if (xmlData[i] === '<') {
|
|
//starting of tag
|
|
//read until you reach to '>' avoiding any '>' in attribute value
|
|
i++;
|
|
if (xmlData[i] === '?') {
|
|
i = readPI(xmlData, ++i);
|
|
if (i.err) {
|
|
return i;
|
|
}
|
|
}
|
|
else if (xmlData[i] === '!') {
|
|
i = readCommentAndCDATA(xmlData, i);
|
|
continue;
|
|
}
|
|
else {
|
|
var closingTag = false;
|
|
if (xmlData[i] === '/') {
|
|
//closing tag
|
|
closingTag = true;
|
|
i++;
|
|
}
|
|
//read tagname
|
|
var tagName = '';
|
|
for (; i < xmlData.length &&
|
|
xmlData[i] !== '>' &&
|
|
xmlData[i] !== ' ' &&
|
|
xmlData[i] !== '\t' &&
|
|
xmlData[i] !== '\n' &&
|
|
xmlData[i] !== '\r'; i++) {
|
|
tagName += xmlData[i];
|
|
}
|
|
tagName = tagName.trim();
|
|
//console.log(tagName);
|
|
if (tagName[tagName.length - 1] === '/') {
|
|
//self closing tag without attributes
|
|
tagName = tagName.substring(0, tagName.length - 1);
|
|
continue;
|
|
}
|
|
if (!validateTagName(tagName, regxTagName)) {
|
|
return { err: { code: 'InvalidTag', msg: 'Tag ' + tagName + ' is an invalid name.' } };
|
|
}
|
|
var result = readAttributeStr(xmlData, i);
|
|
if (result === false) {
|
|
return { err: { code: 'InvalidAttr', msg: 'Attributes for "' + tagName + '" have open quote.' } };
|
|
}
|
|
var attrStr = result.value;
|
|
i = result.index;
|
|
if (attrStr[attrStr.length - 1] === '/') {
|
|
//self closing tag
|
|
attrStr = attrStr.substring(0, attrStr.length - 1);
|
|
var isValid = validateAttributeString(attrStr, options, regxAttrName);
|
|
if (isValid === true) {
|
|
tagFound = true;
|
|
//continue; //text may presents after self closing tag
|
|
}
|
|
else {
|
|
return isValid;
|
|
}
|
|
}
|
|
else if (closingTag) {
|
|
if (!result.tagClosed) {
|
|
return {
|
|
err: { code: 'InvalidTag', msg: 'closing tag "' + tagName + "\" don't have proper closing." },
|
|
};
|
|
}
|
|
else if (attrStr.trim().length > 0) {
|
|
return {
|
|
err: { code: 'InvalidTag', msg: 'closing tag "' + tagName + "\" can't have attributes or invalid starting." },
|
|
};
|
|
}
|
|
else {
|
|
var otg = tags.pop();
|
|
if (tagName !== otg) {
|
|
return {
|
|
err: { code: 'InvalidTag', msg: 'closing tag ' + otg + ' is expected inplace of ' + tagName + '.' },
|
|
};
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
var isValid = validateAttributeString(attrStr, options, regxAttrName);
|
|
if (isValid !== true) {
|
|
return isValid;
|
|
}
|
|
tags.push(tagName);
|
|
tagFound = true;
|
|
}
|
|
//skip tag text value
|
|
//It may include comments and CDATA value
|
|
for (i++; i < xmlData.length; i++) {
|
|
if (xmlData[i] === '<') {
|
|
if (xmlData[i + 1] === '!') {
|
|
//comment or CADATA
|
|
i++;
|
|
i = readCommentAndCDATA(xmlData, i);
|
|
continue;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
} //end of reading tag text value
|
|
if (xmlData[i] === '<') {
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (xmlData[i] === ' ' || xmlData[i] === '\t' || xmlData[i] === '\n' || xmlData[i] === '\r') {
|
|
continue;
|
|
}
|
|
return { err: { code: 'InvalidChar', msg: 'char ' + xmlData[i] + ' is not expected .' } };
|
|
}
|
|
}
|
|
if (!tagFound) {
|
|
return { err: { code: 'InvalidXml', msg: 'Start tag expected.' } };
|
|
}
|
|
else if (tags.length > 0) {
|
|
return {
|
|
err: { code: 'InvalidXml', msg: 'Invalid ' + JSON.stringify(tags, null, 4).replace(/\r?\n/g, '') + ' found.' },
|
|
};
|
|
}
|
|
return true;
|
|
};
|
|
/**
|
|
* Read Processing insstructions and skip
|
|
* @param {*} xmlData
|
|
* @param {*} i
|
|
*/
|
|
function readPI(xmlData, i) {
|
|
var start = i;
|
|
for (; i < xmlData.length; i++) {
|
|
if (xmlData[i] == '?' || xmlData[i] == ' ') {
|
|
//tagname
|
|
var tagname = xmlData.substr(start, i - start);
|
|
if (i > 5 && tagname === 'xml') {
|
|
return { err: { code: 'InvalidXml', msg: 'XML declaration allowed only at the start of the document.' } };
|
|
}
|
|
else if (xmlData[i] == '?' && xmlData[i + 1] == '>') {
|
|
//check if valid attribut string
|
|
i++;
|
|
break;
|
|
}
|
|
else {
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
function readCommentAndCDATA(xmlData, i) {
|
|
if (xmlData.length > i + 5 && xmlData[i + 1] === '-' && xmlData[i + 2] === '-') {
|
|
//comment
|
|
for (i += 3; i < xmlData.length; i++) {
|
|
if (xmlData[i] === '-' && xmlData[i + 1] === '-' && xmlData[i + 2] === '>') {
|
|
i += 2;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (xmlData.length > i + 8 &&
|
|
xmlData[i + 1] === 'D' &&
|
|
xmlData[i + 2] === 'O' &&
|
|
xmlData[i + 3] === 'C' &&
|
|
xmlData[i + 4] === 'T' &&
|
|
xmlData[i + 5] === 'Y' &&
|
|
xmlData[i + 6] === 'P' &&
|
|
xmlData[i + 7] === 'E') {
|
|
var angleBracketsCount = 1;
|
|
for (i += 8; i < xmlData.length; i++) {
|
|
if (xmlData[i] === '<') {
|
|
angleBracketsCount++;
|
|
}
|
|
else if (xmlData[i] === '>') {
|
|
angleBracketsCount--;
|
|
if (angleBracketsCount === 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (xmlData.length > i + 9 &&
|
|
xmlData[i + 1] === '[' &&
|
|
xmlData[i + 2] === 'C' &&
|
|
xmlData[i + 3] === 'D' &&
|
|
xmlData[i + 4] === 'A' &&
|
|
xmlData[i + 5] === 'T' &&
|
|
xmlData[i + 6] === 'A' &&
|
|
xmlData[i + 7] === '[') {
|
|
for (i += 8; i < xmlData.length; i++) {
|
|
if (xmlData[i] === ']' && xmlData[i + 1] === ']' && xmlData[i + 2] === '>') {
|
|
i += 2;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
var doubleQuote = '"';
|
|
var singleQuote = "'";
|
|
/**
|
|
* Keep reading xmlData until '<' is found outside the attribute value.
|
|
* @param {string} xmlData
|
|
* @param {number} i
|
|
*/
|
|
function readAttributeStr(xmlData, i) {
|
|
var attrStr = '';
|
|
var startChar = '';
|
|
var tagClosed = false;
|
|
for (; i < xmlData.length; i++) {
|
|
if (xmlData[i] === doubleQuote || xmlData[i] === singleQuote) {
|
|
if (startChar === '') {
|
|
startChar = xmlData[i];
|
|
}
|
|
else if (startChar !== xmlData[i]) {
|
|
//if vaue is enclosed with double quote then single quotes are allowed inside the value and vice versa
|
|
continue;
|
|
}
|
|
else {
|
|
startChar = '';
|
|
}
|
|
}
|
|
else if (xmlData[i] === '>') {
|
|
if (startChar === '') {
|
|
tagClosed = true;
|
|
break;
|
|
}
|
|
}
|
|
attrStr += xmlData[i];
|
|
}
|
|
if (startChar !== '') {
|
|
return false;
|
|
}
|
|
return { value: attrStr, index: i, tagClosed: tagClosed };
|
|
}
|
|
/**
|
|
* Select all the attributes whether valid or invalid.
|
|
*/
|
|
var validAttrStrRegxp = new RegExp('(\\s*)([^\\s=]+)(\\s*=)?(\\s*([\'"])(([\\s\\S])*?)\\5)?', 'g');
|
|
//attr, ="sd", a="amit's", a="sd"b="saf", ab cd=""
|
|
function validateAttributeString(attrStr, options, regxAttrName) {
|
|
//console.log("start:"+attrStr+":end");
|
|
//if(attrStr.trim().length === 0) return true; //empty string
|
|
var matches = util.getAllMatches(attrStr, validAttrStrRegxp);
|
|
var attrNames = {};
|
|
for (var i = 0; i < matches.length; i++) {
|
|
//console.log(matches[i]);
|
|
if (matches[i][1].length === 0) {
|
|
//nospace before attribute name: a="sd"b="saf"
|
|
return { err: { code: 'InvalidAttr', msg: 'attribute ' + matches[i][2] + ' has no space in starting.' } };
|
|
}
|
|
else if (matches[i][3] === undefined && !options.allowBooleanAttributes) {
|
|
//independent attribute: ab
|
|
return { err: { code: 'InvalidAttr', msg: 'boolean attribute ' + matches[i][2] + ' is not allowed.' } };
|
|
}
|
|
/* else if(matches[i][6] === undefined){//attribute without value: ab=
|
|
return { err: { code:"InvalidAttr",msg:"attribute " + matches[i][2] + " has no value assigned."}};
|
|
} */
|
|
var attrName = matches[i][2];
|
|
if (!validateAttrName(attrName, regxAttrName)) {
|
|
return { err: { code: 'InvalidAttr', msg: 'attribute ' + attrName + ' is an invalid name.' } };
|
|
}
|
|
/*if (!attrNames.hasOwnProperty(attrName)) {*/
|
|
if (!Object.prototype.hasOwnProperty.call(attrNames, attrName)) {
|
|
//check for duplicate attribute.
|
|
attrNames[attrName] = 1;
|
|
}
|
|
else {
|
|
return { err: { code: 'InvalidAttr', msg: 'attribute ' + attrName + ' is repeated.' } };
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
// const validAttrRegxp = /^[_a-zA-Z][\w\-.:]*$/;
|
|
function validateAttrName(attrName, regxAttrName) {
|
|
// const validAttrRegxp = new RegExp(regxAttrName);
|
|
return util.doesMatch(attrName, regxAttrName);
|
|
}
|
|
//const startsWithXML = new RegExp("^[Xx][Mm][Ll]");
|
|
// startsWith = /^([a-zA-Z]|_)[\w.\-_:]*/;
|
|
function validateTagName(tagname, regxTagName) {
|
|
/*if(util.doesMatch(tagname,startsWithXML)) return false;
|
|
else*/
|
|
return !util.doesNotMatch(tagname, regxTagName);
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
/* 17 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
|
|
/* harmony export */ });
|
|
/* harmony import */ var _imageManager__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(5);
|
|
/* harmony import */ var tiny_emitter__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(8);
|
|
/* harmony import */ var tiny_emitter__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(tiny_emitter__WEBPACK_IMPORTED_MODULE_1__);
|
|
|
|
|
|
/**
|
|
* http://www.angelcode.com/products/bmfont/doc/file_format.html
|
|
*/
|
|
var BitMapFont = /** @class */ (function () {
|
|
// pool的实现放到类里面实现并不优雅,先去掉了
|
|
function BitMapFont(name, src, config) {
|
|
var _this = this;
|
|
this.ready = false;
|
|
this.config = config;
|
|
this.chars = this.parseConfig(config);
|
|
this.event = new (tiny_emitter__WEBPACK_IMPORTED_MODULE_1___default().TinyEmitter)();
|
|
this.texture = _imageManager__WEBPACK_IMPORTED_MODULE_0__["default"].loadImage(src, function (texture, fromCache) {
|
|
if (fromCache) {
|
|
_this.texture = texture;
|
|
}
|
|
_this.ready = true;
|
|
_this.event.emit('text__load__done');
|
|
});
|
|
}
|
|
BitMapFont.prototype.parseConfig = function (fntText) {
|
|
fntText = fntText.split('\r\n').join('\n');
|
|
var lines = fntText.split('\n');
|
|
var linesParsed = lines.map(function (line) { return line.trim().split(' '); });
|
|
var charsLine = this.getConfigByLineName(linesParsed, 'chars');
|
|
var charsCount = this.getConfigByKeyInOneLine(charsLine.line, 'count');
|
|
var commonLine = this.getConfigByLineName(linesParsed, 'common');
|
|
this.lineHeight = this.getConfigByKeyInOneLine(commonLine.line, 'lineHeight');
|
|
var infoLine = this.getConfigByLineName(linesParsed, 'info');
|
|
this.fontSize = this.getConfigByKeyInOneLine(infoLine.line, 'size');
|
|
// 接卸 kernings
|
|
var kerningsLine = this.getConfigByLineName(linesParsed, 'kernings');
|
|
var kerningsCount = 0;
|
|
var kerningsStart = -1;
|
|
if (kerningsLine.line) {
|
|
kerningsCount = this.getConfigByKeyInOneLine(kerningsLine.line, 'count');
|
|
kerningsStart = kerningsLine.index + 1;
|
|
}
|
|
var chars = {};
|
|
for (var i = 4; i < 4 + charsCount; i++) {
|
|
var charText = lines[i];
|
|
var letter = String.fromCharCode(this.getConfigByKeyInOneLine(charText, 'id'));
|
|
var c = {
|
|
x: this.getConfigByKeyInOneLine(charText, 'x'),
|
|
y: this.getConfigByKeyInOneLine(charText, 'y'),
|
|
w: this.getConfigByKeyInOneLine(charText, 'width'),
|
|
h: this.getConfigByKeyInOneLine(charText, 'height'),
|
|
offX: this.getConfigByKeyInOneLine(charText, 'xoffset'),
|
|
offY: this.getConfigByKeyInOneLine(charText, 'yoffset'),
|
|
xadvance: this.getConfigByKeyInOneLine(charText, 'xadvance'),
|
|
kerning: {}
|
|
};
|
|
chars[letter] = c;
|
|
}
|
|
// parse kernings
|
|
if (kerningsCount) {
|
|
for (var i = kerningsStart; i <= kerningsStart + kerningsCount; i++) {
|
|
var line = linesParsed[i];
|
|
var first = String.fromCharCode(this.getConfigByKeyInOneLine(line, 'first'));
|
|
var second = String.fromCharCode(this.getConfigByKeyInOneLine(line, 'second'));
|
|
var amount = this.getConfigByKeyInOneLine(line, 'amount');
|
|
if (chars[second]) {
|
|
chars[second].kerning[first] = amount;
|
|
}
|
|
}
|
|
}
|
|
return chars;
|
|
};
|
|
BitMapFont.prototype.getConfigByLineName = function (linesParsed, lineName) {
|
|
if (lineName === void 0) { lineName = ''; }
|
|
var index = -1;
|
|
var line = [];
|
|
var len = linesParsed.length;
|
|
for (var i = 0; i < len; i++) {
|
|
var item = linesParsed[i];
|
|
if (item[0] === lineName) {
|
|
index = i;
|
|
line = item;
|
|
}
|
|
}
|
|
return {
|
|
line: line,
|
|
index: index,
|
|
};
|
|
};
|
|
BitMapFont.prototype.getConfigByKeyInOneLine = function (configText, key) {
|
|
var itemConfigTextList = Array.isArray(configText) ? configText : configText.split(' ');
|
|
for (var i = 0, length_1 = itemConfigTextList.length; i < length_1; i++) {
|
|
var itemConfigText = itemConfigTextList[i];
|
|
if (key === itemConfigText.substring(0, key.length)) {
|
|
var value = itemConfigText.substring(key.length + 1);
|
|
return parseInt(value);
|
|
}
|
|
}
|
|
return 0;
|
|
};
|
|
return BitMapFont;
|
|
}());
|
|
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (BitMapFont);
|
|
|
|
|
|
/***/ }),
|
|
/* 18 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
|
|
/* harmony export */ });
|
|
var DebugInfo = /** @class */ (function () {
|
|
function DebugInfo() {
|
|
this.info = {};
|
|
this.totalStart = 0;
|
|
this.totalCost = 0;
|
|
this.reset();
|
|
}
|
|
DebugInfo.prototype.start = function (name, isInner) {
|
|
if (isInner === void 0) { isInner = false; }
|
|
if (this.totalStart === 0) {
|
|
this.totalStart = Date.now();
|
|
}
|
|
this.info[name] = {
|
|
start: Date.now(),
|
|
isInner: isInner,
|
|
};
|
|
};
|
|
DebugInfo.prototype.end = function (name) {
|
|
if (this.info[name]) {
|
|
this.info[name].end = Date.now();
|
|
this.info[name].cost = this.info[name].end - this.info[name].start;
|
|
this.totalCost = this.info[name].end - this.totalStart;
|
|
}
|
|
};
|
|
DebugInfo.prototype.reset = function () {
|
|
this.info = {};
|
|
this.totalStart = 0;
|
|
this.totalCost = 0;
|
|
};
|
|
DebugInfo.prototype.log = function (needInner) {
|
|
var _this = this;
|
|
if (needInner === void 0) { needInner = false; }
|
|
var logInfo = 'Layout debug info: \n';
|
|
logInfo += Object.keys(this.info).reduce(function (sum, curr) {
|
|
if (_this.info[curr].isInner && !needInner) {
|
|
return sum;
|
|
}
|
|
sum += "".concat(curr, ": ").concat(_this.info[curr].cost, "\n");
|
|
return sum;
|
|
}, '');
|
|
logInfo += "totalCost: ".concat(this.totalCost, "\n");
|
|
return logInfo;
|
|
};
|
|
return DebugInfo;
|
|
}());
|
|
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (DebugInfo);
|
|
|
|
|
|
/***/ }),
|
|
/* 19 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
|
|
/* harmony export */ });
|
|
var Ticker = /** @class */ (function () {
|
|
function Ticker() {
|
|
var _this = this;
|
|
this.count = 0;
|
|
this.started = false;
|
|
this.animationId = null;
|
|
this.cbs = [];
|
|
this.innerCbs = [];
|
|
this.nextCbs = [];
|
|
this.innerNextCbs = [];
|
|
this.update = function () {
|
|
var time = Date.now();
|
|
var deltaTime = time - _this.lastTime;
|
|
_this.lastTime = time;
|
|
// console.log(dt)
|
|
// 优先执行业务的ticker回调,因为有可能会触发reflow
|
|
_this.cbs.forEach(function (cb) {
|
|
cb(deltaTime);
|
|
});
|
|
_this.innerCbs.forEach(function (cb) {
|
|
cb(deltaTime);
|
|
});
|
|
if (_this.innerNextCbs.length) {
|
|
_this.innerNextCbs.forEach(function (cb) { return cb(deltaTime); });
|
|
_this.innerNextCbs = [];
|
|
}
|
|
if (_this.nextCbs.length) {
|
|
_this.nextCbs.forEach(function (cb) { return cb(deltaTime); });
|
|
_this.nextCbs = [];
|
|
}
|
|
_this.count += 1;
|
|
_this.animationId = requestAnimationFrame(_this.update);
|
|
};
|
|
}
|
|
Ticker.prototype.cancelIfNeed = function () {
|
|
if (this.animationId !== null) {
|
|
cancelAnimationFrame(this.animationId);
|
|
this.animationId = null;
|
|
}
|
|
};
|
|
Ticker.prototype.add = function (cb, isInner) {
|
|
if (isInner === void 0) { isInner = false; }
|
|
if (typeof cb === 'function' && this.cbs.indexOf(cb) === -1) {
|
|
isInner ? this.innerCbs.push(cb) : this.cbs.push(cb);
|
|
}
|
|
};
|
|
Ticker.prototype.next = function (cb, isInner) {
|
|
if (isInner === void 0) { isInner = false; }
|
|
if (typeof cb === 'function') {
|
|
isInner ? this.innerNextCbs.push(cb) : this.nextCbs.push(cb);
|
|
}
|
|
};
|
|
Ticker.prototype.removeInner = function () {
|
|
this.innerCbs = [];
|
|
this.innerNextCbs = [];
|
|
};
|
|
Ticker.prototype.remove = function (cb, isInner) {
|
|
if (isInner === void 0) { isInner = false; }
|
|
if (cb === undefined) {
|
|
this.cbs = [];
|
|
this.innerCbs = [];
|
|
this.nextCbs = [];
|
|
this.innerNextCbs = [];
|
|
}
|
|
if (typeof cb === 'function' && (this.cbs.indexOf(cb) > -1 || this.innerCbs.indexOf(cb) > -1)) {
|
|
var list = isInner ? this.innerCbs : this.cbs;
|
|
list.splice(this.cbs.indexOf(cb), 1);
|
|
}
|
|
if (!this.cbs.length && !this.innerCbs.length) {
|
|
this.cancelIfNeed();
|
|
}
|
|
};
|
|
Ticker.prototype.start = function () {
|
|
if (!this.started) {
|
|
this.started = true;
|
|
this.lastTime = Date.now();
|
|
if (this.animationId === null && (this.cbs.length || this.innerCbs.length)) {
|
|
this.animationId = requestAnimationFrame(this.update);
|
|
}
|
|
}
|
|
};
|
|
Ticker.prototype.stop = function () {
|
|
if (this.started) {
|
|
this.started = false;
|
|
this.cancelIfNeed();
|
|
}
|
|
};
|
|
return Ticker;
|
|
}());
|
|
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Ticker);
|
|
|
|
|
|
/***/ }),
|
|
/* 20 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ clone: () => (/* binding */ clone),
|
|
/* harmony export */ create: () => (/* binding */ create),
|
|
/* harmony export */ iterateTree: () => (/* binding */ iterateTree),
|
|
/* harmony export */ layoutChildren: () => (/* binding */ layoutChildren),
|
|
/* harmony export */ registerComponent: () => (/* binding */ registerComponent),
|
|
/* harmony export */ renderChildren: () => (/* binding */ renderChildren),
|
|
/* harmony export */ repaintChildren: () => (/* binding */ repaintChildren),
|
|
/* harmony export */ repaintTree: () => (/* binding */ repaintTree)
|
|
/* harmony export */ });
|
|
/* harmony import */ var _components_index__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(21);
|
|
/* harmony import */ var _util__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(7);
|
|
/* harmony import */ var _env__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(1);
|
|
/* eslint-disable no-param-reassign */
|
|
// components
|
|
|
|
|
|
|
|
var constructorMap = {
|
|
view: _components_index__WEBPACK_IMPORTED_MODULE_0__.View,
|
|
text: _components_index__WEBPACK_IMPORTED_MODULE_0__.Text,
|
|
image: _components_index__WEBPACK_IMPORTED_MODULE_0__.Image,
|
|
scrollview: _components_index__WEBPACK_IMPORTED_MODULE_0__.ScrollView,
|
|
bitmaptext: _components_index__WEBPACK_IMPORTED_MODULE_0__.BitMapText,
|
|
canvas: _components_index__WEBPACK_IMPORTED_MODULE_0__.Canvas,
|
|
button: _components_index__WEBPACK_IMPORTED_MODULE_0__.Button,
|
|
};
|
|
function registerComponent(name, Constructor) {
|
|
constructorMap[name] = Constructor;
|
|
}
|
|
function create(node, style, parent) {
|
|
var _this = this;
|
|
var Constructor = constructorMap[node.name];
|
|
if (!Constructor) {
|
|
console.error("[Layout] \u4E0D\u652F\u6301\u7EC4\u4EF6 ".concat(node.name));
|
|
return null;
|
|
}
|
|
var children = node.children || [];
|
|
var attr = node.attr || {};
|
|
var dataset = {};
|
|
var id = attr.id || '';
|
|
var args = Object.keys(attr).reduce(function (obj, key) {
|
|
var value = attr[key];
|
|
var attribute = key;
|
|
if (key === 'id') {
|
|
obj.style = Object.assign(obj.style || {}, style[id] || {});
|
|
return obj;
|
|
}
|
|
if (key === 'class') {
|
|
obj.style = value.split(/\s+/).reduce(function (res, oneClass) { return Object.assign(res, style[oneClass]); }, obj.style || {});
|
|
return obj;
|
|
}
|
|
if (value === 'true') {
|
|
obj[attribute] = true;
|
|
}
|
|
else if (value === 'false') {
|
|
obj[attribute] = false;
|
|
}
|
|
else {
|
|
obj[attribute] = value;
|
|
}
|
|
if (attribute.startsWith('data-')) {
|
|
var dataKey = attribute.substring(5);
|
|
dataset[dataKey] = value;
|
|
}
|
|
obj.dataset = dataset;
|
|
return obj;
|
|
}, {});
|
|
// 用于后续元素查询
|
|
args.idName = id;
|
|
// @ts-ignore
|
|
this.eleCount += 1;
|
|
// @ts-ignore
|
|
args.id = this.eleCount;
|
|
args.className = attr.class || '';
|
|
var thisStyle = args.style;
|
|
if (thisStyle) {
|
|
var parentStyle = void 0;
|
|
if (parent) {
|
|
parentStyle = parent.style;
|
|
}
|
|
else {
|
|
parentStyle = _env__WEBPACK_IMPORTED_MODULE_2__["default"].getRootCanvasSize();
|
|
}
|
|
if ((0,_util__WEBPACK_IMPORTED_MODULE_1__.isPercent)(thisStyle.width)) {
|
|
thisStyle.width = parentStyle.width ? (0,_util__WEBPACK_IMPORTED_MODULE_1__.convertPercent)(thisStyle.width, parentStyle.width) : 0;
|
|
}
|
|
if ((0,_util__WEBPACK_IMPORTED_MODULE_1__.isPercent)(thisStyle.height)) {
|
|
thisStyle.height = parentStyle.height ? (0,_util__WEBPACK_IMPORTED_MODULE_1__.convertPercent)(thisStyle.height, parentStyle.height) : 0;
|
|
}
|
|
if (typeof thisStyle.opacity === 'undefined') {
|
|
thisStyle.opacity = 1;
|
|
}
|
|
if (parentStyle && parentStyle.opacity !== 1 && typeof parentStyle.opacity === 'number') {
|
|
thisStyle.opacity = parentStyle.opacity * thisStyle.opacity;
|
|
}
|
|
}
|
|
// console.log(args);
|
|
var element = new Constructor(args);
|
|
// @ts-ignore
|
|
element.root = this;
|
|
element.tagName = node.name;
|
|
element.afterCreate();
|
|
children.forEach(function (childNode) {
|
|
// @ts-ignore
|
|
var childElement = create.call(_this, childNode, style, args);
|
|
if (childElement) {
|
|
element.add(childElement);
|
|
}
|
|
});
|
|
return element;
|
|
}
|
|
function renderChildren(children, context, needRender) {
|
|
if (needRender === void 0) { needRender = true; }
|
|
children.forEach(function (child) {
|
|
// child.shouldUpdate = false;
|
|
child.isDirty = false;
|
|
child.insert(context, needRender);
|
|
// ScrollView的子节点渲染交给ScrollView自己,不支持嵌套ScrollView
|
|
return renderChildren(child.children, context, child.type === 'ScrollView' ? false : needRender);
|
|
});
|
|
}
|
|
/**
|
|
* 将布局树的布局信息加工赋值到渲染树
|
|
*/
|
|
function layoutChildren(element) {
|
|
element.children.forEach(function (child) {
|
|
child.layoutBox = child.layoutBox || {};
|
|
['left', 'top', 'width', 'height'].forEach(function (prop) {
|
|
var _a;
|
|
// @ts-ignore
|
|
child.layoutBox[prop] = (_a = child.layout) === null || _a === void 0 ? void 0 : _a[prop];
|
|
});
|
|
if (child.parent) {
|
|
child.layoutBox.absoluteX = (child.parent.layoutBox.absoluteX || 0) + child.layoutBox.left;
|
|
child.layoutBox.absoluteY = (child.parent.layoutBox.absoluteY || 0) + child.layoutBox.top;
|
|
}
|
|
else {
|
|
child.layoutBox.absoluteX = child.layoutBox.left;
|
|
child.layoutBox.absoluteY = child.layoutBox.top;
|
|
}
|
|
child.layoutBox.originalAbsoluteY = child.layoutBox.absoluteY;
|
|
child.layoutBox.originalAbsoluteX = child.layoutBox.absoluteX;
|
|
layoutChildren(child);
|
|
});
|
|
}
|
|
function none() { }
|
|
function iterateTree(element, callback) {
|
|
if (callback === void 0) { callback = none; }
|
|
callback(element);
|
|
element.children.forEach(function (child) {
|
|
iterateTree(child, callback);
|
|
});
|
|
}
|
|
var repaintChildren = function (children) {
|
|
children.forEach(function (child) {
|
|
child.repaint();
|
|
if (child.type !== 'ScrollView') {
|
|
repaintChildren(child.children);
|
|
}
|
|
});
|
|
};
|
|
var repaintTree = function (tree) {
|
|
tree.repaint();
|
|
tree.children.forEach(function (child) {
|
|
child.repaint();
|
|
repaintTree(child);
|
|
});
|
|
};
|
|
function clone(root, element, deep, parent) {
|
|
if (deep === void 0) { deep = true; }
|
|
var Constructor = constructorMap[element.tagName];
|
|
// @ts-ignore
|
|
root.eleCount += 1;
|
|
var args = {
|
|
style: Object.assign({}, element.style),
|
|
idName: element.idName,
|
|
className: element.className,
|
|
// @ts-ignore
|
|
id: root.eleCount,
|
|
dataset: Object.assign({}, element.dataset),
|
|
name: element.tagName,
|
|
};
|
|
if (element instanceof _components_index__WEBPACK_IMPORTED_MODULE_0__.Image) {
|
|
args.src = element.src;
|
|
}
|
|
else if (element instanceof _components_index__WEBPACK_IMPORTED_MODULE_0__.Text || element instanceof _components_index__WEBPACK_IMPORTED_MODULE_0__.BitMapText) {
|
|
args.value = element.value;
|
|
}
|
|
var newElemenet = new Constructor(args);
|
|
newElemenet.root = root;
|
|
// @ts-ignore
|
|
newElemenet.insert(root.renderContext, false);
|
|
newElemenet.observeStyleAndEvent();
|
|
if (parent) {
|
|
parent.add(newElemenet);
|
|
}
|
|
if (deep) {
|
|
element.children.forEach(function (child) {
|
|
clone(root, child, deep, newElemenet);
|
|
});
|
|
}
|
|
return newElemenet;
|
|
}
|
|
|
|
|
|
/***/ }),
|
|
/* 21 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ BitMapText: () => (/* reexport safe */ _bitmaptext__WEBPACK_IMPORTED_MODULE_4__["default"]),
|
|
/* harmony export */ Button: () => (/* reexport safe */ _button__WEBPACK_IMPORTED_MODULE_7__["default"]),
|
|
/* harmony export */ Canvas: () => (/* reexport safe */ _canvas__WEBPACK_IMPORTED_MODULE_5__["default"]),
|
|
/* harmony export */ Element: () => (/* reexport safe */ _elements__WEBPACK_IMPORTED_MODULE_6__["default"]),
|
|
/* harmony export */ Image: () => (/* reexport safe */ _image__WEBPACK_IMPORTED_MODULE_1__["default"]),
|
|
/* harmony export */ ScrollView: () => (/* reexport safe */ _scrollview__WEBPACK_IMPORTED_MODULE_3__["default"]),
|
|
/* harmony export */ Text: () => (/* reexport safe */ _text__WEBPACK_IMPORTED_MODULE_2__["default"]),
|
|
/* harmony export */ View: () => (/* reexport safe */ _view__WEBPACK_IMPORTED_MODULE_0__["default"])
|
|
/* harmony export */ });
|
|
/* harmony import */ var _view__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(22);
|
|
/* harmony import */ var _image__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(23);
|
|
/* harmony import */ var _text__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(24);
|
|
/* harmony import */ var _scrollview__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(25);
|
|
/* harmony import */ var _bitmaptext__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(29);
|
|
/* harmony import */ var _canvas__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(30);
|
|
/* harmony import */ var _elements__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(2);
|
|
/* harmony import */ var _button__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(31);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***/ }),
|
|
/* 22 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
|
|
/* harmony export */ });
|
|
/* harmony import */ var _elements__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
|
|
var __extends = (undefined && undefined.__extends) || (function () {
|
|
var extendStatics = function (d, b) {
|
|
extendStatics = Object.setPrototypeOf ||
|
|
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
return extendStatics(d, b);
|
|
};
|
|
return function (d, b) {
|
|
if (typeof b !== "function" && b !== null)
|
|
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
extendStatics(d, b);
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
})();
|
|
|
|
var View = /** @class */ (function (_super) {
|
|
__extends(View, _super);
|
|
function View(_a) {
|
|
var _b = _a.style, style = _b === void 0 ? {} : _b, _c = _a.idName, idName = _c === void 0 ? '' : _c, _d = _a.className, className = _d === void 0 ? '' : _d, dataset = _a.dataset;
|
|
var _this = _super.call(this, {
|
|
idName: idName,
|
|
className: className,
|
|
style: style,
|
|
dataset: dataset,
|
|
}) || this;
|
|
_this.type = 'View';
|
|
_this.ctx = null;
|
|
return _this;
|
|
}
|
|
View.prototype.destroySelf = function () {
|
|
this.isDestroyed = true;
|
|
this.children = [];
|
|
this.root = null;
|
|
};
|
|
View.prototype.render = function () {
|
|
var ctx = this.ctx;
|
|
ctx.save();
|
|
var _a = this.baseRender(), needStroke = _a.needStroke, needClip = _a.needClip, originX = _a.originX, originY = _a.originY;
|
|
if (needStroke) {
|
|
ctx.stroke();
|
|
}
|
|
ctx.translate(-originX, -originY);
|
|
ctx.restore();
|
|
};
|
|
View.prototype.repaint = function () {
|
|
this.render();
|
|
};
|
|
return View;
|
|
}(_elements__WEBPACK_IMPORTED_MODULE_0__["default"]));
|
|
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (View);
|
|
|
|
|
|
/***/ }),
|
|
/* 23 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
|
|
/* harmony export */ });
|
|
/* harmony import */ var _elements__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
|
|
/* harmony import */ var _common_imageManager__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5);
|
|
var __extends = (undefined && undefined.__extends) || (function () {
|
|
var extendStatics = function (d, b) {
|
|
extendStatics = Object.setPrototypeOf ||
|
|
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
return extendStatics(d, b);
|
|
};
|
|
return function (d, b) {
|
|
if (typeof b !== "function" && b !== null)
|
|
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
extendStatics(d, b);
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
})();
|
|
|
|
|
|
var Image = /** @class */ (function (_super) {
|
|
__extends(Image, _super);
|
|
function Image(opts) {
|
|
var _this = this;
|
|
var _a = opts.style, style = _a === void 0 ? {} : _a, _b = opts.idName, idName = _b === void 0 ? '' : _b, _c = opts.className, className = _c === void 0 ? '' : _c, _d = opts.src, src = _d === void 0 ? '' : _d, dataset = opts.dataset;
|
|
_this = _super.call(this, {
|
|
idName: idName,
|
|
className: className,
|
|
dataset: dataset,
|
|
style: style,
|
|
}) || this;
|
|
_this.type = 'Image';
|
|
_this.imgsrc = src;
|
|
_this.img = _common_imageManager__WEBPACK_IMPORTED_MODULE_1__["default"].loadImage(_this.src, function (img, fromCache) {
|
|
var _a;
|
|
if (fromCache) {
|
|
_this.img = img;
|
|
}
|
|
else {
|
|
if (!_this.isDestroyed) {
|
|
_this.img = img;
|
|
// 当图片加载完成,实例可能已经被销毁了
|
|
(_a = _this.root) === null || _a === void 0 ? void 0 : _a.emit('repaint');
|
|
}
|
|
}
|
|
});
|
|
return _this;
|
|
}
|
|
Object.defineProperty(Image.prototype, "src", {
|
|
get: function () {
|
|
return this.imgsrc;
|
|
},
|
|
set: function (newValue) {
|
|
var _this = this;
|
|
if (newValue !== this.imgsrc) {
|
|
this.imgsrc = newValue;
|
|
_common_imageManager__WEBPACK_IMPORTED_MODULE_1__["default"].loadImage(this.src, function (img) {
|
|
var _a;
|
|
if (!_this.isDestroyed) {
|
|
_this.img = img;
|
|
// 当图片加载完成,实例可能已经被销毁了
|
|
(_a = _this.root) === null || _a === void 0 ? void 0 : _a.emit('repaint');
|
|
}
|
|
});
|
|
}
|
|
},
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
Image.prototype.repaint = function () {
|
|
this.render();
|
|
};
|
|
// 子类填充实现
|
|
Image.prototype.destroySelf = function () {
|
|
this.isDestroyed = true;
|
|
this.img = null;
|
|
this.src = '';
|
|
this.root = null;
|
|
};
|
|
Image.prototype.render = function () {
|
|
var _a;
|
|
if (!this.img || !((_a = this.img) === null || _a === void 0 ? void 0 : _a.complete)) {
|
|
return;
|
|
}
|
|
var ctx = this.ctx;
|
|
ctx.save();
|
|
var _b = this.baseRender(), needStroke = _b.needStroke, needClip = _b.needClip, originX = _b.originX, originY = _b.originY, drawX = _b.drawX, drawY = _b.drawY, width = _b.width, height = _b.height;
|
|
// 自定义渲染逻辑 开始
|
|
|
|
// ===
|
|
|
|
// 九宫格
|
|
if (this.style.slice) {
|
|
|
|
const [top, bottom, left, right] = this.style.slice.split(',').map(v => parseInt(v.trim()));
|
|
|
|
const drawWidth = width;
|
|
const drawHeight = height;
|
|
const imgW = this.img.width;
|
|
const imgH = this.img.height;
|
|
|
|
|
|
// 计算原始九宫格区域
|
|
const centerW = imgW - left - right; // 中间部分宽度
|
|
const centerH = imgH - top - bottom; // 中间部分高度
|
|
|
|
// 计算目标区域大小
|
|
const targetCenterW = drawWidth - left - right;
|
|
const targetCenterH = drawHeight - top - bottom;
|
|
|
|
// Scale9 格式: 9 个区域 (left, center, right) x (top, middle, bottom)
|
|
const parts = [
|
|
// sx, sy, sw, sh, dx, dy, dw, dh
|
|
// 左上角
|
|
[0, 0, left, top, drawX, drawY, left, top],
|
|
// 上边
|
|
[left, 0, centerW, top, drawX + left, drawY, targetCenterW, top],
|
|
// 右上角
|
|
[imgW - right, 0, right, top, drawX + left + targetCenterW, drawY, right, top],
|
|
|
|
// 左边
|
|
[0, top, left, centerH, drawX, drawY + top, left, targetCenterH],
|
|
// 中间
|
|
[left, top, centerW, centerH, drawX + left, drawY + top, targetCenterW, targetCenterH],
|
|
// 右边
|
|
[imgW - right, top, right, centerH, drawX + left + targetCenterW, drawY + top, right, targetCenterH],
|
|
|
|
// 左下角
|
|
[0, imgH - bottom, left, bottom, drawX, drawY + top + targetCenterH, left, bottom],
|
|
// 下边
|
|
[left, imgH - bottom, centerW, bottom, drawX + left, drawY + top + targetCenterH, targetCenterW, bottom],
|
|
// 右下角
|
|
[imgW - right, imgH - bottom, right, bottom, drawX + left + targetCenterW, drawY + top + targetCenterH, right, bottom],
|
|
];
|
|
|
|
// 绘制所有部分
|
|
parts.forEach(([sx, sy, sw, sh, dx, dy, dw, dh]) => {
|
|
ctx.drawImage(this.img, sx, sy, sw, sh, dx, dy, dw, dh);
|
|
});
|
|
}
|
|
// 放大缩小
|
|
else if (this.style.scale) {
|
|
const [scaleX, scaleY] = this.style.scale.split(',').map(v => parseFloat(v.trim()));
|
|
const offsetX = (1 - scaleX) / 2 * width;
|
|
const offsetY = (1 - scaleY) / 2 * height;
|
|
ctx.drawImage(this.img, drawX - originX + offsetX, drawY - originY + offsetY, width * scaleX, height * scaleY);
|
|
}
|
|
// 正常绘制
|
|
else {
|
|
ctx.drawImage(this.img, drawX - originX, drawY - originY, width, height);
|
|
}
|
|
|
|
// 颜色混合
|
|
if (this.style.color) {
|
|
const imageData = ctx.getImageData(0, 0, this.img.width, this.img.height);
|
|
// console.log('xxx', ctx, this.img);
|
|
// console.log('xxx', 'log');
|
|
// const [x, y, z, w] = this.style.color.split(',').map(v => parseInt(v.trim()) / 255);
|
|
// // 获取像素数据
|
|
// const imageData = ctx.getImageData(drawX - originX, drawY - originY, width, height);
|
|
// const data = imageData.data;
|
|
|
|
// // 遍历像素数据
|
|
// for (let i = 0; i < data.length; i += 4) {
|
|
// const r = data[i + 0];
|
|
// const g = data[i + 1];
|
|
// const b = data[i + 2];
|
|
// const a = data[i + 3];
|
|
// data[i + 0] = r * a + x * (1 - a);
|
|
// data[i + 1] = g * a + y * (1 - w);
|
|
// data[i + 2] = b * a + z * (1 - w);
|
|
// data[i + 3] = w;
|
|
// }
|
|
|
|
// // 将修改后的像素数据放回 Canvas
|
|
// ctx.putImageData(imageData, 0, 0);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// ===
|
|
|
|
//ctx.drawImage(this.img, drawX - originX, drawY - originY, width, height);
|
|
// 自定义渲染逻辑 结束
|
|
if (needClip) {
|
|
this.renderBorder(ctx, originX, originY);
|
|
}
|
|
if (needStroke) {
|
|
ctx.stroke();
|
|
}
|
|
ctx.translate(-originX, -originY);
|
|
ctx.restore();
|
|
};
|
|
return Image;
|
|
}(_elements__WEBPACK_IMPORTED_MODULE_0__["default"]));
|
|
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Image);
|
|
|
|
|
|
/***/ }),
|
|
/* 24 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
|
|
/* harmony export */ });
|
|
/* harmony import */ var _elements__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
|
|
/* harmony import */ var _env__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1);
|
|
/* harmony import */ var _styleParser__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(9);
|
|
var __extends = (undefined && undefined.__extends) || (function () {
|
|
var extendStatics = function (d, b) {
|
|
extendStatics = Object.setPrototypeOf ||
|
|
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
return extendStatics(d, b);
|
|
};
|
|
return function (d, b) {
|
|
if (typeof b !== "function" && b !== null)
|
|
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
extendStatics(d, b);
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
})();
|
|
|
|
|
|
|
|
var DEFAULT_FONT_FAMILY = 'sans-serif';
|
|
var context = null;
|
|
var getContext = function () {
|
|
if (context) {
|
|
return context;
|
|
}
|
|
var canvas = _env__WEBPACK_IMPORTED_MODULE_1__["default"].createCanvas();
|
|
canvas.width = 1;
|
|
canvas.height = 1;
|
|
context = canvas.getContext('2d');
|
|
return context;
|
|
};
|
|
function getTextWidth(style, value) {
|
|
var context = getContext();
|
|
context.font = "".concat(style.fontWeight || 'normal', " ").concat(style.fontSize || 12, "px ").concat(style.fontFamily || DEFAULT_FONT_FAMILY);
|
|
return context.measureText(value).width || 0;
|
|
}
|
|
function getTextWidthWithoutSetFont(value) {
|
|
return getContext().measureText(value).width || 0;
|
|
}
|
|
function parseText(style, value) {
|
|
value = String(value);
|
|
var maxWidth = style.width;
|
|
var wordWidth = getTextWidth(style, value);
|
|
// 对文字溢出的处理,默认用...
|
|
var textOverflow = style.textOverflow || 'ellipsis';
|
|
// 文字最大长度不超限制
|
|
if (wordWidth <= maxWidth) {
|
|
return value;
|
|
}
|
|
// 对于用点点点处理的情况,先将最大宽度减去...的宽度
|
|
if (textOverflow === 'ellipsis') {
|
|
maxWidth -= getTextWidthWithoutSetFont('...');
|
|
}
|
|
var length = value.length - 1;
|
|
var str = value.substring(0, length);
|
|
while (getTextWidthWithoutSetFont(str) > maxWidth && length > 0) {
|
|
length -= 1;
|
|
str = value.substring(0, length);
|
|
}
|
|
return (length && textOverflow === 'ellipsis'
|
|
? "".concat(str, "...")
|
|
: str);
|
|
}
|
|
var Text = /** @class */ (function (_super) {
|
|
__extends(Text, _super);
|
|
function Text(_a) {
|
|
var _b = _a.style, style = _b === void 0 ? {} : _b, _c = _a.idName, idName = _c === void 0 ? '' : _c, _d = _a.className, className = _d === void 0 ? '' : _d, _e = _a.value, value = _e === void 0 ? '' : _e, dataset = _a.dataset;
|
|
var _this = this;
|
|
var originStyleWidth = style.width;
|
|
// 没有设置宽度的时候通过canvas计算出文字宽度
|
|
if (originStyleWidth === undefined) {
|
|
style.width = getTextWidth(style, value);
|
|
}
|
|
else if (style.textOverflow === 'ellipsis') {
|
|
value = parseText(style, value);
|
|
}
|
|
if (style.height === undefined) {
|
|
style.height = style.lineHeight || style.fontSize || 12;
|
|
}
|
|
_this = _super.call(this, {
|
|
idName: idName,
|
|
className: className,
|
|
style: style,
|
|
dataset: dataset,
|
|
}) || this;
|
|
_this.valuesrc = '';
|
|
_this.textBaseline = 'bottom';
|
|
_this.font = '';
|
|
_this.textAlign = 'left';
|
|
_this.fillStyle = '#000000';
|
|
_this.renderForLayout = {};
|
|
_this.type = 'Text';
|
|
_this.ctx = null;
|
|
_this.valuesrc = value;
|
|
_this.originStyleWidth = originStyleWidth;
|
|
if (style.textShadow) {
|
|
_this.parseTextShadow(style.textShadow);
|
|
}
|
|
return _this;
|
|
}
|
|
Text.prototype.styleChangeHandler = function (prop, styleOpType, val) {
|
|
if (prop === 'textShadow') {
|
|
if (styleOpType === _elements__WEBPACK_IMPORTED_MODULE_0__.StyleOpType.Set) {
|
|
this.parseTextShadow(val);
|
|
}
|
|
else {
|
|
this.renderForLayout.textShadows = null;
|
|
}
|
|
}
|
|
};
|
|
Text.prototype.parseTextShadow = function (textShadow) {
|
|
if (!(0,_styleParser__WEBPACK_IMPORTED_MODULE_2__.isValidTextShadow)(textShadow)) {
|
|
console.error("[Layout]: ".concat(textShadow, " is not a valid textShadow"));
|
|
}
|
|
else {
|
|
// 解析 text-shadow 字符串
|
|
this.renderForLayout.textShadows = textShadow.split(',').map(function (shadow) {
|
|
var parts = shadow.trim().split(/\s+/);
|
|
var offsetX = parseFloat(parts[0]);
|
|
var offsetY = parseFloat(parts[1]);
|
|
var blurRadius = parseFloat(parts[2]);
|
|
var color = parts[3];
|
|
return { offsetX: offsetX, offsetY: offsetY, blurRadius: blurRadius, color: color };
|
|
});
|
|
}
|
|
};
|
|
Object.defineProperty(Text.prototype, "value", {
|
|
get: function () {
|
|
return this.valuesrc;
|
|
},
|
|
set: function (newValue) {
|
|
if (newValue !== this.valuesrc) {
|
|
if (this.originStyleWidth === undefined) {
|
|
this.style.width = getTextWidth(this.style, newValue);
|
|
}
|
|
else if (this.style.textOverflow === 'ellipsis') {
|
|
newValue = parseText(this.style, newValue);
|
|
}
|
|
this.valuesrc = newValue;
|
|
(0,_elements__WEBPACK_IMPORTED_MODULE_0__.setDirty)(this, 'value change');
|
|
}
|
|
},
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
Text.prototype.toCanvasData = function () {
|
|
var style = this.style || {};
|
|
this.fontSize = style.fontSize || 12;
|
|
this.textBaseline = 'top';
|
|
this.font = "".concat(style.fontWeight || '', " ").concat(style.fontSize || 12, "px ").concat(style.fontFamily || DEFAULT_FONT_FAMILY);
|
|
this.textAlign = style.textAlign || 'left';
|
|
this.fillStyle = style.color || '#000';
|
|
};
|
|
Text.prototype.repaint = function () {
|
|
this.render();
|
|
};
|
|
Text.prototype.destroySelf = function () {
|
|
this.root = null;
|
|
};
|
|
Text.prototype.insert = function (ctx, needRender) {
|
|
this.ctx = ctx;
|
|
this.shouldUpdate = false;
|
|
// this.toCanvasData();
|
|
if (needRender) {
|
|
this.render();
|
|
}
|
|
};
|
|
Text.prototype.render = function () {
|
|
var _this = this;
|
|
var style = this.style;
|
|
var ctx = this.ctx;
|
|
ctx.save();
|
|
this.toCanvasData();
|
|
var _a = this.baseRender('test'), needStroke = _a.needStroke, originX = _a.originX, originY = _a.originY, drawX = _a.drawX, drawY = _a.drawY, width = _a.width, height = _a.height;
|
|
ctx.textBaseline = this.textBaseline;
|
|
ctx.font = this.font;
|
|
ctx.textAlign = this.textAlign;
|
|
if (needStroke) {
|
|
ctx.stroke();
|
|
}
|
|
ctx.fillStyle = this.fillStyle;
|
|
if (this.textAlign === 'center') {
|
|
drawX += width / 2;
|
|
}
|
|
else if (this.textAlign === 'right') {
|
|
drawX += width;
|
|
}
|
|
if (style.lineHeight) {
|
|
ctx.textBaseline = 'middle';
|
|
drawY += style.lineHeight / 2;
|
|
}
|
|
// 纹理文字描边
|
|
if (style.textStrokeColor) {
|
|
ctx.lineWidth = style.textStrokeWidth || 1;
|
|
ctx.strokeStyle = style.textStrokeColor;
|
|
ctx.strokeText(this.value, drawX - originX, drawY - originY);
|
|
}
|
|
// 处理文字阴影
|
|
if (this.renderForLayout.textShadows) {
|
|
this.renderForLayout.textShadows.forEach(function (_a) {
|
|
var offsetX = _a.offsetX, offsetY = _a.offsetY, blurRadius = _a.blurRadius, color = _a.color;
|
|
ctx.shadowOffsetX = offsetX;
|
|
ctx.shadowOffsetY = offsetY;
|
|
ctx.shadowBlur = blurRadius;
|
|
ctx.shadowColor = color;
|
|
ctx.fillText(_this.value, drawX - originX, drawY - originY);
|
|
});
|
|
}
|
|
else {
|
|
ctx.fillText(this.value, drawX - originX, drawY - originY);
|
|
}
|
|
ctx.translate(-originX, -originY);
|
|
ctx.restore();
|
|
};
|
|
return Text;
|
|
}(_elements__WEBPACK_IMPORTED_MODULE_0__["default"]));
|
|
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Text);
|
|
|
|
|
|
/***/ }),
|
|
/* 25 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
|
|
/* harmony export */ });
|
|
/* harmony import */ var _view__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(22);
|
|
/* harmony import */ var _common_util__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(7);
|
|
/* harmony import */ var _libs_scroller_index_js__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(26);
|
|
/* harmony import */ var _common_vd__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(20);
|
|
/* harmony import */ var _elements__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(2);
|
|
/* harmony import */ var _scrollbar__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(28);
|
|
/* harmony import */ var _env__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(1);
|
|
var __extends = (undefined && undefined.__extends) || (function () {
|
|
var extendStatics = function (d, b) {
|
|
extendStatics = Object.setPrototypeOf ||
|
|
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
return extendStatics(d, b);
|
|
};
|
|
return function (d, b) {
|
|
if (typeof b !== "function" && b !== null)
|
|
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
extendStatics(d, b);
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
})();
|
|
/* eslint-disable no-underscore-dangle */
|
|
/* eslint-disable no-param-reassign */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var dpr = _env__WEBPACK_IMPORTED_MODULE_6__["default"].getDevicePixelRatio();
|
|
;
|
|
var ScrollView = /** @class */ (function (_super) {
|
|
__extends(ScrollView, _super);
|
|
function ScrollView(_a) {
|
|
var _b = _a.style, style = _b === void 0 ? {} : _b, _c = _a.idName, idName = _c === void 0 ? '' : _c, _d = _a.className, className = _d === void 0 ? '' : _d, scrollX = _a.scrollX, scrollY = _a.scrollY, dataset = _a.dataset;
|
|
var _this = _super.call(this, {
|
|
style: style,
|
|
idName: idName,
|
|
dataset: dataset,
|
|
className: className,
|
|
}) || this;
|
|
_this.scrollTop = 0;
|
|
_this.scrollLeft = 0;
|
|
_this.hasEventBind = false;
|
|
_this.currentEvent = null;
|
|
_this.type = 'ScrollView';
|
|
_this.vertivalScrollbar = null;
|
|
_this.horizontalScrollbar = null;
|
|
_this.scrollYProp = scrollY;
|
|
_this.innerScrollerOption = {
|
|
scrollingX: !!scrollX,
|
|
scrollingY: !!scrollY,
|
|
};
|
|
return _this;
|
|
}
|
|
Object.defineProperty(ScrollView.prototype, "scrollHeight", {
|
|
/**
|
|
* 获取滚动列表内所有元素的高度和
|
|
* 这里不能简单将所有子元素的高度累加,因为每个元素之间可能是有空隙的
|
|
*/
|
|
get: function () {
|
|
var maxHeight = 0;
|
|
this.children.forEach(function (item) {
|
|
if (!(item instanceof _scrollbar__WEBPACK_IMPORTED_MODULE_5__["default"])) {
|
|
maxHeight = Math.max(maxHeight, item.layoutBox.top + item.layoutBox.height);
|
|
}
|
|
});
|
|
return maxHeight;
|
|
},
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
Object.defineProperty(ScrollView.prototype, "scrollWidth", {
|
|
get: function () {
|
|
var maxWidth = 0;
|
|
this.children.forEach(function (item) {
|
|
if (!(item instanceof _scrollbar__WEBPACK_IMPORTED_MODULE_5__["default"])) {
|
|
maxWidth = Math.max(maxWidth, item.layoutBox.left + item.layoutBox.width);
|
|
}
|
|
});
|
|
return maxWidth;
|
|
},
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
Object.defineProperty(ScrollView.prototype, "scrollX", {
|
|
get: function () {
|
|
return this.innerScrollerOption.scrollingX;
|
|
},
|
|
set: function (value) {
|
|
this.scrollerObj.scrollTo(0, this.scrollTop, true, 1);
|
|
this.scrollerOption = {
|
|
scrollingX: value,
|
|
};
|
|
this.updateScrollBar('scrollX', 'horizontalScrollbar');
|
|
},
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
Object.defineProperty(ScrollView.prototype, "scrollY", {
|
|
get: function () {
|
|
return this.innerScrollerOption.scrollingY;
|
|
},
|
|
set: function (value) {
|
|
if (value !== this.scrollY) {
|
|
this.scrollerObj.scrollTo(this.scrollLeft, 0, true, 1);
|
|
this.scrollerOption = {
|
|
scrollingY: value,
|
|
};
|
|
this.scrollerObj && this.updateScrollBar('scrollY', 'vertivalScrollbar');
|
|
}
|
|
},
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
Object.defineProperty(ScrollView.prototype, "scrollerOption", {
|
|
get: function () {
|
|
return this.innerScrollerOption;
|
|
},
|
|
set: function (value) {
|
|
Object.assign(this.innerScrollerOption, value);
|
|
if (this.scrollerObj) {
|
|
Object.assign(this.scrollerObj.options, this.scrollerOption);
|
|
}
|
|
},
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
ScrollView.prototype.repaint = function () {
|
|
this.scrollRender();
|
|
};
|
|
ScrollView.prototype.destroySelf = function () {
|
|
// this.touch = null;
|
|
this.isDestroyed = true;
|
|
this.ctx = null;
|
|
this.children = [];
|
|
this.root.off('touchend');
|
|
this.root = null;
|
|
};
|
|
ScrollView.prototype.renderTreeWithTop = function (tree) {
|
|
var _this = this;
|
|
if (!(tree instanceof _scrollbar__WEBPACK_IMPORTED_MODULE_5__["default"])) {
|
|
tree.render();
|
|
}
|
|
tree.children.forEach(function (child) {
|
|
_this.renderTreeWithTop(child);
|
|
});
|
|
};
|
|
ScrollView.prototype.clear = function () {
|
|
var box = this.layoutBox;
|
|
this.ctx.clearRect(box.absoluteX, box.absoluteY, box.width, box.height);
|
|
};
|
|
ScrollView.prototype.scrollRender = function () {
|
|
var _this = this;
|
|
var _a, _b;
|
|
var box = this.layoutBox;
|
|
var startX = box.absoluteX, startY = box.absoluteY, width = box.width, height = box.height;
|
|
var ctx = this.ctx;
|
|
// 根据滚动值获取裁剪区域
|
|
var endX = startX + width;
|
|
var endY = startY + height;
|
|
// ScrollView 作为容器本身的渲染
|
|
this.render();
|
|
/**
|
|
* 开始裁剪,只有仔 ScrollView layoutBox 区域内的元素才是可见的
|
|
* 这样 ScrollView 不用单独占用一个 canvas,内存合渲染都会得到优化
|
|
*/
|
|
ctx.save();
|
|
ctx.beginPath();
|
|
ctx.rect(startX, startY, width, height);
|
|
ctx.clip();
|
|
this.children.forEach(function (child) {
|
|
var _a = child.layoutBox, width = _a.width, height = _a.height, absoluteX = _a.absoluteX, absoluteY = _a.absoluteY;
|
|
// 判断处于可视窗口内的子节点,递归渲染该子节点
|
|
if (absoluteY + height >= startY && absoluteY <= endY
|
|
&& absoluteX + width >= startX && absoluteX <= endX) {
|
|
_this.renderTreeWithTop(child);
|
|
}
|
|
});
|
|
// 上面的渲染应该先跳过滚动条,否则可能出现渲染顺序问题,ScrollView的节点反而把滚动条盖住了
|
|
(_a = this.vertivalScrollbar) === null || _a === void 0 ? void 0 : _a.render();
|
|
(_b = this.horizontalScrollbar) === null || _b === void 0 ? void 0 : _b.render();
|
|
ctx.restore();
|
|
};
|
|
ScrollView.prototype.scrollHandler = function (left, top) {
|
|
var _this = this;
|
|
var _a, _b;
|
|
// 可能被销毁了或者节点树还没准备好
|
|
if (!this.isDestroyed && !this.isFirstScroll) {
|
|
(0,_common_vd__WEBPACK_IMPORTED_MODULE_3__.iterateTree)(this, function (ele) {
|
|
if (ele !== _this && !(ele instanceof _scrollbar__WEBPACK_IMPORTED_MODULE_5__["default"])) {
|
|
ele.layoutBox.absoluteY = ele.layoutBox.originalAbsoluteY - top;
|
|
ele.layoutBox.absoluteX = ele.layoutBox.originalAbsoluteX - left;
|
|
}
|
|
});
|
|
// 这里要把滚动状态保存起来,因为在reflow的时候需要做重置,渲染并不依赖这两个信息
|
|
this.scrollTop = top;
|
|
this.scrollLeft = left;
|
|
(_a = this.vertivalScrollbar) === null || _a === void 0 ? void 0 : _a.onScroll(left, top);
|
|
(_b = this.horizontalScrollbar) === null || _b === void 0 ? void 0 : _b.onScroll(left, top);
|
|
this.root.emit('repaint');
|
|
if (this.currentEvent) {
|
|
this.emit('scroll', this.currentEvent);
|
|
}
|
|
}
|
|
if (this.isFirstScroll) {
|
|
this.isFirstScroll = false;
|
|
}
|
|
};
|
|
/**
|
|
* 当执行reflow之后,滚动列表的高度可能发生了变化,滚动条也需要同步进行更新
|
|
*/
|
|
ScrollView.prototype.updateScrollBar = function (scrollProp, scrollBarName) {
|
|
var _this = this;
|
|
var dimensions = {
|
|
width: this.layoutBox.width,
|
|
height: this.layoutBox.height,
|
|
contentWidth: this.scrollerObj.__contentWidth,
|
|
contentHeight: this.scrollerObj.__contentHeight,
|
|
maxScrollLeft: this.scrollerObj.__maxScrollLeft,
|
|
maxScrollTop: this.scrollerObj.__maxScrollTop,
|
|
scrollLeft: this.scrollerObj.__scrollLeft,
|
|
scrollTop: this.scrollerObj.__scrollTop,
|
|
};
|
|
// console.log('updateScrollBar', JSON.stringify(dimensions))
|
|
// 非第一次创建的情况,一般是 reflow 执行到这里
|
|
if (this[scrollProp]) {
|
|
if (this[scrollBarName]) {
|
|
this[scrollBarName].setDimensions(dimensions);
|
|
}
|
|
else {
|
|
var scrollBar = new _scrollbar__WEBPACK_IMPORTED_MODULE_5__["default"]({
|
|
dimensions: dimensions,
|
|
direction: scrollProp === 'scrollY' ? _scrollbar__WEBPACK_IMPORTED_MODULE_5__.ScrollBarDirection.Vertival : _scrollbar__WEBPACK_IMPORTED_MODULE_5__.ScrollBarDirection.Horizontal,
|
|
});
|
|
// this.appendChild(scrollbar);
|
|
scrollBar.root = this.root;
|
|
scrollBar.init();
|
|
// @ts-ignore
|
|
scrollBar.insert(this.root.renderContext, true);
|
|
scrollBar.observeStyleAndEvent();
|
|
this.add(scrollBar);
|
|
(0,_elements__WEBPACK_IMPORTED_MODULE_4__.setDirty)(scrollBar, 'appendToScrollView');
|
|
// @ts-ignore
|
|
this[scrollBarName] = scrollBar;
|
|
// @ts-ignore
|
|
this.root.ticker.next(function () {
|
|
var _a, _b;
|
|
// @ts-ignore
|
|
(_a = _this[scrollBarName]) === null || _a === void 0 ? void 0 : _a.onScroll(_this.scrollerObj.__scrollLeft, _this.scrollerObj.__scheduledTop);
|
|
(_b = _this.root) === null || _b === void 0 ? void 0 : _b.emit('repaint');
|
|
}, true);
|
|
}
|
|
}
|
|
else {
|
|
// 当不再需要纵向滚动的时候销毁纵向滚动条
|
|
if (this[scrollBarName]) {
|
|
var scrollBar = this[scrollBarName];
|
|
scrollBar.remove();
|
|
scrollBar.destroy();
|
|
scrollBar.destroySelf();
|
|
// @ts-ignore
|
|
this[scrollBarName] = null;
|
|
}
|
|
}
|
|
};
|
|
ScrollView.prototype.insert = function (context) {
|
|
var _this = this;
|
|
this.shouldUpdate = false;
|
|
this.ctx = context;
|
|
/**
|
|
* 这里有个非常特殊的兼容逻辑,在低版本没有重构 ScrollView之前,并没有提供单独的 ScrollX 和 ScrollY 属性
|
|
* 而是判断 scrollHeiht 大于容器高度的时候自动实现了纵向滚动(且没有横向滚动能力)
|
|
* 因此这里做一个兼容逻辑,如果 scrollHeight > this.layoutBox.height 自动开启纵向滚动
|
|
*/
|
|
if (this.scrollHeight > this.layoutBox.height && typeof this.scrollYProp === 'undefined') {
|
|
console.log("[Layout] \u81EA\u52A8\u5F00\u542F scrollY");
|
|
this.scrollY = true;
|
|
}
|
|
if (this.hasEventBind) {
|
|
// reflow 高度可能会变化,因此需要执行 setDimensions 刷新可滚动区域
|
|
if (this.layoutBox.width !== this.scrollerObj.__clientWidth
|
|
|| this.layoutBox.height !== this.scrollerObj.__clientHeight
|
|
|| this.scrollWidth !== this.scrollerObj.__contentWidth
|
|
|| this.scrollHeight !== this.scrollerObj.__contentHeight) {
|
|
this.scrollerObj.setDimensions(this.layoutBox.width, this.layoutBox.height, this.scrollWidth, this.scrollHeight);
|
|
/**
|
|
* 这里之所以要延迟一帧是因为这里的变动来自 reflow 之后,正在做 reflow 之后的后续事情
|
|
* 如果立即修改滚动条的样式,实际上并不会生效。
|
|
*/
|
|
// @ts-ignore
|
|
this.root.ticker.next(function () {
|
|
_this.updateScrollBar('scrollY', 'vertivalScrollbar');
|
|
_this.updateScrollBar('scrollX', 'horizontalScrollbar');
|
|
}, true);
|
|
}
|
|
// reflow 之后,会从 csslayout 同步布局信息,原先的滚动信息会丢失,这里需要一个复位的操作
|
|
(0,_common_vd__WEBPACK_IMPORTED_MODULE_3__.iterateTree)(this, function (ele) {
|
|
if (ele !== _this && !(ele instanceof _scrollbar__WEBPACK_IMPORTED_MODULE_5__["default"])) {
|
|
ele.layoutBox.absoluteY = ele.layoutBox.originalAbsoluteY - _this.scrollTop;
|
|
ele.layoutBox.absoluteX = ele.layoutBox.originalAbsoluteX - _this.scrollLeft;
|
|
}
|
|
});
|
|
return;
|
|
}
|
|
this.hasEventBind = true;
|
|
this.isFirstScroll = true;
|
|
// @ts-ignore
|
|
this.scrollerObj = new _libs_scroller_index_js__WEBPACK_IMPORTED_MODULE_2__["default"](this.scrollHandler.bind(this), this.scrollerOption);
|
|
this.scrollerObj.setDimensions(this.layoutBox.width, this.layoutBox.height, this.scrollWidth, this.scrollHeight);
|
|
// @ts-ignore
|
|
this.root.ticker.next(function () {
|
|
_this.updateScrollBar('scrollY', 'vertivalScrollbar');
|
|
_this.updateScrollBar('scrollX', 'horizontalScrollbar');
|
|
}, true);
|
|
this.on('touchstart', function (e) {
|
|
if (!e.touches) {
|
|
e.touches = [e];
|
|
}
|
|
var touches = (0,_common_util__WEBPACK_IMPORTED_MODULE_1__.copyTouchArray)(e.touches);
|
|
touches.forEach(function (touch) {
|
|
if (dpr !== 1) {
|
|
touch.pageX *= dpr;
|
|
touch.pageY *= dpr;
|
|
}
|
|
});
|
|
_this.scrollerObj.doTouchStart(touches, e.timeStamp);
|
|
_this.currentEvent = e;
|
|
});
|
|
this.on('touchmove', function (e) {
|
|
if (!e.touches) {
|
|
e.touches = [e];
|
|
}
|
|
var touches = (0,_common_util__WEBPACK_IMPORTED_MODULE_1__.copyTouchArray)(e.touches);
|
|
touches.forEach(function (touch) {
|
|
if (dpr !== 1) {
|
|
touch.pageX *= dpr;
|
|
touch.pageY *= dpr;
|
|
}
|
|
});
|
|
_this.scrollerObj.doTouchMove(touches, e.timeStamp, undefined);
|
|
_this.currentEvent = e;
|
|
});
|
|
// 这里不应该是监听scrollview的touchend事件而是屏幕的touchend事件
|
|
this.root.on('touchend', function (e) {
|
|
_this.scrollerObj.doTouchEnd(e.timeStamp);
|
|
_this.currentEvent = e;
|
|
});
|
|
};
|
|
ScrollView.prototype.scrollTo = function (left, top, animate) {
|
|
if (left === void 0) { left = 0; }
|
|
if (top === void 0) { top = 0; }
|
|
if (animate === void 0) { animate = true; }
|
|
this.scrollerObj.scrollTo(left, top, animate, 1);
|
|
};
|
|
return ScrollView;
|
|
}(_view__WEBPACK_IMPORTED_MODULE_0__["default"]));
|
|
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (ScrollView);
|
|
|
|
|
|
/***/ }),
|
|
/* 26 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
|
|
/* harmony export */ });
|
|
/* harmony import */ var _animate__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(27);
|
|
/* harmony import */ var _animate__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_animate__WEBPACK_IMPORTED_MODULE_0__);
|
|
/*
|
|
* Scroller
|
|
* http://github.com/zynga/scroller
|
|
*
|
|
* Copyright 2011, Zynga Inc.
|
|
* Licensed under the MIT License.
|
|
* https://raw.github.com/zynga/scroller/master/MIT-LICENSE.txt
|
|
*
|
|
* Based on the work of: Unify Project (unify-project.org)
|
|
* http://unify-project.org
|
|
* Copyright 2011, Deutsche Telekom AG
|
|
* License: MIT + Apache (V2)
|
|
*/
|
|
|
|
var NOOP = function () { };
|
|
// Easing Equations (c) 2003 Robert Penner, all rights reserved.
|
|
// Open source under the BSD License.
|
|
/**
|
|
* @param pos {Number} position between 0 (start of effect) and 1 (end of effect)
|
|
**/
|
|
var easeOutCubic = function (pos) {
|
|
return (Math.pow((pos - 1), 3) + 1);
|
|
};
|
|
/**
|
|
* @param pos {Number} position between 0 (start of effect) and 1 (end of effect)
|
|
**/
|
|
var easeInOutCubic = function (pos) {
|
|
if ((pos /= 0.5) < 1) {
|
|
return 0.5 * Math.pow(pos, 3);
|
|
}
|
|
return 0.5 * (Math.pow((pos - 2), 3) + 2);
|
|
};
|
|
/**
|
|
* A pure logic 'component' for 'virtual' scrolling/zooming.
|
|
*/
|
|
var Scroller = /** @class */ (function () {
|
|
function Scroller(callback, options) {
|
|
/*
|
|
---------------------------------------------------------------------------
|
|
INTERNAL FIELDS :: STATUS
|
|
---------------------------------------------------------------------------
|
|
*/
|
|
/** {Boolean} Whether only a single finger is used in touch handling */
|
|
this.__isSingleTouch = false;
|
|
/** {Boolean} Whether a touch event sequence is in progress */
|
|
this.__isTracking = false;
|
|
/** {Boolean} Whether a deceleration animation went to completion. */
|
|
this.__didDecelerationComplete = false;
|
|
/**
|
|
* {Boolean} Whether a gesture zoom/rotate event is in progress. Activates when
|
|
* a gesturestart event happens. This has higher priority than dragging.
|
|
*/
|
|
this.__isGesturing = false;
|
|
/**
|
|
* {Boolean} Whether the user has moved by such a distance that we have enabled
|
|
* dragging mode. Hint = It's only enabled after some pixels of movement t;
|
|
* not interrupt with clicks etc.
|
|
*/
|
|
this.__isDragging = false;
|
|
/**
|
|
* {Boolean} Not touching and dragging anymore, and smoothly animating the
|
|
* touch sequence using deceleration.
|
|
*/
|
|
this.__isDecelerating = false;
|
|
/**
|
|
* {Boolean} Smoothly animating the currently configured change
|
|
*/
|
|
this.__isAnimating = false;
|
|
/*
|
|
---------------------------------------------------------------------------
|
|
INTERNAL FIELDS :: DIMENSIONS
|
|
---------------------------------------------------------------------------
|
|
*/
|
|
/** {Integer} Viewport left boundary */
|
|
this.__clientLeft = 0;
|
|
/** {Integer} Viewport right boundary */
|
|
this.__clientTop = 0;
|
|
/** {Integer} Viewport width */
|
|
this.__clientWidth = 0;
|
|
/** {Integer} Viewport height */
|
|
this.__clientHeight = 0;
|
|
/** {Integer} Full content's width */
|
|
this.__contentWidth = 0;
|
|
/** {Integer} Full content's height */
|
|
this.__contentHeight = 0;
|
|
/** {Integer} Snapping width for content */
|
|
this.__snapWidth = 100;
|
|
/** {Integer} Snapping height for content */
|
|
this.__snapHeight = 100;
|
|
/** {Number} Zoom level */
|
|
this.__zoomLevel = 1;
|
|
/** {Number} Scroll position on x-axis */
|
|
this.__scrollLeft = 0;
|
|
/** {Number} Scroll position on y-axis */
|
|
this.__scrollTop = 0;
|
|
/** {Integer} Maximum allowed scroll position on x-axis */
|
|
this.__maxScrollLeft = 0;
|
|
/** {Integer} Maximum allowed scroll position on y-axis */
|
|
this.__maxScrollTop = 0;
|
|
/* {Number} Scheduled left position (final position when animating) */
|
|
this.__scheduledLeft = 0;
|
|
/* {Number} Scheduled top position (final position when animating) */
|
|
this.__scheduledTop = 0;
|
|
/* {Number} Scheduled zoom level (final scale when animating) */
|
|
this.__scheduledZoom = 0;
|
|
/*
|
|
---------------------------------------------------------------------------
|
|
INTERNAL FIELDS :: LAST POSITIONS
|
|
---------------------------------------------------------------------------
|
|
*/
|
|
/** {Number} Left position of finger at start */
|
|
this.__lastTouchLeft = null;
|
|
/** {Number} Top position of finger at start */
|
|
this.__lastTouchTop = null;
|
|
/** {Date} Timestamp of last move of finger. Used to limit tracking range for deceleration speed. */
|
|
this.__lastTouchMove = null;
|
|
/** {Array} List of positions, uses three indexes for each state = left, top, timestamp *;
|
|
__positions = null;
|
|
|
|
|
|
|
|
/*
|
|
---------------------------------------------------------------------------
|
|
INTERNAL FIELDS : = DECELERATION SUPPOR;
|
|
---------------------------------------------------------------------------
|
|
*/
|
|
/** {Integer} Minimum left scroll position during deceleration */
|
|
this.__minDecelerationScrollLeft = null;
|
|
/** {Integer} Minimum top scroll position during deceleration */
|
|
this.__minDecelerationScrollTop = null;
|
|
/** {Integer} Maximum left scroll position during deceleration */
|
|
this.__maxDecelerationScrollLeft = null;
|
|
/** {Integer} Maximum top scroll position during deceleration */
|
|
this.__maxDecelerationScrollTop = null;
|
|
/** {Number} Current factor to modify horizontal scroll position with on every step */
|
|
this.__decelerationVelocityX = null;
|
|
/** {Number} Current factor to modify vertical scroll position with on every step */
|
|
this.__decelerationVelocityY = null;
|
|
this.__callback = callback;
|
|
this.options = {
|
|
/** Enable scrolling on x-axis */
|
|
scrollingX: true,
|
|
/** Enable scrolling on y-axis */
|
|
scrollingY: true,
|
|
/** Enable animations for deceleration, snap back, zooming and scrolling */
|
|
animating: true,
|
|
/** duration for animations triggered by scrollTo/zoomTo */
|
|
animationDuration: 250,
|
|
/** Enable bouncing (content can be slowly moved outside and jumps back after releasing) */
|
|
bouncing: true,
|
|
/** Enable locking to the main axis if user moves only slightly on one of them at start */
|
|
locking: true,
|
|
/** Enable pagination mode (switching between full page content panes) */
|
|
paging: false,
|
|
/** Enable snapping of content to a configured pixel grid */
|
|
snapping: false,
|
|
/** Enable zooming of content via API, fingers and mouse wheel */
|
|
zooming: false,
|
|
/** Minimum zoom level */
|
|
minZoom: 0.5,
|
|
/** Maximum zoom level */
|
|
maxZoom: 3,
|
|
/** Multiply or decrease scrolling speed **/
|
|
speedMultiplier: 1,
|
|
/** Callback that is fired on the later of touch end or deceleration end,
|
|
provided that another scrolling action has not begun. Used to know
|
|
when to fade out a scrollbar. */
|
|
scrollingComplete: NOOP,
|
|
/** This configures the amount of change applied to deceleration when reaching boundaries **/
|
|
penetrationDeceleration: 0.03,
|
|
/** This configures the amount of change applied to acceleration when reaching boundaries **/
|
|
penetrationAcceleration: 0.08
|
|
};
|
|
for (var key in options) {
|
|
this.options[key] = options[key];
|
|
}
|
|
}
|
|
/*
|
|
---------------------------------------------------------------------------
|
|
PUBLIC API
|
|
---------------------------------------------------------------------------
|
|
*/
|
|
/**
|
|
* Configures the dimensions of the client (outer) and content (inner) elements.
|
|
* Requires the available space for the outer element and the outer size of the inner element.
|
|
* All values which are falsy (null or zero etc.) are ignored and the old value is kept.
|
|
*
|
|
* @param clientWidth {Integer ? null} Inner width of outer element
|
|
* @param clientHeight {Integer ? null} Inner height of outer element
|
|
* @param contentWidth {Integer ? null} Outer width of inner element
|
|
* @param contentHeight {Integer ? null} Outer height of inner element
|
|
*/
|
|
Scroller.prototype.setDimensions = function (clientWidth, clientHeight, contentWidth, contentHeight) {
|
|
// Only update values which are defined
|
|
if (clientWidth !== null) {
|
|
this.__clientWidth = clientWidth;
|
|
}
|
|
if (clientHeight !== null) {
|
|
this.__clientHeight = clientHeight;
|
|
}
|
|
if (contentWidth !== null) {
|
|
this.__contentWidth = contentWidth;
|
|
}
|
|
if (contentHeight !== null) {
|
|
this.__contentHeight = contentHeight;
|
|
}
|
|
// Refresh maximums
|
|
this.__computeScrollMax();
|
|
// Refresh scroll position
|
|
this.scrollTo(this.__scrollLeft, this.__scrollTop, false);
|
|
};
|
|
/**
|
|
* Sets the client coordinates in relation to the document.
|
|
*
|
|
* @param left {Integer ? 0} Left position of outer element
|
|
* @param top {Integer ? 0} Top position of outer element
|
|
*/
|
|
Scroller.prototype.setPosition = function (left, top) {
|
|
this.__clientLeft = left || 0;
|
|
this.__clientTop = top || 0;
|
|
};
|
|
/**
|
|
* Configures the snapping (when snapping is active)
|
|
*
|
|
* @param width {Integer} Snapping width
|
|
* @param height {Integer} Snapping height
|
|
*/
|
|
Scroller.prototype.setSnapSize = function (width, height) {
|
|
this.__snapWidth = width;
|
|
this.__snapHeight = height;
|
|
};
|
|
/**
|
|
* Returns the scroll position and zooming values
|
|
*
|
|
* @return {Map} `left` and `top` scroll position and `zoom` level
|
|
*/
|
|
Scroller.prototype.getValues = function () {
|
|
return {
|
|
left: this.__scrollLeft,
|
|
top: this.__scrollTop,
|
|
right: this.__scrollLeft + this.__clientWidth / this.__zoomLevel,
|
|
bottom: this.__scrollTop + this.__clientHeight / this.__zoomLevel,
|
|
zoom: this.__zoomLevel
|
|
};
|
|
};
|
|
/**
|
|
* Get point in in content space from scroll coordinates.
|
|
*/
|
|
Scroller.prototype.getPoint = function (scrollLeft, scrollTop) {
|
|
var values = this.getValues();
|
|
return {
|
|
left: scrollLeft / values.zoom,
|
|
top: scrollTop / values.zoom
|
|
};
|
|
};
|
|
/**
|
|
* Returns the maximum scroll values
|
|
*
|
|
* @return {Map} `left` and `top` maximum scroll values
|
|
*/
|
|
Scroller.prototype.getScrollMax = function () {
|
|
return {
|
|
left: this.__maxScrollLeft,
|
|
top: this.__maxScrollTop
|
|
};
|
|
};
|
|
/**
|
|
* Zooms to the given level. Supports optional animation. Zooms
|
|
* the center when no coordinates are given.
|
|
*
|
|
* @param level {Number} Level to zoom to
|
|
* @param isAnimated {Boolean ? false} Whether to use animation
|
|
* @param fixedLeft {Number ? undefined} Stationary point's left coordinate (vector in client space)
|
|
* @param fixedTop {Number ? undefined} Stationary point's top coordinate (vector in client space)
|
|
* @param callback {Function ? null} A callback that gets fired when the zoom is complete.
|
|
*/
|
|
Scroller.prototype.zoomTo = function (level, isAnimated, fixedLeft, fixedTop, callback) {
|
|
if (!this.options.zooming) {
|
|
throw new Error("Zooming is not enabled!");
|
|
}
|
|
// Add callback if exists
|
|
if (callback) {
|
|
this.__zoomComplete = callback;
|
|
}
|
|
// Stop deceleration
|
|
if (this.__isDecelerating) {
|
|
_animate__WEBPACK_IMPORTED_MODULE_0___default().stop(this.__isDecelerating);
|
|
this.__isDecelerating = false;
|
|
}
|
|
var oldLevel = this.__zoomLevel;
|
|
// Normalize fixed point to center of viewport if not defined
|
|
if (fixedLeft === undefined) {
|
|
fixedLeft = this.__clientWidth / 2;
|
|
}
|
|
if (fixedTop === undefined) {
|
|
fixedTop = this.__clientHeight / 2;
|
|
}
|
|
// Limit level according to configuration
|
|
level = Math.max(Math.min(level, this.options.maxZoom), this.options.minZoom);
|
|
// Recompute maximum values while temporary tweaking maximum scroll ranges
|
|
this.__computeScrollMax(level);
|
|
// Recompute left and top scroll positions based on new zoom level.
|
|
// Choosing the new viewport so that the origin's position remains
|
|
// fixed, we have central dilation about the origin.
|
|
// * Fixed point, $F$, remains stationary in content space and in the
|
|
// viewport.
|
|
// * Initial scroll position, $S_i$, in content space.
|
|
// * Final scroll position, $S_f$, in content space.
|
|
// * Initial scaling factor, $k_i$.
|
|
// * Final scaling factor, $k_f$.
|
|
//
|
|
// * $S_i \mapsto S_f$.
|
|
// * $(S_i - F) k_i = (S_f - F) k_f$.
|
|
// * $(S_i - F) k_i/k_f = (S_f - F)$.
|
|
// * $S_f = F + (S_i - F) k_i/k_f$.
|
|
//
|
|
// Fixed point location, $\vector{f} = (F - S_i) k_i$.
|
|
// * $F = S_i + \vector{f}/k_i$.
|
|
// * $S_f = S_i + \vector{f}/k_i + (S_i - S_i - \vector{f}/k_i) k_i/k_f$.
|
|
// * $S_f = S_i + \vector{f}/k_i - \vector{f}/k_f$.
|
|
// * $S_f k_f = S_i k_f + (k_f/k_i - 1)\vector{f}$.
|
|
// * $S_f k_f = (k_f/k_i)(S_i k_i) + (k_f/k_i - 1) \vector{f}$.
|
|
var k = level / oldLevel;
|
|
var left = k * (this.__scrollLeft + fixedLeft) - fixedLeft;
|
|
var top = k * (this.__scrollTop + fixedTop) - fixedTop;
|
|
// Limit x-axis
|
|
if (left > this.__maxScrollLeft) {
|
|
left = this.__maxScrollLeft;
|
|
}
|
|
else if (left < 0) {
|
|
left = 0;
|
|
}
|
|
// Limit y-axis
|
|
if (top > this.__maxScrollTop) {
|
|
top = this.__maxScrollTop;
|
|
}
|
|
else if (top < 0) {
|
|
top = 0;
|
|
}
|
|
// Push values out
|
|
this.__publish(left, top, level, isAnimated);
|
|
};
|
|
/**
|
|
* Zooms the content by the given factor.
|
|
*
|
|
* @param factor {Number} Zoom by given factor
|
|
* @param isAnimated {Boolean ? false} Whether to use animation
|
|
* @param originLeft {Number ? 0} Zoom in at given left coordinate
|
|
* @param originTop {Number ? 0} Zoom in at given top coordinate
|
|
* @param callback {Function ? null} A callback that gets fired when the zoom is complete.
|
|
*/
|
|
Scroller.prototype.zoomBy = function (factor, isAnimated, originLeft, originTop, callback) {
|
|
this.zoomTo(this.__zoomLevel * factor, isAnimated, originLeft, originTop, callback);
|
|
};
|
|
/**
|
|
* Scrolls to the given position. Respect limitations and snapping automatically.
|
|
*
|
|
* @param left {Number?null} Horizontal scroll position, keeps current if value is <code>null</code>
|
|
* @param top {Number?null} Vertical scroll position, keeps current if value is <code>null</code>
|
|
* @param isAnimated {Boolean?false} Whether the scrolling should happen using an animation
|
|
* @param zoom {Number} [1.0] Zoom level to go to
|
|
*/
|
|
Scroller.prototype.scrollTo = function (left, top, isAnimated, zoom) {
|
|
// Stop deceleration
|
|
if (this.__isDecelerating) {
|
|
_animate__WEBPACK_IMPORTED_MODULE_0___default().stop(this.__isDecelerating);
|
|
this.__isDecelerating = false;
|
|
}
|
|
// Correct coordinates based on new zoom level
|
|
if (zoom !== undefined && zoom !== this.__zoomLevel) {
|
|
if (!this.options.zooming) {
|
|
throw new Error("Zooming is not enabled!");
|
|
}
|
|
left *= zoom;
|
|
top *= zoom;
|
|
// Recompute maximum values while temporary tweaking maximum scroll ranges
|
|
this.__computeScrollMax(zoom);
|
|
}
|
|
else {
|
|
// Keep zoom when not defined
|
|
zoom = this.__zoomLevel;
|
|
}
|
|
if (!this.options.scrollingX) {
|
|
left = this.__scrollLeft;
|
|
}
|
|
else {
|
|
if (this.options.paging) {
|
|
left = Math.round(left / this.__clientWidth) * this.__clientWidth;
|
|
}
|
|
else if (this.options.snapping) {
|
|
left = Math.round(left / this.__snapWidth) * this.__snapWidth;
|
|
}
|
|
}
|
|
if (!this.options.scrollingY) {
|
|
top = this.__scrollTop;
|
|
}
|
|
else {
|
|
if (this.options.paging) {
|
|
top = Math.round(top / this.__clientHeight) * this.__clientHeight;
|
|
}
|
|
else if (this.options.snapping) {
|
|
top = Math.round(top / this.__snapHeight) * this.__snapHeight;
|
|
}
|
|
}
|
|
// Limit for allowed ranges
|
|
left = Math.max(Math.min(this.__maxScrollLeft, left), 0);
|
|
top = Math.max(Math.min(this.__maxScrollTop, top), 0);
|
|
// Don't animate when no change detected, still call publish to make sure
|
|
// that rendered position is really in-sync with internal data
|
|
if (left === this.__scrollLeft && top === this.__scrollTop) {
|
|
isAnimated = false;
|
|
}
|
|
// Publish new values
|
|
this.__publish(left, top, zoom, isAnimated);
|
|
};
|
|
/**
|
|
* Scroll by the given offset
|
|
*
|
|
* @param left {Number ? 0} Scroll x-axis by given offset
|
|
* @param top {Number ? 0} Scroll x-axis by given offset
|
|
* @param isAnimated {Boolean ? false} Whether to animate the given change
|
|
*/
|
|
Scroller.prototype.scrollBy = function (left, top, isAnimated) {
|
|
var startLeft = this.__isAnimating ? this.__scheduledLeft : this.__scrollLeft;
|
|
var startTop = this.__isAnimating ? this.__scheduledTop : this.__scrollTop;
|
|
this.scrollTo(startLeft + (left || 0), startTop + (top || 0), isAnimated);
|
|
};
|
|
/*
|
|
---------------------------------------------------------------------------
|
|
EVENT CALLBACKS
|
|
---------------------------------------------------------------------------
|
|
*/
|
|
/**
|
|
* Mouse wheel handler for zooming support
|
|
*/
|
|
Scroller.prototype.doMouseZoom = function (wheelDelta, timeStamp, pageX, pageY) {
|
|
var change = wheelDelta > 0 ? 0.97 : 1.03;
|
|
return this.zoomTo(this.__zoomLevel * change, false, pageX - this.__clientLeft, pageY - this.__clientTop);
|
|
};
|
|
/**
|
|
* Touch start handler for scrolling support
|
|
*/
|
|
Scroller.prototype.doTouchStart = function (touches, timeStamp) {
|
|
// Array-like check is enough here
|
|
if (touches.length === undefined) {
|
|
throw new Error("Invalid touch list: " + touches);
|
|
}
|
|
if (timeStamp instanceof Date) {
|
|
timeStamp = timeStamp.valueOf();
|
|
}
|
|
if (typeof timeStamp !== "number") {
|
|
throw new Error("Invalid timestamp value: " + timeStamp);
|
|
}
|
|
// Reset interruptedAnimation flag
|
|
this.__interruptedAnimation = true;
|
|
// Stop deceleration
|
|
if (this.__isDecelerating) {
|
|
_animate__WEBPACK_IMPORTED_MODULE_0___default().stop(this.__isDecelerating);
|
|
this.__isDecelerating = false;
|
|
this.__interruptedAnimation = true;
|
|
}
|
|
// Stop animation
|
|
if (this.__isAnimating) {
|
|
_animate__WEBPACK_IMPORTED_MODULE_0___default().stop(this.__isAnimating);
|
|
this.__isAnimating = false;
|
|
this.__interruptedAnimation = true;
|
|
}
|
|
// Use center point when dealing with two fingers
|
|
var currentTouchLeft, currentTouchTop;
|
|
var isSingleTouch = touches.length === 1;
|
|
if (isSingleTouch) {
|
|
currentTouchLeft = touches[0].pageX;
|
|
currentTouchTop = touches[0].pageY;
|
|
}
|
|
else {
|
|
currentTouchLeft = Math.abs(touches[0].pageX + touches[1].pageX) / 2;
|
|
currentTouchTop = Math.abs(touches[0].pageY + touches[1].pageY) / 2;
|
|
}
|
|
// Store initial positions
|
|
this.__initialTouchLeft = currentTouchLeft;
|
|
this.__initialTouchTop = currentTouchTop;
|
|
// Store current zoom level
|
|
this.__zoomLevelStart = this.__zoomLevel;
|
|
// Store initial touch positions
|
|
this.__lastTouchLeft = currentTouchLeft;
|
|
this.__lastTouchTop = currentTouchTop;
|
|
// Store initial move time stamp
|
|
this.__lastTouchMove = timeStamp;
|
|
// Reset initial scale
|
|
this.__lastScale = 1;
|
|
// Reset locking flags
|
|
this.__enableScrollX = !isSingleTouch && this.options.scrollingX;
|
|
this.__enableScrollY = !isSingleTouch && this.options.scrollingY;
|
|
// Reset tracking flag
|
|
this.__isTracking = true;
|
|
// Reset deceleration complete flag
|
|
this.__didDecelerationComplete = false;
|
|
// Dragging starts directly with two fingers, otherwise lazy with an offset
|
|
this.__isDragging = !isSingleTouch;
|
|
// Some features are disabled in multi touch scenarios
|
|
this.__isSingleTouch = isSingleTouch;
|
|
// Clearing data structure
|
|
this.__positions = [];
|
|
};
|
|
/**
|
|
* Touch move handler for scrolling support
|
|
* @param {Number} [1.0] scale - ....
|
|
*/
|
|
Scroller.prototype.doTouchMove = function (touches, timeStamp, scale) {
|
|
// Array-like check is enough here
|
|
if (touches.length === undefined) {
|
|
throw new Error("Invalid touch list: " + touches);
|
|
}
|
|
if (timeStamp instanceof Date) {
|
|
timeStamp = timeStamp.valueOf();
|
|
}
|
|
if (typeof timeStamp !== "number") {
|
|
throw new Error("Invalid timestamp value: " + timeStamp);
|
|
}
|
|
// Ignore event when tracking is not enabled (event might be outside of element)
|
|
if (!this.__isTracking) {
|
|
return;
|
|
}
|
|
var currentTouchLeft, currentTouchTop;
|
|
// Compute move based around of center of fingers
|
|
if (touches.length === 2) {
|
|
currentTouchLeft = Math.abs(touches[0].pageX + touches[1].pageX) / 2;
|
|
currentTouchTop = Math.abs(touches[0].pageY + touches[1].pageY) / 2;
|
|
}
|
|
else {
|
|
currentTouchLeft = touches[0].pageX;
|
|
currentTouchTop = touches[0].pageY;
|
|
}
|
|
var positions = this.__positions;
|
|
// Are we already is dragging mode?
|
|
if (this.__isDragging) {
|
|
// Compute move distance
|
|
var moveX = currentTouchLeft - this.__lastTouchLeft;
|
|
var moveY = currentTouchTop - this.__lastTouchTop;
|
|
// Read previous scroll position and zooming
|
|
var scrollLeft = this.__scrollLeft;
|
|
var scrollTop = this.__scrollTop;
|
|
var level = this.__zoomLevel;
|
|
// Work with scaling
|
|
if (scale !== undefined && this.options.zooming) {
|
|
var oldLevel = level;
|
|
// Recompute level based on previous scale and new scale
|
|
level = level / this.__lastScale * scale;
|
|
// Limit level according to configuration
|
|
level = Math.max(Math.min(level, this.options.maxZoom), this.options.minZoom);
|
|
// Only do further compution when change happened
|
|
if (oldLevel !== level) {
|
|
// Compute relative event position to container
|
|
var currentTouchLeftRel = currentTouchLeft - this.__clientLeft;
|
|
var currentTouchTopRel = currentTouchTop - this.__clientTop;
|
|
// Recompute left and top coordinates based on new zoom level
|
|
scrollLeft = ((currentTouchLeftRel + scrollLeft) * level / oldLevel) - currentTouchLeftRel;
|
|
scrollTop = ((currentTouchTopRel + scrollTop) * level / oldLevel) - currentTouchTopRel;
|
|
// Recompute max scroll values
|
|
this.__computeScrollMax(level);
|
|
}
|
|
}
|
|
if (this.__enableScrollX) {
|
|
scrollLeft -= moveX * this.options.speedMultiplier;
|
|
var maxScrollLeft = this.__maxScrollLeft;
|
|
if (scrollLeft > maxScrollLeft || scrollLeft < 0) {
|
|
// Slow down on the edges
|
|
if (this.options.bouncing) {
|
|
scrollLeft += (moveX / 2 * this.options.speedMultiplier);
|
|
}
|
|
else if (scrollLeft > maxScrollLeft) {
|
|
scrollLeft = maxScrollLeft;
|
|
}
|
|
else {
|
|
scrollLeft = 0;
|
|
}
|
|
}
|
|
}
|
|
// Compute new vertical scroll position
|
|
if (this.__enableScrollY) {
|
|
scrollTop -= moveY * this.options.speedMultiplier;
|
|
// console.log(moveY)
|
|
var maxScrollTop = this.__maxScrollTop;
|
|
if (scrollTop > maxScrollTop || scrollTop < 0) {
|
|
// Slow down on the edges
|
|
if (this.options.bouncing) {
|
|
scrollTop += (moveY / 2 * this.options.speedMultiplier);
|
|
}
|
|
else if (scrollTop > maxScrollTop) {
|
|
scrollTop = maxScrollTop;
|
|
}
|
|
else {
|
|
scrollTop = 0;
|
|
}
|
|
}
|
|
}
|
|
// Keep list from growing infinitely (holding min 10, max 20 measure points)
|
|
if (positions.length > 60) {
|
|
positions.splice(0, 30);
|
|
}
|
|
// Track scroll movement for decleration
|
|
positions.push(scrollLeft, scrollTop, timeStamp);
|
|
// Sync scroll position
|
|
this.__publish(scrollLeft, scrollTop, level);
|
|
// Otherwise figure out whether we are switching into dragging mode now.
|
|
}
|
|
else {
|
|
var minimumTrackingForScroll = this.options.locking ? 3 : 0;
|
|
var minimumTrackingForDrag = 5;
|
|
var distanceX = Math.abs(currentTouchLeft - this.__initialTouchLeft);
|
|
var distanceY = Math.abs(currentTouchTop - this.__initialTouchTop);
|
|
this.__enableScrollX = this.options.scrollingX && distanceX >= minimumTrackingForScroll;
|
|
this.__enableScrollY = this.options.scrollingY && distanceY >= minimumTrackingForScroll;
|
|
positions.push(this.__scrollLeft, this.__scrollTop, timeStamp);
|
|
this.__isDragging = (this.__enableScrollX || this.__enableScrollY) && (distanceX >= minimumTrackingForDrag || distanceY >= minimumTrackingForDrag);
|
|
if (this.__isDragging) {
|
|
this.__interruptedAnimation = false;
|
|
}
|
|
}
|
|
// Update last touch positions and time stamp for next event
|
|
this.__lastTouchLeft = currentTouchLeft;
|
|
this.__lastTouchTop = currentTouchTop;
|
|
this.__lastTouchMove = timeStamp;
|
|
this.__lastScale = scale;
|
|
};
|
|
/**
|
|
* Touch end handler for scrolling support
|
|
*/
|
|
Scroller.prototype.doTouchEnd = function (timeStamp) {
|
|
if (timeStamp instanceof Date) {
|
|
timeStamp = timeStamp.valueOf();
|
|
}
|
|
if (typeof timeStamp !== "number") {
|
|
throw new Error("Invalid timestamp value: " + timeStamp);
|
|
}
|
|
// Ignore event when tracking is not enabled (no touchstart event on element)
|
|
// This is required as this listener ('touchmove') sits on the document and not on the element itself.
|
|
if (!this.__isTracking) {
|
|
return;
|
|
}
|
|
// Not touching anymore (when two finger hit the screen there are two touch end events)
|
|
this.__isTracking = false;
|
|
// Be sure to reset the dragging flag now. Here we also detect whether
|
|
// the finger has moved fast enough to switch into a deceleration animation.
|
|
if (this.__isDragging) {
|
|
// Reset dragging flag
|
|
this.__isDragging = false;
|
|
// Start deceleration
|
|
// Verify that the last move detected was in some relevant time frame
|
|
if (this.__isSingleTouch && this.options.animating && (timeStamp - this.__lastTouchMove) <= 100) {
|
|
// Then figure out what the scroll position was about 100ms ago
|
|
var positions = this.__positions;
|
|
var endPos = positions.length - 1;
|
|
var startPos = endPos;
|
|
// Move pointer to position measured 100ms ago
|
|
for (var i = endPos; i > 0 && positions[i] > (this.__lastTouchMove - 100); i -= 3) {
|
|
startPos = i;
|
|
}
|
|
// If start and stop position is identical in a 100ms timeframe,
|
|
// we cannot compute any useful deceleration.
|
|
if (startPos !== endPos) {
|
|
// Compute relative movement between these two points
|
|
var timeOffset = positions[endPos] - positions[startPos];
|
|
var movedLeft = this.__scrollLeft - positions[startPos - 2];
|
|
var movedTop = this.__scrollTop - positions[startPos - 1];
|
|
// Based on 50ms compute the movement to apply for each render step
|
|
this.__decelerationVelocityX = movedLeft / timeOffset * (1000 / 60);
|
|
this.__decelerationVelocityY = movedTop / timeOffset * (1000 / 60);
|
|
// How much velocity is required to start the deceleration
|
|
var minVelocityToStartDeceleration = this.options.paging || this.options.snapping ? 4 : 1;
|
|
// Verify that we have enough velocity to start deceleration
|
|
if (Math.abs(this.__decelerationVelocityX) > minVelocityToStartDeceleration || Math.abs(this.__decelerationVelocityY) > minVelocityToStartDeceleration) {
|
|
this.__startDeceleration(timeStamp);
|
|
}
|
|
}
|
|
else {
|
|
this.options.scrollingComplete();
|
|
}
|
|
}
|
|
else if ((timeStamp - this.__lastTouchMove) > 100) {
|
|
this.options.scrollingComplete();
|
|
}
|
|
}
|
|
// If this was a slower move it is per default non decelerated, but this
|
|
// still means that we want snap back to the bounds which is done here.
|
|
// This is placed outside the condition above to improve edge case stability
|
|
// e.g. touchend fired without enabled dragging. This should normally do not
|
|
// have modified the scroll positions or even showed the scrollbars though.
|
|
if (!this.__isDecelerating) {
|
|
if (this.__interruptedAnimation || this.__isDragging) {
|
|
this.options.scrollingComplete();
|
|
}
|
|
this.scrollTo(this.__scrollLeft, this.__scrollTop, true, this.__zoomLevel);
|
|
}
|
|
// Fully cleanup list
|
|
this.__positions.length = 0;
|
|
};
|
|
/*
|
|
---------------------------------------------------------------------------
|
|
PRIVATE API
|
|
---------------------------------------------------------------------------
|
|
*/
|
|
/**
|
|
* Applies the scroll position to the content element
|
|
*
|
|
* @param left {Number} Left scroll position
|
|
* @param top {Number} Top scroll position
|
|
* @param isAnimated {Boolean?false} Whether animation should be used to move to the new coordinates
|
|
*/
|
|
Scroller.prototype.__publish = function (left, top, zoom, isAnimated) {
|
|
// Remember whether we had an animation, then we try to continue
|
|
// based on the current "drive" of the animation.
|
|
var wasAnimating = this.__isAnimating;
|
|
if (wasAnimating) {
|
|
_animate__WEBPACK_IMPORTED_MODULE_0___default().stop(wasAnimating);
|
|
this.__isAnimating = false;
|
|
}
|
|
if (isAnimated && this.options.animating) {
|
|
// Keep scheduled positions for scrollBy/zoomBy functionality.
|
|
this.__scheduledLeft = left;
|
|
this.__scheduledTop = top;
|
|
this.__scheduledZoom = zoom;
|
|
var oldLeft = this.__scrollLeft;
|
|
var oldTop = this.__scrollTop;
|
|
var oldZoom = this.__zoomLevel;
|
|
var diffLeft = left - oldLeft;
|
|
var diffTop = top - oldTop;
|
|
var diffZoom = zoom - oldZoom;
|
|
var step = function (percent, now, render) {
|
|
if (render) {
|
|
this.__scrollLeft = oldLeft + (diffLeft * percent);
|
|
this.__scrollTop = oldTop + (diffTop * percent);
|
|
this.__zoomLevel = oldZoom + (diffZoom * percent);
|
|
// Push values out
|
|
if (this.__callback) {
|
|
this.__callback(this.__scrollLeft, this.__scrollTop, this.__zoomLevel);
|
|
}
|
|
}
|
|
}.bind(this);
|
|
var verify = function (id) {
|
|
return this.__isAnimating === id;
|
|
}.bind(this);
|
|
var completed = function (renderedFramesPerSecond, animationId, wasFinished) {
|
|
if (animationId === this.__isAnimating) {
|
|
this.__isAnimating = false;
|
|
}
|
|
if (this.__didDecelerationComplete || wasFinished) {
|
|
this.options.scrollingComplete();
|
|
}
|
|
if (this.options.zooming) {
|
|
this.__computeScrollMax();
|
|
if (this.__zoomComplete) {
|
|
this.__zoomComplete();
|
|
this.__zoomComplete = null;
|
|
}
|
|
}
|
|
}.bind(this);
|
|
// When continuing based on previous animation we choose an ease-out animation instead of ease-in-out
|
|
this.__isAnimating = _animate__WEBPACK_IMPORTED_MODULE_0___default().start(step, verify, completed, this.options.animationDuration, wasAnimating ? easeOutCubic : easeInOutCubic);
|
|
}
|
|
else {
|
|
this.__scheduledLeft = this.__scrollLeft = left;
|
|
this.__scheduledTop = this.__scrollTop = top;
|
|
this.__scheduledZoom = this.__zoomLevel = zoom;
|
|
// Push values out
|
|
if (this.__callback) {
|
|
this.__callback(left, top, zoom);
|
|
}
|
|
// Fix max scroll ranges
|
|
if (this.options.zooming) {
|
|
this.__computeScrollMax();
|
|
if (this.__zoomComplete) {
|
|
this.__zoomComplete();
|
|
this.__zoomComplete = null;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
/**
|
|
* Recomputes scroll minimum values based on client dimensions and content dimensions.
|
|
*/
|
|
Scroller.prototype.__computeScrollMax = function (zoomLevel) {
|
|
if (zoomLevel === undefined) {
|
|
zoomLevel = this.__zoomLevel;
|
|
}
|
|
this.__maxScrollLeft = Math.max(this.__contentWidth * zoomLevel - this.__clientWidth, 0);
|
|
this.__maxScrollTop = Math.max(this.__contentHeight * zoomLevel - this.__clientHeight, 0);
|
|
};
|
|
/*
|
|
---------------------------------------------------------------------------
|
|
ANIMATION (DECELERATION) SUPPORT
|
|
---------------------------------------------------------------------------
|
|
*/
|
|
/**
|
|
* Called when a touch sequence end and the speed of the finger was high enough
|
|
* to switch into deceleration mode.
|
|
*/
|
|
Scroller.prototype.__startDeceleration = function (timeStamp) {
|
|
if (this.options.paging) {
|
|
var scrollLeft = Math.max(Math.min(this.__scrollLeft, this.__maxScrollLeft), 0);
|
|
var scrollTop = Math.max(Math.min(this.__scrollTop, this.__maxScrollTop), 0);
|
|
var clientWidth = this.__clientWidth;
|
|
var clientHeight = this.__clientHeight;
|
|
// We limit deceleration not to the min/max values of the allowed range, but to the size of the visible client area.
|
|
// Each page should have exactly the size of the client area.
|
|
this.__minDecelerationScrollLeft = Math.floor(scrollLeft / clientWidth) * clientWidth;
|
|
this.__minDecelerationScrollTop = Math.floor(scrollTop / clientHeight) * clientHeight;
|
|
this.__maxDecelerationScrollLeft = Math.ceil(scrollLeft / clientWidth) * clientWidth;
|
|
this.__maxDecelerationScrollTop = Math.ceil(scrollTop / clientHeight) * clientHeight;
|
|
}
|
|
else {
|
|
this.__minDecelerationScrollLeft = 0;
|
|
this.__minDecelerationScrollTop = 0;
|
|
this.__maxDecelerationScrollLeft = this.__maxScrollLeft;
|
|
this.__maxDecelerationScrollTop = this.__maxScrollTop;
|
|
}
|
|
// Wrap class method
|
|
var step = function (percent, now, render) {
|
|
this.__stepThroughDeceleration(render);
|
|
}.bind(this);
|
|
// How much velocity is required to keep the deceleration running
|
|
var minVelocityToKeepDecelerating = this.options.snapping ? 4 : 0.1;
|
|
// Detect whether it's still worth to continue animating steps
|
|
// If we are already slow enough to not being user perceivable anymore, we stop the whole process here.
|
|
var verify = function () {
|
|
var shouldContinue = Math.abs(this.__decelerationVelocityX) >= minVelocityToKeepDecelerating || Math.abs(this.__decelerationVelocityY) >= minVelocityToKeepDecelerating;
|
|
if (!shouldContinue) {
|
|
this.__didDecelerationComplete = true;
|
|
}
|
|
return shouldContinue;
|
|
}.bind(this);
|
|
var completed = function (renderedFramesPerSecond, animationId, wasFinished) {
|
|
this.__isDecelerating = false;
|
|
if (this.__didDecelerationComplete) {
|
|
this.options.scrollingComplete();
|
|
}
|
|
// Animate to grid when snapping is active, otherwise just fix out-of-boundary positions
|
|
this.scrollTo(this.__scrollLeft, this.__scrollTop, this.options.snapping);
|
|
}.bind(this);
|
|
// Start animation and switch on flag
|
|
this.__isDecelerating = _animate__WEBPACK_IMPORTED_MODULE_0___default().start(step, verify, completed);
|
|
};
|
|
/**
|
|
* Called on every step of the animation
|
|
*
|
|
* @param inMemory {Boolean?false} Whether to not render the current step, but keep it in memory only. Used internally only!
|
|
*/
|
|
Scroller.prototype.__stepThroughDeceleration = function (render) {
|
|
//
|
|
// COMPUTE NEXT SCROLL POSITION
|
|
//
|
|
// Add deceleration to scroll position
|
|
var scrollLeft = this.__scrollLeft + this.__decelerationVelocityX;
|
|
var scrollTop = this.__scrollTop + this.__decelerationVelocityY;
|
|
//
|
|
// HARD LIMIT SCROLL POSITION FOR NON BOUNCING MODE
|
|
//
|
|
if (!this.options.bouncing) {
|
|
var scrollLeftFixed = Math.max(Math.min(this.__maxDecelerationScrollLeft, scrollLeft), this.__minDecelerationScrollLeft);
|
|
if (scrollLeftFixed !== scrollLeft) {
|
|
scrollLeft = scrollLeftFixed;
|
|
this.__decelerationVelocityX = 0;
|
|
}
|
|
var scrollTopFixed = Math.max(Math.min(this.__maxDecelerationScrollTop, scrollTop), this.__minDecelerationScrollTop);
|
|
if (scrollTopFixed !== scrollTop) {
|
|
scrollTop = scrollTopFixed;
|
|
this.__decelerationVelocityY = 0;
|
|
}
|
|
}
|
|
//
|
|
// UPDATE SCROLL POSITION
|
|
//
|
|
if (render) {
|
|
this.__publish(scrollLeft, scrollTop, this.__zoomLevel);
|
|
}
|
|
else {
|
|
this.__scrollLeft = scrollLeft;
|
|
this.__scrollTop = scrollTop;
|
|
}
|
|
//
|
|
// SLOW DOWN
|
|
//
|
|
// Slow down velocity on every iteration
|
|
if (!this.options.paging) {
|
|
// This is the factor applied to every iteration of the animation
|
|
// to slow down the process. This should emulate natural behavior where
|
|
// objects slow down when the initiator of the movement is removed
|
|
var frictionFactor = 0.95;
|
|
this.__decelerationVelocityX *= frictionFactor;
|
|
this.__decelerationVelocityY *= frictionFactor;
|
|
}
|
|
//
|
|
// BOUNCING SUPPORT
|
|
//
|
|
if (this.options.bouncing) {
|
|
var scrollOutsideX = 0;
|
|
var scrollOutsideY = 0;
|
|
// This configures the amount of change applied to deceleration/acceleration when reaching boundaries
|
|
var penetrationDeceleration = this.options.penetrationDeceleration;
|
|
var penetrationAcceleration = this.options.penetrationAcceleration;
|
|
// Check limits
|
|
if (scrollLeft < this.__minDecelerationScrollLeft) {
|
|
scrollOutsideX = this.__minDecelerationScrollLeft - scrollLeft;
|
|
}
|
|
else if (scrollLeft > this.__maxDecelerationScrollLeft) {
|
|
scrollOutsideX = this.__maxDecelerationScrollLeft - scrollLeft;
|
|
}
|
|
if (scrollTop < this.__minDecelerationScrollTop) {
|
|
scrollOutsideY = this.__minDecelerationScrollTop - scrollTop;
|
|
}
|
|
else if (scrollTop > this.__maxDecelerationScrollTop) {
|
|
scrollOutsideY = this.__maxDecelerationScrollTop - scrollTop;
|
|
}
|
|
// Slow down until slow enough, then flip back to snap position
|
|
if (scrollOutsideX !== 0) {
|
|
if (scrollOutsideX * this.__decelerationVelocityX <= 0) {
|
|
this.__decelerationVelocityX += scrollOutsideX * penetrationDeceleration;
|
|
}
|
|
else {
|
|
this.__decelerationVelocityX = scrollOutsideX * penetrationAcceleration;
|
|
}
|
|
}
|
|
if (scrollOutsideY !== 0) {
|
|
if (scrollOutsideY * this.__decelerationVelocityY <= 0) {
|
|
this.__decelerationVelocityY += scrollOutsideY * penetrationDeceleration;
|
|
}
|
|
else {
|
|
this.__decelerationVelocityY = scrollOutsideY * penetrationAcceleration;
|
|
}
|
|
}
|
|
}
|
|
};
|
|
return Scroller;
|
|
}());
|
|
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Scroller);
|
|
|
|
|
|
/***/ }),
|
|
/* 27 */
|
|
/***/ (function(module, exports) {
|
|
|
|
"use strict";
|
|
var __WEBPACK_AMD_DEFINE_FACTORY__, __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;
|
|
/*
|
|
* Scroller
|
|
* http://github.com/zynga/scroller
|
|
*
|
|
* Copyright 2011, Zynga Inc.
|
|
* Licensed under the MIT License.
|
|
* https://raw.github.com/zynga/scroller/master/MIT-LICENSE.txt
|
|
*
|
|
* Based on the work of: Unify Project (unify-project.org)
|
|
* http://unify-project.org
|
|
* Copyright 2011, Deutsche Telekom AG
|
|
* License: MIT + Apache (V2)
|
|
*/
|
|
/**
|
|
* Generic animation class with support for dropped frames both optional easing and duration.
|
|
*
|
|
* Optional duration is useful when the lifetime is defined by another condition than time
|
|
* e.g. speed of an animating object, etc.
|
|
*
|
|
* Dropped frame logic allows to keep using the same updater logic independent from the actual
|
|
* rendering. This eases a lot of cases where it might be pretty complex to break down a state
|
|
* based on the pure time difference.
|
|
*/
|
|
(function (root, factory) {
|
|
if (true) {
|
|
// AMD. Register as an anonymous module.
|
|
!(__WEBPACK_AMD_DEFINE_ARRAY__ = [exports], __WEBPACK_AMD_DEFINE_FACTORY__ = (factory),
|
|
__WEBPACK_AMD_DEFINE_RESULT__ = (typeof __WEBPACK_AMD_DEFINE_FACTORY__ === 'function' ?
|
|
(__WEBPACK_AMD_DEFINE_FACTORY__.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__)) : __WEBPACK_AMD_DEFINE_FACTORY__),
|
|
__WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
|
|
}
|
|
else {}
|
|
}(this, function (exports) {
|
|
var global = typeof window === 'undefined' ? this : window;
|
|
var time = Date.now || function () {
|
|
return +new Date();
|
|
};
|
|
var desiredFrames = 60;
|
|
var millisecondsPerSecond = 1000;
|
|
var running = {};
|
|
var counter = 1;
|
|
/**
|
|
* Stops the given animation.
|
|
*
|
|
* @param id {Integer} Unique animation ID
|
|
* @return {Boolean} Whether the animation was stopped (aka, was running before)
|
|
*/
|
|
exports.stop = function (id) {
|
|
var cleared = (running[id] !== null);
|
|
if (cleared) {
|
|
running[id] = null;
|
|
}
|
|
return cleared;
|
|
};
|
|
/**
|
|
* Whether the given animation is still running.
|
|
*
|
|
* @param id {Integer} Unique animation ID
|
|
* @return {Boolean} Whether the animation is still running
|
|
*/
|
|
exports.isRunning = function (id) {
|
|
return running[id] !== null;
|
|
};
|
|
/**
|
|
* Start the animation.
|
|
*
|
|
* @param stepCallback {Function} Pointer to function which is executed on every step.
|
|
* Signature of the method should be `function(percent, now, virtual) { return continueWithAnimation; }`
|
|
* @param verifyCallback {Function} Executed before every animation step.
|
|
* Signature of the method should be `function() { return continueWithAnimation; }`
|
|
* @param completedCallback {Function}
|
|
* Signature of the method should be `function(droppedFrames, finishedAnimation, optional wasFinished) {}`
|
|
* @param duration {Integer} Milliseconds to run the animation
|
|
* @param easingMethod {Function} Pointer to easing function
|
|
* Signature of the method should be `function(percent) { return modifiedValue; }`
|
|
* @param root {Element} Render root. Used for internal usage of requestAnimationFrame.
|
|
* @return {Integer} Identifier of animation. Can be used to stop it any time.
|
|
*/
|
|
exports.start = function (stepCallback, verifyCallback, completedCallback, duration, easingMethod, root) {
|
|
var start = time();
|
|
var lastFrame = start;
|
|
var percent = 0;
|
|
var dropCounter = 0;
|
|
var id = counter++;
|
|
// Compacting running db automatically every few new animations
|
|
if (id % 20 === 0) {
|
|
var newRunning = {};
|
|
for (var usedId in running) {
|
|
newRunning[usedId] = true;
|
|
}
|
|
running = newRunning;
|
|
}
|
|
// This is the internal step method which is called every few milliseconds
|
|
var step = function (virtual) {
|
|
// Normalize virtual value
|
|
var render = virtual !== true;
|
|
// Get current time
|
|
var now = time();
|
|
// Verification is executed before next animation step
|
|
if (!running[id] || (verifyCallback && !verifyCallback(id))) {
|
|
running[id] = null;
|
|
completedCallback(desiredFrames - (dropCounter / ((now - start) / millisecondsPerSecond)), id, false);
|
|
return;
|
|
}
|
|
// For the current rendering to apply let's update omitted steps in memory.
|
|
// This is important to bring internal state variables up-to-date with progress in time.
|
|
if (render) {
|
|
var droppedFrames = Math.round((now - lastFrame) / (millisecondsPerSecond / desiredFrames)) - 1;
|
|
for (var j = 0; j < Math.min(droppedFrames, 4); j++) {
|
|
step(true);
|
|
dropCounter++;
|
|
}
|
|
}
|
|
// Compute percent value
|
|
if (duration) {
|
|
percent = (now - start) / duration;
|
|
if (percent > 1) {
|
|
percent = 1;
|
|
}
|
|
}
|
|
// Execute step callback, then...
|
|
var value = easingMethod ? easingMethod(percent) : percent;
|
|
if ((stepCallback(value, now, render) === false || percent === 1) && render) {
|
|
running[id] = null;
|
|
completedCallback(desiredFrames - (dropCounter / ((now - start) / millisecondsPerSecond)), id, percent === 1 || duration === undefined);
|
|
}
|
|
else if (render) {
|
|
lastFrame = now;
|
|
requestAnimationFrame(step, root);
|
|
}
|
|
};
|
|
// Mark as running
|
|
running[id] = true;
|
|
// Init first step
|
|
requestAnimationFrame(step, root);
|
|
// Return unique animation ID
|
|
return id;
|
|
};
|
|
}));
|
|
|
|
|
|
/***/ }),
|
|
/* 28 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ ScrollBarDirection: () => (/* binding */ ScrollBarDirection),
|
|
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
|
|
/* harmony export */ });
|
|
/* harmony import */ var _view__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(22);
|
|
/* harmony import */ var _common_util__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(7);
|
|
var __extends = (undefined && undefined.__extends) || (function () {
|
|
var extendStatics = function (d, b) {
|
|
extendStatics = Object.setPrototypeOf ||
|
|
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
return extendStatics(d, b);
|
|
};
|
|
return function (d, b) {
|
|
if (typeof b !== "function" && b !== null)
|
|
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
extendStatics(d, b);
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
})();
|
|
|
|
|
|
var ScrollBarDirection;
|
|
(function (ScrollBarDirection) {
|
|
ScrollBarDirection[ScrollBarDirection["Vertival"] = 0] = "Vertival";
|
|
ScrollBarDirection[ScrollBarDirection["Horizontal"] = 1] = "Horizontal";
|
|
})(ScrollBarDirection || (ScrollBarDirection = {}));
|
|
/**
|
|
* 根据滚动条的尺寸、ScrollView 视口和滚动窗口尺寸、滚动防线信息确认滚动条的样式信息
|
|
*/
|
|
function updateStyleFromDimensions(width, direction, dimensions) {
|
|
var isVertical = direction === ScrollBarDirection.Vertival;
|
|
var scrollWidth = dimensions.width, scrollHeight = dimensions.height, contentWidth = dimensions.contentWidth, contentHeight = dimensions.contentHeight;
|
|
return {
|
|
width: isVertical ? width : scrollWidth * (scrollWidth / contentWidth),
|
|
height: isVertical ? scrollHeight * (scrollHeight / contentHeight) : width,
|
|
left: isVertical ? scrollWidth - width : 0,
|
|
top: isVertical ? 0 : scrollHeight - width,
|
|
};
|
|
}
|
|
function checkNeedHideScrollBar(direction, dimensions) {
|
|
return !!(direction === ScrollBarDirection.Vertival && dimensions.maxScrollTop === 0
|
|
|| direction === ScrollBarDirection.Horizontal && dimensions.maxScrollLeft === 0);
|
|
}
|
|
/**
|
|
* 滚动组件的滚动条组件,滚动条本身也是Layout的一个节点
|
|
*/
|
|
var ScrollBar = /** @class */ (function (_super) {
|
|
__extends(ScrollBar, _super);
|
|
function ScrollBar(_a) {
|
|
var direction = _a.direction, dimensions = _a.dimensions, _b = _a.backgroundColor, backgroundColor = _b === void 0 ? 'rgba(162, 162, 162, 0.7)' : _b, _c = _a.width, width = _c === void 0 ? 10 : _c;
|
|
var _this = this;
|
|
var style = Object.assign({
|
|
backgroundColor: backgroundColor,
|
|
position: 'absolute',
|
|
borderRadius: width / 2,
|
|
opacity: 0,
|
|
}, updateStyleFromDimensions(width, direction, dimensions));
|
|
_this = _super.call(this, {
|
|
style: style,
|
|
}) || this;
|
|
// 滚动完毕后一段时间后自动隐藏
|
|
_this.autoHide = true;
|
|
// 滚动完毕后自动隐藏时间
|
|
_this.autoHideTime = 2000;
|
|
_this.autoHideDelayTime = 1500;
|
|
_this.autoHideRemainingTime = 0;
|
|
_this.innerWidth = 10;
|
|
_this.isHide = false;
|
|
_this.currLeft = 0;
|
|
_this.currTop = 0;
|
|
_this.update = function (dt) {
|
|
if (!_this.autoHide || _this.autoHideRemainingTime <= 0 || _this.isHide) {
|
|
return;
|
|
}
|
|
_this.autoHideRemainingTime -= dt;
|
|
if (_this.autoHideRemainingTime <= _this.autoHideTime) {
|
|
_this.autoHideRemainingTime = Math.max(0, _this.autoHideRemainingTime);
|
|
_this.style.opacity = _this.style.opacity * (_this.autoHideRemainingTime / _this.autoHideTime);
|
|
}
|
|
};
|
|
_this.direction = direction;
|
|
_this.dimensions = dimensions;
|
|
_this.innerWidth = width;
|
|
// FIXME: yxu 强行关闭滚动条
|
|
if (true || checkNeedHideScrollBar(direction, dimensions)) {
|
|
_this.hide();
|
|
}
|
|
return _this;
|
|
}
|
|
Object.defineProperty(ScrollBar.prototype, "width", {
|
|
get: function () {
|
|
return this.innerWidth;
|
|
},
|
|
/**
|
|
* 滚动条的粗细,因为要兼容横竖滚动,所以 style.width 在不同模式下代表的意思不一样
|
|
* 因此通过单独的 width 属性来代表滚动条的粗细
|
|
*/
|
|
set: function (value) {
|
|
if (value !== this.innerWidth) {
|
|
this.innerWidth = value;
|
|
}
|
|
this.style.borderRadius = this.innerWidth / 2;
|
|
this.setDimensions(this.dimensions);
|
|
},
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
ScrollBar.prototype.init = function () {
|
|
var _this = this;
|
|
if (!this.root) {
|
|
console.warn('[Layout]: please set root for scrollbar');
|
|
}
|
|
else {
|
|
// @ts-ignore
|
|
this.root.ticker.add(this.update, true);
|
|
this.root.on('before_reflow', function () {
|
|
// console.log('before_reflow')
|
|
var _a = _this.calculteScrollValue(_this.currLeft, _this.currTop), scrollLeft = _a.scrollLeft, scrollTop = _a.scrollTop;
|
|
// console.log(this, scrollLeft, scrollTop)
|
|
if (_this.direction === ScrollBarDirection.Vertival) {
|
|
_this.style.top = scrollTop;
|
|
}
|
|
else {
|
|
_this.style.left = scrollLeft;
|
|
}
|
|
});
|
|
}
|
|
};
|
|
ScrollBar.prototype.hide = function () {
|
|
this.isHide = true;
|
|
this.style.opacity = 0;
|
|
};
|
|
ScrollBar.prototype.show = function () {
|
|
this.isHide = false;
|
|
this.style.opacity = 1;
|
|
};
|
|
/**
|
|
* 根据 ScrollView 容器宽高和实际内容宽高决定滚动条的位置和尺寸信息
|
|
* 但核心需要考虑的情况是:
|
|
* 1. 在不断地 reflow 过程中,ScrollBar 也会存在需要切换展示和隐藏的情况
|
|
* 2. reflow 之后,ScrollBar 的位置不是简单的设置为 ScrollView 顶部和左边,还可能是滚动了一段距离后执行的 reflow
|
|
*/
|
|
ScrollBar.prototype.setDimensions = function (dimensions) {
|
|
var style = updateStyleFromDimensions(this.width, this.direction, dimensions);
|
|
Object.assign(this.style, style);
|
|
if (checkNeedHideScrollBar(this.direction, dimensions)) {
|
|
this.hide();
|
|
}
|
|
else if (this.isHide) {
|
|
this.show();
|
|
}
|
|
this.dimensions = dimensions;
|
|
// 已经滚动过一段距离的情况,重新计算新的滚动位置
|
|
var _a = this.calculteScrollValue(dimensions.scrollLeft, dimensions.scrollTop), scrollLeft = _a.scrollLeft, scrollTop = _a.scrollTop;
|
|
if (this.direction === ScrollBarDirection.Vertival) {
|
|
this.style.top = scrollTop;
|
|
}
|
|
else {
|
|
this.style.left = scrollLeft;
|
|
}
|
|
this.autoHideRemainingTime = this.autoHideTime + this.autoHideDelayTime;
|
|
};
|
|
ScrollBar.prototype.calculteScrollValue = function (left, top) {
|
|
var scrollLeft = 0;
|
|
var scrollTop = 0;
|
|
if (this.direction === ScrollBarDirection.Vertival) {
|
|
var canScrollPercent = 1 - this.dimensions.height / this.dimensions.contentHeight;
|
|
// 滚动条最大滚动高度
|
|
var scrollBarMaxScrollTop = this.dimensions.height * canScrollPercent;
|
|
var percent = top / this.dimensions.maxScrollTop;
|
|
var percentTop = scrollBarMaxScrollTop * percent;
|
|
scrollTop = (0,_common_util__WEBPACK_IMPORTED_MODULE_1__.clamp)(percentTop, 0, scrollBarMaxScrollTop);
|
|
}
|
|
else {
|
|
var canScrollPercent = 1 - this.dimensions.width / this.dimensions.contentWidth;
|
|
var scrollBarMaxScrollLeft = this.dimensions.width * canScrollPercent;
|
|
var percent = left / this.dimensions.maxScrollLeft;
|
|
scrollLeft = (0,_common_util__WEBPACK_IMPORTED_MODULE_1__.clamp)(scrollBarMaxScrollLeft * percent, 0, scrollBarMaxScrollLeft);
|
|
}
|
|
return { scrollLeft: scrollLeft, scrollTop: scrollTop };
|
|
};
|
|
ScrollBar.prototype.onScroll = function (left, top) {
|
|
if (this.isHide) {
|
|
return;
|
|
}
|
|
this.currLeft = left;
|
|
this.currTop = top;
|
|
var _a = this.calculteScrollValue(left, top), scrollLeft = _a.scrollLeft, scrollTop = _a.scrollTop;
|
|
if (this.direction === ScrollBarDirection.Vertival) {
|
|
this.layoutBox.absoluteY = this.parent.layoutBox.originalAbsoluteY + scrollTop;
|
|
}
|
|
else {
|
|
this.layoutBox.absoluteX = this.parent.layoutBox.originalAbsoluteX + scrollLeft;
|
|
}
|
|
if (this.autoHide) {
|
|
// this.autoHideRemainingTime = this.autoHideTime;
|
|
this.autoHideRemainingTime = this.autoHideTime + this.autoHideDelayTime;
|
|
}
|
|
this.style.opacity = 1;
|
|
};
|
|
ScrollBar.prototype.destroySelf = function () {
|
|
// @ts-ignore
|
|
this.root.ticker.remove(this.update, true);
|
|
this.isDestroyed = true;
|
|
this.root = null;
|
|
};
|
|
return ScrollBar;
|
|
}(_view__WEBPACK_IMPORTED_MODULE_0__["default"]));
|
|
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (ScrollBar);
|
|
|
|
|
|
/***/ }),
|
|
/* 29 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
|
|
/* harmony export */ });
|
|
/* harmony import */ var _elements__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
|
|
/* harmony import */ var _common_pool__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6);
|
|
var __extends = (undefined && undefined.__extends) || (function () {
|
|
var extendStatics = function (d, b) {
|
|
extendStatics = Object.setPrototypeOf ||
|
|
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
return extendStatics(d, b);
|
|
};
|
|
return function (d, b) {
|
|
if (typeof b !== "function" && b !== null)
|
|
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
extendStatics(d, b);
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
})();
|
|
|
|
|
|
var bitMapPool = new _common_pool__WEBPACK_IMPORTED_MODULE_1__["default"]('bitMapPool');
|
|
var BitMapText = /** @class */ (function (_super) {
|
|
__extends(BitMapText, _super);
|
|
function BitMapText(opts) {
|
|
var _this = this;
|
|
var _a = opts.style, style = _a === void 0 ? {} : _a, _b = opts.idName, idName = _b === void 0 ? '' : _b, _c = opts.className, className = _c === void 0 ? '' : _c, _d = opts.value, value = _d === void 0 ? '' : _d, _e = opts.font, font = _e === void 0 ? '' : _e, dataset = opts.dataset;
|
|
_this = _super.call(this, {
|
|
idName: idName,
|
|
className: className,
|
|
style: style,
|
|
dataset: dataset,
|
|
}) || this;
|
|
_this.type = 'BitMapText';
|
|
_this.ctx = null;
|
|
_this.valuesrc = value;
|
|
_this.font = bitMapPool.get(font);
|
|
if (!_this.font) {
|
|
console.error("Missing BitmapFont \"".concat(font, "\", please invoke API \"registBitMapFont\" before using \"BitMapText\""));
|
|
}
|
|
return _this;
|
|
}
|
|
Object.defineProperty(BitMapText.prototype, "value", {
|
|
get: function () {
|
|
return this.valuesrc;
|
|
},
|
|
set: function (newValue) {
|
|
if (newValue !== this.valuesrc) {
|
|
this.valuesrc = newValue;
|
|
this.emit('repaint');
|
|
}
|
|
},
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
BitMapText.prototype.repaint = function () {
|
|
this.render();
|
|
};
|
|
BitMapText.prototype.destroySelf = function () {
|
|
this.root = null;
|
|
};
|
|
BitMapText.prototype.render = function () {
|
|
var _this = this;
|
|
if (!this.font) {
|
|
return;
|
|
}
|
|
if (this.font.ready) {
|
|
this.renderText(this.ctx);
|
|
}
|
|
else {
|
|
this.font.event.on('text__load__done', function () {
|
|
if (!_this.isDestroyed) {
|
|
_this.renderText(_this.ctx);
|
|
}
|
|
});
|
|
}
|
|
};
|
|
BitMapText.prototype.getTextBounds = function () {
|
|
var style = this.style;
|
|
var _a = style.letterSpacing, letterSpacing = _a === void 0 ? 0 : _a;
|
|
var width = 0;
|
|
// 记录上一个字符,方便处理 kerning
|
|
var prevCharCode = null;
|
|
for (var i = 0, len = this.value.length; i < len; i++) {
|
|
var char = this.value[i];
|
|
var cfg = this.font.chars[char];
|
|
if (cfg) {
|
|
if (prevCharCode && cfg.kerning[prevCharCode]) {
|
|
width += cfg.kerning[prevCharCode];
|
|
}
|
|
width += cfg.xadvance;
|
|
if (i < len - 1) {
|
|
width += letterSpacing;
|
|
}
|
|
}
|
|
}
|
|
return { width: width, height: this.font.lineHeight };
|
|
};
|
|
BitMapText.prototype.renderText = function (ctx) {
|
|
var bounds = this.getTextBounds();
|
|
var defaultLineHeight = this.font.lineHeight;
|
|
ctx.save();
|
|
var _a = this.baseRender(), needStroke = _a.needStroke, originX = _a.originX, originY = _a.originY, drawX = _a.drawX, drawY = _a.drawY;
|
|
var style = this.style;
|
|
var _b = style.width, width = _b === void 0 ? 0 : _b, // 没有设置采用计算出来的宽度
|
|
_c = style.height, // 没有设置采用计算出来的宽度
|
|
height = _c === void 0 ? 0 : _c, // 没有设置则采用计算出来的宽度
|
|
textAlign = style.textAlign, // 文字左右对齐方式
|
|
verticalAlign = style.verticalAlign, _d = style.letterSpacing, letterSpacing = _d === void 0 ? 0 : _d;
|
|
// 没有设置则采用计算出来的高度
|
|
var lineHeight = (style.lineHeight || defaultLineHeight);
|
|
var scaleY = lineHeight / defaultLineHeight;
|
|
var realWidth = scaleY * bounds.width;
|
|
// 如果文字的渲染区域高度小于盒子高度,采用对齐方式
|
|
if (lineHeight < height) {
|
|
if (verticalAlign === 'middle') {
|
|
drawY += (height - lineHeight) / 2;
|
|
}
|
|
else if (verticalAlign === 'bottom') {
|
|
drawY = drawY + height - lineHeight;
|
|
}
|
|
}
|
|
if (width > realWidth) {
|
|
if (textAlign === 'center') {
|
|
drawX += (width - realWidth) / 2;
|
|
}
|
|
else if (textAlign === 'right') {
|
|
drawX += (width - realWidth);
|
|
}
|
|
}
|
|
// 记录上一个字符,方便处理 kerning
|
|
var prevCharCode = null;
|
|
for (var i = 0; i < this.value.length; i++) {
|
|
var char = this.value[i];
|
|
var cfg = this.font.chars[char];
|
|
if (prevCharCode && cfg.kerning[prevCharCode]) {
|
|
drawX += cfg.kerning[prevCharCode];
|
|
}
|
|
if (cfg) {
|
|
ctx.drawImage(this.font.texture, cfg.x, cfg.y, cfg.w, cfg.h, drawX + cfg.offX * scaleY - originX, drawY + cfg.offY * scaleY - originY, cfg.w * scaleY, cfg.h * scaleY);
|
|
drawX += (cfg.xadvance * scaleY + letterSpacing);
|
|
prevCharCode = char;
|
|
}
|
|
}
|
|
if (needStroke) {
|
|
ctx.stroke();
|
|
}
|
|
ctx.translate(-originX, -originY);
|
|
ctx.restore();
|
|
};
|
|
return BitMapText;
|
|
}(_elements__WEBPACK_IMPORTED_MODULE_0__["default"]));
|
|
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (BitMapText);
|
|
|
|
|
|
/***/ }),
|
|
/* 30 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
|
|
/* harmony export */ });
|
|
/* harmony import */ var _elements__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(2);
|
|
/* harmony import */ var _env__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(1);
|
|
var __extends = (undefined && undefined.__extends) || (function () {
|
|
var extendStatics = function (d, b) {
|
|
extendStatics = Object.setPrototypeOf ||
|
|
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
return extendStatics(d, b);
|
|
};
|
|
return function (d, b) {
|
|
if (typeof b !== "function" && b !== null)
|
|
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
extendStatics(d, b);
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
})();
|
|
|
|
|
|
var Canvas = /** @class */ (function (_super) {
|
|
__extends(Canvas, _super);
|
|
function Canvas(opts) {
|
|
var _this = this;
|
|
var _a = opts.style, style = _a === void 0 ? {} : _a, _b = opts.idName, idName = _b === void 0 ? '' : _b, _c = opts.className, className = _c === void 0 ? '' : _c, dataset = opts.dataset, _d = opts.width, width = _d === void 0 ? 100 : _d, _e = opts.height, height = _e === void 0 ? 100 : _e, _f = opts.autoCreateCanvas, autoCreateCanvas = _f === void 0 ? false : _f;
|
|
_this = _super.call(this, {
|
|
idName: idName,
|
|
className: className,
|
|
dataset: dataset,
|
|
style: style,
|
|
}) || this;
|
|
_this.canvasInstance = null;
|
|
/**
|
|
* 微信小游戏场景下,sharedCanvas 实例不方便自动创建,提供 setter 手动设置
|
|
*/
|
|
if (autoCreateCanvas) {
|
|
_this.canvasInstance = _env__WEBPACK_IMPORTED_MODULE_1__["default"].createCanvas();
|
|
_this.canvasInstance.width = Number(width);
|
|
_this.canvasInstance.height = Number(height);
|
|
}
|
|
return _this;
|
|
}
|
|
Object.defineProperty(Canvas.prototype, "canvas", {
|
|
get: function () {
|
|
return this.canvasInstance;
|
|
},
|
|
set: function (cvs) {
|
|
this.canvasInstance = cvs;
|
|
},
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
Canvas.prototype.update = function () {
|
|
this.root.emit('repaint');
|
|
};
|
|
Canvas.prototype.repaint = function () {
|
|
this.render();
|
|
};
|
|
// 子类填充实现
|
|
Canvas.prototype.destroySelf = function () {
|
|
this.isDestroyed = true;
|
|
this.root = null;
|
|
this.canvasInstance = null;
|
|
};
|
|
Canvas.prototype.render = function () {
|
|
if (!this.canvasInstance) {
|
|
return;
|
|
}
|
|
var ctx = this.ctx;
|
|
ctx.save();
|
|
var _a = this.baseRender(), needStroke = _a.needStroke, originX = _a.originX, originY = _a.originY, drawX = _a.drawX, drawY = _a.drawY, width = _a.width, height = _a.height;
|
|
// 自定义渲染逻辑 开始
|
|
ctx.drawImage(this.canvasInstance, drawX - originX, drawY - originY, width, height);
|
|
// 自定义渲染逻辑 结束
|
|
if (needStroke) {
|
|
ctx.stroke();
|
|
}
|
|
if (this.renderForLayout.rotate) {
|
|
ctx.translate(-originX, -originY);
|
|
}
|
|
ctx.restore();
|
|
};
|
|
return Canvas;
|
|
}(_elements__WEBPACK_IMPORTED_MODULE_0__["default"]));
|
|
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Canvas);
|
|
|
|
|
|
/***/ }),
|
|
/* 31 */
|
|
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
|
|
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ "default": () => (__WEBPACK_DEFAULT_EXPORT__)
|
|
/* harmony export */ });
|
|
/* harmony import */ var _text__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(24);
|
|
/* harmony import */ var _common_util__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(7);
|
|
var __extends = (undefined && undefined.__extends) || (function () {
|
|
var extendStatics = function (d, b) {
|
|
extendStatics = Object.setPrototypeOf ||
|
|
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
return extendStatics(d, b);
|
|
};
|
|
return function (d, b) {
|
|
if (typeof b !== "function" && b !== null)
|
|
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
extendStatics(d, b);
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
})();
|
|
var __assign = (undefined && undefined.__assign) || function () {
|
|
__assign = Object.assign || function(t) {
|
|
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
s = arguments[i];
|
|
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
t[p] = s[p];
|
|
}
|
|
return t;
|
|
};
|
|
return __assign.apply(this, arguments);
|
|
};
|
|
|
|
|
|
var Button = /** @class */ (function (_super) {
|
|
__extends(Button, _super);
|
|
function Button(_a) {
|
|
var _b = _a.style, style = _b === void 0 ? {} : _b, _c = _a.idName, idName = _c === void 0 ? '' : _c, _d = _a.className, className = _d === void 0 ? '' : _d, _e = _a.value, value = _e === void 0 ? '' : _e, dataset = _a.dataset;
|
|
var _this = _super.call(this, {
|
|
idName: idName,
|
|
className: className,
|
|
style: __assign(__assign({ width: 300, height: 60, lineHeight: 60, fontSize: 30, borderRadius: 10, backgroundColor: '#34a123', color: '#ffffff', textAlign: 'center' }, style), { ':active': __assign({ transform: 'scale(1.05, 1.05)' }, style[':active']) }),
|
|
value: value,
|
|
dataset: dataset,
|
|
}) || this;
|
|
// 缩放动画的时长
|
|
_this.scaleDuration = 100;
|
|
// 当前缩放动画是否播放完毕
|
|
_this.scaleDone = true;
|
|
// 缩放动画开始的时间
|
|
_this.timeClick = 0;
|
|
// 缩放动画的 scale 初始值,这并不是固定不变的,当点击结束,可能需要从大到小变换
|
|
_this.fromScale = 1;
|
|
// 缩放动画的 scale 目标值
|
|
_this.toScale = 1;
|
|
_this.update = function (dt) {
|
|
if (_this.scaleDone) {
|
|
return;
|
|
}
|
|
_this.timeClick += dt;
|
|
var ratio = 1;
|
|
ratio = _this.timeClick / _this.scaleDuration;
|
|
if (ratio > 1) {
|
|
ratio = 1;
|
|
}
|
|
var scale = (0,_common_util__WEBPACK_IMPORTED_MODULE_1__.lerp)(_this.fromScale, _this.toScale, ratio);
|
|
var transform = "scale(".concat(scale, ", ").concat(scale, ")");
|
|
_this.style.transform = transform;
|
|
if (ratio === 1) {
|
|
_this.scaleDone = true;
|
|
}
|
|
};
|
|
return _this;
|
|
}
|
|
Button.prototype.afterCreate = function () {
|
|
// @ts-ignore
|
|
this.root.ticker.add(this.update);
|
|
};
|
|
Button.prototype.destroySelf = function () {
|
|
// @ts-ignore
|
|
this.root.ticker.remove(this.update);
|
|
this.isDestroyed = true;
|
|
this.root = null;
|
|
};
|
|
return Button;
|
|
}(_text__WEBPACK_IMPORTED_MODULE_0__["default"]));
|
|
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (Button);
|
|
|
|
|
|
/***/ })
|
|
/******/ ]);
|
|
/************************************************************************/
|
|
/******/ // The module cache
|
|
/******/ var __webpack_module_cache__ = {};
|
|
/******/
|
|
/******/ // The require function
|
|
/******/ function __webpack_require__(moduleId) {
|
|
/******/ // Check if module is in cache
|
|
/******/ var cachedModule = __webpack_module_cache__[moduleId];
|
|
/******/ if (cachedModule !== undefined) {
|
|
/******/ return cachedModule.exports;
|
|
/******/ }
|
|
/******/ // Create a new module (and put it into the cache)
|
|
/******/ var module = __webpack_module_cache__[moduleId] = {
|
|
/******/ // no module.id needed
|
|
/******/ // no module.loaded needed
|
|
/******/ exports: {}
|
|
/******/ };
|
|
/******/
|
|
/******/ // Execute the module function
|
|
/******/ __webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
|
/******/
|
|
/******/ // Return the exports of the module
|
|
/******/ return module.exports;
|
|
/******/ }
|
|
/******/
|
|
/************************************************************************/
|
|
/******/ /* webpack/runtime/compat get default export */
|
|
/******/ (() => {
|
|
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
|
/******/ __webpack_require__.n = (module) => {
|
|
/******/ var getter = module && module.__esModule ?
|
|
/******/ () => (module['default']) :
|
|
/******/ () => (module);
|
|
/******/ __webpack_require__.d(getter, { a: getter });
|
|
/******/ return getter;
|
|
/******/ };
|
|
/******/ })();
|
|
/******/
|
|
/******/ /* webpack/runtime/define property getters */
|
|
/******/ (() => {
|
|
/******/ // define getter functions for harmony exports
|
|
/******/ __webpack_require__.d = (exports, definition) => {
|
|
/******/ for(var key in definition) {
|
|
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
|
|
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
|
|
/******/ }
|
|
/******/ }
|
|
/******/ };
|
|
/******/ })();
|
|
/******/
|
|
/******/ /* webpack/runtime/hasOwnProperty shorthand */
|
|
/******/ (() => {
|
|
/******/ __webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
|
|
/******/ })();
|
|
/******/
|
|
/******/ /* webpack/runtime/make namespace object */
|
|
/******/ (() => {
|
|
/******/ // define __esModule on exports
|
|
/******/ __webpack_require__.r = (exports) => {
|
|
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
|
|
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
/******/ }
|
|
/******/ Object.defineProperty(exports, '__esModule', { value: true });
|
|
/******/ };
|
|
/******/ })();
|
|
/******/
|
|
/************************************************************************/
|
|
var __webpack_exports__ = {};
|
|
// This entry needs to be wrapped in an IIFE because it needs to be in strict mode.
|
|
(() => {
|
|
"use strict";
|
|
__webpack_require__.r(__webpack_exports__);
|
|
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
|
|
/* harmony export */ BitMapText: () => (/* reexport safe */ _components__WEBPACK_IMPORTED_MODULE_13__.BitMapText),
|
|
/* harmony export */ Button: () => (/* reexport safe */ _components__WEBPACK_IMPORTED_MODULE_13__.Button),
|
|
/* harmony export */ Canvas: () => (/* reexport safe */ _components__WEBPACK_IMPORTED_MODULE_13__.Canvas),
|
|
/* harmony export */ EE: () => (/* binding */ EE),
|
|
/* harmony export */ Element: () => (/* reexport safe */ _components_elements__WEBPACK_IMPORTED_MODULE_1__["default"]),
|
|
/* harmony export */ Image: () => (/* reexport safe */ _components__WEBPACK_IMPORTED_MODULE_13__.Image),
|
|
/* harmony export */ Layout: () => (/* binding */ Layout),
|
|
/* harmony export */ ScrollView: () => (/* reexport safe */ _components__WEBPACK_IMPORTED_MODULE_13__.ScrollView),
|
|
/* harmony export */ Text: () => (/* reexport safe */ _components__WEBPACK_IMPORTED_MODULE_13__.Text),
|
|
/* harmony export */ View: () => (/* reexport safe */ _components__WEBPACK_IMPORTED_MODULE_13__.View),
|
|
/* harmony export */ "default": () => (/* binding */ layout),
|
|
/* harmony export */ env: () => (/* reexport safe */ _env__WEBPACK_IMPORTED_MODULE_0__["default"])
|
|
/* harmony export */ });
|
|
/* harmony import */ var _env__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
|
|
/* harmony import */ var _components_elements__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);
|
|
/* harmony import */ var _common_pool__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(6);
|
|
/* harmony import */ var tiny_emitter__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(8);
|
|
/* harmony import */ var tiny_emitter__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(tiny_emitter__WEBPACK_IMPORTED_MODULE_3__);
|
|
/* harmony import */ var css_layout__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(10);
|
|
/* harmony import */ var css_layout__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(css_layout__WEBPACK_IMPORTED_MODULE_4__);
|
|
/* harmony import */ var _common_util__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(7);
|
|
/* harmony import */ var _libs_fast_xml_parser_parser_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(11);
|
|
/* harmony import */ var _common_bitMapFont__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(17);
|
|
/* harmony import */ var _common_debugInfo__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(18);
|
|
/* harmony import */ var _common_ticker__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(19);
|
|
/* harmony import */ var _common_vd__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(20);
|
|
/* harmony import */ var _common_rect__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(4);
|
|
/* harmony import */ var _common_imageManager__WEBPACK_IMPORTED_MODULE_12__ = __webpack_require__(5);
|
|
/* harmony import */ var _components__WEBPACK_IMPORTED_MODULE_13__ = __webpack_require__(21);
|
|
var __extends = (undefined && undefined.__extends) || (function () {
|
|
var extendStatics = function (d, b) {
|
|
extendStatics = Object.setPrototypeOf ||
|
|
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
return extendStatics(d, b);
|
|
};
|
|
return function (d, b) {
|
|
if (typeof b !== "function" && b !== null)
|
|
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
|
|
extendStatics(d, b);
|
|
function __() { this.constructor = d; }
|
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
};
|
|
})();
|
|
var __spreadArray = (undefined && undefined.__spreadArray) || function (to, from, pack) {
|
|
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
if (ar || !(i in from)) {
|
|
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
ar[i] = from[i];
|
|
}
|
|
}
|
|
return to.concat(ar || Array.prototype.slice.call(from));
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 全局事件管道
|
|
var EE = new (tiny_emitter__WEBPACK_IMPORTED_MODULE_3___default().TinyEmitter)();
|
|
var imgPool = new _common_pool__WEBPACK_IMPORTED_MODULE_2__["default"]('imgPool');
|
|
var bitMapPool = new _common_pool__WEBPACK_IMPORTED_MODULE_2__["default"]('bitMapPool');
|
|
var debugInfo = new _common_debugInfo__WEBPACK_IMPORTED_MODULE_8__["default"]();
|
|
/**
|
|
* 默认暴露 Layout 的实例,但在某些场景下,可能需要多个 Layout 实例,因此 Layout 类也暴露出去
|
|
* const myLayout = new Layout({
|
|
* style: {
|
|
* width: 0,
|
|
* height: 0,
|
|
* },
|
|
* name: 'myLayoutName',
|
|
* });
|
|
*/
|
|
var Layout = /** @class */ (function (_super) {
|
|
__extends(Layout, _super);
|
|
function Layout(_a) {
|
|
var style = _a.style;
|
|
var _this = _super.call(this, {
|
|
style: style,
|
|
id: 0,
|
|
}) || this;
|
|
/**
|
|
* 当前 Layout 版本,一般跟小游戏插件版本对齐
|
|
*/
|
|
_this.version = '1.0.14';
|
|
_this.env = _env__WEBPACK_IMPORTED_MODULE_0__["default"];
|
|
/**
|
|
* Layout 渲染的目标画布对应的 2d context
|
|
*/
|
|
_this.renderContext = null;
|
|
_this.renderport = {
|
|
width: 0,
|
|
height: 0,
|
|
};
|
|
_this.viewport = {
|
|
width: 0,
|
|
height: 0,
|
|
x: 0,
|
|
y: 0,
|
|
};
|
|
/**
|
|
* 画布尺寸和实际被渲染到屏幕的物理尺寸比
|
|
*/
|
|
_this.viewportScale = 1;
|
|
/**
|
|
* 用于标识updateViewPort方法是否被调用过了,这在小游戏环境非常重要
|
|
*/
|
|
_this.hasViewPortSet = false;
|
|
/**
|
|
* 最终渲染到屏幕的左上角物理坐标
|
|
*/
|
|
_this.realLayoutBox = {
|
|
realX: 0,
|
|
realY: 0,
|
|
};
|
|
_this.bitMapFonts = [];
|
|
_this.eleCount = 0;
|
|
_this.state = _common_util__WEBPACK_IMPORTED_MODULE_5__.STATE.UNINIT;
|
|
/**
|
|
* 用于在 ticker 的循环里面标识当前帧是否需要重绘
|
|
* 重绘一般是图片加载完成、文字修改等场景
|
|
*/
|
|
_this.isNeedRepaint = false;
|
|
_this.ticker = new _common_ticker__WEBPACK_IMPORTED_MODULE_9__["default"]();
|
|
_this.tickerFunc = function () {
|
|
if (_this.isDirty) {
|
|
// console.log('before_reflow')
|
|
_this.emit('before_reflow', '');
|
|
_this.reflow();
|
|
}
|
|
else if (_this.isNeedRepaint) {
|
|
_this.repaint();
|
|
}
|
|
};
|
|
_this.styleSheet = {};
|
|
_this.activeElements = [];
|
|
_this.eventHandler = function (eventName) {
|
|
return function (e) {
|
|
var touch;
|
|
if ((0,_common_util__WEBPACK_IMPORTED_MODULE_5__.isGameTouchEvent)(e)) {
|
|
touch = (e.touches && e.touches[0]) || (e.changedTouches && e.changedTouches[0]);
|
|
}
|
|
else {
|
|
touch = e;
|
|
}
|
|
// const touch = (e.touches && e.touches[0]) || (e.changedTouches && e.changedTouches[0]) || e;
|
|
if (!touch || !touch.pageX || !touch.pageY) {
|
|
return;
|
|
}
|
|
if (!touch.timeStamp) {
|
|
// @ts-ignore
|
|
touch.timeStamp = e.timeStamp;
|
|
}
|
|
var list = [];
|
|
if (touch) {
|
|
_this.getChildByPos(_this, touch.pageX, touch.pageY, list);
|
|
}
|
|
if (!list.length) {
|
|
list.push(_this);
|
|
}
|
|
var item = list[list.length - 1];
|
|
item && item.emit(eventName, e);
|
|
if (eventName === 'touchstart' || eventName === 'touchend') {
|
|
_this.eventHandlerData.touchMsg[eventName] = touch;
|
|
}
|
|
if (eventName === 'touchend' && (0,_common_util__WEBPACK_IMPORTED_MODULE_5__.isClick)(_this.eventHandlerData.touchMsg)) {
|
|
item && item.emit('click', e);
|
|
}
|
|
};
|
|
};
|
|
/**
|
|
* 将组件挂到Layout
|
|
*/
|
|
_this.Element = _components_elements__WEBPACK_IMPORTED_MODULE_1__["default"];
|
|
_this.View = _components__WEBPACK_IMPORTED_MODULE_13__.View;
|
|
_this.Text = _components__WEBPACK_IMPORTED_MODULE_13__.Text;
|
|
_this.Image = _components__WEBPACK_IMPORTED_MODULE_13__.Image;
|
|
_this.ScrollView = _components__WEBPACK_IMPORTED_MODULE_13__.ScrollView;
|
|
_this.BitMapText = _components__WEBPACK_IMPORTED_MODULE_13__.BitMapText;
|
|
_this.Canvas = _components__WEBPACK_IMPORTED_MODULE_13__.Canvas;
|
|
_this.Button = _components__WEBPACK_IMPORTED_MODULE_13__.Button;
|
|
_this.registerComponent = _common_vd__WEBPACK_IMPORTED_MODULE_10__.registerComponent;
|
|
_this.eventHandlerData = {
|
|
hasEventBind: false,
|
|
touchMsg: {},
|
|
handlers: {
|
|
touchStart: _this.eventHandler('touchstart').bind(_this),
|
|
touchMove: _this.eventHandler('touchmove').bind(_this),
|
|
touchEnd: _this.eventHandler('touchend').bind(_this),
|
|
touchCancel: _this.eventHandler('touchcancel').bind(_this),
|
|
},
|
|
};
|
|
/**
|
|
* 对于不会影响布局的改动,比如图片只是改个地址、加个背景色之类的改动,会触发 Layout 的 repaint 操作
|
|
* 触发的方式是给 Layout 抛个 `repaint` 的事件,为了性能,每次接收到 repaint 请求不会执行真正的渲染
|
|
* 而是执行一个置脏操作,ticker 每一次执行 update 会检查这个标记位,进而执行真正的重绘操作
|
|
*/
|
|
_this.on('repaint', function () {
|
|
_this.isNeedRepaint = true;
|
|
});
|
|
/**
|
|
* 将 Tween 挂载到 Layout,对于 Tween 的使用完全遵循 Tween.js 的文档
|
|
* https://github.com/tweenjs/tween.js/
|
|
* 只不过当 Tween 改动了节点会触发 repaint、reflow 的属性时,Layout 会执行相应的操作
|
|
* 业务侧不用感知到 repaint 和 reflow
|
|
*/
|
|
// this.TWEEN = TWEEN;
|
|
console.log("[Layout] v".concat(_this.version));
|
|
return _this;
|
|
}
|
|
Object.defineProperty(Layout.prototype, "debugInfo", {
|
|
// 与老版本兼容
|
|
get: function () {
|
|
var info = debugInfo.log();
|
|
info += "elementCount: ".concat(this.eleCount, "\n");
|
|
return info;
|
|
},
|
|
enumerable: false,
|
|
configurable: true
|
|
});
|
|
/**
|
|
* 更新被绘制canvas的窗口信息,本渲染引擎并不关心是否会和其他游戏引擎共同使用
|
|
* 而本身又需要支持事件处理,因此,如果被渲染内容是绘制到离屏canvas,需要将最终绘制在屏幕上
|
|
* 的绝对尺寸和位置信息更新到本渲染引擎。
|
|
* 其中,width为物理像素宽度,height为物理像素高度,x为距离屏幕左上角的物理像素x坐标,y为距离屏幕左上角的物理像素
|
|
* y坐标
|
|
*/
|
|
Layout.prototype.updateViewPort = function (box) {
|
|
this.viewport.width = box.width || 0;
|
|
this.viewport.height = box.height || 0;
|
|
this.viewport.x = box.x || 0;
|
|
this.viewport.y = box.y || 0;
|
|
this.realLayoutBox = {
|
|
realX: this.viewport.x,
|
|
realY: this.viewport.y,
|
|
};
|
|
this.hasViewPortSet = true;
|
|
};
|
|
Layout.prototype.init = function (template, style, attrValueProcessor) {
|
|
debugInfo.start('init');
|
|
var elementArray = this.insertElementArray(template, style, attrValueProcessor, true);
|
|
this.add(elementArray[0]);
|
|
this.state = _common_util__WEBPACK_IMPORTED_MODULE_5__.STATE.INITED;
|
|
this.ticker.add(this.tickerFunc, true);
|
|
this.ticker.start();
|
|
debugInfo.end('init');
|
|
};
|
|
Layout.prototype.reflow = function (isFirst) {
|
|
if (isFirst === void 0) { isFirst = false; }
|
|
if (!isFirst) {
|
|
debugInfo.reset();
|
|
}
|
|
debugInfo.start('layout_reflow');
|
|
/**
|
|
* 计算布局树
|
|
* 经过 Layout 计算,节点树带上了 layout、lastLayout、shouldUpdate 布局信息
|
|
* Layout本身并不作为布局计算,只是作为节点树的容器
|
|
*/
|
|
debugInfo.start('computeLayout', true);
|
|
css_layout__WEBPACK_IMPORTED_MODULE_4___default()(this.children[0]);
|
|
debugInfo.end('computeLayout');
|
|
var rootEle = this.children[0];
|
|
if (rootEle.style.width === undefined || rootEle.style.height === undefined) {
|
|
console.error('[Layout] Please set width and height property for root element');
|
|
}
|
|
else {
|
|
this.renderport.width = rootEle.style.width;
|
|
this.renderport.height = rootEle.style.height;
|
|
}
|
|
// 将布局树的布局信息加工赋值到渲染树
|
|
debugInfo.start('layoutChildren', true);
|
|
(0,_common_vd__WEBPACK_IMPORTED_MODULE_10__.layoutChildren)(this);
|
|
debugInfo.end('layoutChildren');
|
|
this.viewportScale = this.viewport.width / this.renderport.width;
|
|
(0,_common_util__WEBPACK_IMPORTED_MODULE_5__.clearCanvas)(this.renderContext);
|
|
// 遍历节点树,依次调用节点的渲染接口实现渲染
|
|
debugInfo.start('renderChildren', true);
|
|
(0,_common_vd__WEBPACK_IMPORTED_MODULE_10__.renderChildren)(this.children, this.renderContext, false);
|
|
debugInfo.end('renderChildren');
|
|
debugInfo.start('repaint', true);
|
|
this.repaint();
|
|
debugInfo.end('repaint');
|
|
this.isDirty = false;
|
|
// iterateTree(this.children[0], (ele) => {
|
|
// console.log(ele.props);
|
|
// });
|
|
debugInfo.end('layout_reflow');
|
|
};
|
|
/**
|
|
* init阶段核心仅仅是根据xml和css创建了节点树
|
|
* 要实现真正的渲染,需要调用 layout 函数,之所以将 layout 单独抽象为一个函数,是因为 layout 应当是可以重复调用的
|
|
* 比如改变了一个元素的尺寸,实际上节点树是没变的,仅仅是需要重新计算布局,然后渲染
|
|
* 一个完整的 layout 分成下面的几步:
|
|
* 1. 执行画布清理,因为布局变化页面需要重绘,这里没有做很高级的剔除等操作,一律清除重画,实际上性能已经很好
|
|
* 2. 节点树都含有 style 属性,css-layout 能够根据这些信息计算出最终布局,详情可见 https://www.npmjs.com/package/css-layout
|
|
* 3. 经过 Layout 计算,节点树带上了 layout、lastLayout、shouldUpdate 布局信息,但这些信息并不是能够直接用的
|
|
* 比如 layout.top 是指在一个父容器内的 top,最终要实现渲染,实际上要递归加上复容器的 top
|
|
* 这样每次 repaint 的时候只需要直接使用计算好的值即可,不需要每次都递归计算
|
|
* 这一步称为 layoutChildren,目的在于将 css-layout 进一步处理为可以渲染直接用的布局信息
|
|
* 4. renderChildren:执行渲染
|
|
* 5. bindEvents:执行事件绑定
|
|
*/
|
|
// @ts-ignore
|
|
Layout.prototype.layout = function (context) {
|
|
this.renderContext = context;
|
|
if (!this.hasViewPortSet) {
|
|
console.error('[Layout] Please invoke method `updateViewPort` before method `layout`');
|
|
}
|
|
debugInfo.start('layout');
|
|
this.reflow(true);
|
|
debugInfo.start('layout_other');
|
|
this.bindEvents();
|
|
debugInfo.start('layout_observeStyleAndEvent', true);
|
|
(0,_common_vd__WEBPACK_IMPORTED_MODULE_10__.iterateTree)(this.children[0], function (element) { return element.observeStyleAndEvent(); });
|
|
debugInfo.end('layout_observeStyleAndEvent');
|
|
this.state = _common_util__WEBPACK_IMPORTED_MODULE_5__.STATE.RENDERED;
|
|
debugInfo.end('layout');
|
|
debugInfo.end('layout_other');
|
|
};
|
|
/**
|
|
* 执行节点数的重绘制,一般业务侧无需调用该方法
|
|
*/
|
|
Layout.prototype.repaint = function () {
|
|
(0,_common_util__WEBPACK_IMPORTED_MODULE_5__.clearCanvas)(this.renderContext);
|
|
this.isNeedRepaint = false;
|
|
(0,_common_vd__WEBPACK_IMPORTED_MODULE_10__.repaintChildren)(this.children);
|
|
};
|
|
/**
|
|
* 返回一个节点在屏幕中的位置和尺寸信息,前提是正确调用updateViewPort。
|
|
*/
|
|
Layout.prototype.getElementViewportRect = function (element) {
|
|
var _a = this, realLayoutBox = _a.realLayoutBox, viewportScale = _a.viewportScale;
|
|
var _b = element.layoutBox, absoluteX = _b.absoluteX, absoluteY = _b.absoluteY, width = _b.width, height = _b.height;
|
|
var realX = absoluteX * viewportScale + realLayoutBox.realX;
|
|
var realY = absoluteY * viewportScale + realLayoutBox.realY;
|
|
var realWidth = width * viewportScale;
|
|
var realHeight = height * viewportScale;
|
|
return new _common_rect__WEBPACK_IMPORTED_MODULE_11__["default"](realX, realY, realWidth, realHeight);
|
|
};
|
|
Layout.prototype.getChildByPos = function (tree, x, y, itemList) {
|
|
var _this = this;
|
|
tree.children.forEach(function (ele) {
|
|
var _a = ele.layoutBox, absoluteX = _a.absoluteX, absoluteY = _a.absoluteY, width = _a.width, height = _a.height;
|
|
var realX = absoluteX * _this.viewportScale + _this.realLayoutBox.realX;
|
|
var realY = absoluteY * _this.viewportScale + _this.realLayoutBox.realY;
|
|
var realWidth = width * _this.viewportScale;
|
|
var realHeight = height * _this.viewportScale;
|
|
if ((realX <= x && x <= realX + realWidth) && (realY <= y && y <= realY + realHeight)) {
|
|
/**
|
|
* 相关issue:https://github.com/wechat-miniprogram/minigame-canvas-engine/issues/17
|
|
* 这里只要满足条件的都要记录,否则可能出现 issue 里面提到的问题
|
|
*/
|
|
itemList.push(ele);
|
|
if (ele.children.length) {
|
|
_this.getChildByPos(ele, x, y, itemList);
|
|
}
|
|
}
|
|
});
|
|
};
|
|
/**
|
|
* 执行全局的事件绑定逻辑
|
|
*/
|
|
Layout.prototype.bindEvents = function () {
|
|
var _this = this;
|
|
if (this.eventHandlerData.hasEventBind) {
|
|
return;
|
|
}
|
|
this.eventHandlerData.hasEventBind = true;
|
|
_env__WEBPACK_IMPORTED_MODULE_0__["default"].onTouchStart(this.eventHandlerData.handlers.touchStart);
|
|
_env__WEBPACK_IMPORTED_MODULE_0__["default"].onTouchMove(this.eventHandlerData.handlers.touchMove);
|
|
_env__WEBPACK_IMPORTED_MODULE_0__["default"].onTouchEnd(this.eventHandlerData.handlers.touchEnd);
|
|
_env__WEBPACK_IMPORTED_MODULE_0__["default"].onTouchCancel(this.eventHandlerData.handlers.touchCancel);
|
|
/**
|
|
* 当触发 touchstart 事件的时候,如果手指移除元素外,不会触发 touchend,这就导致 deactiveHandler 不能触发
|
|
* 要做到比较完善,事件系统要做较大改用,目前比较好的做法就是根节点在监听到 touchend 和 touchcancel 的时候兜底
|
|
* 触发下 deactiveHandler
|
|
*/
|
|
this.on('touchend', function () {
|
|
_this.activeElements.forEach(function (ele) {
|
|
ele.deactiveHandler();
|
|
});
|
|
_this.activeElements = [];
|
|
});
|
|
this.on('touchcancel', function () {
|
|
_this.activeElements.forEach(function (ele) {
|
|
ele.deactiveHandler();
|
|
});
|
|
_this.activeElements = [];
|
|
});
|
|
};
|
|
/**
|
|
* 全局事件解绑
|
|
*/
|
|
Layout.prototype.unBindEvents = function () {
|
|
_env__WEBPACK_IMPORTED_MODULE_0__["default"].offTouchStart(this.eventHandlerData.handlers.touchStart);
|
|
_env__WEBPACK_IMPORTED_MODULE_0__["default"].offTouchMove(this.eventHandlerData.handlers.touchMove);
|
|
_env__WEBPACK_IMPORTED_MODULE_0__["default"].offTouchEnd(this.eventHandlerData.handlers.touchEnd);
|
|
_env__WEBPACK_IMPORTED_MODULE_0__["default"].offTouchCancel(this.eventHandlerData.handlers.touchCancel);
|
|
this.eventHandlerData.hasEventBind = false;
|
|
};
|
|
Layout.prototype.emit = function (event, data) {
|
|
EE.emit(event, data);
|
|
};
|
|
Layout.prototype.on = function (event, callback) {
|
|
EE.on(event, callback);
|
|
};
|
|
Layout.prototype.once = function (event, callback) {
|
|
EE.once(event, callback);
|
|
};
|
|
Layout.prototype.off = function (event, callback) {
|
|
EE.off(event, callback);
|
|
};
|
|
Layout.prototype.destroyAll = function (tree) {
|
|
var _this = this;
|
|
var children = tree.children;
|
|
children.forEach(function (child) {
|
|
child.destroy();
|
|
_this.destroyAll(child);
|
|
child.destroySelf && child.destroySelf();
|
|
});
|
|
};
|
|
/**
|
|
* 清理画布,之前的计算出来的渲染树也会一并清理,此时可以再次执行init和layout方法渲染界面。
|
|
*/
|
|
Layout.prototype.clear = function (options) {
|
|
if (options === void 0) { options = {}; }
|
|
var _a = options.removeTicker, removeTicker = _a === void 0 ? true : _a;
|
|
debugInfo.reset();
|
|
this.destroyAll(this);
|
|
// this.elementTree = null;
|
|
this.children = [];
|
|
this.state = _common_util__WEBPACK_IMPORTED_MODULE_5__.STATE.CLEAR;
|
|
this.isDirty = false;
|
|
(0,_common_util__WEBPACK_IMPORTED_MODULE_5__.clearCanvas)(this.renderContext);
|
|
this.eleCount = 0;
|
|
this.unBindEvents();
|
|
if (removeTicker) {
|
|
this.ticker.remove();
|
|
this.ticker.stop();
|
|
}
|
|
else {
|
|
// inner的应该默认都移除,否则前后两次初始化会导致前后状态有问题
|
|
this.ticker.removeInner();
|
|
}
|
|
this.activeElements = [];
|
|
};
|
|
Layout.prototype.clearPool = function () {
|
|
imgPool.clear();
|
|
};
|
|
/**
|
|
* 比起 Layout.clear 更彻底的清理,会清空图片对象池,减少内存占用。
|
|
*/
|
|
Layout.prototype.clearAll = function () {
|
|
this.clear();
|
|
this.clearPool();
|
|
};
|
|
/**
|
|
* 对于图片资源,如果不提前加载,渲染过程中可能出现挨个出现图片效果,影响体验
|
|
* 通过Layout.loadImgs可以预加载图片资源,在调用Layout.layout的时候渲染性能更好,体验更佳。
|
|
*/
|
|
Layout.prototype.loadImgs = function (arr) {
|
|
if (arr === void 0) { arr = []; }
|
|
return Promise.all(arr.map(function (src) { return _common_imageManager__WEBPACK_IMPORTED_MODULE_12__["default"].loadImagePromise(src); }));
|
|
};
|
|
/**
|
|
* 注册 bitmaptext 可用的字体。
|
|
*/
|
|
Layout.prototype.registBitMapFont = function (name, src, config) {
|
|
if (!bitMapPool.get(name)) {
|
|
var font = new _common_bitMapFont__WEBPACK_IMPORTED_MODULE_7__["default"](name, src, config);
|
|
this.bitMapFonts.push(font);
|
|
bitMapPool.set(name, font);
|
|
}
|
|
};
|
|
/**
|
|
* 创建节点,创建之后会返回Element列表,可以传入parent立刻插入节点,也可以稍后主动appendChild到需要的节点下
|
|
*/
|
|
Layout.prototype.insertElement = function (template, style, parent) {
|
|
var elementArray = this.insertElementArray(template, style);
|
|
elementArray.forEach(function (it) {
|
|
(0,_common_vd__WEBPACK_IMPORTED_MODULE_10__.iterateTree)(it, function (element) { return element.observeStyleAndEvent(); });
|
|
if (parent) {
|
|
parent.appendChild(it);
|
|
}
|
|
});
|
|
return elementArray;
|
|
};
|
|
/**
|
|
* 克隆节点,克隆后的节点可以添加到 Layout 的某个节点中
|
|
* 该方法可以在数据有变化的时候避免重新执行 Layout.init 流程。
|
|
*/
|
|
Layout.prototype.cloneNode = function (element, deep) {
|
|
if (deep === void 0) { deep = true; }
|
|
return (0,_common_vd__WEBPACK_IMPORTED_MODULE_10__.clone)(this, element, deep);
|
|
};
|
|
/**
|
|
* 安装给定的插件
|
|
*/
|
|
Layout.prototype.use = function (plugin) {
|
|
var options = [];
|
|
for (var _i = 1; _i < arguments.length; _i++) {
|
|
options[_i - 1] = arguments[_i];
|
|
}
|
|
if (Layout.installedPlugins.includes(plugin)) {
|
|
console.warn('[Layout] 该插件已安装.');
|
|
return;
|
|
}
|
|
plugin.install.apply(plugin, __spreadArray([this], options, false));
|
|
Layout.installedPlugins.push(plugin);
|
|
// console.log(`[Layout] 插件 ${plugin.name || ''} 已安装`)
|
|
};
|
|
/**
|
|
* 卸载给定插件
|
|
*/
|
|
Layout.prototype.unUse = function (plugin) {
|
|
var options = [];
|
|
for (var _i = 1; _i < arguments.length; _i++) {
|
|
options[_i - 1] = arguments[_i];
|
|
}
|
|
var pluginIndex = Layout.installedPlugins.indexOf(plugin);
|
|
if (pluginIndex === -1) {
|
|
console.warn('[Layout] This plugin is not installed.');
|
|
return;
|
|
}
|
|
if (plugin.uninstall) {
|
|
plugin.uninstall.apply(plugin, __spreadArray([this], options, false));
|
|
}
|
|
// console.log(`[Layout] 插件 ${plugin.name || ''} 已卸载`)
|
|
Layout.installedPlugins.splice(pluginIndex, 1);
|
|
};
|
|
/**
|
|
* 创建节点,创建之后会返回Element列表
|
|
*/
|
|
Layout.prototype.insertElementArray = function (template, style, attrValueProcessor, onlyFirst) {
|
|
var _this = this;
|
|
// 样式表存到全局
|
|
this.styleSheet = Object.assign(this.styleSheet, style);
|
|
var parseConfig = {
|
|
attributeNamePrefix: '',
|
|
attrNodeName: 'attr', // default is 'false'
|
|
textNodeName: '#text',
|
|
ignoreAttributes: false,
|
|
ignoreNameSpace: true,
|
|
allowBooleanAttributes: true,
|
|
parseNodeValue: false,
|
|
parseAttributeValue: false,
|
|
trimValues: true,
|
|
parseTrueNumberOnly: false,
|
|
alwaysCreateTextNode: true,
|
|
};
|
|
if (attrValueProcessor && typeof attrValueProcessor === 'function') {
|
|
// @ts-ignore
|
|
parseConfig.attrValueProcessor = attrValueProcessor;
|
|
}
|
|
debugInfo.start('insert_xmlParse');
|
|
// 将xml字符串解析成xml节点树
|
|
var jsonObj = _libs_fast_xml_parser_parser_js__WEBPACK_IMPORTED_MODULE_6__.parse(template, parseConfig, true);
|
|
// console.log(jsonObj)
|
|
debugInfo.end('insert_xmlParse');
|
|
var getElements = [];
|
|
jsonObj.children.forEach(function (xmlTree, index) {
|
|
if (onlyFirst && index > 0) {
|
|
return;
|
|
}
|
|
// XML树生成渲染树
|
|
debugInfo.start('insert_xml2Layout');
|
|
var layoutTree = _common_vd__WEBPACK_IMPORTED_MODULE_10__.create.call(_this, xmlTree, _this.styleSheet);
|
|
debugInfo.end('insert_xml2Layout');
|
|
getElements.push(layoutTree);
|
|
});
|
|
return getElements;
|
|
};
|
|
Layout.installedPlugins = [];
|
|
return Layout;
|
|
}(_components_elements__WEBPACK_IMPORTED_MODULE_1__["default"]));
|
|
var layout = new Layout({
|
|
style: {
|
|
width: 0,
|
|
height: 0,
|
|
},
|
|
name: 'layout',
|
|
});
|
|
|
|
|
|
})();
|
|
|
|
module.exports = __webpack_exports__;
|
|
/******/ })()
|
|
;
|