Cómo encuentra el televisor el siguiente punto de enfoque cuando presionas el D-pad

La primera empresa para la que trabajé era principalmente una aplicación que se ejecutaba en un televisor inteligente. En realidad, era una aplicación de Android y también era una aplicación de desarrollo mixto. Muchas de sus páginas fueron desarrolladas por H5.

Todos sabemos que el televisor funciona con control remoto y que la pantalla no se puede tocar sin un mouse, por lo que la operación de "clic" se convierte en presionar las teclas "arriba, abajo, izquierda y derecha" en el control remoto, por lo que un "enfoque " debe ser necesario para decirle al usuario Dónde está el foco actual.

En ese momento, la página de desarrollo usaba una biblioteca de enfoque desarrollada por predecesores, que monitoreaba las teclas de flecha y calculaba automáticamente el siguiente elemento enfocado.

¿Por qué de repente pensé en esto después de muchos años? De hecho, cuando recientemente agregué la función de navegación con teclas de flecha a mi mapa mental de código abierto, pensé que en realidad es muy similar a la función de enfoque de TV. Se calcula y automáticamente enfocado presionando las teclas de flecha Ir al siguiente elemento o nodo:

Entonces, cómo encontrar el siguiente enfoque, combinado con el principio de la biblioteca de enfoque que usé en ese momento, veamos a continuación.

1. El algoritmo más simple

El primer algoritmo es el más simple. De acuerdo con la dirección, primero encuentre todos los demás nodos en la dirección del nodo actual y luego encuentre el que tenga la distancia en línea recta más cercana. Por ejemplo, cuando se presiona la tecla de flecha izquierda, aparece lo siguiente nodos son todos los nodos que cumplen los requisitos:

El más cercano se selecciona como el siguiente nodo enfocado.

La información de ubicación de los nodos se muestra a continuación:

focus(dir) {
    
    
    // 当前聚焦的节点
    let currentActiveNode = this.mindMap.renderer.activeNodeList[0]
    // 当前聚焦节点的位置信息
    let currentActiveNodeRect = this.getNodeRect(currentActiveNode)
    // 寻找的下一个聚焦节点
    let targetNode = null
    let targetDis = Infinity
    // 保存并维护距离最近的节点
    let checkNodeDis = (rect, node) => {
    
    
        let dis = this.getDistance(currentActiveNodeRect, rect)
        if (dis < targetDis) {
    
    
            targetNode = node
            targetDis = dis
        }
    }
    // 1.最简单的算法
    this.getFocusNodeBySimpleAlgorithm({
    
    
        currentActiveNode,
        currentActiveNodeRect,
        dir,
        checkNodeDis
    })
    // 找到了则让目标节点聚焦
    if (targetNode) {
    
    
        targetNode.active()
    }
}

Independientemente del algoritmo que se use, primero encuentra todos los nodos que cumplen con los requisitos y luego encuentra el nodo con la distancia más cercana al nodo enfocado actual, por lo que la función de mantener el nodo más cercano se puede reutilizar y pasar al nodo específico. nodo en forma de función de cálculo de parámetros.

// 1.最简单的算法
getFocusNodeBySimpleAlgorithm({
     
     
    currentActiveNode,
    currentActiveNodeRect,
    dir,
    checkNodeDis
}) {
    
    
    // 遍历思维导图节点树
    bfsWalk(this.mindMap.renderer.root, node => {
    
    
        // 跳过当前聚焦的节点
        if (node === currentActiveNode) return
        // 当前遍历到的节点的位置信息
        let rect = this.getNodeRect(node)
        let {
    
     left, top, right, bottom } = rect
        let match = false
        // 按下了左方向键
        if (dir === 'Left') {
    
    
            // 判断节点是否在当前节点的左侧
            match = right <= currentActiveNodeRect.left
            // 按下了右方向键
        } else if (dir === 'Right') {
    
    
            // 判断节点是否在当前节点的右侧
            match = left >= currentActiveNodeRect.right
            // 按下了上方向键
        } else if (dir === 'Up') {
    
    
            // 判断节点是否在当前节点的上面
            match = bottom <= currentActiveNodeRect.top
            // 按下了下方向键
        } else if (dir === 'Down') {
    
    
            // 判断节点是否在当前节点的下面
            match = top >= currentActiveNodeRect.bottom
        }
        // 符合要求,判断是否是最近的节点
        if (match) {
    
    
            checkNodeDis(rect, node)
        }
    })
}

