db-lgtv-focus-engine
Version:
the Best TV focus engine
127 lines (120 loc) • 4.57 kB
JavaScript
import Trunk from "../Trunk"
function getRealWidth(el) {
let style = getComputedStyle(el)
return (parseInt(style.marginRight) + parseInt(style.marginLeft) + el.offsetWidth)
}
function getRealHeight(el) {
let style = getComputedStyle(el)
return (parseInt(style.marginTop) + parseInt(style.marginBottom) + el.offsetHeight)
}
class LongScroll extends Trunk {
constructor(engine, el) {
super(engine, el)
this.key = this.el.getAttribute('db-data')
if (!engine) {
engine = {}
}
if (engine[this.key] && engine[this.key].scroll.el === this.el) {
return
}
this.initScroll()
}
render() {
let _o = this.engine[this.key]
let current_row = Math.floor((_o.focused_index) / _o.row_sum)
let start_index = _o.row_sum * (current_row - _o.column_sum)
if (start_index < 0) {
start_index = 0
}
let end_index = _o.row_sum * (current_row + _o.column_sum + 1)
if (end_index > this.engine[this.key].data.length) {
end_index = this.engine[this.key].data.length
this.el.dispatchEvent(new Event("touchBottom"))
}
let el_list = _o.data.slice(start_index, end_index).map((_i) => {
let stringify = this.key + JSON.stringify(_i)
if (!window[stringify]) {window[stringify] = this.convertDataToDom(_i)}
return window[stringify]
})
let _f = document.createDocumentFragment()
let top = document.createElement('div')
top.style.width = '100%'
top.style.height = `${start_index / _o.row_sum * _o.item_height}px`
let bottom = document.createElement('div')
bottom.style.width = '100%'
bottom.style.height = `50px`
_f.appendChild(top)
el_list.forEach(_i => {
_f.appendChild(_i)
})
_f.appendChild(bottom)
let if_el_list = _f.querySelectorAll('[db-if]')
if_el_list.forEach(element => {
if (!eval(element.getAttribute('db-if'))) {
element.remove()
}
})
let style_el_list = _f.querySelectorAll('[db-style]')
style_el_list.forEach(element => {
element.style.cssText = element.getAttribute('db-style')
})
let class_el_list = _f.querySelectorAll('[db-class]')
class_el_list.forEach(element => {
let oldClass = element.getAttribute('class') || ''
let dbClass = ' ' + element.getAttribute('db-class')
let newClass = oldClass.concat(dbClass)
element.setAttribute('class', newClass)
})
this.el.innerHTML = ''
this.el.appendChild(_f)
this.el.children.forEach((_i, index) => {
let real_index = start_index + index - 1
let handler = () => {
_o.focused_index = real_index
this.render()
}
if (_i.handler) {
_i.removeEventListener('focus', _i.handler)
}
_i.addEventListener('focus', handler)
_i.handler = handler
})
return true
}
convertDataToDom(data) {
if (!data) return ''
let _d = document.createElement('div')
_d.innerHTML = this.engine[this.key].template.replace('db-focus', '').replace(/\[.*?\]/g, function (key) {
let res = key.replace(/item/g, 'data').replace(/&/g, '&')
let array = res.split('')
array.slice(1, array.length - 2)
return eval(array.join(''))
})
return _d.children[0]
}
initScroll() {
// 重新进入页面
// el被销毁
this.engine[this.key] = {
scroll: this,
template: this.el.innerHTML,
focused_index: 0,
row_sum: Math.floor(this.el.offsetWidth / getRealWidth(this.el.children[0])),
column_sum: Math.ceil(this.el.offsetHeight / getRealHeight(this.el.children[0])),
item_height: getRealHeight(this.el.children[0]),
data: []
}
this.el.innerHTML = ''
Object.defineProperty(this.engine[this.key], 'data', {
get: () => {
return this.engine['$' + this.el.getAttribute('db-data')] || []
},
set: (_v) => {
this.engine['$' + this.el.getAttribute('db-data')] = _v
this.render()
},
configurable: true
})
}
}
export default LongScroll