性能优化

深入学习Canvas性能优化技术,包括离屏渲染、分层渲染、内存管理等核心优化策略 性能优化是Canvas应用开发中的关键环节。本章将深入探讨各种性能优化技术,帮助你创建流畅、高效的Canvas应用。

性能优化概述

Canvas性能优化的核心目标是减少CPU和GPU的负载,提高渲染效率。主要优化方向包括:

优化方向描述效果
离屏渲染使用离屏Canvas预渲染减少重复绘制
分层渲染多Canvas分层处理减少重绘区域
状态优化减少上下文状态切换降低API调用开销
区域优化只重绘变化区域减少像素处理
动画优化合理使用动画帧平滑动画效果
内存管理及时释放资源避免内存泄漏

性能分析工具

Chrome DevTools 性能分析

class PerformanceMonitor {
  constructor() {
    this.metrics = {
      fps: 0,
      frameTime: 0,
      drawCalls: 0,
      memory: 0
    }
    
    this.frames = []
    this.lastTime = performance.now()
    this.frameCount = 0
  }
  
  beginFrame() {
    this.frameStartTime = performance.now()
  }
  
  endFrame() {
    const now = performance.now()
    const frameTime = now - this.frameStartTime
    
    this.frames.push(frameTime)
    if (this.frames.length > 60) {
      this.frames.shift()
    }
    
    this.frameCount++
    
    if (now - this.lastTime >= 1000) {
      this.metrics.fps = this.frameCount
      this.metrics.frameTime = this.frames.reduce((a, b) => a + b, 0) / this.frames.length
      this.metrics.drawCalls = this.drawCalls
      this.metrics.memory = performance.memory 
        ? performance.memory.usedJSHeapSize / 1024 / 1024 
        : 0
      
      this.frameCount = 0
      this.drawCalls = 0
      this.lastTime = now
    }
  }
  
  recordDrawCall() {
    this.drawCalls++
  }
  
  getMetrics() {
    return { ...this.metrics }
  }
  
  render(ctx, x = 10, y = 10) {
    const metrics = this.getMetrics()
    
    ctx.save()
    ctx.fillStyle = 'rgba(0, 0, 0, 0.7)'
    ctx.fillRect(x, y, 180, 90)
    
    ctx.fillStyle = '#00ff00'
    ctx.font = '12px monospace'
    ctx.textBaseline = 'top'
    
    ctx.fillText(`FPS: ${metrics.fps}`, x + 10, y + 10)
    ctx.fillText(`Frame: ${metrics.frameTime.toFixed(2)}ms`, x + 10, y + 28)
    ctx.fillText(`Draw Calls: ${metrics.drawCalls}`, x + 10, y + 46)
    ctx.fillText(`Memory: ${metrics.memory.toFixed(2)}MB`, x + 10, y + 64)
    
    ctx.restore()
  }
}

性能标记工具

class PerformanceMarker {
  constructor() {
    this.marks = new Map()
    this.measures = []
  }
  
  mark(name) {
    this.marks.set(name, performance.now())
    
    if (typeof performance !== 'undefined' && performance.mark) {
      performance.mark(name)
    }
  }
  
  measure(name, startMark, endMark) {
    const start = this.marks.get(startMark)
    const end = this.marks.get(endMark)
    
    if (start !== undefined && end !== undefined) {
      const duration = end - start
      this.measures.push({ name, duration, start, end })
      return duration
    }
    return 0
  }
  
  getMeasures() {
    return [...this.measures]
  }
  
  clear() {
    this.marks.clear()
    this.measures = []
  }
  
  report() {
    console.table(this.measures.map(m => ({
      Name: m.name,
      Duration: `${m.duration.toFixed(2)}ms`,
      Start: `${m.start.toFixed(2)}ms`,
      End: `${m.end.toFixed(2)}ms`
    })))
  }
}

章节导航

性能优化原则

1. 最小化绘制操作

class OptimizedRenderer {
  constructor(canvas) {
    this.canvas = canvas
    this.ctx = canvas.getContext('2d')
    this.dirtyRects = []
    this.lastRenderState = null
  }
  
  addDirtyRect(x, y, width, height) {
    this.dirtyRects.push({ x, y, width, height })
  }
  
  mergeDirtyRects() {
    if (this.dirtyRects.length === 0) return null
    
    let minX = Infinity, minY = Infinity
    let maxX = -Infinity, maxY = -Infinity
    
    this.dirtyRects.forEach(rect => {
      minX = Math.min(minX, rect.x)
      minY = Math.min(minY, rect.y)
      maxX = Math.max(maxX, rect.x + rect.width)
      maxY = Math.max(maxY, rect.y + rect.height)
    })
    
    this.dirtyRects = []
    return { x: minX, y: minY, width: maxX - minX, height: maxY - minY }
  }
  
  render(objects) {
    const dirtyRect = this.mergeDirtyRects()
    
    if (dirtyRect) {
      this.ctx.save()
      this.ctx.beginPath()
      this.ctx.rect(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height)
      this.ctx.clip()
      
      this.ctx.clearRect(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height)
      
      objects.forEach(obj => {
        if (this.intersects(obj, dirtyRect)) {
          obj.render(this.ctx)
        }
      })
      
      this.ctx.restore()
    }
  }
  
