SOURCE

console 命令行工具 X clear

                    
>
console
const WIDTH = 400
const HEIGHT = 400
const DEVICEPIXELRATIO = window.devicePixelRatio

const global = (() => {
  const canvas = document.getElementById('canvas')
  canvas.style.width = `${WIDTH}px`
  canvas.style.height = `${HEIGHT}px`
  const ctx = canvas.getContext('2d')
  canvas.width = WIDTH * DEVICEPIXELRATIO
  canvas.height = HEIGHT * DEVICEPIXELRATIO
  const layers = []
  return {
    canvas,
    ctx,
    layers
  }
})()

function renderAll() {
  global.ctx.clearRect(0, 0, global.canvas.width, global.canvas.height)
  global.layers.forEach(layer => {
    layer.render()
  })
  drawShapesBorder()
}

class IText {
  constructor(x, y, text) {
    this.x = x
    this.y = y
    this.text = text
    this.color = '#ff00ff'
  }
  setColor(color) {
    this.color = color
  }
  render() {
    global.ctx.save()
    global.ctx.font = '36px sans-serif'
    global.ctx.fillStyle = this.color
    global.ctx.fillText(this.text, this.x, this.y)
    global.ctx.restore()
  }
}
class Line {
  constructor(x1, y1, x2, y2) {
    this.x1 = x1
    this.y1 = y1
    this.x2 = x2
    this.y2 = y2
    this.color = '#ff00ff'
  }
  setColor(color) {
    this.color = color
  }
  render() {
    global.ctx.save()
    global.ctx.strokeStyle = this.color
    global.ctx.lineWidth = 3
    global.ctx.beginPath()
    global.ctx.moveTo(this.x1, this.y1)
    global.ctx.lineTo(this.x2, this.y2)
    global.ctx.stroke()
    global.ctx.restore()
  }
}

function throttle(fn, delay) {
  let lastTime = 0
  return (...arg) => {
    const now = Date.now()
    if (now - lastTime >= delay) {
      fn.apply(null, arg)
      lastTime = now
    }
  }
}

function drawShapesBorder() {
  const { data: imageData = [] } = global.ctx.getImageData(0, 0, global.canvas.width, global.canvas.height)
  let minX = minY = Number.MAX_SAFE_INTEGER
  let maxX = maxY = -Number.MAX_SAFE_INTEGER
  const alphas = []
  for (let i = 0; i < imageData.length; i+=4) {
    const alpha = imageData[i + 3]
    alphas.push(alpha)
    if (alpha > 0) {
      const [x, y] = getPixelPointToCoord(i + 4)
      minX = Math.min(minX, x)
      minY = Math.min(minY, y)
      maxX = Math.max(maxX, x)
      maxY = Math.max(maxY, y)
    }
  }
  global.ctx.strokeStyle = '#00ff00'
  global.ctx.strokeRect(minX, minY, maxX - minX, maxY - minY)
  
}
function getPixelPointToCoord(index) {
  const rowWidth = global.canvas.width
  const rowPixelCount = rowWidth * 4
  const x = (index % rowPixelCount) / 4
  const y = parseFloat(index / rowPixelCount)
  return [x, y]
}

function testHit(cssX, cssY, tolerance = 2) {
  const pixelX = cssX * DEVICEPIXELRATIO
  const pixelY = cssY * DEVICEPIXELRATIO
  const rowWidth = global.canvas.width
  const rowCount = rowWidth
  const index = (pixelY - 1) * rowCount + pixelX - 1
  const { data: imageData = [] } = global.ctx.getImageData(0, 0, global.canvas.width, global.canvas.height)
  const filterCells = []
  for (let i = -(tolerance + 1) * DEVICEPIXELRATIO + 1; i <= tolerance * DEVICEPIXELRATIO; i++ ) {
  	filterCells.push(i)
  }
  const filterRowsIndexs = filterCells.map(g => index + g * rowCount)
  for (let i = 0; i < filterRowsIndexs.length; i++) {
    for (let j = 0; j < filterCells.length; j++) {
      const cellIndex = (filterRowsIndexs[i] + filterCells[j]) * 4 + 3
      if (imageData[cellIndex] > 0) return true
    }
  }
  return false
}
function addHitShape() {
  const highLightColor = '#0000ff'
  global.canvas.addEventListener('mousemove', throttle(e => {
    const { offsetX, offsetY } = e
    const isHover = testHit(offsetX, offsetY)
    let isUpdate = false
    global.layers.forEach(layer => {
      const oldColor = layer.color
      layer.setColor(isHover ? '#0000ff' : '#ff00ff')
      if (oldColor !== layer.color) isUpdate = true
    })
    if (isUpdate) {
      renderAll()
    }
  }, 10))
}
const iText = new IText(global.canvas.width / 2, global.canvas.height / 2, 'Text 文字')
const line = new Line(global.canvas.width / 2, global.canvas.height / 2, global.canvas.width / 2 - 150, global.canvas.height / 2 + 200)
global.layers.push(iText)
global.layers.push(line)
renderAll()
addHitShape()
<canvas id="canvas"></canvas>
#canvas {
  position: absolute;
  left: 50%;
  transform: translateX(-50%);
  cursor: pointer;
  border: 1px solid #ddd
}