/codding.cn

My World | By 田小号

Primary LanguageJavaScript

codding.cn

我的世界 | By 田小号

return this.r.listDir || [{
  path: 'c:/',
  style: {
    width: '600px',
    height: '400px',
    left: '10%',
    top: '30px'
  }
}]
class TreeView {
 constructor(d = {}) {
   const me = this
   me.d = d

   d.gd = d.canvas.getContext('2d')
   d.conf = {
     spaceBetween: 2,
     lineHeight: 40,
     itemWidth: 30,
     itemHeight: 20,
   }
   d.mapId = {}
   d.mapPid = {}

   me.init()
 }
 init() {
   const me = this
   const d = me.d

   d.gd.font = '14px Arial'
   d.data.forEach((v) => {
     v.width = Math.ceil(d.gd.measureText(v.id).width) + 20
     d.mapId[v.id] = v
     d.mapPid[v.pid] = d.mapPid[v.pid] || []
     d.mapPid[v.pid].push(v)
   })

   d.root = d.data[0]

   for (let i = 0, len = d.data.length; i < len; i++) {
     const node = d.data[i]
     if (node.id == node.pid)
       throw new Error('node.id == node.pid', node.id, node.pid)
   }

   me.setDepth()
   me.setLayout()
   {
     const space = 40
     const l = Math.min(...d.data.map(v => v.x))
     const r = Math.max(...d.data.map(v => v.x + v.width))

     d.canvas.width = r - l + space
     d.canvas.height = Math.max(...d.data.map(v => v.y + d.conf.itemHeight)) + space
     me.translate(d.root, -l + space / 2, space / 2)
   }
   me.render()
 }
 setDepth() {
   const me = this
   const d = me.d

   d.stair = []

   const setDepth = (node, depth = 0) => {
     !d.stair[depth] && (d.stair[depth] = [])
     node.hIdx = d.stair[depth].length
     node.depth = depth
     d.stair[depth].push(node)

     ;(d.mapPid[node.id] || []).forEach((v) => {
       setDepth(v, depth + 1)
     })
   }

   setDepth(d.root)
 }
 translate(node, x, y) {
   const me = this
   const d = me.d

   const translate = (node) => {
     node.x += x
     node.y += y
     ;(d.mapPid[node.id] || []).forEach(translate)
   }
   translate(node)
 }
 setLayout() {
   const me = this
   const d = me.d

   for (let depth = d.stair.length - 1; depth > -1; depth--) {
     const row = d.stair[depth]
     row.forEach((v, idx, arr) => {
       const nodePrev = arr[idx - 1]
       const children = d.mapPid[v.id] || []

       if (children.length > 0) {
         const head = children.first()
         const tail = children.last()
         v.x = (head.x + tail.x + tail.width) / 2 - v.width / 2
       } else {
         v.x = nodePrev ? nodePrev.x + nodePrev.width : 0
       }

       if (nodePrev && nodePrev.hIdx === 0 && !d.mapPid[nodePrev.id] && v.x > nodePrev.x + v.width) {
         me.translate(nodePrev, v.x - nodePrev.width, 0)
       }

       {
         let nodeL = nodePrev
         let nodeR = v

         while (nodeL && nodeR) {
           if (nodeL.x + nodeL.width > nodeR.x) {
             me.translate(v, nodeL.x + nodeL.width - nodeR.x, 0)
           }
           nodeR = (d.mapPid[nodeR.id] || []).first()
           nodeR && (nodeL = d.stair[nodeR.depth][nodeR.hIdx - 1])
         }
       }

       v.y = depth * d.conf.lineHeight
     })
   }
 }
 render() {
   const me = this
   const d = me.d
   const {canvas, gd} = d

   const renderLine = () => {
     gd.beginPath()

     d.data.forEach((v) => {
       const nodeP = d.mapId[v.pid]
       if (!nodeP) return
       const x1 = v.x + v.width / 2
       const y1 = v.y + d.conf.itemHeight / 2

       const x4 = nodeP.x + nodeP.width / 2
       const y4 = nodeP.y + d.conf.itemHeight / 2

       const x2 = x1
       const y2 = (y1 + y4) / 2

       const x3 = x4
       const y3 = (y1 + y4) / 2

       gd.moveTo(x1, y1)
       gd.bezierCurveTo(
         x2, y2,
         x3, y3,
         x4, y4,
       )
     })

     gd.strokeStyle = '#000'
     gd.stroke()
   }

   const renderNode = () => {
     gd.beginPath()
     d.data.forEach((v) => {
       gd.rect(v.x + 1, v.y, v.width - 2, d.conf.itemHeight)
     })
     gd.fillStyle = 'rgba(0,170,255,.75)'
     gd.fill()

     gd.font = '14px Arial'
     gd.textAlign = 'center'
     gd.textBaseline = 'middle'
     gd.fillStyle = '#fff'
     d.data.forEach((v) => {
       gd.fillText(v.id, v.x + v.width / 2, v.y + d.conf.itemHeight / 2)
     })
   }

   gd.fillStyle = '#fff'
   gd.fillRect(0, 0, d.canvas.width, d.canvas.height)

   gd.save()
   renderLine()
   renderNode()
   gd.restore()
 }
}