  intersects(obj, rect) {
    return obj.x < rect.x + rect.width &&
           obj.x + obj.width > rect.x &&
           obj.y < rect.y + rect.height &&
           obj.y + obj.height > rect.y
  }
}

2. 批量处理

class BatchRenderer {
  constructor(ctx) {
    this.ctx = ctx
    this.batches = new Map()
  }
  
  addToBatch(type, renderData) {
    if (!this.batches.has(type)) {
      this.batches.set(type, [])
    }
    this.batches.get(type).push(renderData)
  }
  
  flush() {
    this.batches.forEach((items, type) => {
      this.ctx.save()
      
      items.forEach(item => {
        this.renderItem(type, item)
      })
      
      this.ctx.restore()
    })
    
    this.batches.clear()
  }
  
  renderItem(type, item) {
    switch (type) {
      case 'rect':
        if (item.fillStyle) this.ctx.fillStyle = item.fillStyle
        this.ctx.fillRect(item.x, item.y, item.width, item.height)
        break
      case 'circle':
        this.ctx.beginPath()
        this.ctx.arc(item.x, item.y, item.radius, 0, Math.PI * 2)
        if (item.fillStyle) {
          this.ctx.fillStyle = item.fillStyle
          this.ctx.fill()
        }
        break
      case 'line':
        this.ctx.beginPath()
        this.ctx.moveTo(item.x1, item.y1)
        this.ctx.lineTo(item.x2, item.y2)
        if (item.strokeStyle) this.ctx.strokeStyle = item.strokeStyle
        if (item.lineWidth) this.ctx.lineWidth = item.lineWidth
        this.ctx.stroke()
        break
    }
  }
}

3. 对象池模式

class ObjectPool {
  constructor(factory, initialSize = 100) {
    this.factory = factory
    this.pool = []
    this.active = new Set()
    
    for (let i = 0; i < initialSize; i++) {
      this.pool.push(factory())
    }
  }
  
  acquire() {
    let obj
    
    if (this.pool.length > 0) {
      obj = this.pool.pop()
    } else {
      obj = this.factory()
    }
    
    this.active.add(obj)
    return obj
  }
  
  release(obj) {
    if (this.active.has(obj)) {
      this.active.delete(obj)
      
      if (typeof obj.reset === 'function') {
        obj.reset()
      }
      
      this.pool.push(obj)
    }
  }
  
  releaseAll() {
    this.active.forEach(obj => {
      if (typeof obj.reset === 'function') {
        obj.reset()
      }
      this.pool.push(obj)
    })
    this.active.clear()
  }
  
  getStats() {
    return {
      pooled: this.pool.length,
      active: this.active.size,
      total: this.pool.length + this.active.size
    }
  }
}

class Particle {
  constructor() {
    this.reset()
  }
  
  reset() {
    this.x = 0
    this.y = 0
    this.vx = 0
    this.vy = 0
    this.life = 0
    this.maxLife = 0
    this.size = 0
    this.color = '#ffffff'
  }
  
  init(x, y, vx, vy, life, size, color) {
    this.x = x
    this.y = y
    this.vx = vx
    this.vy = vy
    this.life = life
    this.maxLife = life
    this.size = size
    this.color = color
  }
  
  update(dt) {
    this.x += this.vx * dt
    this.y += this.vy * dt
    this.life -= dt
    return this.life > 0
  }
  
  render(ctx) {
    const alpha = this.life / this.maxLife
    ctx.globalAlpha = alpha
    ctx.fillStyle = this.color
    ctx.beginPath()
    ctx.arc(this.x, this.y, this.size, 0, Math.PI * 2)
    ctx.fill()
  }
}

const particlePool = new ObjectPool(() => new Particle(), 500)

性能检测清单

const performanceChecklist = {
  rendering: [
    '使用离屏Canvas预渲染静态内容',
    '实现分层渲染减少重绘',
    '使用脏矩形技术只更新变化区域',
    '批量处理相同类型的绘制操作',
    '避免过度使用阴影和模糊效果'
  ],
  
  state: [
    '减少fillStyle/strokeStyle切换次数',
    '合理使用save/restore',
    '避免在循环中设置相同状态',
    '使用路径批量绘制',
    '减少字体设置次数'
  ],
  
  animation: [
    '使用requestAnimationFrame',
    '实现帧率控制',
    '跳过不必要的帧',
    '使用CSS transform处理简单动画',
    '避免同时运行过多动画'
  ],
  
  memory: [
    '及时释放不再使用的对象',
    '使用对象池复用对象',
    '避免创建临时对象',
    '清除不再使用的Canvas引用',
    '监控内存使用情况'
  ],
  
  data: [
    '使用TypedArray处理大量数据',
    '避免在渲染循环中创建数组',
    '使用空间索引加速碰撞检测',
    '缓存计算结果',
    '使用Web Worker处理复杂计算'
  ]
}

总结

Canvas性能优化是一个系统工程,需要从多个维度综合考虑:

  1. 渲染优化:使用离屏Canvas、分层渲染、脏矩形技术
  2. 状态优化:减少状态切换、批量处理绘制操作
  3. 动画优化:合理使用requestAnimationFrame、帧率控制
  4. 内存优化:对象池、及时释放资源、避免内存泄漏
  5. 数据优化:TypedArray、空间索引、Web Worker

通过合理运用这些优化技术,可以显著提升Canvas应用的性能,实现流畅的用户体验。