SOURCE

console 命令行工具 X clear

                    
>
console
window.onload = function () {
    var fpsEle = document.getElementById('fps')
    var inputWord = document.getElementById('input-word')
    var btnGenerate = document.getElementById('btn-generate')

    var fontSize = 180
    var canvas = document.getElementById('particle_canvas')
    var CANVAS_WIDTH = 600
    var CANVAS_HEIGHT = 300
    canvas.width = CANVAS_WIDTH
    canvas.height = CANVAS_HEIGHT

    // var smallCvs = document.createElement("canvas");
    // var smallCtx = smallCvs.getContext('2d')
    // smallCtx.font = 'bold ' + fontSize + 'px Microsoft YaHei'
    // smallCtx.fillStyle = 'blue'
    // smallCtx.textAlign = 'left'
    // smallCtx.textBaseline = 'top'

    var ctx = canvas.getContext('2d')
    ctx.font = 'bold ' + fontSize + 'px Microsoft YaHei'
    ctx.fillStyle = 'blue'
    ctx.textAlign = 'left'
    ctx.textBaseline = 'top'

    var circleList
    var colors = [
      '#a09d1d',
      '#84b826',
      '#168a30',
      '#155fbf',
      '#40148c',
      '#5f168b',
      '#93148c',
      '#970c0d',
      '#af2e15',
      '#ab4913',
      '#a45a12',
      '#514e0e',
    ]

    var Circle = function (targetX, targetY, curX, curY) {
      this.targetX = targetX
      this.targetY = targetY
      this.x = curX || targetX
      this.y = curY || targetY
      this.color = colors[Math.floor(Math.random() * colors.length)]
      var _speed = 0.1
      this.done = false

      this.update = function () {
        if (Math.abs(this.targetX - this.x) >= 0.05 || Math.abs(this.targetY - this.y) >= 0.05) {
          this.x += (this.targetX - this.x) * _speed
          this.y += (this.targetY - this.y) * _speed
        }
        else {
          this.done = true
        }
        this.render()
      }

      this.render = function () {
        Circle.ctx.save()
        Circle.ctx.fillStyle = this.color
        Circle.ctx.beginPath()
        Circle.ctx.arc(this.x, this.y, Circle.radius, 0, 2 * Math.PI, false)
        Circle.ctx.fill()
        Circle.ctx.restore()
      }
    }

    Circle.ctx = ctx
    Circle.diameter = 6
    Circle.radius = Circle.diameter / 2
    Circle.space = 2

    function generate (word) {
      var wordWidth = ctx.measureText(word).width
      var wordPosition = {
        x: (CANVAS_WIDTH - wordWidth) / 2,
        y: 0
      }
      ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT)
      ctx.fillText(word, wordPosition.x, wordPosition.y) //轻微调整绘制字符位置
      var imageData = new Uint32Array(ctx.getImageData(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT).data.buffer)
      var i, j
      !circleList && (circleList = [])
      var totalCount = 0
      var circle
      for (i = 0; i < CANVAS_HEIGHT; i += Circle.diameter + Circle.space) {
        for (j = 0; j < CANVAS_WIDTH; j += Circle.diameter + Circle.space) {
          if (!!imageData[i * CANVAS_WIDTH + j]) {
            if (totalCount < circleList.length) {
              circle = circleList[totalCount]
              circle.targetX = j
              circle.targetY = i
              circle.done = false
            }
            else {
              circleList.push(new Circle(j, i, wordPosition.x + Math.random() * wordWidth, Math.random() * 200))
            }
            totalCount++
          }
        }
      }
      circleList = circleList.slice(0, totalCount - 1)
      tick(true)
    }

    var doneCount = 0

    function updateAll () {
      ctx.clearRect(0, 0, CANVAS_WIDTH, CANVAS_HEIGHT)
      doneCount = 0
      for (var i = 0; i < circleList.length; i++) {
        circleList[i].update()
        circleList[i].done && doneCount++
      }
      doneCount === circleList.length && (rendering = false)
    }

    var rendering = true
    var last = Date.now()
    var now
    var delta
    var fps
    var tick = function (forceRender) {
      forceRender === true && (rendering = true)
      if (!rendering) return false
      now = Date.now()
      delta = now - last
      last = now
      updateAll()
      fps = Math.floor((1000 / delta))
      fpsEle.innerHTML = fps
      requestAnimationFrame(tick)
    }

    generate('start!')

    btnGenerate.onclick = function (e) {
      generate(inputWord.value)
    }
    }
<p id="fps"></p>
<canvas id="particle_canvas"></canvas>
<input type="text" placeholder="输入文字" id="input-word"/>
<button id="btn-generate">生成</button>
* {
            padding: 0;
            margin: 0;
        }

        html, body {
            width: 100%;
            height: 100%;
        }

        body {
            background-color: #eee;
        }

        canvas {
            display: block;
            border: 1px solid #ccc;
            box-sizing: border-box;
        }