/******/ (() => { // 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) { // 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 . 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 . 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 . 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 . 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; } // 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; } } // 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 ); } // 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; } // // // Note(prenaux): More than one line, we need to layout the crossAxis // according to alignContent. // // Note that we could probably remove 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; } } // 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); } } } // 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(//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(/()/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 null * @param top {Number?null} Vertical scroll position, keeps current if value is null * @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__; /******/ })() ;