/* * Version: 2.0.20 * Build time: Tue Feb 28 2017 19:19:39 GMT+0800 (中国标准时间) * Copyright 2016, Alibaba */ /******/ (function(modules) { // webpackBootstrap /******/ // The module cache /******/ var installedModules = {}; /******/ // The require function /******/ function __webpack_require__(moduleId) { /******/ // Check if module is in cache /******/ if(installedModules[moduleId]) /******/ return installedModules[moduleId].exports; /******/ // Create a new module (and put it into the cache) /******/ var module = installedModules[moduleId] = { /******/ exports: {}, /******/ id: moduleId, /******/ loaded: false /******/ }; /******/ // Execute the module function /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__); /******/ // Flag the module as loaded /******/ module.loaded = true; /******/ // Return the exports of the module /******/ return module.exports; /******/ } /******/ // expose the modules object (__webpack_modules__) /******/ __webpack_require__.m = modules; /******/ // expose the module cache /******/ __webpack_require__.c = installedModules; /******/ // __webpack_public_path__ /******/ __webpack_require__.p = ""; /******/ // Load entry module and return exports /******/ return __webpack_require__(0); /******/ }) /************************************************************************/ /******/ ([ /* 0 */ /***/ function(module, exports, __webpack_require__) { // webpack入口,用于打包标签引用的build文件,引入页面后会创建全局的FocusEngine对象。 function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _libEngineJs = __webpack_require__(1); var _libEngineJs2 = _interopRequireDefault(_libEngineJs); if (!window.FocusEngine) { window.FocusEngine = _libEngineJs2['default']; } /***/ }, /* 1 */ /***/ function(module, exports, __webpack_require__) { Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } var _event = __webpack_require__(2); var _event2 = _interopRequireDefault(_event); var _widget = __webpack_require__(3); var _widget2 = _interopRequireDefault(_widget); var _switch = __webpack_require__(4); var _switch2 = _interopRequireDefault(_switch); var _scroll = __webpack_require__(5); var _scroll2 = _interopRequireDefault(_scroll); var _grid = __webpack_require__(8); var _grid2 = _interopRequireDefault(_grid); var _grille = __webpack_require__(9); var _grille2 = _interopRequireDefault(_grille); var Engine = { Event: _event2['default'], Widget: _widget2['default'], Switch: _switch2['default'], Scroll: _scroll2['default'], Grid: _grid2['default'], Grille: _grille2['default'], roles: ['Widget', 'Switch', 'Scroll', 'Grid', 'Grille'], render: function render(dom, noPreventDefault) { if (dom === undefined) dom = document.body; var domRole = dom.getAttribute('fe-role'); // 如果fe-role错误,抛出 if (domRole && this.roles.indexOf(domRole) === -1) { console.error(dom); console.error('[FocusEngine_Error](' + domRole + ')不是支持的fe-role类型,只支持以下类型:' + this.roles.join('、')); return; } // 默认Widget if (!domRole) { dom.setAttribute('fe-role', 'Widget'); } // 如果该页面已经有组件树,那么只需要reRender该组件树即可 if (window.$FocusEngineGlobalRoot) { window.$FocusEngineGlobalRoot.reRender(); return window.$FocusEngineGlobalRoot.getWidgetById(dom.id); } // 如果该页面还没有组件树,那么需要从body渲染页面组件树,然后返回dom对应的组件 var bodyRole = document.body.getAttribute('fe-role'); // 如果fe-role错误,抛出 if (bodyRole && this.roles.indexOf(bodyRole) === -1) { console.error('[FocusEngine_Error](' + bodyRole + ')不是支持的fe-role类型,只支持以下类型:' + this.roles.join('、')); return; } // 默认Widget if (!bodyRole) { document.body.setAttribute('fe-role', 'Widget'); bodyRole = 'Widget'; } window.$FocusEngineGlobalRoot = new this[bodyRole](document.body); window.$FocusEngineGlobalRoot.focus(); window.$FocusEngineGlobalRoot.handleRootEvent(noPreventDefault); return window.$FocusEngineGlobalRoot.getWidgetById(dom.id); }, // 通过dom节点创建一个widget并递归创建dom的内部widget createWidget: function createWidget(dom) { var domRole = dom.getAttribute('fe-role'); // 如果fe-role错误,抛出 if (domRole && this.roles.indexOf(domRole) === -1) { console.error(dom); console.error('[FocusEngine_Error](' + domRole + ')不是支持的fe-role类型,只支持以下类型:' + this.roles.join('、')); return; } // 默认Widget if (!domRole) { dom.setAttribute('fe-role', 'Widget'); } return new this[domRole](dom); }, appendWidget: function appendWidget(dom) { var widget = this.createWidget(dom); while (dom.parentNode) { dom = dom.parentNode; if (!dom.getAttribute('fe-role')) { continue; } var id = dom.id; this.getWidgetById(id).addChildWidget(widget); return; } }, // 获取整个页面组件树的根组件 getRoot: function getRoot() { return window.$FocusEngineGlobalRoot; }, getWidgetById: function getWidgetById(id) { if (window.$FocusEngineGlobalRoot) { return window.$FocusEngineGlobalRoot.getWidgetById(id); } else { return null; } }, getFocusedLeaf: function getFocusedLeaf() { if (window.$FocusEngineGlobalRoot) { return window.$FocusEngineGlobalRoot.getFocusedLeaf(); } else { return null; } }, freeze: function freeze(blur) { window.$FocusEngineGlobalRoot && window.$FocusEngineGlobalRoot.freeze(blur); }, activate: function activate() { window.$FocusEngineGlobalRoot && window.$FocusEngineGlobalRoot.activate(); }, disableClick: function disableClick() { window.$FocusEngineGlobalRoot && window.$FocusEngineGlobalRoot.disableClick(); }, enableClick: function enableClick() { window.$FocusEngineGlobalRoot && window.$FocusEngineGlobalRoot.enableClick(); } }; exports['default'] = Engine; module.exports = exports['default']; /***/ }, /* 2 */ /***/ function(module, exports) { Object.defineProperty(exports, "__esModule", { value: true }); var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Event = (function () { /** * type:事件类型 * target:触发事件的组件 * data:其他事件属性,如{keyCode: 13} */ function Event(type, target, data) { _classCallCheck(this, Event); this.type = type; this._propagationStopped = false; this._defaultPrevented = false; this._listenerPrevented = false; if (target) { this.target = target; } if (data) { for (var pro in data) { this[pro] = data[pro]; } } } // 停止冒泡 Event.prototype.stopPropagation = function stopPropagation() { this._propagationStopped = true; }; // 该事件是否已经停止冒泡 Event.prototype.isPropagationStopped = function isPropagationStopped() { return this._propagationStopped; }; // 禁止默认事件 Event.prototype.preventDefault = function preventDefault() { this._defaultPrevented = true; }; // 是否已经禁止默认事件 Event.prototype.isDefaultPrevented = function isDefaultPrevented() { return this._defaultPrevented; }; // 禁止触发通过on添加的listener Event.prototype.preventListener = function preventListener() { this._listenerPrevented = true; }; // 是否已经禁止Listener Event.prototype.isListenerPrevented = function isListenerPrevented() { return this._listenerPrevented; }; _createClass(Event, null, [{ key: "VK_ENTER", get: function get() { return 13; } }, { key: "VK_LEFT", get: function get() { return 37; } }, { key: "VK_UP", get: function get() { return 38; } }, { key: "VK_RIGHT", get: function get() { return 39; } }, { key: "VK_DOWN", get: function get() { return 40; } }]); return Event; })(); exports["default"] = Event; module.exports = exports["default"]; /***/ }, /* 3 */ /***/ function(module, exports, __webpack_require__) { Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } var _event = __webpack_require__(2); var _event2 = _interopRequireDefault(_event); var _engine = __webpack_require__(1); var _engine2 = _interopRequireDefault(_engine); var Widget = (function () { /** * @param container {HTMLElement} 组件的容器dom元素 */ function Widget(container) { _classCallCheck(this, Widget); if (!container) { console.error('[FocusEngine_Error]创建组件必须传入相应的容器元素。'); return; } this.con = container; if (this.con.id) { this.id = this.con.id; if (document.querySelector('#' + this.id) !== this.con) { console.error('[FocusEngine_Error]页面元素有重复的id(' + this.id + '),请检查!'); } } else { // 生成随机ID var randomId = Math.random().toString().slice(2, 12); var confirmed = false; while (!confirmed) { if (document.getElementById('fe-' + randomId)) { randomId = Math.random().toString().slice(2, 12); } else { confirmed = true; } } this.id = 'fe-' + randomId; this.con.id = this.id; } this.options = this._getOptions(container); this.parentWidget = null; this.focusedChildWidget = null; this.cachedChildWidget = null; this.active = true; this.disabled = this.options.cfg.disable === 'yes'; this.noClick = this.options.cfg.disable_click === 'yes'; if (this.disabled) { this.addClass('fe-disable'); } this.eventListeners = []; this._initChildWidgets(); this._getOriginalRect(); } /** * 当widget对应的dom结构发生改变时,进行重新渲染。 * 渲染会根据变动的dom来针对性的进行,如果某个widget的直接子widget没有变化,那么不进行rerender */ Widget.prototype.reRender = function reRender() { if (!document.querySelector('#' + this.id)) { return; } this.con = document.querySelector('#' + this.id); this.options = this._getOptions(this.con); this._reRenderChildWidget(); this._getOriginalRect(); }; Widget.prototype._getOriginalRect = function _getOriginalRect() { this.originalRect = this.getWidgetRect(); }; Widget.prototype.getFutureRect = function getFutureRect() { var widget = this; var rect = widget.getWidgetRect(); var originalRect = widget.originalRect; while (widget.getParentWidget()) { var _parent = widget.getParentWidget(); if (_parent instanceof _engine2['default'].Scroll) { if (_parent.dir === 'v') { rect.top = originalRect.top + _parent.futureOffset; rect.bottom = originalRect.bottom + _parent.futureOffset; rect.centerY = originalRect.centerY + _parent.futureOffset; } else if (_parent.dir === 'h') { rect.left = originalRect.left + _parent.futureOffset; rect.right = originalRect.right + _parent.futureOffset; rect.centerX = originalRect.centerX + _parent.futureOffset; } } if (_parent instanceof _engine2['default'].Grid) { if (_parent.horizontal) { rect.left = originalRect.left + widget.futureOffset; rect.right = originalRect.right + widget.futureOffset; rect.centerX = originalRect.centerX + widget.futureOffset; } else { rect.top = originalRect.top + widget.futureOffset; rect.bottom = originalRect.bottom + widget.futureOffset; rect.centerY = originalRect.centerY + widget.futureOffset; } } widget = _parent; } return rect; }; Widget.prototype._reRenderChildWidget = function _reRenderChildWidget() { var descendantNodes = Array.prototype.slice.call(this.con.querySelectorAll('[fe-role]')); // 排除掉深层次的可初始化为组件的dom, // 只保留遍历到的首层dom for (var i = 0; i < descendantNodes.length; i++) { var descd = descendantNodes[i]; var role = descd.getAttribute('fe-role'); if (_engine2['default'].roles.indexOf(role) === -1) { console.error(descd); console.error('[FocusEngine_Error](' + role + ')不是支持的fe-role类型,只支持以下类型:' + _engine2['default'].roles.join('、')); continue; } descendantNodes = this._filterArray(descendantNodes, Array.prototype.slice.call(descendantNodes[i].querySelectorAll('[fe-role]'))); } // 首先删除容器已经被删除的子组件 for (var i = 0; i < this.childWidgets.length; i++) { if (descendantNodes.indexOf(this.childWidgets[i].getDOMNode()) === -1) { this.removeChildWidget(i); i--; continue; } var tempRole = this.childWidgets[i].getDOMNode().getAttribute('fe-role'); if (_engine2['default'].roles.indexOf(tempRole) === -1 || !(this.childWidgets[i] instanceof _engine2['default'][tempRole])) { this.removeChildWidget(i); i--; } } // 其次递归遍历需要保持(容器还存在)的子组件 for (var i = 0; i < this.childWidgets.length; i++) { this.childWidgets[i].reRender(); } // 最后创建新增加的组件,并添加到子组件中 for (var i = 0; i < descendantNodes.length && descendantNodes.length > this.childWidgets.length; i++) { var tempNode = descendantNodes[i]; var alreadyHas = false; for (var j = 0; j < this.childWidgets.length; j++) { var tempWidget = this.childWidgets[j]; if (tempWidget.getDOMNode() === tempNode) { alreadyHas = true; } } if (!alreadyHas) { var role = tempNode.getAttribute('fe-role'); if (_engine2['default'].roles.indexOf(role) !== -1) { var newWidget = new _engine2['default'][role](tempNode); this.addChildWidget(newWidget, i); } } } if (this.focused) { this.focus(true, true); } }; // 获取整个页面组件树的根组件 Widget.prototype.getRoot = function getRoot() { return window.$FocusEngineGlobalRoot; }; /** * 挂起组件树。 * @param blur {Boolean} 是否在挂起后清空焦点链路。 */ Widget.prototype.freeze = function freeze(blur) { var root = this.getRoot(); if (!root) { return this; } root.active = false; if (blur) { // 若清空焦点,记录挂起前处于焦点状态的叶子节点 root.focusedLeafBeforeFreeze = root.getFocusedLeaf(); root.blur(); } return this; }; // 激活组件同时还原焦点链路(若挂起时清空) Widget.prototype.activate = function activate() { var root = this.getRoot(); if (!root) { return this; } root.active = true; // 若挂起的时候有缓存焦点,激活的时候要还原缓存的焦点 if (root.focusedLeafBeforeFreeze) { if (document.querySelector('#' + root.focusedLeafBeforeFreeze.id) === root.focusedLeafBeforeFreeze.getDOMNode()) { root.focusedLeafBeforeFreeze.focus(); } else { root.focus(); } root.focusedLeafBeforeFreeze = null; } return this; }; Widget.prototype.disable = function disable() { this.disabled = true; this.addClass('fe-disable'); return this; }; Widget.prototype.enable = function enable() { this.disabled = false; this.removeClass('fe-disable'); return this; }; Widget.prototype._getOptions = function _getOptions(node) { var options = {}; this.feCfg = this._removeSpaces(node.getAttribute('fe-cfg')); var cfg = {}; if (this.feCfg) { var cfgArr = this.feCfg.split(/,|;/); cfgArr.forEach(function (c) { var tmpArr = c.split(':'); if (tmpArr.length < 2) return false; tmpArr[0] = tmpArr[0].replace(/switch_|scroll_|grid_/g, ''); cfg[tmpArr[0]] = tmpArr[1]; }); } options.cfg = cfg; var dataDir = this._removeSpaces(node.getAttribute('fe-goto')); var dir = {}; if (dataDir) { var dirArr = dataDir.split(';'); if (dirArr[0]) dir.up = dirArr[0]; if (dirArr[1]) dir.right = dirArr[1]; if (dirArr[2]) dir.down = dirArr[2]; if (dirArr[3]) dir.left = dirArr[3]; } options.dir = dir; options.tab = this._removeSpaces(node.getAttribute('fe-index')); return options; }; // 遍历dom树,自动映射为组件树 Widget.prototype._initChildWidgets = function _initChildWidgets() { this.childWidgets = []; this.childMap = {}; var descendantNodes = Array.prototype.slice.call(this.con.querySelectorAll('[fe-role]')); // 排除掉深层次的可初始化为组件的dom, // 只保留遍历到的首层dom for (var i = 0; i < descendantNodes.length; i++) { var descd = descendantNodes[i]; var role = descd.getAttribute('fe-role'); if (_engine2['default'].roles.indexOf(role) === -1) { console.error(descd); console.error('[FocusEngine_Error](' + role + ')不是支持的fe-role类型,只支持以下类型:' + _engine2['default'].roles.join('、')); continue; } descendantNodes = this._filterArray(descendantNodes, Array.prototype.slice.call(descendantNodes[i].querySelectorAll('[fe-role]'))); } var node = undefined, w = undefined; for (var i = 0, j = descendantNodes.length; i < j; i++) { node = descendantNodes[i]; // 如果fe-role为可以初始化的组件名,则初始化子组件 var role = node.getAttribute('fe-role'); if (_engine2['default'].roles.indexOf(role) !== -1) { // 子组件不需要focus,但需要继续递归初始化 w = new _engine2['default'][role](node); this.addChildWidget(w); } } }; Widget.prototype.handleRootEvent = function handleRootEvent(noPreventDefault) { var _this = this; document.addEventListener('keydown', function (e) { if (!_this.active) { return; } e = e || window.event; var k = e.keyCode, l = _this.getFocusedLeaf(); if (k) { l.fire(new _event2['default']('keydown', l, { keyCode: k })); if (l && k === _event2['default'].VK_ENTER) { l.fire(new _event2['default']('okdown', l, { keyCode: k })); } if (!noPreventDefault) { e.preventDefault(); } } }); document.addEventListener('keyup', function (e) { if (!_this.active) { return; } e = e || window.event; var k = e.keyCode, l = _this.getFocusedLeaf(); if (k) { l && l.fire(new _event2['default']('keyup', l, { keyCode: k })); if (l && k === _event2['default'].VK_ENTER) { l.fire(new _event2['default']('ok', l, { keyCode: k })); } if (!noPreventDefault) { e.preventDefault(); } } }); document.addEventListener('click', function (e) { if (!_this.active) { return; } e = e || window.event; var t = e.target; var w = null; while (t && t !== document) { // 自下向上找到第一个组件容器 if (_engine2['default'].roles.indexOf(t.getAttribute('fe-role')) !== -1) { w = _this.getWidgetById(t.id); // 如果是叶子组件的容器,那么触发click事件并focus if (w && w.isLeafWidget()) { var p = w; while (p) { if (p.noClick) { return; } p = p.parentWidget; } w && w.fire(new _event2['default']('click', w)); if (!noPreventDefault) { e.preventDefault(); } if (!w.disabled && w.getDOMNode().style.visibility != 'hidden') { var canFocus = w.getDOMNode().getAttribute('can-focus'); if (canFocus != 'no') { w.focus(); } setTimeout(function() { w.fire(new _event2['default']('ok', w, { isClick: true })); }, 50); // if (w.focused) { // w.fire(new _event2['default']('ok', w, { isClick: true })); // } else { // var canFocus = w.getDOMNode().getAttribute('can-focus'); // if (canFocus != 'no') { // w.focus(); // } // } } return; // 如果找到的第一个组件不是叶子组件,则不进行操作 } else { return; } // 不是组件容器,继续向上查询 } else { t = t.parentNode; } } }); }; /** * 获取当前选中的叶子节点 * @return {Widget | null} 组件实例 */ Widget.prototype.getFocusedLeaf = function getFocusedLeaf() { if (this.isLeafWidget()) { return this; } else if (!this.getFocusedChildWidget()) { return null; } else { return this.getFocusedChildWidget().getFocusedLeaf(); } }; /** * 为widget的容器添加类名 * @param className {String} 待添加的类名 */ Widget.prototype.addClass = function addClass(className) { this.con.classList.add(className); }; /** * 为widget的容器删除类名 * @param className {String} 待删除的类名 */ Widget.prototype.removeClass = function removeClass(className) { this.con.classList.remove(className); }; /** * 判断widget的容器是否有类名 * @param className {String} 待判断的类名 * @return {Boolean} 是否包含类名 */ Widget.prototype.hasClass = function hasClass(className) { return this.con.classList.contains(className); }; /** * 显示组件 */ Widget.prototype.show = function show() { this.con.style.display = 'block'; }; /** * 隐藏组件 */ Widget.prototype.hide = function hide() { this.con.style.display = 'none'; }; /** * 选中当前组件,并递归选中 * @param {Boolean} 是否强制执行focus */ Widget.prototype.focus = function focus(force, preventListener) { if (!force && this.focused) { return this; } // 如果不是叶子节点,找到默认的叶子节点,触发focus事件 var willFocusedLeaf = this; while (!willFocusedLeaf.isLeafWidget()) { var cache = willFocusedLeaf.getCachedChildWidget(); if (cache && willFocusedLeaf.options.cfg.disable_child_cache !== 'yes') { willFocusedLeaf = cache; continue; } var defaul = willFocusedLeaf.getDefaultFocusedChildWidget(); if (defaul) { willFocusedLeaf = defaul; continue; } var first = willFocusedLeaf.getFirstFocusableChildWidget(); if (first) { willFocusedLeaf = first; continue; } break; } var focusEvent = new _event2['default']('focus', willFocusedLeaf, { force: force }); if (preventListener) { focusEvent.preventListener(); } willFocusedLeaf.fire(focusEvent); return this; }; // focus时的默认处理 Widget.prototype.focusDefault = function focusDefault(e) { if (this.focused && !e.force) { return; } var parent = this.getParentWidget(); if (parent) { var oldFocusedChildWidget = parent.getFocusedChildWidget(); if (oldFocusedChildWidget && oldFocusedChildWidget !== this) { if (oldFocusedChildWidget.isLeafWidget()) { e.blurLeaf = oldFocusedChildWidget; } else if (oldFocusedChildWidget.getFocusedLeaf()) { e.blurLeaf = oldFocusedChildWidget.getFocusedLeaf(); } oldFocusedChildWidget.blur(); } parent.setFocusedChildWidget(this); } this.addClass('fe-focus'); this.focused = true; this.cache(); }; Widget.prototype.blur = function blur() { if (!this.focused) { return this; } var parent = this.getParentWidget(); parent && parent.setFocusedChildWidget(null); this.removeClass('fe-focus'); this.focused = false; var focusedChildWidget = this.getFocusedChildWidget(); if (focusedChildWidget) { focusedChildWidget.blur(); } this.fire(new _event2['default']('blur', this)); return this; }; Widget.prototype.cache = function cache() { if (this.cached) { return this; } var parent = this.getParentWidget(); if (parent) { var oldCacheChildWidget = parent.getCachedChildWidget(); if (oldCacheChildWidget) { oldCacheChildWidget.removeClass('fe-cache'); oldCacheChildWidget.cached = false; } parent.setCachedChildWidget(this); } this.addClass('fe-cache'); this.cached = true; return this; }; /** * 当前组件是否为叶子节点 * @return {Boolean} 是否为叶子节点 */ Widget.prototype.isLeafWidget = function isLeafWidget() { return !this.getFirstFocusableChildWidget(); }; /** * 获取父组件 * @return {Widget} 返回widget实例 */ Widget.prototype.getParentWidget = function getParentWidget() { return this.parentWidget; }; /** * 获取当前选中的子组件 * @return {Widget} 返回widget实例 */ Widget.prototype.getFocusedChildWidget = function getFocusedChildWidget() { if (!this.focusedChildWidget) { return null; } if (this.childWidgets.indexOf(this.focusedChildWidget) === -1) { var widgetById = this.getWidgetById(this.focusedChildWidget.id); if (this.childWidgets.indexOf(widgetById) !== -1) { return widgetById; } return null; } return this.focusedChildWidget; }; Widget.prototype.setFocusedChildWidget = function setFocusedChildWidget(widget) { if (widget && this.childWidgets.indexOf(widget) === -1) { return; } this.focusedChildWidget = widget; }; Widget.prototype.getCachedChildWidget = function getCachedChildWidget() { // 没有缓存 if (!this.cachedChildWidget) { return null; } // 缓存的子组件已经删除 if (this.childWidgets.indexOf(this.cachedChildWidget) === -1) { return null; } // 缓存的子组件disable if (this.cachedChildWidget.disabled) { return null; } return this.cachedChildWidget; }; Widget.prototype.setCachedChildWidget = function setCachedChildWidget(widget) { if (widget && this.childWidgets.indexOf(widget) === -1) { return; } this.cachedChildWidget = widget; }; Widget.prototype.getDefaultFocusedChildWidget = function getDefaultFocusedChildWidget() { if (!this.defaultFocusedChildWidget) { return null; } // 默认子组件已经删除 if (this.childWidgets.indexOf(this.defaultFocusedChildWidget) === -1) { return null; } // 默认子组件disable if (this.defaultFocusedChildWidget.disabled) { return null; } return this.defaultFocusedChildWidget; }; Widget.prototype.getFirstFocusableChildWidget = function getFirstFocusableChildWidget() { for (var i = 0; i < this.childWidgets.length; i++) { var tempWidget = this.childWidgets[i]; // 子组件disable if (tempWidget.disabled) { continue; } return tempWidget; } return null; }; Widget.prototype.setDefaultFocusedChildWidget = function setDefaultFocusedChildWidget(widget) { if (widget && this.childWidgets.indexOf(widget) === -1) { return; } this.defaultFocusedChildWidget = widget; }; /** * 根据id获取组件实例 * @param id {Number} 组件id */ Widget.prototype.getWidgetById = function getWidgetById(id) { if (this.id === id) { return this; } if (this.childMap[id]) { return this.childMap[id]; } for (var i = 0, j = this.childWidgets.length; i < j; i++) { var w = this.childWidgets[i].getWidgetById(id); if (w) { return w; } } return null; }; /** * 在子组件数组的特定位置添加一个新的子组件 * @param widget {Widget} 组件实例,可以是继承自Widget的任意组件实例 * @param index {Number} 组件在组件列表的索引位置,从0开始,如果不传,默认在最后面添加 */ Widget.prototype.addChildWidget = function addChildWidget(widget, index) { if (!index && index !== 0) index = this.childWidgets.length; this.childWidgets.splice(index, 0, widget); // 为查询效率,增加childMap this.childMap[widget.id] = widget; widget.parentWidget = this; if (widget.options.cfg.default_focus === 'yes') { this.setDefaultFocusedChildWidget(widget); } }; /** * 判断widget是否在子组件中 * @param widget {Widget} 组件实例,可以是继承自Widget的任意组件实例 */ Widget.prototype.hasChildWidget = function hasChildWidget(widget) { return this.childWidgets.indexOf(widget) !== -1; }; /** * 删除子组件数组特定位置的子组件 * @param widgetOrIndex {Number} 组件本身,或组件在组件列表的索引位置(从0开始) * @return widget {Number} 被删除的组件 */ Widget.prototype.removeChildWidget = function removeChildWidget(widgetOrIndex) { var removeArr = undefined; // 如果是Widget,则查询index if (widgetOrIndex instanceof Widget) { widgetOrIndex = this.childWidgets.indexOf(widgetOrIndex); } if (widgetOrIndex < 0) { return; } removeArr = this.childWidgets.splice(widgetOrIndex, 1); var removedWidget = removeArr.length ? removeArr[0] : null; if (!removedWidget) { return; } // 从map中删除 delete this.childMap[removedWidget.id]; // 如果删除的正好是当前选中的子组件 if (removedWidget === this.getFocusedChildWidget()) { var willFocusedWidget = this.getDefaultFocusedChildWidget() === removedWidget ? this.childWidgets[0] : this.getDefaultFocusedChildWidget(); willFocusedWidget.focus(); } // 如果删除的是缓存的子组件 if (removedWidget === this.getCachedChildWidget()) { this.cachedChildWidget = null; } return removedWidget; }; /** * 获取容器dom节点 * @return {HTMLElement} */ Widget.prototype.getDOMNode = function getDOMNode() { return this.con; }; // 得到dom的上下左右宽高中心位置信息 Widget.prototype.getWidgetRect = function getWidgetRect(cache) { if (cache && this.rect) { return this.rect; } var el = this.getDOMNode(); var elRect = el.getBoundingClientRect(); this.rect = { top: elRect.top, bottom: elRect.bottom, right: elRect.right, left: elRect.left, width: elRect.width, height: elRect.height, centerX: elRect.width / 2 + elRect.left, centerY: elRect.height / 2 + elRect.top }; return this.rect; }; Widget.prototype.getWidgetRectAsync = function getWidgetRectAsync(cb) { var _this2 = this; window.requestAnimationFrame(function () { var el = _this2.getDOMNode(); var elRect = el.getBoundingClientRect(); var rect = { top: elRect.top, bottom: elRect.bottom, right: elRect.right, left: elRect.left, width: elRect.width, height: elRect.height, centerX: elRect.width / 2 + elRect.left, centerY: elRect.height / 2 + elRect.top }; _this2.rect || (_this2.rect = rect); cb && cb(rect); }); }; /** * 增加事件监听 * @param type {String} 事件类型 * @param cb {Function} 事件回调函数 * @param ctx {Object} 回调函数执行上下文 * @param after {Bollean} 监听在默认操作之后执行 * @return index {Number} 返回该监听的index,可以用来解绑 */ Widget.prototype.on = function on(type, cb, ctx, after) { if (type.constructor !== String) { console.warn('[FocusEngine] 事件type类型必须是String'); return -1; } if (cb.constructor !== Function) { console.warn('[FocusEngine] 事件监听类型必须是Function'); return -1; } type = type.toLowerCase(); this.eventListeners.push({ type: type, cb: cb, ctx: ctx || null, after: !!after }); return this.eventListeners.length - 1; }; /** * 移除事件监听 * @param index {Number} 事件index,即on方法的返回 */ Widget.prototype.off = function off(index) { if (index < 0) { return; } this.eventListeners[index] = null; }; /** * 解除事件绑定 * @param type {String} 事件类型 * @param cb {Function} 绑定事件的回调函数 */ Widget.prototype.detach = function detach(type, cb) { console.warn('[FocusEngine] detach方法不再推荐使用,请使用off方法'); type = type.toLowerCase(); for (var i = 0, l = this.eventListeners.length; i < l; i++) { var listener = this.eventListeners[i]; if (listener.type === type && listener.cb === cb) { this.eventListeners[i] = null; } } }; /** * 触发事件,支持冒泡和捕获 * @param type {Event} 事件对象 * @param data {Object} 事件额外参数 */ Widget.prototype.fire = function fire(e) { this.exeEventListeners(e); this.exeEventDefault(e); this.exeEventListeners(e, true); var widget = this; while (widget.parentWidget && !e.isPropagationStopped()) { widget = widget.parentWidget; widget.exeEventListeners(e); widget.exeEventDefault(e); widget.exeEventListeners(e, true); } return this; }; // 执行自定义的事件监听 Widget.prototype.exeEventListeners = function exeEventListeners(e, after) { // 如果阻止了Listener,那么禁止执行 if (e.isListenerPrevented()) { return; } e.type = e.type.toLowerCase(); for (var i = 0, l = this.eventListeners.length; i < l; i++) { var listener = this.eventListeners[i]; if (!listener) { continue; } if (listener.type === e.type && listener.after === !!after) { listener.cb.call(listener.ctx, e); } } }; // 执行默认的事件处理 Widget.prototype.exeEventDefault = function exeEventDefault(e) { // 统一管理所有的默认事件处理,任何继承Widget的组件想处理某个事件只需要增加相应的处理函数即可: // 如:scrollend事件的处理函数是 scrollendDefault。 var eventDefaultHandler = e.type + 'Default'; this[eventDefaultHandler] && this[eventDefaultHandler](e); }; /** * 销毁组件及其子组件 * @param isClearDom {Boolean} 销毁组件的时候是否把dom架构连带销毁 */ Widget.prototype.destroy = function destroy(isClearDom) { for (var i = 0; i < this.childWidgets.length; i++) { this.childWidgets[i].destroy(isClearDom); } if (this.getParentWidget()) { this.getParentWidget().removeChildWidget(this); } delete this.eventListeners; if (isClearDom) { this.con.parentNode.removeChild(this.con); } }; Widget.prototype._filterArray = function _filterArray(arr1, arr2) { return arr1.filter(function (el, i, arr) { return arr2.indexOf(el) === -1; }); }; Widget.prototype._removeSpaces = function _removeSpaces(str) { if (!str) { return ''; } return str.replace(/\s+/g, ''); }; Widget.prototype.parseDom = function parseDom(html) { var tmp = document.createElement('div'); tmp.innerHTML = html; return tmp.children; }; Widget.prototype.disableClick = function disableClick() { this.noClick = true; }; Widget.prototype.enableClick = function enableClick() { this.noClick = false; }; // 得到组件会触发的所有事件类型 Widget.prototype.getEvents = function getEvents() { return ['keydown', 'keyup', 'ok', 'okdown', 'click', 'focus', 'blur']; }; return Widget; })(); exports['default'] = Widget; module.exports = exports['default']; /***/ }, /* 4 */ /***/ function(module, exports, __webpack_require__) { Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _widget = __webpack_require__(3); var _widget2 = _interopRequireDefault(_widget); var _event = __webpack_require__(2); var _event2 = _interopRequireDefault(_event); var Switch = (function (_Widget) { _inherits(Switch, _Widget); /** * @param container {HTMLElement} 组件的容器dom元素 */ function Switch(container) { _classCallCheck(this, Switch); _Widget.call(this, container); this.minInterval = this.options.cfg.interval > 0 ? this.options.cfg.interval * 1000 : 0; // 配置:是否禁用区域算法 this.disableArea = this.options.cfg.disable_area === 'yes'; // 配置:是否禁用算法 this.disableShadow = this.options.cfg.disable_shadow === 'yes'; this.useLeafRect = this.options.cfg.use_leaf_rect === 'yes'; this.cacheRect = this.options.cfg.disable_cache_rect === 'yes' ? false : true; } /** * 删除子组件数组特定位置的子组件,由于Switch需要处理方向缓存,增加Widget没有的逻辑 * @param widgetOrIndex {Number} 组件本身,或组件在组件列表的索引位置(从0开始) * @return widget {Number} 被删除的组件 */ Switch.prototype.removeChildWidget = function removeChildWidget(widgetOrIndex) { var removedWidget = _Widget.prototype.removeChildWidget.call(this, widgetOrIndex); // 如果删除的是逆方向缓存的子组件 if (removedWidget === this.lastFocusedChildWidget) { this.lastFocusedChildWidget = null; } return removedWidget; }; Switch.prototype.keydownDefault = function keydownDefault(e) { if (this.isLeafWidget()) { this.fire(new _event2['default']('switchend', this, { keyCode: e.keyCode })); return; } // 如果keydown是方向键,而且没有被阻止默认处理,则进行switch操作 if (!e.isDefaultPrevented() && e.target && [_event2['default'].VK_LEFT, _event2['default'].VK_RIGHT, _event2['default'].VK_UP, _event2['default'].VK_DOWN].indexOf(e.keyCode) !== -1) { this.doSwitch(e); } }; // 进行switch操作 Switch.prototype.doSwitch = function doSwitch(e) { // 如果小于设置的最低间隔时间,取消这次switch var currentTime = new Date().getTime(); if (currentTime - this.lastKeydownTime < this.minInterval) { e.preventDefault(); return; } this.lastKeydownTime = currentTime; var fChildWidget = this.getFocusedChildWidget(); var optionDir = fChildWidget.options.dir; var newFocusChildWidget = null; var oppositeVK = undefined; var dirName = undefined; if (e.keyCode === _event2['default'].VK_LEFT) { oppositeVK = _event2['default'].VK_RIGHT; dirName = 'left'; } else if (e.keyCode === _event2['default'].VK_UP) { oppositeVK = _event2['default'].VK_DOWN; dirName = 'up'; } else if (e.keyCode === _event2['default'].VK_RIGHT) { oppositeVK = _event2['default'].VK_LEFT; dirName = 'right'; } else if (e.keyCode === _event2['default'].VK_DOWN) { oppositeVK = _event2['default'].VK_UP; dirName = 'down'; } // 第一优先级:缓存 if (this.options.cfg.disable_cache_pre !== 'yes' && this.lastKeyCode === oppositeVK && this.lastFocusedChildWidget && !this.lastFocusedChildWidget.disabled) { newFocusChildWidget = this.lastFocusedChildWidget; } // 第二优先级:配置 if (!newFocusChildWidget && optionDir && optionDir[dirName]) { newFocusChildWidget = this._queryWidgetByCfg(fChildWidget, optionDir[dirName]); } // 第三优先级:阴影算法 if (!newFocusChildWidget && !this.disableShadow && fChildWidget.options.cfg.disable_self_shadow !== 'yes') { newFocusChildWidget = this._queryWidgetByShadowAlgorithm(fChildWidget, dirName, e); } // 第四优先级:区域算法 if (!newFocusChildWidget && !this.disableArea && fChildWidget.options.cfg.disable_self_area !== 'yes') { newFocusChildWidget = this._queryWidgetByAreaAlgorithm(fChildWidget, dirName, e); } if (!newFocusChildWidget) { this.fire(new _event2['default']('switchend', this, { keyCode: e.keyCode })); return; } newFocusChildWidget.focus(); // 如果没有配置disable_cache_pre,记录上次的焦点及按键 if (this.options.cfg.disable_cache_pre !== 'yes') { this.lastFocusedChildWidget = fChildWidget; this.lastKeyCode = e.keyCode; } // 不再执行后续的默认事件处理,但是会继续冒泡。 e.preventDefault(); }; Switch.prototype._queryWidgetByCfg = function _queryWidgetByCfg(widget, tabIndex) { if (tabIndex == -1) return widget; for (var i = 0, j = this.childWidgets.length; i < j; i++) { var tempChild = this.childWidgets[i]; if (tempChild === widget) { continue; } if (tempChild.disabled) { continue; } if (tempChild.options.tab === tabIndex && tempChild.getDOMNode().style.display !== 'none') { return this.childWidgets[i]; } } return null; }; Switch.prototype._queryWidgetByShadowAlgorithm = function _queryWidgetByShadowAlgorithm(widget, dir, e) { var currentRect = widget.getWidgetRect(this.cacheRect); if ((this.useLeafRect || widget.options.cfg.use_self_leaf_rect === 'yes') && e) { currentRect = e.target.getWidgetRect(this.cacheRect); } var resultWidget = null; var dis = Infinity; for (var i = 0; i < this.childWidgets.length; i++) { var tempWidget = this.childWidgets[i]; if (tempWidget === widget) { continue; } if (tempWidget.disabled) { continue; } var tempWidgetRect = tempWidget.getWidgetRect(this.cacheRect); if (dir === 'left' && tempWidgetRect.right < currentRect.right && tempWidgetRect.left < currentRect.left && tempWidgetRect.top < currentRect.bottom && tempWidgetRect.bottom > currentRect.top && currentRect.right - tempWidgetRect.right < dis) { resultWidget = tempWidget; dis = currentRect.right - tempWidgetRect.right; } else if (dir === 'right' && tempWidgetRect.left > currentRect.left && tempWidgetRect.right > currentRect.right && tempWidgetRect.top < currentRect.bottom && tempWidgetRect.bottom > currentRect.top && tempWidgetRect.left - currentRect.left < dis) { resultWidget = tempWidget; dis = tempWidgetRect.left - currentRect.left; } else if (dir === 'up' && tempWidgetRect.bottom < currentRect.bottom && tempWidgetRect.top < currentRect.top && tempWidgetRect.left < currentRect.right && tempWidgetRect.right > currentRect.left && currentRect.bottom - tempWidgetRect.bottom < dis) { resultWidget = tempWidget; dis = currentRect.bottom - tempWidgetRect.bottom; } else if (dir === 'down' && tempWidgetRect.top > currentRect.top && tempWidgetRect.bottom > currentRect.bottom && tempWidgetRect.left < currentRect.right && tempWidgetRect.right > currentRect.left && tempWidgetRect.top - currentRect.top < dis) { resultWidget = tempWidget; dis = tempWidgetRect.top - currentRect.top; } } return resultWidget; }; Switch.prototype._queryWidgetByAreaAlgorithm = function _queryWidgetByAreaAlgorithm(widget, dir, e) { var currentRect = widget.getWidgetRect(this.cacheRect); if ((this.useLeafRect || widget.options.cfg.use_self_leaf_rect === 'yes') && e) { currentRect = e.target.getWidgetRect(this.cacheRect); } var resultWidget = null; var dis = Infinity; for (var i = 0; i < this.childWidgets.length; i++) { var tempWidget = this.childWidgets[i]; if (tempWidget === widget) { continue; } if (tempWidget.disabled) { continue; } var tempWidgetRect = tempWidget.getWidgetRect(this.cacheRect); if (dir === 'left' && tempWidgetRect.right <= currentRect.left) { var tempDis = Infinity; if (tempWidgetRect.bottom < currentRect.top) { tempDis = Math.sqrt(Math.pow(currentRect.left - tempWidgetRect.right, 2) + Math.pow(currentRect.top - tempWidgetRect.bottom, 2)); } else if (tempWidgetRect.top > currentRect.bottom) { tempDis = Math.sqrt(Math.pow(currentRect.left - tempWidgetRect.right, 2) + Math.pow(tempWidgetRect.top - currentRect.bottom, 2)); } else { tempDis = currentRect.left - tempWidgetRect.right; } if (tempDis < dis) { resultWidget = tempWidget; dis = tempDis; } } else if (dir === 'right' && tempWidgetRect.left >= currentRect.right) { var tempDis = Infinity; if (tempWidgetRect.bottom < currentRect.top) { tempDis = Math.sqrt(Math.pow(tempWidgetRect.left - currentRect.right, 2) + Math.pow(currentRect.top - tempWidgetRect.bottom, 2)); } else if (tempWidgetRect.top > currentRect.bottom) { tempDis = Math.sqrt(Math.pow(tempWidgetRect.left - currentRect.right, 2) + Math.pow(tempWidgetRect.top - currentRect.bottom, 2)); } else { tempDis = tempWidgetRect.left - currentRect.right; } if (tempDis < dis) { resultWidget = tempWidget; dis = tempDis; } } else if (dir === 'up' && tempWidgetRect.bottom <= currentRect.top) { var tempDis = Infinity; if (tempWidgetRect.right < currentRect.left) { tempDis = Math.sqrt(Math.pow(currentRect.top - tempWidgetRect.bottom, 2) + Math.pow(currentRect.left - tempWidgetRect.right, 2)); } else if (tempWidgetRect.left > currentRect.right) { tempDis = Math.sqrt(Math.pow(currentRect.top - tempWidgetRect.bottom, 2) + Math.pow(tempWidgetRect.left - currentRect.right, 2)); } else { tempDis = currentRect.top - tempWidgetRect.bottom; } if (tempDis < dis) { resultWidget = tempWidget; dis = tempDis; } } else if (dir === 'down' && tempWidgetRect.top >= currentRect.bottom) { var tempDis = Infinity; if (tempWidgetRect.right < currentRect.left) { tempDis = Math.sqrt(Math.pow(tempWidgetRect.top - currentRect.bottom, 2) + Math.pow(currentRect.left - tempWidgetRect.right, 2)); } else if (tempWidgetRect.left > currentRect.right) { tempDis = Math.sqrt(Math.pow(tempWidgetRect.top - currentRect.bottom, 2) + Math.pow(tempWidgetRect.left - currentRect.right, 2)); } else { tempDis = tempWidgetRect.top - currentRect.bottom; } if (tempDis < dis) { resultWidget = tempWidget; dis = tempDis; } } } return resultWidget; }; Switch.prototype.getEvents = function getEvents() { return _Widget.prototype.getEvents.call(this).concat(['switchend']); }; return Switch; })(_widget2['default']); exports['default'] = Switch; module.exports = exports['default']; /***/ }, /* 5 */ /***/ function(module, exports, __webpack_require__) { Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _switch = __webpack_require__(4); var _switch2 = _interopRequireDefault(_switch); var _event = __webpack_require__(2); var _event2 = _interopRequireDefault(_event); var _animate = __webpack_require__(6); var _animate2 = _interopRequireDefault(_animate); var Scroll = (function (_Switch) { _inherits(Scroll, _Switch); /** * @param container {HTMLElement} 组件的容器dom元素 */ function Scroll(container) { var _this = this; _classCallCheck(this, Scroll); _Switch.call(this, container); this.cacheRect = false; this.dir = this.options.cfg.dir === 'h' ? this.options.cfg.dir : 'v'; this.centerDOM = this.options.cfg.center === 'document' ? this.options.cfg.center : 'con'; this.duration = this.options.cfg.duration ? parseFloat(this.options.cfg.duration) : 0.3; this.speedup = this.options.cfg.speedup ? parseFloat(this.options.cfg.speedup) : 0.6; this.easing = this.options.cfg.easing ? this.options.cfg.easing : 'ease-out'; this.speedupEasing = this.options.cfg.speedup_easing ? this.options.cfg.speedup_easing : 'linear'; this.useRAF = this.options.cfg.use_ram === 'yes' || this.options.cfg.use_raf === 'yes'; this.lazyload = this.options.cfg.disable_lazyload !== 'yes'; this.lazyloadDelay = this.options.cfg.lazyload_delay !== undefined ? parseFloat(this.options.cfg.lazyload_delay) : 0.1; this.autoListWidth = this.options.cfg.auto_list_width === 'yes'; this.conList = this.con.querySelector('.scroll-list'); this.offset = 0; this.desArr = []; if (!this.conList) { console.error('[FocusEngine_Error]Scroll组件必须含有一个类名为scroll-list的子元素。'); return; } var listStyles = getComputedStyle(this.conList); if (listStyles.left !== 'auto' && parseInt(listStyles.left) !== 0 && this.dir === 'h') { console.error('[FocusEngine_Error]横向Scroll容器的.scroll-list子元素不可设置left值'); } if (listStyles.top !== 'auto' && parseInt(listStyles.top) !== 0 && this.dir === 'v') { console.error('[FocusEngine_Error]纵向Scroll容器的.scroll-list子元素不可设置top值'); } if (listStyles.position === 'static') { this.conList.style.position = 'relative'; } this._render(); if (this.autoListWidth) { this._setAutoListWidth(); } var defaultFocusedChild = this.getDefaultFocusedChildWidget(); if (defaultFocusedChild) { this.scrollToWidget(defaultFocusedChild, 0); } if (this.lazyload) { setTimeout(function () { _this.loadVisibleImage(); }, 10); } } // 计算容器和list的宽高 Scroll.prototype._computeScrollRect = function _computeScrollRect() { this.conRect = this.getWidgetRect(); this.conWidth = this.conRect.width; this.conHeight = this.conRect.height; this.listRect = this.conList.getBoundingClientRect(); this.listWidth = this.listRect.width; this.listHeight = this.listRect.height; if (this.centerDOM === 'document') { var docRect = document.documentElement.getBoundingClientRect(); if (this.dir === 'v') { this.center = docRect.height / 2; } else { this.center = docRect.width / 2; } } else { if (this.dir === 'v') { this.center = this.conRect.height / 2; } else { this.center = this.conRect.width / 2; } } }; // 自动设置横向列表的宽度 Scroll.prototype._setAutoListWidth = function _setAutoListWidth() { this.rightPaddingWithUnit = this.options.cfg.right_padding; this.rightPadding = 0; if (this.rightPaddingWithUnit) { if (this.rightPaddingWithUnit.indexOf('rem') !== -1) { this.rightPadding = parseFloat(this.rightPaddingWithUnit) * parseFloat(document.querySelector('html').style.fontSize); } else { this.rightPadding = parseFloat(this.rightPaddingWithUnit); } } this.conList.style.width = '99999px'; for (var i = 0, _length = this.childWidgets.length; i < _length; i++) { var right = this.childWidgets[i].getWidgetRect().right; var width = right - this.listRect.left + this.rightPadding; if (width > this.listWidth) { this.listWidth = width; } } this.conList.style.width = this.listWidth + 'px'; }; Scroll.prototype.reRender = function reRender() { var _this2 = this; if (!document.querySelector('#' + this.id)) { return; } this.con = document.querySelector('#' + this.id); this.conList = this.con.querySelector('.scroll-list'); if (!this.conList) { console.error('[FocusEngine_Error]Scroll组件必须含有一个类名为scroll-list的子元素。'); return; } var listStyles = getComputedStyle(this.conList); if (listStyles.position === 'static') { this.conList.style.position = 'relative'; } this.options = this._getOptions(this.con); this.dir = this.options.cfg.dir === 'h' ? this.options.cfg.dir : 'v'; this.centerDOM = this.options.cfg.center === 'ducoment' ? this.options.cfg.center : 'con'; this.duration = this.options.cfg.duration ? parseFloat(this.options.cfg.duration) : 0.3; this.easing = this.options.cfg.easing ? this.options.cfg.easing : 'linear'; this.useRAF = this.options.cfg.use_ram === 'yes' || this.options.cfg.use_raf === 'yes'; this._render(true); this._reRenderChildWidget(); if (this.autoListWidth) { this._setAutoListWidth(); } if (this.lazyload) { setTimeout(function () { _this2.loadVisibleImage(); }, 10); } }; Scroll.prototype._render = function _render(isReRender) { var _this3 = this; this._computeScrollRect(); if (!isReRender) { if (this.dir === 'v') { this.conList.style.top = '0px'; } else { this.conList.style.left = '0px'; } } if (!this.useRAF) { if (this.dir === 'v') { this.conList.style.transition = 'top ' + this.duration + 's ' + this.easing; this.conList.style.webkitTransition = 'top ' + this.duration + 's ' + this.easing; } else { this.conList.style.transition = 'left ' + this.duration + 's ' + this.easing; this.conList.style.webkitTransition = 'left ' + this.duration + 's ' + this.easing; } if (!isReRender) { // 避免首次render后也会触发scrollend事件 this.rendered = false; this.conList.addEventListener('webkitTransitionEnd', function (e) { if (e.target !== _this3.conList) { return; } if (_this3.rendered) { _this3.fire(new _event2['default']('scrollend', _this3)); } _this3.rendered = true; }); this.conList.addEventListener('transitionend', function (e) { if (e.target !== _this3.conList) { return; } if (_this3.rendered) { _this3.fire(new _event2['default']('scrollend', _this3)); } _this3.rendered = true; }); } } }; Scroll.prototype.focusDefault = function focusDefault(e) { _Switch.prototype.focusDefault.call(this, e); if (!this.getFirstFocusableChildWidget()) { return; } if (!e.isDefaultPrevented()) { // 如果这些值都是0,那么说明在focus之前是display:none的,因此focus时重新计算这些值 if (!this.conWidth && !this.conHeight && !this.listWidth && !this.listHeight && !this.center) { this._computeScrollRect(); if (this.autoListWidth) { this._setAutoListWidth(); } var defaultFocusedChild = this.getDefaultFocusedChildWidget(); if (defaultFocusedChild) { this.scrollToWidget(defaultFocusedChild, 0); return; } } this.doScroll(e); } }; Scroll.prototype.scrollstartDefault = function scrollstartDefault(e) { this.loadImageTimeout && clearTimeout(this.loadImageTimeout); }; Scroll.prototype.scrollendDefault = function scrollendDefault(e) { var _this4 = this; if (this.lazyload && e.target === this) { this.loadImageTimeout = setTimeout(function () { _this4.loadVisibleImage(); }, this.lazyloadDelay * 1000); } }; Scroll.prototype.doSwitch = function doSwitch(e) { _Switch.prototype.doSwitch.call(this, e); }; // 进行scroll操作 Scroll.prototype.doScroll = function doScroll(e) { var fWidget = this.getFocusedChildWidget(); this.scrollToWidget(fWidget, this.duration, e); }; /** * 将scrolllist滚动到某个widget获取焦点时应该在的位置,但是并不会focus该widget * @Param {Widget} widget 目标Widget * @Param {Event} e 事件 */ Scroll.prototype.scrollToWidget = function scrollToWidget(widget, duration, e) { var offset = this.computeOffsetByWidget(widget); if (duration === undefined) { duration = this.duration; } this.scrollListTo(offset, duration, e); return this; }; /** * 计算滚动到某个widget时list的offset值 * @Param {Widget} widget 目标Widget * @Returns {Number} offset值 */ Scroll.prototype.computeOffsetByWidget = function computeOffsetByWidget(widget) { if (widget.options.cfg.to_start === 'yes') { return 0; } if (widget.options.cfg.to_end === 'yes') { return this.dir === 'v' ? this.conHeight - this.listHeight : this.conWidth - this.listWidth; } this._computeScrollRect(); var dis = this.center - this._getExpectCenter(widget); if (this.dir === 'v') { if (dis < 0) { // 如果向上滚动时下边界偏出,设置成最向上的位置,如果上次滚动已经达到顶,返回。 if (this.offset + dis < this.conHeight - this.listHeight) { dis = this.conHeight - this.listHeight - this.offset; if (dis > 0) { return this.offset; } } } else if (dis > 0) { // 同上,防止上边界偏出。 if (this.offset * -1 < dis) { dis = this.offset * -1; if (dis < 0) { return this.offset; } } } else { return this.offset; } return this.offset + dis; } // horizontal if (dis < 0) { // 如果向左滚动时下边界偏出,设置成最向左的位置,如果上次滚动已经达到顶,返回。 if (this.offset + dis < this.conWidth - this.listWidth) { dis = this.conWidth - this.listWidth - this.offset; if (dis > 0) { return this.offset; } } } else if (dis > 0) { // 同上,防止向右边界偏出 if (this.offset * -1 < dis) { dis = this.offset * -1; if (dis < 0) { return this.offset; } } } else { return this.offset; } return this.offset + dis; }; /** * 滚动scroll-list到一个位置,des是目的地位置;e是事件 * @Param {Number} des 目标offset值 * @Param {Event} e 事件 */ Scroll.prototype.scrollListTo = function scrollListTo(des, duration, e) { var _this5 = this; if (duration === undefined) { duration = this.duration; } this.fire(new _event2['default']('scrollstart', this)); if (this.useRAF) { if (this.desArr.length > 0 && des === this.desArr[this.desArr.length - 1]) { return; } if (e && e.type === 'focus') { this.futureOffset = des; } this.desArr.push(des); if (this.desArr.length === 1) { this.doAnimate(duration, this.easing); } } else { this.offset = des; if (this.dir === 'v') { this.conList.style.transition = 'top ' + duration + 's ' + this.easing; this.conList.style.webkitTransition = 'top ' + duration + 's ' + this.easing; } else { this.conList.style.transition = 'left ' + duration + 's ' + this.easing; this.conList.style.webkitTransition = 'left ' + duration + 's ' + this.easing; } setTimeout(function () { if (_this5.dir === 'v') { _this5.conList.style.transition = 'top ' + _this5.duration + 's ' + _this5.easing; _this5.conList.style.webkitTransition = 'top ' + _this5.duration + 's ' + _this5.easing; } else { _this5.conList.style.transition = 'left ' + _this5.duration + 's ' + _this5.easing; _this5.conList.style.webkitTransition = 'left ' + _this5.duration + 's ' + _this5.easing; } }, 1); if (this.dir === 'v') { this.conList.style.top = this.offset + 'px'; } else { this.conList.style.left = this.offset + 'px'; } if (e && e.type === 'focus') { this.futureOffset = this.offset; } } return this; }; Scroll.prototype.doAnimate = function doAnimate(duration, easing, isSpeedup) { var _this6 = this; // 如果下次动画offset地跟当前offset相同,取消这次动画 if (this.desArr[0] === this.offset) { this.desArr.shift(); this.doAnimate(duration, easing); return; } if (this.desArr.length === 0) { this.fire(new _event2['default']('scrollend', this)); return; } var endCb = function endCb() { _this6.scrollEndTimeout && clearTimeout(_this6.scrollEndTimeout); _this6.offset = _this6.desArr.shift(); if (_this6.desArr.length > 0) { _this6.doAnimate(duration, easing, true); } else { _this6.scrollEndTimeout = setTimeout(function () { _this6.fire(new _event2['default']('scrollend', _this6)); }, 50); } }; (0, _animate2['default'])({ dom: this.conList, style: this.dir === 'v' ? 'top' : 'left', start: this.offset, end: this.desArr[0], duration: isSpeedup ? duration * this.speedup : duration, easing: easing, cb: endCb }); }; /** *得到上次滚动结束后,widget中心点相对于con应该所在的位置(水平或垂直)。 *由于滚动过程中得到的位置是不准确的,而且也不是滚动结束后应该的位置,所以通过设置的offset和相对距离来计算。 *widgetRect.centerY - listRect.top是widget.center相对于conList的top的距离,由于两者是动画过程中同时获取的,所以动画对相对距离无影响。 *两者相加即滚动结束后widget.center相对于con的位置。 **/ Scroll.prototype._getExpectCenter = function _getExpectCenter(widget) { var widgetRect = widget.getWidgetRect(); if (this.dir === 'v') { return this.offset + widgetRect.centerY - this.listRect.top; } else { return this.offset + widgetRect.centerX - this.listRect.left; } }; Scroll.prototype.getListNode = function getListNode() { return this.conList; }; Scroll.prototype.loadVisibleImage = function loadVisibleImage() { var conRect = this.getWidgetRect(); var conLeft = conRect.left; var conTop = conRect.top; var conRight = conRect.right; var conBottom = conRect.bottom; var lazyImages = this.con.querySelectorAll('.fe-lazyload'); var _loop = function (i) { var tempImage = lazyImages[i]; var dataSrc = tempImage.getAttribute('fe-src'); var rect = tempImage.getBoundingClientRect(); // 未加载过,而且在viewport之内,加载图片 if (tempImage.src !== dataSrc && !(rect.left <= conLeft && rect.right <= conLeft || rect.left >= conRight && rect.right >= conRight || rect.top >= conBottom && rect.bottom >= conBottom || rect.top <= conTop && rect.bottom <= conTop)) { var hiddenImage = new Image(); hiddenImage.src = dataSrc; hiddenImage.onload = function () { tempImage.src = dataSrc; }; } }; for (var i = 0; i < lazyImages.length; i++) { _loop(i); } }; Scroll.prototype.getEvents = function getEvents() { return _Switch.prototype.getEvents.call(this).concat(['scrollstart', 'scrollend']); }; return Scroll; })(_switch2['default']); exports['default'] = Scroll; module.exports = exports['default']; /***/ }, /* 6 */ /***/ function(module, exports, __webpack_require__) { // requestAnimationFrame shim for Blitz Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } // Easing functions refer to 'http://greweb.me/2012/02/bezier-curve-based-easing-functions-from-concept-to-implementation/' var _bezierEasing = __webpack_require__(7); var _bezierEasing2 = _interopRequireDefault(_bezierEasing); window.requestAnimationFrame = window.requestAnimationFrame || function (operation) { setTimeout(operation, 0); }; var Easing = { // Blitz 不支持改语法的babel翻译,用下方的实现 // 'ease-in': BezierEasing(0.42, 0.0, 1.00, 1.0), // 'ease-out': BezierEasing(0.00, 0.0, 0.58, 1.0), // 'ease-in-out': BezierEasing(0.42, 0.0, 0.58, 1.0), // CSS3 Easing Functions: http://www.w3schools.com/cssref/css3_pr_transition-timing-function.asp linear: function linear(t) { return t; }, 'ease': _bezierEasing2['default'].apply(null, [0.25, 0.1, 0.25, 1]), 'ease-in': _bezierEasing2['default'].apply(null, [0.42, 0.0, 1.00, 1.0]), 'ease-out': _bezierEasing2['default'].apply(null, [0.00, 0.0, 0.58, 1.0]), 'ease-in-out': _bezierEasing2['default'].apply(null, [0.42, 0.0, 0.58, 1.0]), /** *自定义Bezier曲线 *@param arr {Array} 是四个Bezier曲线的定义值 **/ custom: function custom(arr) { return _bezierEasing2['default'].apply(null, arr); } // 去掉下方的曲线,只用css默认支持的方式及自定义曲线 // easeInQuad(t) { return t*t;}, // easeOutQuad(t) { return t*(2-t);}, // easeInOutQuad(t) { return t<.5 ? 2*t*t : -1+(4-2*t)*t;}, // easeInCubic(t) { return t*t*t;}, // easeOutCubic(t) { return (--t)*t*t+1;}, // easeInOutCubic(t) { return t<.5 ? 4*t*t*t : (t-1)*(2*t-2)*(2*t-2)+1;}, // easeInQuart(t) { return t*t*t*t;}, // easeOutQuart(t) { return 1-(--t)*t*t*t;}, // easeInOutQuart(t) { return t<.5 ? 8*t*t*t*t : 1-8*(--t)*t*t*t;}, // easeInQuint(t) { return t*t*t*t*t;}, // easeOutQuint(t) { return 1+(--t)*t*t*t*t;}, // easeInOutQuint(t) { return t<.5 ? 16*t*t*t*t*t : 1+16*(--t)*t*t*t*t;}, }; // let frameTime = window.yunos ? 16 : 10; // let a = Date.now(); // const setFrameTime = () => { // const b = Date.now(); // frameTime = b - a; // console.log(frameTime); // a = b; // window.requestAnimationFrame(setFrameTime); // }; // window.requestAnimationFrame(setFrameTime); /** * DOM动画函数, * 暂仅支持以数字加单位为值的属性,如top、width等,不支持transform类似的属性。 * 暂仅支持一次缓动一个属性。 * @Param {Object} cfg 具体动画配置; * @Param {HTMLElement} cfg.dom DOM对象; * @Param {String} cfg.style style属性; * @Param {String} cfg.transitionType 使用何种方式实现动画,目前支持'css'和'js'两种,默认'js'; * @Param {Number} cfg.start 起始值; * @Param {Number} cfg.end 结束值; * @Param {String} [cfg.unit] 单位,默认px; * @Param {Number} [cfg.duration] 执行动画需要的时间,以秒为单位,默认1; * @Param {String} [cfg.easing] 缓动函数,目前支持:'ease'(默认)、'ease-out'、'ease-in'、'linear'; * @Param {Function} [cfg.cb] 动画结束后的回调函数; */ function animate(cfg) { if (!(cfg && cfg.dom && cfg.style && cfg.end !== undefined)) { console.error('[FocusEngine_Error]animate方法的cfg参数:dom、style、end必需!'); return; } var dom = cfg.dom; var style = cfg.style; var transitionType = cfg.transitionType === 'css' ? 'css' : 'js'; var start = cfg.start; if (start === undefined) { var computedValue = parseFloat(getComputedStyle(dom)[style]); start = isNaN(computedValue) ? computedValue : 0; } var end = cfg.end; var duration = cfg.duration !== undefined ? parseFloat(cfg.duration) : 0.3; var milliDuration = duration * 1000; var easing = cfg.easing || 'ease-out'; var unit = cfg.unit || 'px'; var cb = cfg.cb || null; // 如果使用css的方式执行动画 if (transitionType === 'css') { var _ret = (function () { var easingFunction = ''; if (easing.constructor === Array) { easingFunction = 'cubic-bezier(' + easing.join(',') + ')'; } else if (easing.constructor === String) { easingFunction = easing; } var transitionendFunc = function transitionendFunc(e) { if (e.target === dom) { dom.style.transition = ''; dom.removeEventListener('transitionend', transitionendFunc); cb && cb(); } }; dom.addEventListener('transitionend', transitionendFunc); dom.style.transition = style + ' ' + duration + 's ' + easingFunction; dom.style[style] = end + unit; return { v: undefined }; })(); if (typeof _ret === 'object') return _ret.v; } // 如果使用js的方式执行动画,默认 var easingFunction = null; if (easing.constructor === Array) { easingFunction = Easing.custom(easing); } else if (easing.constructor === String) { easingFunction = Easing[easing]; } // 最后权衡:使用requestAnimationFrame/依赖帧的方式 var allFrames = milliDuration / 14; var currentFrame = 1; // const startTime = Date.now(); var doAnimate = function doAnimate() { // 每帧都获取时间在Blitz上对动画性能影响较大,因此还是按照帧数来动画 // const now = Date.now(); if (currentFrame > allFrames) { dom.style[style] = end + unit; cb && cb(); } else { var percent = easingFunction(currentFrame / allFrames); var currentValue = start + percent * (end - start); dom.style[style] = currentValue + unit; currentFrame++; window.requestAnimationFrame(doAnimate); } }; window.requestAnimationFrame(doAnimate); /* 每个动画使用单一setInterval的方式 const minInterval = window.yunos ? 0 : 14; const interval = setInterval(() => { if (currentFrame >= allFrames) { clearInterval(interval); dom.style[style] = end + unit; cb && cb(); } else { const percent = easingFunction(currentFrame / allFrames); const currentValue = start + percent * (end - start); dom.style[style] = currentValue + unit; currentFrame++; } }, minInterval); */ /* 全局单一interval的方式 aniArr.push({ easingFunction, dom, style, unit, start, end, change: end - start, milliDuration, cb, startTime: Date.now() }); if (!aniInterval) { aniInterval = setInterval(() => { const now = Date.now(); let aniLen = aniArr.length; let index = 0; while(index < aniLen) { const tempAni = aniArr[index]; const past = now - tempAni.startTime; if (past < tempAni.milliDuration) { const percent = tempAni.easingFunction(past / tempAni.milliDuration); tempAni.dom.style[tempAni.style] = tempAni.start + (percent * tempAni.change) + tempAni.unit; index++; continue; } tempAni.dom.style[tempAni.style] = tempAni.end + tempAni.unit; setTimeout(() => { tempAni.cb && tempAni.cb(); }, 0); aniArr.splice(index, 1); aniLen--; if (aniLen === 0) { clearInterval(aniInterval); aniInterval = null; } } }, window.yonos ? 0 : 10); } */ } window.animate = animate; exports['default'] = animate; module.exports = exports['default']; /***/ }, /* 7 */ /***/ function(module, exports) { /** * https://github.com/gre/bezier-easing * BezierEasing - use bezier curve for transition easing function * by Gaëtan Renaudeau 2014 - 2015 – MIT License */ // These values are established by empiricism with tests (tradeoff: performance VS precision) var NEWTON_ITERATIONS = 4; var NEWTON_MIN_SLOPE = 0.001; var SUBDIVISION_PRECISION = 0.0000001; var SUBDIVISION_MAX_ITERATIONS = 10; var kSplineTableSize = 11; var kSampleStepSize = 1.0 / (kSplineTableSize - 1.0); var float32ArraySupported = typeof Float32Array === 'function'; function A(aA1, aA2) { return 1.0 - 3.0 * aA2 + 3.0 * aA1; } function B(aA1, aA2) { return 3.0 * aA2 - 6.0 * aA1; } function C(aA1) { return 3.0 * aA1; } // Returns x(t) given t, x1, and x2, or y(t) given t, y1, and y2. function calcBezier(aT, aA1, aA2) { return ((A(aA1, aA2) * aT + B(aA1, aA2)) * aT + C(aA1)) * aT; } // Returns dx/dt given t, x1, and x2, or dy/dt given t, y1, and y2. function getSlope(aT, aA1, aA2) { return 3.0 * A(aA1, aA2) * aT * aT + 2.0 * B(aA1, aA2) * aT + C(aA1); } function binarySubdivide(aX, aA, aB, mX1, mX2) { var currentX, currentT, i = 0; do { currentT = aA + (aB - aA) / 2.0; currentX = calcBezier(currentT, mX1, mX2) - aX; if (currentX > 0.0) { aB = currentT; } else { aA = currentT; } } while (Math.abs(currentX) > SUBDIVISION_PRECISION && ++i < SUBDIVISION_MAX_ITERATIONS); return currentT; } function newtonRaphsonIterate(aX, aGuessT, mX1, mX2) { for (var i = 0; i < NEWTON_ITERATIONS; ++i) { var currentSlope = getSlope(aGuessT, mX1, mX2); if (currentSlope === 0.0) { return aGuessT; } var currentX = calcBezier(aGuessT, mX1, mX2) - aX; aGuessT -= currentX / currentSlope; } return aGuessT; } module.exports = function bezier(mX1, mY1, mX2, mY2) { if (!(0 <= mX1 && mX1 <= 1 && 0 <= mX2 && mX2 <= 1)) { throw new Error('bezier x values must be in [0, 1] range'); } // Precompute samples table var sampleValues = float32ArraySupported ? new Float32Array(kSplineTableSize) : new Array(kSplineTableSize); if (mX1 !== mY1 || mX2 !== mY2) { for (var i = 0; i < kSplineTableSize; ++i) { sampleValues[i] = calcBezier(i * kSampleStepSize, mX1, mX2); } } function getTForX(aX) { var intervalStart = 0.0; var currentSample = 1; var lastSample = kSplineTableSize - 1; for (; currentSample !== lastSample && sampleValues[currentSample] <= aX; ++currentSample) { intervalStart += kSampleStepSize; } --currentSample; // Interpolate to provide an initial guess for t var dist = (aX - sampleValues[currentSample]) / (sampleValues[currentSample + 1] - sampleValues[currentSample]); var guessForT = intervalStart + dist * kSampleStepSize; var initialSlope = getSlope(guessForT, mX1, mX2); if (initialSlope >= NEWTON_MIN_SLOPE) { return newtonRaphsonIterate(aX, guessForT, mX1, mX2); } else if (initialSlope === 0.0) { return guessForT; } else { return binarySubdivide(aX, intervalStart, intervalStart + kSampleStepSize, mX1, mX2); } } return function BezierEasing(x) { if (mX1 === mY1 && mX2 === mY2) { return x; // linear } // Because JavaScript number are imprecise, we should guarantee the extremes are right. if (x === 0) { return 0; } if (x === 1) { return 1; } return calcBezier(getTForX(x), mY1, mY2); }; }; /***/ }, /* 8 */ /***/ function(module, exports, __webpack_require__) { Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _widget = __webpack_require__(3); var _widget2 = _interopRequireDefault(_widget); var _event = __webpack_require__(2); var _event2 = _interopRequireDefault(_event); var _animate = __webpack_require__(6); var _animate2 = _interopRequireDefault(_animate); var Grid = (function (_Widget) { _inherits(Grid, _Widget); function Grid(container) { _classCallCheck(this, Grid); _Widget.call(this, container); this._init(); this._writeGridInnerHTML(); // 初始化子组件 _Widget.prototype._initChildWidgets.call(this); this['_checkWidgets_' + this.dir](); } Grid.prototype._initChildWidgets = function _initChildWidgets() { return; }; // 判断grid是否需要reRender比较特殊,需要特殊处理 Grid.prototype.reRender = function reRender() {} // if (!document.querySelector('#' + this.id)) { // return; // } // this.con = document.querySelector('#' + this.id); // const firstChildWidget = this.childWidgets[0]; // const firstChildNode = this.getDOMNode().children[0]; // let needReRender = false; // // 判断第一个子dom是否还是原来的dom,如果是,则用原来的templateHTML,如果不是,用新的templateHTML // if (firstChildNode !== firstChildWidget.getDOMNode()) { // this.templateHTML = firstChildNode.outerHTML; // needReRender = true; // } // // 比较fe-cfg的几个属性,如果有改变,则需要reRender // const options = this._getOptions(this.con); // if (this.options.cfg.dir !== options.cfg.dir || // this.options.cfg.row !== options.cfg.row || // this.options.cfg.col !== options.cfg.col // ) { // this.options = options; // needReRender = true; // } // if (!needReRender) { // return; // } // this.setCachedChildWidget(null); // this.setFocusedChildWidget(null); // this._init(); // this._writeGridInnerHTML(); // super._initChildWidgets(); // this[`_checkWidgets_${this.dir}`](); // if (this.focused) { // this.focus(true); // } // 设置用户配置内容并初始化Grid ; Grid.prototype._init = function _init() { this.dir = this.options.cfg.dir === 'h' ? 'h' : 'v'; this.style = this.options.cfg.dir === 'h' ? 'left' : 'top'; // Grid初始化 if (isNaN(this.options.cfg.row) || isNaN(this.options.cfg.col)) { console.error('[FocusEngine_Error]Grid组件必须设置行和列!行列数为视口内可见的最多行列数。'); return; } this.duration = this.options.cfg.duration !== undefined ? parseFloat(this.options.cfg.duration) : 0.3; this.speedupDuration = this.options.cfg.speedup_duration !== undefined ? parseFloat(this.options.cfg.speedup_duration) : this.duration * 0.6; this.easing = this.options.cfg.easing ? this.options.cfg.easing : 'ease-out'; this.speedupEasing = this.options.cfg.speedup_easing ? this.options.cfg.speedup_easing : 'linear'; this.delay = this.options.cfg.delay !== undefined ? parseFloat(this.options.cfg.delay) : 0; this.noEmpty = this.options.cfg.no_empty === 'yes'; if (this.delay > 0 && this.delay < 0.05) { // 限制最小delay this.delay = 0.05; } this.delayStyle = this.options.cfg.delay_style === '1' ? 1 : 0; // grid对应的列表数据 this.data = this.data || []; // 内容的渲染器 this.itemRender = this.itemRender || null; // 是否正在滚动中 this.scrolling = false; // 居中原点数,如果是1,则每次都居中对齐,如果是2,则两排对齐。 this.originSize = this.options.cfg.origin_size ? parseInt(this.options.cfg.origin_size) : 1; this.origin = 0; // 当前widget对应的数据 this.dataIndex = 0; this['_init_more_' + this.dir](); }; Grid.prototype._init_more_v = function _init_more_v() { this.row = parseInt(this.options.cfg.row) + 2; this.col = parseInt(this.options.cfg.col); // 如果是设置居中原点数,那么行数必须等于排数,强制相等 if (this.originSize > 1) { this.row = this.originSize + 2; // 如果是居中对齐,则行数必须是奇数才可,强制设置为奇数 } else if (this.row % 2 === 0) { this.row++; } this.upRow = 0; this.downRow = this.row - 1; }; Grid.prototype._init_more_h = function _init_more_h() { this.row = parseInt(this.options.cfg.row); this.col = parseInt(this.options.cfg.col) + 2; // 如果是设置居中原点数,那么行数必须等于排数,强制相等 if (this.originSize > 1) { this.col = this.originSize + 2; // 如果是居中对齐,则列数必须是奇数才可,强制设置为奇数 } else if (this.col % 2 === 0) { this.col++; } this.lefCol = 0; this.rightCol = this.col - 1; }; // 根据第一个子元素来写grid的子组件html Grid.prototype._writeGridInnerHTML = function _writeGridInnerHTML() { var templateDOM = this.con.children[0]; templateDOM.id = ''; this.templateHTML = templateDOM.outerHTML; this.con.innerHTML = ''; this['_writeGridInnerHTML_' + this.dir](); }; Grid.prototype._writeGridInnerHTML_v = function _writeGridInnerHTML_v() { var rowHTML = ''; for (var i = 0; i < this.col; i++) { rowHTML += this.templateHTML; } rowHTML += '
'; for (var i = 0; i < this.row; i++) { var rowDom = document.createElement('div'); rowDom.innerHTML = rowHTML; rowDom.style.position = 'relative'; // 先设置隐藏,等setData后再显示 // rowDom.style.opacity = 0; this.con.appendChild(rowDom); } }; Grid.prototype._writeGridInnerHTML_h = function _writeGridInnerHTML_h() { var colHTML = ''; for (var i = 0; i < this.row; i++) { colHTML += this.templateHTML + '
'; } var listDom = document.createElement('div'); listDom.style.width = '99999px'; listDom.style.height = '100%'; for (var i = 0; i < this.col; i++) { var colDom = document.createElement('div'); colDom.innerHTML = colHTML; colDom.style.position = 'relative'; colDom.style.float = 'left'; // 先设置隐藏,等setData后再显示 // colDom.style.opacity = 0; listDom.appendChild(colDom); } listDom.appendChild(this.parseDom('
')[0]); this.con.appendChild(listDom); }; // 渲染并检查子组件 Grid.prototype._checkWidgets_v = function _checkWidgets_v() { // chidWidgets的二维数组 this.widgetArr = []; var j = 0; for (var i = 0, l = this.childWidgets.length; i < l; i += this.col) { var rowArr = this.childWidgets.slice(i, i + this.col); rowArr = rowArr.map(function (v, k) { v.row = j; v.col = k; return v; }); this.widgetArr.push(rowArr); j++; } var conRect = this.getWidgetRect(); // 容器的高度 this.conHeight = conRect.height; // grid子组件行高,即上下两个组件中心点之间的纵向距离 this.rowHeight = this.childWidgets[0].con.parentNode.getBoundingClientRect().height; // 选中的组件的排列位置数组,如果是1,那么都是以中心居中排列,如果是2,则两行排列 this.originArr = []; if (this.originSize === 1) { this.originArr.push(this.conHeight / 2); } else { for (var i = 0, halfHeight = this.rowHeight / 2; i < this.originSize; i++) { this.originArr.push(halfHeight + i * this.rowHeight); } } // 当前widget this.curWid = this.childWidgets[0]; // 检查子组件 for (var i = 0; i < this.childWidgets.length; i++) { var widget = this.childWidgets[i]; var styles = getComputedStyle(widget.getDOMNode()); // 初始时该widget的中心点距顶部距离 widget.oriOffsetCY = widget.getWidgetRect().centerY - conRect.top; widget.oriCenteY = widget.oriOffsetCY; // 纪录当前offset widget.offset = 0; widget.animationArr = []; widget.isEmpty = true; // 相对于当前中的子组件,组件的行数,负代表在上方,正代表在下方 widget.offsetRow = widget.row; widget.whichData = i; } }; // 渲染并检查子组件 Grid.prototype._checkWidgets_h = function _checkWidgets_h() { // chidWidgets的二维数组 this.widgetArr = []; var j = 0; for (var i = 0, l = this.childWidgets.length; i < l; i += this.row) { var rowArr = this.childWidgets.slice(i, i + this.row); rowArr = rowArr.map(function (v, k) { v.col = j; v.row = k; return v; }); this.widgetArr.push(rowArr); j++; } var conRect = this.getWidgetRect(); // 容器的宽度 this.conWidth = conRect.width; // grid子组件列宽,即左右两个组件中心点之间的横向距离 this.colWidth = this.childWidgets[0].con.parentNode.getBoundingClientRect().width; // 选中的组件的排列位置数组,如果是1,那么都是以中心居中排列,如果是2,则两行排列 this.originArr = []; if (this.originSize === 1) { this.originArr.push(this.conWidth / 2); } else { for (var i = 0, halfWidth = this.colWidth / 2; i < this.originSize; i++) { this.originArr.push(halfWidth + i * this.colWidth); } } // 当前widget this.curWid = this.childWidgets[0]; // 检查子组件 for (var i = 0; i < this.childWidgets.length; i++) { var widget = this.childWidgets[i]; var styles = getComputedStyle(widget.getDOMNode()); // 初始时该widget的中心点距左侧距离 widget.oriOffsetCX = widget.getWidgetRect().centerX - conRect.left; widget.oriCenteX = widget.oriOffsetCX; // 纪录当前offset widget.offset = 0; widget.animationArr = []; widget.isEmpty = true; // 相对于当前中的子组件,组件的列数,负代表在左侧,正代表在右侧 widget.offsetCol = widget.col; widget.whichData = i; } }; Grid.prototype.keydownDefault = function keydownDefault(e) { if (this.stopSwitch) { e.preventDefault(); return; } if (e.isDefaultPrevented()) { return; } this['_keydownDefault_' + this.dir](e); }; Grid.prototype._keydownDefault_v = function _keydownDefault_v(e) { var nexIndex = this._getNextDataIndex_v(e.keyCode); if (nexIndex !== -1) { e.preventDefault(); var nexFocWid = this.childWidgets[Math.floor(nexIndex / this.col) % this.row * this.col + nexIndex % this.col]; nexFocWid.focus(); } }; Grid.prototype._keydownDefault_h = function _keydownDefault_h(e) { var nexIndex = this._getNextDataIndex_h(e.keyCode); if (nexIndex !== -1) { e.preventDefault(); var nexFocWid = this.childWidgets[Math.floor(nexIndex / this.row) % this.col * this.row + nexIndex % this.row]; nexFocWid.focus(); } }; Grid.prototype._getNextDataIndex_v = function _getNextDataIndex_v(keyCode) { var nexIndex = this.dataIndex; // 向左 if (keyCode === _event2['default'].VK_LEFT) { if (nexIndex % this.col !== 0) { return --nexIndex; } return -1; } if (keyCode === _event2['default'].VK_RIGHT) { if (nexIndex % this.col !== this.col - 1 && nexIndex + 1 < this.data.length) { return ++nexIndex; } return -1; } if (keyCode === _event2['default'].VK_UP) { if (nexIndex >= this.col) { return nexIndex -= this.col; } return -1; } if (keyCode === _event2['default'].VK_DOWN) { if (nexIndex + this.col < this.data.length) { return nexIndex += this.col; } if (nexIndex + this.col >= this.data.length && Math.floor(nexIndex / this.col) < Math.floor((this.data.length - 1) / this.col)) { return this.data.length - 1; } this.fire(new _event2['default']('reachend', this)); return -1; } return -1; }; Grid.prototype._getNextDataIndex_h = function _getNextDataIndex_h(keyCode) { var nexIndex = this.dataIndex; // 向左 if (keyCode === _event2['default'].VK_LEFT) { if (nexIndex >= this.row) { return nexIndex -= this.row; } return -1; } if (keyCode === _event2['default'].VK_RIGHT) { if (nexIndex + this.row < this.data.length) { return nexIndex += this.row; } if (nexIndex + this.row >= this.data.length && Math.floor(nexIndex / this.row) < Math.floor((this.data.length - 1) / this.row)) { return this.data.length - 1; } this.fire(new _event2['default']('reachend', this)); return -1; } if (keyCode === _event2['default'].VK_UP) { if (nexIndex % this.row !== 0) { return --nexIndex; } return -1; } if (keyCode === _event2['default'].VK_DOWN) { if (nexIndex % this.row !== this.row - 1 && nexIndex + 1 < this.data.length) { return ++nexIndex; } return -1; } return -1; }; Grid.prototype.focusDefault = function focusDefault(e) { _Widget.prototype.focusDefault.call(this, e); // 如果是setData调用,那么只需要更改focus状态,无需移动 if (this.isSetData) { return; } // focus时监测一次容器宽高,如果都是0,说明focus前是display:none,需要重新_checkWidgets if (!this.conHeight && !this.conWidth) { this['_checkWidgets_' + this.dir](); } this['_focusDefault_' + this.dir](e); }; Grid.prototype._focusDefault_v = function _focusDefault_v(e) { var nexWid = this.getFocusedChildWidget(); this.dataIndex = nexWid.whichData; this.curWid = nexWid; // 如果左右切换,则不滚动 if (nexWid.offsetRow === 0) { return; } this._computeGridStatus_v(nexWid); this._computeAllWidgetStatus_v(nexWid); this._triggerWidgetsAnimation_v(nexWid); }; Grid.prototype._focusDefault_h = function _focusDefault_h(e) { var nexWid = this.getFocusedChildWidget(); this.dataIndex = nexWid.whichData; this.curWid = nexWid; // 如果上下切换,则不滚动 if (nexWid.offsetCol === 0) { return; } this._computeGridStatus_h(nexWid); this._computeAllWidgetStatus_h(nexWid); this._triggerWidgetsAnimation_h(nexWid); }; // 根据焦点widget计算纵向Grid滚动的一些状态: // 1. 是向上还是向下滚动;2. 滚动结束后上下各有多少行;3. 焦点组件的位置 Grid.prototype._computeGridStatus_v = function _computeGridStatus_v(curWid) { // 如果是setData调用,那么一些属性不需要再次计算 if (!this.isSetData) { // 确定是向上还是向下滚动 this.isUp = curWid.offsetRow < 0; // 确定下次focus子组件的origin if (this.isUp && this.origin > 0) { this.origin--; } else if (!this.isUp && this.origin < this.originArr.length - 1) { this.origin++; } } // 选中的组件的中心点应在的位置 this.focsedCY = this.originArr[this.origin]; // 确定滚动结束后当前选中的行上下各应该有多少行 // 没有设置originSize的情况 if (this.originSize === 1) { this.upRow = Math.floor(this.row / 2); this.downRow = Math.floor(this.row / 2); var curDataRow = Math.floor(this.dataIndex / this.col); var lastDataRow = Math.floor((this.data.length - 1) / this.col); var tempFocsedCY = this.focsedCY; // 到达底部的情况 var downOffset = (lastDataRow - curDataRow) * this.rowHeight + this.rowHeight / 2; if (downOffset < tempFocsedCY) { this.focsedCY = this.conHeight - downOffset; this.downRow = lastDataRow - curDataRow; this.upRow = this.row - 1 - this.downRow; } // 到达顶部的情况,如果同时到达顶部和底部,则按顶部算 var upOffset = curDataRow * this.rowHeight + this.rowHeight / 2; var listHeight = (lastDataRow + 1) * this.rowHeight; if (upOffset < tempFocsedCY || listHeight < this.conHeight) { this.focsedCY = upOffset; this.upRow = curDataRow; this.downRow = this.row - 1 - this.upRow; return; } // 设置originSize的情况 } else { this.upRow = this.origin + 1; this.downRow = this.originSize - this.origin; // 到达顶部的情况 if (Math.floor(this.dataIndex / this.col) === 0) { this.upRow = 0; this.downRow = this.row - 1; return; } // 到达底部的情况 if (Math.floor(this.dataIndex / this.col) === Math.floor((this.data.length - 1) / this.col)) { this.upRow = this.row - 1; this.downRow = 0; } } }; // 根据焦点widget计算横向Grid滚动的一些状态: // 1. 是向左还是向右滚动;2. 滚动结束后左右各有多少列;3. 焦点组件的位置 Grid.prototype._computeGridStatus_h = function _computeGridStatus_h(curWid) { // 如果是setData调用,那么一些属性不需要再次计算 if (!this.isSetData) { // 确定是向上还是向下滚动 this.isLeft = curWid.offsetCol < 0; // 确定下次focus子组件的origin if (this.isLeft && this.origin > 0) { this.origin--; } else if (!this.isLeft && this.origin < this.originArr.length - 1) { this.origin++; } } // 选中的组件的中心点应在的位置 this.focsedCX = this.originArr[this.origin]; // 确定滚动结束后当前选中的行左右各应该有多少列 // 没有设置originSize的情况 if (this.originSize === 1) { this.leftCol = Math.floor(this.col / 2); this.rightCol = Math.floor(this.col / 2); var curDataCol = Math.floor(this.dataIndex / this.row); var lastDataCol = Math.floor((this.data.length - 1) / this.row); var tempFocsedCX = this.focsedCX; // 到达右部的情况 var rightOffset = (lastDataCol - curDataCol) * this.colWidth + this.colWidth / 2; if (rightOffset < tempFocsedCX) { this.focsedCX = this.conWidth - rightOffset; this.rightCol = lastDataCol - curDataCol; this.leftCol = this.col - 1 - this.rightCol; } // 到达左部的情况,如果同时到达左部和右部,则按左部算 var leftOffset = curDataCol * this.colWidth + this.colWidth / 2; var listWidth = (lastDataCol + 1) * this.colWidth; if (leftOffset < tempFocsedCX || listWidth < this.conWidth) { this.focsedCX = leftOffset; this.leftCol = curDataCol; this.rightCol = this.col - 1 - this.leftCol; return; } // 设置originSize的情况 } else { this.leftCol = this.origin + 1; this.rightCol = this.originSize - this.origin; // 到达左部的情况 if (this.dataIndex < this.row) { this.leftCol = 0; this.rightCol = this.col - 1; return; } // 到达右部的情况 if (Math.floor(this.dataIndex / this.row) === Math.floor((this.data.length - 1) / this.row)) { this.leftCol = this.col - 1; this.rightCol = 0; } } }; // 根据Grid的状态计算每个widget自身的状态 // curWid:当前焦点子组件 // 纵向Grid使用 Grid.prototype._computeAllWidgetStatus_v = function _computeAllWidgetStatus_v(curWid) { var curWidRow = curWid.row; var curDataRow = Math.floor(this.dataIndex / this.col); this._computeRowWidgetStatus_v(curWidRow, curDataRow, 0); for (var i = -1; i >= -this.upRow; i--) { this._computeRowWidgetStatus_v(curWidRow, curDataRow, i); } for (var i = 1; i <= this.downRow; i++) { this._computeRowWidgetStatus_v(curWidRow, curDataRow, i); } }; // 横向Grid使用 Grid.prototype._computeAllWidgetStatus_h = function _computeAllWidgetStatus_h(curWid) { var curWidCol = curWid.col; var curDataCol = Math.floor(this.dataIndex / this.row); this._computeColWidgetStatus_h(curWidCol, curDataCol, 0); for (var i = -1; i >= -this.leftCol; i--) { this._computeColWidgetStatus_h(curWidCol, curDataCol, i); } for (var i = 1; i <= this.rightCol; i++) { this._computeColWidgetStatus_h(curWidCol, curDataCol, i); } }; // 设置某行widget的状态,纵向Grid使用 // 如:动画、位置、whichData等 Grid.prototype._computeRowWidgetStatus_v = function _computeRowWidgetStatus_v(curWidRow, curDataRow, offsetRow) { var widRow = (curWidRow + offsetRow + this.row) % this.row; var dataRow = curDataRow + offsetRow; for (var i = 0; i < this.col; i++) { var widget = this.childWidgets[this.col * widRow + i]; var whichData = this.col * dataRow + i; var opacityAfterAnimation = 1; if (whichData >= this.data.length || whichData < 0) { opacityAfterAnimation = 0; } widget.whichData = whichData; widget.whichDataRow = dataRow; if (this.isSetData) { widget.offset = this.focsedCY + offsetRow * this.rowHeight - widget.oriCenteY; widget.con.parentNode.style.top = widget.offset + 'px'; widget.con.style.opacity = opacityAfterAnimation; widget.offsetRow = offsetRow; } else { var nexOffset = this.focsedCY + offsetRow * this.rowHeight - widget.oriCenteY; widget.futureOffset = nexOffset; widget.animationArr.push({ offset: nexOffset, jump: widget.offsetRow * offsetRow < 0, opacity: opacityAfterAnimation }); widget.offsetRow = offsetRow; } } }; // 设置某行widget的状态,横向Grid使用 // 如:动画、位置、whichData等 Grid.prototype._computeColWidgetStatus_h = function _computeColWidgetStatus_h(curWidCol, curDataCol, offsetCol) { var widCol = (curWidCol + offsetCol + this.col) % this.col; var dataCol = curDataCol + offsetCol; for (var i = 0; i < this.row; i++) { var widget = this.childWidgets[this.row * widCol + i]; var whichData = dataCol * this.row + i; var opacityAfterAnimation = 1; if (whichData >= this.data.length || whichData < 0) { opacityAfterAnimation = 0; } widget.whichData = whichData; widget.whichDataCol = dataCol; if (this.isSetData) { widget.offset = this.focsedCX + offsetCol * this.colWidth - widget.oriCenteX; widget.con.parentNode.style.left = widget.offset + 'px'; widget.con.style.opacity = opacityAfterAnimation; widget.offsetCol = offsetCol; } else { var nexOffset = this.focsedCX + offsetCol * this.colWidth - widget.oriCenteX; widget.futureOffset = nexOffset; widget.animationArr.push({ offset: nexOffset, jump: widget.offsetCol * offsetCol < 0, opacity: opacityAfterAnimation }); widget.offsetCol = offsetCol; } } }; // 如果是还未开始动画,计算动画延迟且触发动画 Grid.prototype._triggerWidgetsAnimation_v = function _triggerWidgetsAnimation_v() { for (var i = 0; i < this.row; i++) { var widget = this.widgetArr[i][0]; if (widget.animationArr.length !== 1) { return; } this['_startScroll_' + this.dir](widget); } }; // 如果是还未开始动画,计算动画延迟且触发动画 Grid.prototype._triggerWidgetsAnimation_h = function _triggerWidgetsAnimation_h() { for (var i = 0; i < this.col; i++) { var widget = this.widgetArr[i][0]; if (widget.animationArr.length !== 1) { return; } this['_startScroll_' + this.dir](widget); } }; Grid.prototype._startScroll_v = function _startScroll_v(widget, speedup) { var _this = this; var rowWidgets = this.widgetArr[widget.row]; for (var i = 0, l = rowWidgets.length; i < l; i++) { var tempWidget = rowWidgets[i]; var _animation = tempWidget.animationArr[0]; if (_animation.jump) { if (this.noEmpty) { var item = this.data[tempWidget.whichData]; if (item) { this.itemRender(tempWidget.getDOMNode(), item); tempWidget.isEmpty = false; } } else if (!tempWidget.isEmpty) { this.itemRender(tempWidget.getDOMNode()); tempWidget.isEmpty = true; } } } var endCb = function endCb() { for (var i = 0, l = rowWidgets.length; i < l; i++) { var tempWidget = rowWidgets[i]; var preAnimation = tempWidget.animationArr.shift(); tempWidget.offset = preAnimation.offset; tempWidget.con.style.opacity = preAnimation.opacity; } if (widget.animationArr.length > 0) { _this._startScroll_v(widget, true); } else { if (_this.scrolling && widget.row === 0) { _this.scrolling = false; _this.fire(new _event2['default']('scrollend', _this)); } } }; this.stopSwitch = widget.animationArr.length >= 2; var animation = widget.animationArr[0]; if (animation.offset !== widget.offset && !animation.jump) { if (!this.scrolling) { this.scrolling = true; this.fire(new _event2['default']('scrollstart', this, { distance: animation.offset - widget.offset })); } } (0, _animate2['default'])({ dom: widget.con.parentNode, style: this.style, start: animation.jump ? animation.offset : widget.offset, end: animation.offset, duration: speedup ? this.speedupDuration : this.duration, easing: speedup ? this.speedupEasing : this.easing, cb: endCb }); }; Grid.prototype._startScroll_h = function _startScroll_h(widget, speedup) { var _this2 = this; var colWidgets = this.widgetArr[widget.col]; for (var i = 0, l = colWidgets.length; i < l; i++) { var tempWidget = colWidgets[i]; var _animation2 = tempWidget.animationArr[0]; if (_animation2.jump) { if (this.noEmpty) { var item = this.data[tempWidget.whichData]; if (item) { this.itemRender(tempWidget.getDOMNode(), item); tempWidget.isEmpty = false; } } else if (!tempWidget.isEmpty) { this.itemRender(tempWidget.getDOMNode()); tempWidget.isEmpty = true; } } } var endCb = function endCb() { for (var i = 0, l = colWidgets.length; i < l; i++) { var tempWidget = colWidgets[i]; var preAnimation = tempWidget.animationArr.shift(); tempWidget.offset = preAnimation.offset; tempWidget.con.style.opacity = preAnimation.opacity; } if (widget.animationArr.length > 0) { _this2._startScroll_h(widget, true); } else { if (_this2.scrolling && widget.col === 0) { _this2.scrolling = false; _this2.fire(new _event2['default']('scrollend', _this2)); } } }; this.stopSwitch = widget.animationArr.length >= 2; var animation = widget.animationArr[0]; if (animation.offset !== widget.offset && !animation.jump) { if (!this.scrolling) { this.scrolling = true; this.fire(new _event2['default']('scrollstart', this, { distance: animation.offset - widget.offset })); } } (0, _animate2['default'])({ dom: widget.con.parentNode, style: this.style, start: animation.jump ? animation.offset : widget.offset, end: animation.offset, duration: speedup ? this.speedupDuration : this.duration, easing: speedup ? this.speedupEasing : this.easing, cb: endCb }); }; Grid.prototype.scrollendDefault = function scrollendDefault(e) { if (e.isDefaultPrevented()) { return; } if (this.scrolling) { return; } this['_toggleDelay_' + this.dir](0); }; Grid.prototype.scrollstartDefault = function scrollstartDefault(e) { if (e.isDefaultPrevented()) { return; } this['_toggleDelay_' + this.dir](e.distance); this.loadContentTimeout && clearTimeout(this.loadContentTimeout); // Blitz内核提供的禁止图片加载的方法,优化动画 window.stopImgLoading && window.stopImgLoading(); }; // 开始或者结束delay状态 // @param {Number} moveDistance 此次焦点滚动的距离,根据改距离确定delay幅度 Grid.prototype._toggleDelay_v = function _toggleDelay_v() { var _this3 = this; var moveDistance = arguments.length <= 0 || arguments[0] === undefined ? 0 : arguments[0]; var focusedWidget = this.getFocusedChildWidget() || this.getCachedChildWidget(); if (!focusedWidget) { return; } var focusCol = focusedWidget.col; for (var i = 0; i < this.col; i++) { for (var j = 0; j < this.row; j++) { var widget = this.widgetArr[j][i]; var delayOffset = -(moveDistance * this.delay * Math.abs(widget.col - focusCol * this.delayStyle)); var aniEndCb = null; if (delayOffset === 0 && i === 0 && j === 0) { aniEndCb = function () { _this3.loadContent(); }; } (0, _animate2['default'])({ dom: widget.con, style: this.style, start: widget.delayOffset || 0, end: delayOffset, duration: this.duration, easing: 'ease-out', cb: aniEndCb }); widget.delayOffset = delayOffset; } } }; // 开始或者结束delay状态 // @param {Number} moveDistance 此次焦点滚动的距离,根据改距离确定delay幅度 Grid.prototype._toggleDelay_h = function _toggleDelay_h() { var _this4 = this; var moveDistance = arguments.length <= 0 || arguments[0] === undefined ? 0 : arguments[0]; var focusedWidget = this.getFocusedChildWidget() || this.getCachedChildWidget(); if (!focusedWidget) { return; } var focusRow = focusedWidget.row; for (var i = 0; i < this.row; i++) { for (var j = 0; j < this.col; j++) { var widget = this.widgetArr[j][i]; var delayOffset = -(moveDistance * this.delay * Math.abs(widget.row - focusRow * this.delayStyle)); var aniEndCb = null; if (delayOffset === 0 && i === 0 && j === 0) { aniEndCb = function () { _this4.loadContent(); }; } (0, _animate2['default'])({ dom: widget.con, style: this.style, start: widget.delayOffset || 0, end: delayOffset, duration: this.duration, easing: 'ease-out', cb: aniEndCb }); widget.delayOffset = delayOffset; } } }; Grid.prototype.setItemRender = function setItemRender(fun) { this.itemRender = function (dom, item) { if (!item) { dom.setAttribute('can-focus', 'no'); } else { dom.removeAttribute('can-focus'); } fun(dom, item); // Blitz提供的加载文字的方法,用于避免滚动过程中文字加载不全的问题 dom.createTextGraphics && dom.createTextGraphics(); }; return this; }; Grid.prototype.addData = function addData(data) { if (!Array.isArray(data)) { return; } var newData = this.data.concat(data); var dataIndex = this.focused ? this.getFocusedChildWidget().whichData : this.dataIndex; this.setData(newData, dataIndex, this.origin); return this; }; Grid.prototype.setData = function setData(data, index, origin) { if (Array.isArray(data)) { this.data = data.concat([]); } this.isSetData = true; this.allDataCol = Math.floor(this.data.length / this.row); this.allDataRow = Math.floor(this.data.length / this.col); if (index !== undefined && index < this.data.length) { this.dataIndex = index; } else { this.dataIndex = 0; } this.origin = this['_getSetDataOrigin_' + this.dir](this.data.length, this.dataIndex, origin); var nexWid = this['_getWidgetByDataIndex_' + this.dir](this.dataIndex); this['_computeGridStatus_' + this.dir](nexWid); this['_computeAllWidgetStatus_' + this.dir](nexWid); nexWid.dataIndex = this.dataIndex; if (this.focused) { nexWid.focus(true, true); } else { nexWid.cache(); } this.curWid = nexWid; this.loadContent(); this.isSetData = false; return this; }; // 根据dataIndex获取当前对应的是哪个widget Grid.prototype._getWidgetByDataIndex_v = function _getWidgetByDataIndex_v(dataIndex) { return this.childWidgets[Math.floor(dataIndex / this.col) % this.row * this.col + dataIndex % this.col]; }; // 根据dataIndex获取当前对应的是哪个widget Grid.prototype._getWidgetByDataIndex_h = function _getWidgetByDataIndex_h(dataIndex) { return this.childWidgets[Math.floor(dataIndex / this.row) % this.col * this.row + dataIndex % this.row]; }; // 根据数据长度,当前setData的index及origin返回合适的origin Grid.prototype._getSetDataOrigin_v = function _getSetDataOrigin_v(dataLength, index, origin) { if (origin === undefined || origin > this.originSize) { origin = 0; } var row = Math.floor(this.dataIndex / this.col); var lastRow = Math.floor((this.data.length - 1) / this.col); // 最后的几行item有最小的origin限制 if (lastRow - row < this.originSize) { var minOrigin = row + this.originSize - lastRow - 1; if (minOrigin >= 0 && origin < minOrigin) { origin = minOrigin; } } // 最前的几个item的origin有最大的origin限制 if (row < this.originSize) { var maxOrigin = row; if (origin > maxOrigin) { origin = maxOrigin; } } return origin; }; // 根据数据长度,当前setData的index及origin返回合适的origin Grid.prototype._getSetDataOrigin_h = function _getSetDataOrigin_h(dataLength, index, origin) { if (origin === undefined || origin > this.originSize) { origin = 0; } var col = Math.floor(this.dataIndex / this.row); var lastCol = Math.floor((this.data.length - 1) / this.row); // 最后的几列item有最小的origin限制 if (lastCol - col < this.originSize) { var minOrigin = col + this.originSize - lastCol - 1; if (minOrigin >= 0 && origin < minOrigin) { origin = minOrigin; } } // 最前的几个item的origin有最大的origin限制 if (col < this.originSize) { var maxOrigin = col; if (origin > maxOrigin) { origin = maxOrigin; } } return origin; }; // 加载内容 Grid.prototype.loadContent = function loadContent() { if (this.scrolling) { return; } for (var i = 0, _length = this.childWidgets.length; i < _length; i++) { var widget = this.childWidgets[i]; // 如果是setData调用,那么需要强制重新渲染item if (widget.isEmpty || this.isSetData) { var item = this.data[widget.whichData]; this.itemRender && this.itemRender(widget.getDOMNode(), item); widget.isEmpty = false; } } // Blitz内核提供的恢复图片加载的方法 window.resumeImgLoading && window.resumeImgLoading(); this.fire(new _event2['default']('contentloaded', this)); }; // 更改Grid的内容 // 参数同数组的splice Grid.prototype.splice = function splice() { Array.prototype.splice.apply(this.data, arguments); this.isSetData = true; this.loadContent(); this.isSetData = false; }; Grid.prototype.getEvents = function getEvents() { return _Widget.prototype.getEvents.call(this).concat(['reachend', 'contentloaded']); }; return Grid; })(_widget2['default']); exports['default'] = Grid; module.exports = exports['default']; /***/ }, /* 9 */ /***/ function(module, exports, __webpack_require__) { Object.defineProperty(exports, '__esModule', { value: true }); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } var _engine = __webpack_require__(1); var _engine2 = _interopRequireDefault(_engine); var _widget = __webpack_require__(3); var _widget2 = _interopRequireDefault(_widget); var _event = __webpack_require__(2); var _event2 = _interopRequireDefault(_event); var _animate = __webpack_require__(6); var _animate2 = _interopRequireDefault(_animate); // TODO:补充注释 var Grille = (function (_Widget) { _inherits(Grille, _Widget); function Grille(container) { _classCallCheck(this, Grille); _Widget.call(this, container); this._init(); } Grille.prototype._init = function _init() { var _this = this; // this.horizontal = this.options.cfg.dir === 'h'; // 创建内部的list this.listDom = document.createElement('div'); this.listDom.style.position = 'relative'; this.listDom.style.width = '100%'; this.con.innerHTML = ''; this.con.appendChild(this.listDom); this.duration = this.options.cfg.duration !== undefined ? parseFloat(this.options.cfg.duration) : 0.3; this.delay = this.options.cfg.delay !== undefined ? parseFloat(this.options.cfg.delay) : 0.1; this.easing = this.options.cfg.easing ? this.options.cfg.easing : 'ease-out'; this.bottomPadding = this.options.cfg.bottom_padding !== undefined ? parseFloat(this.options.cfg.bottom_padding) : 0; this.visibleRow = this.options.cfg.visible_row !== undefined ? parseInt(this.options.cfg.visible_row) : 5; this.listDom.style.transition = 'transform ' + this.duration + 's ' + this.easing; this.listDom.addEventListener('transitionend', function (e) { if (e.target === _this.listDom) { _this.fire(new _event2['default']('scrollend', _this)); } }); this.getWidgetRect(); this.align = this.rect.height / 2; this.offset = 0; this._reset(); }; Grille.prototype._reset = function _reset() { // 记录最后一个item是第几行第几列 this.lastWidgetRow = 0; this.lastWidgetCol = 0; this.lastWidgetCenter = 0; this.listHeight = 0; // 用二维数组记录子widget,便于查找 this.childrenArr = []; // 只记录子Widget this.childWidgets = []; // 记录所有类型的子代 this.childItems = []; this.focusPos = { row: 0, col: 0 }; }; Grille.prototype.setData = function setData(data, index) { var _this2 = this; this.isSetData = true; if (this.childItems.length == 0) { this.listDom.innerHTML = ''; this._reset(); this.dataIndex = index !== undefined ? index : 0; this.data = [].concat(data); this._renderChildren(); return; } var transitionEndSetted = false; for (var i = 0; i < this.childItems.length; i++) { var item = this.childItems[i]; if (item.con && item.visible) { item.con.style.transform = 'scale3d(0.3,0.3,0.3)'; if (!transitionEndSetted) { transitionEndSetted = true; item.con.addEventListener('transitionend', function (e) { _this2.listDom.innerHTML = ''; _this2._reset(); _this2.dataIndex = index !== undefined ? index : 0; _this2.data = [].concat(data); _this2._renderChildren(); }); } } } }; Grille.prototype.addData = function addData(data) { this.data = this.data.concat(data); this._renderChildren(); }; Grille.prototype.setItemRender = function setItemRender(itemRender) { this.itemRender = itemRender; }; Grille.prototype._renderChildren = function _renderChildren() { var _this3 = this; // renderChildren时需要同时有data且有itemRender if (!this.data || !this.itemRender) { return; } this.isSetData && (this.listDom.style.visibility = 'hidden'); var _loop = function (i) { var tempData = _this3.data[i]; var dom = _this3.itemRender(tempData); // 如果render返回的是string,那么将string parse成dom对象 if (typeof dom === 'string') { var children = _this3.parseDom(dom); if (children.length > 1) { console.error('[FocusEngine_Error]Grille组件的itemRender返回的字符串必须是可解析成一个dom对象。'); return 'continue'; } dom = children[0]; } var lazyDom = dom.querySelector('.grille-lazyload'); lazyDom && dom.removeChild(lazyDom); _this3.listDom.appendChild(dom); if (!dom.getAttribute('fe-role')) { _this3.childItems.push(dom); return 'continue'; } var widget = _engine2['default'].createWidget(dom); _this3.addChildWidget(widget); _this3.childItems.push(widget); widget.data = tempData; widget.index = i; widget.lazyDom = lazyDom; widget.getWidgetRectAsync(function (widgetRect) { // 计算正常情况下,该widget获取焦点时整个列表的偏移量 // 初始情况下,组件中心点距离容器top的距离 widget.oriOffset = widgetRect.centerY - _this3.rect.top - _this3.offset; if (_this3.listHeight < widgetRect.bottom - _this3.rect.top - _this3.offset + _this3.bottomPadding) { _this3.listHeight = widgetRect.bottom - _this3.rect.top - _this3.offset + _this3.bottomPadding; } if (!_this3.lastWidgetCenter) { // 第一个widget时 widget.row = _this3.lastWidgetRow; widget.col = _this3.lastWidgetCol; _this3.lastWidgetCenter = widgetRect.centerY; _this3.childrenArr[_this3.lastWidgetRow] = [widget]; } else if (widgetRect.centerY - _this3.offset === _this3.lastWidgetCenter) { // 同一行的情况 widget.row = _this3.lastWidgetRow; _this3.lastWidgetCol++; widget.col = _this3.lastWidgetCol; _this3.childrenArr[_this3.lastWidgetRow].push(widget); } else if (widgetRect.centerY - _this3.offset > _this3.lastWidgetCenter) { // 换行的情况 _this3.lastWidgetRow++; widget.row = _this3.lastWidgetRow; _this3.lastWidgetCol = 0; widget.col = _this3.lastWidgetCol; _this3.lastWidgetCenter = widgetRect.centerY - _this3.offset; _this3.childrenArr[_this3.lastWidgetRow] = [widget]; } if (widget.row <= _this3.visibleRow) { widget.visible = true; widget.con.style.transition = 'transform ' + _this3.duration * 0.8 + 's ' + _this3.easing; } if (i === _this3.data.length - 1) { _this3.focused && _this3.childItems[_this3.dataIndex].focus(); if (_this3.isSetData) { _this3.listDom.style.visibility = 'visible'; var setWidget = _this3.childItems[_this3.dataIndex]; _this3.loadContent(); _this3._doScroll(setWidget); _this3.setCachedChildWidget(setWidget); } } }); }; for (var i = this.childItems.length; i < this.data.length; i++) { var _ret = _loop(i); if (_ret === 'continue') continue; } }; Grille.prototype.focusDefault = function focusDefault(e) { _Widget.prototype.focusDefault.call(this, e); if (e.target === this) { return; } if (this.isSetData) { return; } this.dataIndex = e.target.index; this._doScroll(e.target); }; Grille.prototype._doScroll = function _doScroll(curWidget) { var _this4 = this; this.focusPos.row = curWidget.row; this.focusPos.col = curWidget.col; // 如果列表高度小于容器高度,不滚动 if (this.listHeight < this.rect.height) { return; } var offset = this.align - curWidget.oriOffset; // 到达顶部的情况 if (offset > 0) { offset = 0; } // 到达底部的情况 if (this.listHeight - curWidget.oriOffset < this.rect.height - this.align) { offset = this.rect.height - this.listHeight; } // 配置位置的情况 if (curWidget.data.align !== undefined) { offset = this.align - curWidget.oriOffset; } // 如果同一行,不滚动 if (this.offset === offset && !this.isSetData) { return; } if (this.isSetData) { this.listDom.style.transition = ''; // this.listDom.style.transform = 'translateY(' + (offset + this.rect.height) + 'px)'; this.offset = offset + this.rect.height; setTimeout(function () { _this4.listDom.style.transition = 'transform ' + _this4.duration + 's ' + _this4.easing; // _this4.listDom.style.transform = 'translateY(' + offset + 'px)'; var des = -_this4.rect.height / 3; _this4.offset = offset; !_this4.isScrolling && _this4.fire(new _event2['default']('scrollstart', _this4, { des: des })); _this4.isSetData = false; }, 300); } else { this.listDom.style.transition = 'transform ' + this.duration + 's ' + this.easing; // this.listDom.style.transform = 'translateY(' + offset + 'px)'; var des = offset - this.offset; this.offset = offset; !this.isScrolling && this.fire(new _event2['default']('scrollstart', this, { des: des })); } }; Grille.prototype.keydownDefault = function keydownDefault(e) { var curRow = this.focusPos.row; var curCol = this.focusPos.col; if (e.keyCode === _event2['default'].VK_UP && curRow > 0) { curRow--; if (curCol >= this.childrenArr[curRow].length) { curCol = this.childrenArr[curRow].length - 1; } e.preventDefault(); } if (e.keyCode === _event2['default'].VK_DOWN && curRow == this.lastWidgetRow - 1) { this.fire(new _event2['default']('reachend', this)); } if (e.keyCode === _event2['default'].VK_DOWN && curRow < this.lastWidgetRow) { curRow++; if (curCol >= this.childrenArr[curRow].length) { curCol = this.childrenArr[curRow].length - 1; } e.preventDefault(); } if (e.keyCode === _event2['default'].VK_LEFT && curCol > 0) { curCol--; e.preventDefault(); } if (e.keyCode === _event2['default'].VK_RIGHT && curCol < this.childrenArr[curRow].length - 1) { curCol++; e.preventDefault(); } if (curRow === this.focusPos.row && curCol === this.focusPos.col) { return; } else { this.focusPos.row = curRow; this.focusPos.col = curCol; var tempWidget = this.childrenArr[curRow][curCol]; tempWidget.focus(); } }; Grille.prototype.scrollstartDefault = function scrollstartDefault(e) { this.isScrolling = true; // Blitz内核提供的禁止图片加载的方法,优化动画 window.stopImgLoading && window.stopImgLoading(); clearTimeout(this.loadContentTimeout); var curWidget = this.childItems[this.dataIndex]; for (var i = 0; i < this.childWidgets.length; i++) { var widget = this.childWidgets[i]; var delay = -Math.abs(widget.col - curWidget.col) * e.des * this.delay; // widget.con.style.transform = 'translateY(' + delay + 'px)'; } }; Grille.prototype.scrollendDefault = function scrollendDefault(e) { var _this5 = this; this.isScrolling = false; var curWidget = this.childItems[this.dataIndex]; for (var i = 0; i < this.childWidgets.length; i++) { var widget = this.childWidgets[i]; var loadRowMax = Math.floor(this.visibleRow / 2); if (Math.abs(widget.row - curWidget.row) <= loadRowMax) { widget.con.style.transition = 'transform ' + this.duration * 0.8 + 's ' + this.easing; widget.visible = true; } else if (widget.visible) { widget.con.style.transition = ''; widget.visible = false; } widget.con.style.transform = ''; } this.loadContentTimeout = setTimeout(function () { _this5.loadContent(); }, 500); }; Grille.prototype.loadContent = function loadContent() { for (var i = 0; i < this.childWidgets.length; i++) { var widget = this.childWidgets[i]; if (widget.lazyDom && widget.visible && !widget.lazyLoaded) { widget.con.appendChild(widget.lazyDom); widget.lazyLoaded = true; } else if (widget.lazyDom && !widget.visible && widget.lazyLoaded) { widget.con.removeChild(widget.lazyDom); widget.lazyLoaded = false; } } // Blitz内核提供的恢复图片加载的方法 window.resumeImgLoading && window.resumeImgLoading(); this.fire(new _event2['default']('contentloaded', this)); }; Grille.prototype.getEvents = function getEvents() { return _Widget.prototype.getEvents.call(this).concat(['scrollstart', 'scrollend', 'contentloaded', 'reachend']); }; return Grille; })(_widget2['default']); exports['default'] = Grille; module.exports = exports['default']; /***/ } /******/ ]);