El efecto es el siguiente:

Básicamente funciona, pero podemos ver que hay una gran desventaja. Por ejemplo, cuando presionamos el botón arriba, debemos enfocarnos en el nodo hermano de arriba, pero en realidad enfocarnos en el nodo hijo:

Debido a que este nodo secundario está en el nodo actual y es el más cercano, veamos el segundo algoritmo para resolver este problema.

2. Algoritmo de sombreado

Este algoritmo también trata las cuatro direcciones por separado, pero en comparación con el primer algoritmo anterior, requiere además que la extensión del nodo en la dirección especificada tenga una intersección. La extensión se puede imaginar como la sombra del nodo, que es el origen del nombre:

Después de encontrar todos los nodos con intersecciones, también encontramos el nodo más cercano como el siguiente nodo enfocado y modificamos focusel método para usar el algoritmo de sombra:

focus(dir) {
    
    
    // 当前聚焦的节点
    let currentActiveNode = this.mindMap.renderer.activeNodeList[0]
    // 当前聚焦节点的位置信息
    let currentActiveNodeRect = this.getNodeRect(currentActiveNode)
    // 寻找的下一个聚焦节点
    // ...
    // 保存并维护距离最近的节点
    // ...

    // 2.阴影算法
    this.getFocusNodeByShadowAlgorithm({
    
    
        currentActiveNode,
        currentActiveNodeRect,
        dir,
        checkNodeDis
    })

    // 找到了则让目标节点聚焦
    if (targetNode) {
    
    
        targetNode.active()
    }
}
// 2.阴影算法
getFocusNodeByShadowAlgorithm({
     
     
    currentActiveNode,
    currentActiveNodeRect,
    dir,
    checkNodeDis
}) {
    
    
    bfsWalk(this.mindMap.renderer.root, node => {
    
    
        if (node === currentActiveNode) return
        let rect = this.getNodeRect(node)
        let {
    
     left, top, right, bottom } = rect
        let match = false
        if (dir === 'Left') {
    
    
            match =
                left < currentActiveNodeRect.left &&
                top < currentActiveNodeRect.bottom &&
                bottom > currentActiveNodeRect.top
        } else if (dir === 'Right') {
    
    
            match =
                right > currentActiveNodeRect.right &&
                top < currentActiveNodeRect.bottom &&
                bottom > currentActiveNodeRect.top
        } else if (dir === 'Up') {
    
    
            match =
                top < currentActiveNodeRect.top &&
                left < currentActiveNodeRect.right &&
                right > currentActiveNodeRect.left
        } else if (dir === 'Down') {
    
    
            match =
                bottom > currentActiveNodeRect.bottom &&
                left < currentActiveNodeRect.right &&
                right > currentActiveNodeRect.left
        }
        if (match) {
    
    
            checkNodeDis(rect, node)
        }
    })
}

Es decir, la condición de juicio agrega la comparación de cruzar, y el efecto es el siguiente:

Se puede ver que el algoritmo de la sombra resuelve con éxito el problema de salto anterior, pero no es perfecto.Por ejemplo, en la siguiente situación, el nodo enfocable no se puede encontrar presionando la tecla de flecha izquierda:

Debido a que no hay un nodo de intersección a la izquierda, pero en realidad se puede enfocar en el nodo principal, ¿qué debemos hacer? Echemos un vistazo primero al siguiente algoritmo.

3. Algoritmo de área

El llamado algoritmo de área también es muy simple. El área circundante del nodo enfocado actual se divide en cuatro áreas, correspondientes a las cuatro direcciones. Para encontrar el siguiente nodo en qué dirección, primero encuentre todos los nodos cuyos puntos centrales están en esta área, y luego seleccione el más cercano. Uno hará:

focus(dir) {
    
    
    // 当前聚焦的节点
    let currentActiveNode = this.mindMap.renderer.activeNodeList[0]
    // 当前聚焦节点的位置信息
    let currentActiveNodeRect = this.getNodeRect(currentActiveNode)
    // 寻找的下一个聚焦节点
    // ...
    // 保存并维护距离最近的节点
    // ...

    // 3.区域算法
    this.getFocusNodeByAreaAlgorithm({
    
    
        currentActiveNode,
        currentActiveNodeRect,
        dir,
        checkNodeDis
    })

    // 找到了则让目标节点聚焦
    if (targetNode) {
    
    
        targetNode.active()
    }
}
// 3.区域算法
getFocusNodeByAreaAlgorithm({
     
     
    currentActiveNode,
    currentActiveNodeRect,
    dir,
    checkNodeDis
}) {
    
    
    // 当前聚焦节点的中心点
    let cX = (currentActiveNodeRect.right + currentActiveNodeRect.left) / 2
    let cY = (currentActiveNodeRect.bottom + currentActiveNodeRect.top) / 2
    bfsWalk(this.mindMap.renderer.root, node => {
    
    
        if (node === currentActiveNode) return
        let rect = this.getNodeRect(node)
        let {
    
     left, top, right, bottom } = rect
        // 遍历到的节点的中心点
        let ccX = (right + left) / 2
        let ccY = (bottom + top) / 2
        // 节点的中心点坐标和当前聚焦节点的中心点坐标的差值
        let offsetX = ccX - cX
        let offsetY = ccY - cY
        if (offsetX === 0 && offsetY === 0) return
        let match = false
        if (dir === 'Left') {
    
    
            match = offsetX <= 0 && offsetX <= offsetY && offsetX <= -offsetY
        } else if (dir === 'Right') {
    
    
            match = offsetX > 0 && offsetX >= -offsetY && offsetX >= offsetY
        } else if (dir === 'Up') {
    
    
            match = offsetY <= 0 && offsetY < offsetX && offsetY < -offsetX
        } else if (dir === 'Down') {
    
    
            match = offsetY > 0 && -offsetY < offsetX && offsetY > offsetX
        }
        if (match) {
    
    
            checkNodeDis(rect, node)
        }
    })
}

La lógica de comparación puede hacer referencia a la siguiente figura:

Combinando algoritmo de sombra y algoritmo de área

Cuando presentamos el algoritmo de sombra anteriormente, dijimos que tiene ciertas limitaciones, y los resultados calculados por el algoritmo de área pueden complementarlo, pero idealmente, los resultados del algoritmo de sombra son los más acordes con nuestras expectativas, por lo que es muy simple, podemos ponerlos Combine los dos, ajuste el orden, primero use el algoritmo de sombra para calcular el nodo, si no se encuentra el algoritmo de sombra, luego use el algoritmo de área para encontrar el nodo, el algoritmo simple también se puede agregar en el fin:

focus(dir) {
    
    
    // 当前聚焦的节点
    let currentActiveNode = this.mindMap.renderer.activeNodeList[0]
    // 当前聚焦节点的位置信息
    let currentActiveNodeRect = this.getNodeRect(currentActiveNode)
    // 寻找的下一个聚焦节点
    // ...
    // 保存并维护距离最近的节点
    // ...

    // 第一优先级:阴影算法
    this.getFocusNodeByShadowAlgorithm({
    
    
        currentActiveNode,
        currentActiveNodeRect,
        dir,
        checkNodeDis
    })

    // 第二优先级:区域算法
    if (!targetNode) {
    
    
        this.getFocusNodeByAreaAlgorithm({
    
    
            currentActiveNode,
            currentActiveNodeRect,
            dir,
            checkNodeDis
        })
    }

    // 第三优先级:简单算法
    if (!targetNode) {
    
    
        this.getFocusNodeBySimpleAlgorithm({
    
    
            currentActiveNode,
            currentActiveNodeRect,
            dir,
            checkNodeDis
        })
    }

    // 找到了则让目标节点聚焦
    if (targetNode) {
    
    
        targetNode.active()
    }
}

El efecto es el siguiente:

¿Es muy simple? Para una experiencia detallada, puede hacer clic en el mapa mental .

Supongo que te gusta

Origin blog.csdn.net/sinat_33488770/article/details/129033988
Recomendado
Clasificación