Canvas/SVG 冷门用法:实现动态背景与简易数据可视化

Canvas/SVG 冷门用法:实现动态背景与简易数据可视化

文章目录

  • [Canvas/SVG 冷门用法:实现动态背景与简易数据可视化](#Canvas/SVG 冷门用法:实现动态背景与简易数据可视化)
    • [1. Canvas冷门用法实战](#1. Canvas冷门用法实战)
      • [1.1 动态粒子背景系统](#1.1 动态粒子背景系统)
      • [1.2 音频可视化效果](#1.2 音频可视化效果)
      • [1.3 图像处理与滤镜](#1.3 图像处理与滤镜)
      • [1.4 物理引擎模拟](#1.4 物理引擎模拟)
    • [2. SVG冷门用法实战](#2. SVG冷门用法实战)
      • [2.1 动态图表生成](#2.1 动态图表生成)
      • [2.2 路径动画](#2.2 路径动画)
    • [3. 动态背景实现](#3. 动态背景实现)
      • [3.1 波浪背景](#3.1 波浪背景)
      • [3.2 几何图形动画](#3.2 几何图形动画)
    • [4. 简易数据可视化](#4. 简易数据可视化)
      • [4.1 实时图表](#4.1 实时图表)
      • [4.2 进度指示器](#4.2 进度指示器)
    • [5. 性能优化技巧](#5. 性能优化技巧)
      • [5.1 Canvas性能优化](#5.1 Canvas性能优化)
      • [5.2 SVG性能优化](#5.2 SVG性能优化)
    • [6. 实战项目:动态背景库](#6. 实战项目:动态背景库)
      • [6.1 完整的动态背景库](#6.1 完整的动态背景库)
    • 总结

Canvas和SVG作为前端图形处理的两大核心技术,除了常见的图表绘制和简单动画外,还隐藏着许多鲜为人知的高级用法。本文将带你探索这些冷门技巧,打造炫酷的动态背景和实用的数据可视化效果。

1. Canvas冷门用法实战

1.1 动态粒子背景系统

粒子系统是创建动态背景的经典方法,通过Canvas可以实现高性能的粒子效果:

javascript 复制代码
class ParticleSystem {
    constructor(canvas) {
        this.canvas = canvas;
        this.ctx = canvas.getContext('2d');
        this.particles = [];
        this.mouse = { x: 0, y: 0 };
        this.connectionDistance = 100;
        this.init();
    }
    
    init() {
        this.resize();
        this.createParticles();
        this.bindEvents();
        this.animate();
    }
    
    resize() {
        this.canvas.width = window.innerWidth;
        this.canvas.height = window.innerHeight;
    }
    
    createParticles() {
        const particleCount = Math.floor((this.canvas.width * this.canvas.height) / 15000);
        
        for (let i = 0; i < particleCount; i++) {
            this.particles.push({
                x: Math.random() * this.canvas.width,
                y: Math.random() * this.canvas.height,
                vx: (Math.random() - 0.5) * 0.5,
                vy: (Math.random() - 0.5) * 0.5,
                radius: Math.random() * 2 + 1,
                opacity: Math.random() * 0.5 + 0.5
            });
        }
    }
    
    bindEvents() {
        window.addEventListener('resize', () => this.resize());
        this.canvas.addEventListener('mousemove', (e) => {
            this.mouse.x = e.clientX;
            this.mouse.y = e.clientY;
        });
    }
    
    updateParticles() {
        this.particles.forEach(particle => {
            // 基础移动
            particle.x += particle.vx;
            particle.y += particle.vy;
            
            // 边界检测
            if (particle.x < 0 || particle.x > this.canvas.width) particle.vx *= -1;
            if (particle.y < 0 || particle.y > this.canvas.height) particle.vy *= -1;
            
            // 鼠标交互
            const dx = this.mouse.x - particle.x;
            const dy = this.mouse.y - particle.y;
            const distance = Math.sqrt(dx * dx + dy * dy);
            
            if (distance < 100) {
                const force = (100 - distance) / 100;
                particle.vx += (dx / distance) * force * 0.02;
                particle.vy += (dy / distance) * force * 0.02;
            }
            
            // 速度限制
            const speed = Math.sqrt(particle.vx * particle.vx + particle.vy * particle.vy);
            if (speed > 2) {
                particle.vx = (particle.vx / speed) * 2;
                particle.vy = (particle.vy / speed) * 2;
            }
        });
    }
    
    drawConnections() {
        this.ctx.strokeStyle = 'rgba(100, 200, 255, 0.1)';
        this.ctx.lineWidth = 1;
        
        for (let i = 0; i < this.particles.length; i++) {
            for (let j = i + 1; j < this.particles.length; j++) {
                const dx = this.particles[i].x - this.particles[j].x;
                const dy = this.particles[i].y - this.particles[j].y;
                const distance = Math.sqrt(dx * dx + dy * dy);
                
                if (distance < this.connectionDistance) {
                    this.ctx.globalAlpha = (1 - distance / this.connectionDistance) * 0.5;
                    this.ctx.beginPath();
                    this.ctx.moveTo(this.particles[i].x, this.particles[i].y);
                    this.ctx.lineTo(this.particles[j].x, this.particles[j].y);
                    this.ctx.stroke();
                }
            }
        }
    }
    
    drawParticles() {
        this.particles.forEach(particle => {
            this.ctx.globalAlpha = particle.opacity;
            this.ctx.fillStyle = 'rgba(100, 200, 255, 1)';
            this.ctx.beginPath();
            this.ctx.arc(particle.x, particle.y, particle.radius, 0, Math.PI * 2);
            this.ctx.fill();
        });
    }
    
    animate() {
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
        
        this.updateParticles();
        this.drawConnections();
        this.drawParticles();
        
        requestAnimationFrame(() => this.animate());
    }
}

// 使用示例
const canvas = document.createElement('canvas');
document.body.appendChild(canvas);
new ParticleSystem(canvas);

1.2 音频可视化效果

结合Web Audio API,Canvas可以实现炫酷的音频可视化:

javascript 复制代码
class AudioVisualizer {
    constructor(canvas, audioElement) {
        this.canvas = canvas;
        this.ctx = canvas.getContext('2d');
        this.audio = audioElement;
        this.audioContext = new (window.AudioContext || window.webkitAudioContext)();
        this.analyser = this.audioContext.createAnalyser();
        this.source = null;
        this.dataArray = null;
        this.animationId = null;
        
        this.setupAnalyser();
        this.connectAudio();
    }
    
    setupAnalyser() {
        this.analyser.fftSize = 256;
        this.analyser.smoothingTimeConstant = 0.8;
        const bufferLength = this.analyser.frequencyBinCount;
        this.dataArray = new Uint8Array(bufferLength);
    }
    
    connectAudio() {
        if (this.audio.src) {
            this.source = this.audioContext.createMediaElementSource(this.audio);
            this.source.connect(this.analyser);
            this.analyser.connect(this.audioContext.destination);
        }
    }
    
    drawCircularVisualizer() {
        const centerX = this.canvas.width / 2;
        const centerY = this.canvas.height / 2;
        const radius = Math.min(centerX, centerY) - 50;
        
        this.analyser.getByteFrequencyData(this.dataArray);
        
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
        
        // 绘制圆形频谱
        for (let i = 0; i < this.dataArray.length; i++) {
            const angle = (i / this.dataArray.length) * Math.PI * 2;
            const barHeight = (this.dataArray[i] / 255) * radius;
            
            const x1 = centerX + Math.cos(angle) * radius;
            const y1 = centerY + Math.sin(angle) * radius;
            const x2 = centerX + Math.cos(angle) * (radius + barHeight);
            const y2 = centerY + Math.sin(angle) * (radius + barHeight);
            
            const gradient = this.ctx.createLinearGradient(x1, y1, x2, y2);
            gradient.addColorStop(0, `hsl(${(i / this.dataArray.length) * 360}, 100%, 50%)`);
            gradient.addColorStop(1, `hsl(${(i / this.dataArray.length) * 360}, 100%, 70%)`);
            
            this.ctx.strokeStyle = gradient;
            this.ctx.lineWidth = 3;
            this.ctx.beginPath();
            this.ctx.moveTo(x1, y1);
            this.ctx.lineTo(x2, y2);
            this.ctx.stroke();
        }
        
        // 绘制中心圆
        this.ctx.beginPath();
        this.ctx.arc(centerX, centerY, radius * 0.3, 0, Math.PI * 2);
        this.ctx.fillStyle = 'rgba(0, 0, 0, 0.5)';
        this.ctx.fill();
        
        this.animationId = requestAnimationFrame(() => this.drawCircularVisualizer());
    }
    
    drawWaveform() {
        this.analyser.getByteTimeDomainData(this.dataArray);
        
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
        
        this.ctx.lineWidth = 2;
        this.ctx.strokeStyle = 'rgb(0, 255, 0)';
        this.ctx.beginPath();
        
        const sliceWidth = this.canvas.width / this.dataArray.length;
        let x = 0;
        
        for (let i = 0; i < this.dataArray.length; i++) {
            const v = this.dataArray[i] / 128.0;
            const y = v * this.canvas.height / 2;
            
            if (i === 0) {
                this.ctx.moveTo(x, y);
            } else {
                this.ctx.lineTo(x, y);
            }
            
            x += sliceWidth;
        }
        
        this.ctx.stroke();
        this.animationId = requestAnimationFrame(() => this.drawWaveform());
    }
    
    start(visualizationType = 'circular') {
        if (this.animationId) {
            cancelAnimationFrame(this.animationId);
        }
        
        switch (visualizationType) {
            case 'circular':
                this.drawCircularVisualizer();
                break;
            case 'waveform':
                this.drawWaveform();
                break;
            default:
                this.drawCircularVisualizer();
        }
    }
    
    stop() {
        if (this.animationId) {
            cancelAnimationFrame(this.animationId);
            this.animationId = null;
        }
    }
}

// 使用示例
const audio = document.querySelector('audio');
const canvas = document.getElementById('audio-visualizer');
const visualizer = new AudioVisualizer(canvas, audio);

audio.addEventListener('play', () => visualizer.start('circular'));
audio.addEventListener('pause', () => visualizer.stop());

1.3 图像处理与滤镜

Canvas可以实现实时的图像处理和滤镜效果:

javascript 复制代码
class ImageProcessor {
    constructor(canvas) {
        this.canvas = canvas;
        this.ctx = canvas.getContext('2d');
        this.originalImageData = null;
    }
    
    loadImage(src) {
        return new Promise((resolve, reject) => {
            const img = new Image();
            img.onload = () => {
                this.canvas.width = img.width;
                this.canvas.height = img.height;
                this.ctx.drawImage(img, 0, 0);
                this.originalImageData = this.ctx.getImageData(0, 0, img.width, img.height);
                resolve(img);
            };
            img.onerror = reject;
            img.src = src;
        });
    }
    
    applyGrayscale() {
        const imageData = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);
        const data = imageData.data;
        
        for (let i = 0; i < data.length; i += 4) {
            const gray = data[i] * 0.299 + data[i + 1] * 0.587 + data[i + 2] * 0.114;
            data[i] = gray;
            data[i + 1] = gray;
            data[i + 2] = gray;
        }
        
        this.ctx.putImageData(imageData, 0, 0);
    }
    
    applySepia() {
        const imageData = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);
        const data = imageData.data;
        
        for (let i = 0; i < data.length; i += 4) {
            const r = data[i];
            const g = data[i + 1];
            const b = data[i + 2];
            
            data[i] = Math.min(255, (r * 0.393) + (g * 0.769) + (b * 0.189));
            data[i + 1] = Math.min(255, (r * 0.349) + (g * 0.686) + (b * 0.168));
            data[i + 2] = Math.min(255, (r * 0.272) + (g * 0.534) + (b * 0.131));
        }
        
        this.ctx.putImageData(imageData, 0, 0);
    }
    
    applyBlur(radius = 5) {
        const imageData = this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height);
        const data = imageData.data;
        const width = this.canvas.width;
        const height = this.canvas.height;
        const output = new Uint8ClampedArray(data);
        
        const kernel = this.createGaussianKernel(radius);
        const kernelSize = kernel.length;
        const halfSize = Math.floor(kernelSize / 2);
        
        for (let y = 0; y < height; y++) {
            for (let x = 0; x < width; x++) {
                let r = 0, g = 0, b = 0, a = 0;
                
                for (let ky = -halfSize; ky <= halfSize; ky++) {
                    for (let kx = -halfSize; kx <= halfSize; kx++) {
                        const px = Math.min(width - 1, Math.max(0, x + kx));
                        const py = Math.min(height - 1, Math.max(0, y + ky));
                        const weight = kernel[ky + halfSize][kx + halfSize];
                        const idx = (py * width + px) * 4;
                        
                        r += data[idx] * weight;
                        g += data[idx + 1] * weight;
                        b += data[idx + 2] * weight;
                        a += data[idx + 3] * weight;
                    }
                }
                
                const outputIdx = (y * width + x) * 4;
                output[outputIdx] = r;
                output[outputIdx + 1] = g;
                output[outputIdx + 2] = b;
                output[outputIdx + 3] = a;
            }
        }
        
        const outputImageData = new ImageData(output, width, height);
        this.ctx.putImageData(outputImageData, 0, 0);
    }
    
    createGaussianKernel(radius) {
        const size = radius * 2 + 1;
        const kernel = [];
        const sigma = radius / 3;
        const sigma22 = 2 * sigma * sigma;
        const sqrtPiSigma = Math.sqrt(2 * Math.PI) * sigma;
        let sum = 0;
        
        for (let y = -radius; y <= radius; y++) {
            const row = [];
            for (let x = -radius; x <= radius; x++) {
                const distance = x * x + y * y;
                const weight = Math.exp(-distance / sigma22) / sqrtPiSigma;
                row.push(weight);
                sum += weight;
            }
            kernel.push(row);
        }
        
        // 归一化
        for (let y = 0; y < size; y++) {
            for (let x = 0; x < size; x++) {
                kernel[y][x] /= sum;
            }
        }
        
        return kernel;
    }
    
    reset() {
        if (this.originalImageData) {
            this.ctx.putImageData(this.originalImageData, 0, 0);
        }
    }
}

// 使用示例
const processor = new ImageProcessor(document.getElementById('image-canvas'));
processor.loadImage('path/to/image.jpg').then(() => {
    processor.applyGrayscale();
    // processor.applySepia();
    // processor.applyBlur(10);
});

1.4 物理引擎模拟

Canvas可以实现简单的物理引擎模拟:

javascript 复制代码
class PhysicsEngine {
    constructor(canvas) {
        this.canvas = canvas;
        this.ctx = canvas.getContext('2d');
        this.bodies = [];
        this.gravity = { x: 0, y: 0.5 };
        this.damping = 0.99;
        this.bounce = 0.8;
        
        this.animate();
    }
    
    addBody(body) {
        this.bodies.push(body);
    }
    
    update() {
        this.bodies.forEach(body => {
            // 应用重力
            body.velocity.x += this.gravity.x;
            body.velocity.y += this.gravity.y;
            
            // 应用阻尼
            body.velocity.x *= this.damping;
            body.velocity.y *= this.damping;
            
            // 更新位置
            body.x += body.velocity.x;
            body.y += body.velocity.y;
            
            // 边界碰撞检测
            if (body.x - body.radius < 0) {
                body.x = body.radius;
                body.velocity.x *= -this.bounce;
            }
            if (body.x + body.radius > this.canvas.width) {
                body.x = this.canvas.width - body.radius;
                body.velocity.x *= -this.bounce;
            }
            if (body.y - body.radius < 0) {
                body.y = body.radius;
                body.velocity.y *= -this.bounce;
            }
            if (body.y + body.radius > this.canvas.height) {
                body.y = this.canvas.height - body.radius;
                body.velocity.y *= -this.bounce;
            }
        });
        
        // 简单的碰撞检测
        for (let i = 0; i < this.bodies.length; i++) {
            for (let j = i + 1; j < this.bodies.length; j++) {
                this.checkCollision(this.bodies[i], this.bodies[j]);
            }
        }
    }
    
    checkCollision(body1, body2) {
        const dx = body2.x - body1.x;
        const dy = body2.y - body1.y;
        const distance = Math.sqrt(dx * dx + dy * dy);
        const minDistance = body1.radius + body2.radius;
        
        if (distance < minDistance) {
            // 分离物体
            const overlap = minDistance - distance;
            const separationX = (dx / distance) * overlap * 0.5;
            const separationY = (dy / distance) * overlap * 0.5;
            
            body1.x -= separationX;
            body1.y -= separationY;
            body2.x += separationX;
            body2.y += separationY;
            
            // 计算碰撞响应
            const relativeVelocityX = body2.velocity.x - body1.velocity.x;
            const relativeVelocityY = body2.velocity.y - body1.velocity.y;
            const velocityAlongNormal = relativeVelocityX * (dx / distance) + relativeVelocityY * (dy / distance);
            
            if (velocityAlongNormal > 0) return;
            
            const restitution = Math.min(this.bounce, this.bounce);
            const impulse = -(1 + restitution) * velocityAlongNormal / (1/body1.mass + 1/body2.mass);
            
            body1.velocity.x -= impulse * (dx / distance) / body1.mass;
            body1.velocity.y -= impulse * (dy / distance) / body1.mass;
            body2.velocity.x += impulse * (dx / distance) / body2.mass;
            body2.velocity.y += impulse * (dy / distance) / body2.mass;
        }
    }
    
    draw() {
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
        
        this.bodies.forEach(body => {
            this.ctx.beginPath();
            this.ctx.arc(body.x, body.y, body.radius, 0, Math.PI * 2);
            this.ctx.fillStyle = body.color;
            this.ctx.fill();
            this.ctx.strokeStyle = 'rgba(0, 0, 0, 0.2)';
            this.ctx.stroke();
        });
    }
    
    animate() {
        this.update();
        this.draw();
        requestAnimationFrame(() => this.animate());
    }
}

// 使用示例
const canvas = document.getElementById('physics-canvas');
const engine = new PhysicsEngine(canvas);

// 添加物体
for (let i = 0; i < 10; i++) {
    engine.addBody({
        x: Math.random() * canvas.width,
        y: Math.random() * canvas.height * 0.5,
        radius: Math.random() * 20 + 10,
        velocity: { x: (Math.random() - 0.5) * 5, y: 0 },
        mass: 1,
        color: `hsl(${Math.random() * 360}, 70%, 50%)`
    });
}

2. SVG冷门用法实战

2.1 动态图表生成

SVG可以创建复杂的动态图表,具有矢量图形的优势:

javascript 复制代码
class DynamicChart {
    constructor(container, options = {}) {
        this.container = container;
        this.svg = this.createSVG();
        this.options = {
            width: 800,
            height: 400,
            margin: { top: 20, right: 20, bottom: 40, left: 50 },
            animationDuration: 1000,
            ...options
        };
        this.data = [];
        this.scales = {};
        
        this.setupChart();
    }
    
    createSVG() {
        const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
        svg.setAttribute('width', this.options.width);
        svg.setAttribute('height', this.options.height);
        this.container.appendChild(svg);
        return svg;
    }
    
    setupChart() {
        const { width, height, margin } = this.options;
        
        // 创建主组
        this.mainGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g');
        this.mainGroup.setAttribute('transform', `translate(${margin.left}, ${margin.top})`);
        this.svg.appendChild(this.mainGroup);
        
        // 创建坐标轴
        this.createAxes();
        
        // 创建图表区域
        this.chartArea = document.createElementNS('http://www.w3.org/2000/svg', 'g');
        this.mainGroup.appendChild(this.chartArea);
    }
    
    createAxes() {
        const { width, height, margin } = this.options;
        const innerWidth = width - margin.left - margin.right;
        const innerHeight = height - margin.top - margin.bottom;
        
        // X轴
        const xAxis = document.createElementNS('http://www.w3.org/2000/svg', 'line');
        xAxis.setAttribute('x1', 0);
        xAxis.setAttribute('y1', innerHeight);
        xAxis.setAttribute('x2', innerWidth);
        xAxis.setAttribute('y2', innerHeight);
        xAxis.setAttribute('stroke', '#ccc');
        this.mainGroup.appendChild(xAxis);
        
        // Y轴
        const yAxis = document.createElementNS('http://www.w3.org/2000/svg', 'line');
        yAxis.setAttribute('x1', 0);
        yAxis.setAttribute('y1', 0);
        yAxis.setAttribute('x2', 0);
        yAxis.setAttribute('y2', innerHeight);
        yAxis.setAttribute('stroke', '#ccc');
        this.mainGroup.appendChild(yAxis);
        
        // 网格线
        for (let i = 0; i <= 5; i++) {
            const y = (innerHeight / 5) * i;
            const gridLine = document.createElementNS('http://www.w3.org/2000/svg', 'line');
            gridLine.setAttribute('x1', 0);
            gridLine.setAttribute('y1', y);
            gridLine.setAttribute('x2', innerWidth);
            gridLine.setAttribute('y2', y);
            gridLine.setAttribute('stroke', '#f0f0f0');
            gridLine.setAttribute('stroke-dasharray', '2,2');
            this.mainGroup.appendChild(gridLine);
        }
    }
    
    updateData(newData) {
        this.data = newData;
        this.updateScales();
        this.render();
    }
    
    updateScales() {
        const { width, height, margin } = this.options;
        const innerWidth = width - margin.left - margin.right;
        const innerHeight = height - margin.top - margin.bottom;
        
        const xExtent = [0, this.data.length - 1];
        const yExtent = [0, Math.max(...this.data.map(d => d.value))];
        
        this.scales.x = {
            domain: xExtent,
            range: [0, innerWidth],
            scale: (value) => {
                const ratio = (value - xExtent[0]) / (xExtent[1] - xExtent[0]);
                return ratio * innerWidth;
            }
        };
        
        this.scales.y = {
            domain: yExtent,
            range: [innerHeight, 0],
            scale: (value) => {
                const ratio = (value - yExtent[0]) / (yExtent[1] - yExtent[0]);
                return innerHeight - (ratio * innerHeight);
            }
        };
    }
    
    render() {
        // 清除现有内容
        while (this.chartArea.firstChild) {
            this.chartArea.removeChild(this.chartArea.firstChild);
        }
        
        // 创建柱状图
        this.data.forEach((d, i) => {
            const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
            const x = this.scales.x.scale(i);
            const y = this.scales.y.scale(d.value);
            const width = this.scales.x.scale(1) - this.scales.x.scale(0) - 2;
            const height = this.scales.y.scale(0) - y;
            
            rect.setAttribute('x', x);
            rect.setAttribute('y', this.scales.y.scale(0));
            rect.setAttribute('width', width);
            rect.setAttribute('height', 0);
            rect.setAttribute('fill', `hsl(${(i / this.data.length) * 360}, 70%, 50%)`);
            rect.setAttribute('opacity', '0.8');
            
            // 动画
            const animate = document.createElementNS('http://www.w3.org/2000/svg', 'animate');
            animate.setAttribute('attributeName', 'y');
            animate.setAttribute('from', this.scales.y.scale(0));
            animate.setAttribute('to', y);
            animate.setAttribute('dur', `${this.options.animationDuration}ms`);
            animate.setAttribute('fill', 'freeze');
            rect.appendChild(animate);
            
            const animateHeight = document.createElementNS('http://www.w3.org/2000/svg', 'animate');
            animateHeight.setAttribute('attributeName', 'height');
            animateHeight.setAttribute('from', '0');
            animateHeight.setAttribute('to', height);
            animateHeight.setAttribute('dur', `${this.options.animationDuration}ms`);
            animateHeight.setAttribute('fill', 'freeze');
            rect.appendChild(animateHeight);
            
            // 鼠标交互
            rect.addEventListener('mouseenter', () => {
                rect.setAttribute('opacity', '1');
                rect.setAttribute('transform', 'scale(1.05)');
            });
            
            rect.addEventListener('mouseleave', () => {
                rect.setAttribute('opacity', '0.8');
                rect.setAttribute('transform', 'scale(1)');
            });
            
            this.chartArea.appendChild(rect);
        });
    }
}

// 使用示例
const chartContainer = document.getElementById('chart-container');
const chart = new DynamicChart(chartContainer);

// 模拟数据更新
let dataIndex = 0;
const generateData = () => {
    return Array.from({ length: 10 }, (_, i) => ({
        name: `Item ${i + 1}`,
        value: Math.random() * 100
    }));
};

setInterval(() => {
    chart.updateData(generateData());
}, 3000);

chart.updateData(generateData());

2.2 路径动画

SVG的路径动画可以创建复杂的动画效果:

javascript 复制代码
class PathAnimator {
    constructor(svg) {
        this.svg = svg;
        this.paths = [];
        this.animations = [];
    }
    
    createPathAnimation(pathData, duration = 2000, color = '#3498db') {
        const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
        path.setAttribute('d', pathData);
        path.setAttribute('stroke', color);
        path.setAttribute('stroke-width', '3');
        path.setAttribute('fill', 'none');
        path.setAttribute('stroke-dasharray', '1000');
        path.setAttribute('stroke-dashoffset', '1000');
        
        this.svg.appendChild(path);
        
        // 创建动画
        const animate = document.createElementNS('http://www.w3.org/2000/svg', 'animate');
        animate.setAttribute('attributeName', 'stroke-dashoffset');
        animate.setAttribute('from', '1000');
        animate.setAttribute('to', '0');
        animate.setAttribute('dur', `${duration}ms`);
        animate.setAttribute('fill', 'freeze');
        
        path.appendChild(animate);
        
        // 添加发光效果
        const filter = document.createElementNS('http://www.w3.org/2000/svg', 'filter');
        filter.setAttribute('id', `glow-${Date.now()}`);
        
        const feGaussianBlur = document.createElementNS('http://www.w3.org/2000/svg', 'feGaussianBlur');
        feGaussianBlur.setAttribute('stdDeviation', '3');
        feGaussianBlur.setAttribute('result', 'coloredBlur');
        
        const feMerge = document.createElementNS('http://www.w3.org/2000/svg', 'feMerge');
        const feMergeNode1 = document.createElementNS('http://www.w3.org/2000/svg', 'feMergeNode');
        feMergeNode1.setAttribute('in', 'coloredBlur');
        const feMergeNode2 = document.createElementNS('http://www.w3.org/2000/svg', 'feMergeNode');
        feMergeNode2.setAttribute('in', 'SourceGraphic');
        
        feMerge.appendChild(feMergeNode1);
        feMerge.appendChild(feMergeNode2);
        filter.appendChild(feGaussianBlur);
        filter.appendChild(feMerge);
        
        this.svg.appendChild(filter);
        path.setAttribute('filter', `url(#glow-${Date.now()})`);
        
        animate.beginElement();
        
        return { path, animate };
    }
    
    createTextPathAnimation(text, pathData, duration = 5000) {
        const textPath = document.createElementNS('http://www.w3.org/2000/svg', 'text');
        textPath.setAttribute('font-size', '24');
        textPath.setAttribute('fill', '#e74c3c');
        textPath.setAttribute('font-family', 'Arial, sans-serif');
        
        const textNode = document.createTextNode(text);
        textPath.appendChild(textNode);
        
        const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
        const pathId = `text-path-${Date.now()}`;
        path.setAttribute('id', pathId);
        path.setAttribute('d', pathData);
        path.setAttribute('fill', 'none');
        
        const textPathElement = document.createElementNS('http://www.w3.org/2000/svg', 'textPath');
        textPathElement.setAttribute('href', `#${pathId}`);
        textPathElement.setAttribute('startOffset', '0%');
        
        // 将文本移到textPath中
        textPathElement.appendChild(textNode);
        textPath.appendChild(textPathElement);
        
        this.svg.appendChild(path);
        this.svg.appendChild(textPath);
        
        // 创建动画
        const animate = document.createElementNS('http://www.w3.org/2000/svg', 'animate');
        animate.setAttribute('attributeName', 'startOffset');
        animate.setAttribute('from', '0%');
        animate.setAttribute('to', '100%');
        animate.setAttribute('dur', `${duration}ms`);
        animate.setAttribute('repeatCount', 'indefinite');
        
        textPathElement.appendChild(animate);
        animate.beginElement();
        
        return { textPath, path, animate };
    }
    
    createMorphingAnimation(pathData1, pathData2, duration = 2000) {
        const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
        path.setAttribute('d', pathData1);
        path.setAttribute('stroke', '#9b59b6');
        path.setAttribute('stroke-width', '4');
        path.setAttribute('fill', 'none');
        
        this.svg.appendChild(path);
        
        // 创建变形动画
        const animate = document.createElementNS('http://www.w3.org/2000/svg', 'animate');
        animate.setAttribute('attributeName', 'd');
        animate.setAttribute('from', pathData1);
        animate.setAttribute('to', pathData2);
        animate.setAttribute('dur', `${duration}ms`);
        animate.setAttribute('repeatCount', 'indefinite');
        animate.setAttribute('calcMode', 'spline');
        animate.setAttribute('keySplines', '0.4 0 0.6 1');
        
        path.appendChild(animate);
        animate.beginElement();
        
        return { path, animate };
    }
}

// 使用示例
const svg = document.getElementById('path-animation-svg');
const animator = new PathAnimator(svg);

// 创建路径绘制动画
const heartPath = "M213.1,6.7c-32.4-14.4-73.7,0-88.1,30.6C110.6,4.9,67.5-9.5,36.9,6.7C2.8,22.9-13.4,62.4,13.5,110.9C33.3,145.1,67.5,170.3,125,217c59.3-46.7,93.5-71.9,111.5-106.1C263.4,64.2,247.2,22.9,213.1,6.7z";
animator.createPathAnimation(heartPath, 3000, '#e74c3c');

// 创建文本路径动画
const wavePath = "M10,80 Q95,10 180,80 T350,80";
animator.createTextPathAnimation("Hello SVG Animation!", wavePath, 5000);

// 创建形状变形动画
const circlePath = "M50,50 A30,30 0 1,1 50,51 Z";
const starPath = "M50,10 L61,35 L90,35 L68,55 L79,80 L50,65 L21,80 L32,55 L10,35 L39,35 Z";
animator.createMorphingAnimation(circlePath, starPath, 3000);

3. 动态背景实现

3.1 波浪背景

使用Canvas创建动态波浪背景:

javascript 复制代码
class WaveBackground {
    constructor(canvas) {
        this.canvas = canvas;
        this.ctx = canvas.getContext('2d');
        this.waves = [
            { amplitude: 20, frequency: 0.01, speed: 0.02, offset: 0, color: 'rgba(52, 152, 219, 0.3)' },
            { amplitude: 30, frequency: 0.015, speed: 0.025, offset: Math.PI / 3, color: 'rgba(46, 204, 113, 0.3)' },
            { amplitude: 15, frequency: 0.008, speed: 0.018, offset: Math.PI / 2, color: 'rgba(155, 89, 182, 0.3)' }
        ];
        
        this.animate();
    }
    
    drawWave(wave, time) {
        this.ctx.beginPath();
        this.ctx.moveTo(0, this.canvas.height);
        
        for (let x = 0; x <= this.canvas.width; x += 2) {
            const y = this.canvas.height / 2 + 
                     Math.sin(x * wave.frequency + time * wave.speed + wave.offset) * wave.amplitude +
                     Math.sin(x * wave.frequency * 2 + time * wave.speed * 1.5) * wave.amplitude * 0.5;
            
            this.ctx.lineTo(x, y);
        }
        
        this.ctx.lineTo(this.canvas.width, this.canvas.height);
        this.ctx.lineTo(0, this.canvas.height);
        this.ctx.closePath();
        
        this.ctx.fillStyle = wave.color;
        this.ctx.fill();
    }
    
    animate() {
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
        
        const time = Date.now() * 0.001;
        
        this.waves.forEach(wave => {
            this.drawWave(wave, time);
        });
        
        requestAnimationFrame(() => this.animate());
    }
}

// 使用示例
const waveCanvas = document.getElementById('wave-background');
new WaveBackground(waveCanvas);

3.2 几何图形动画

创建动态的几何图形背景:

javascript 复制代码
class GeometricBackground {
    constructor(canvas) {
        this.canvas = canvas;
        this.ctx = canvas.getContext('2d');
        this.shapes = [];
        this.createShapes();
        this.animate();
    }
    
    createShapes() {
        const shapeCount = 15;
        
        for (let i = 0; i < shapeCount; i++) {
            this.shapes.push({
                x: Math.random() * this.canvas.width,
                y: Math.random() * this.canvas.height,
                size: Math.random() * 50 + 20,
                rotation: Math.random() * Math.PI * 2,
                rotationSpeed: (Math.random() - 0.5) * 0.02,
                color: `hsla(${Math.random() * 360}, 70%, 50%, 0.1)`,
                type: Math.floor(Math.random() * 3), // 0: 三角形, 1: 正方形, 2: 六边形
                pulsePhase: Math.random() * Math.PI * 2
            });
        }
    }
    
    drawTriangle(shape) {
        this.ctx.save();
        this.ctx.translate(shape.x, shape.y);
        this.ctx.rotate(shape.rotation);
        
        this.ctx.beginPath();
        this.ctx.moveTo(0, -shape.size);
        this.ctx.lineTo(-shape.size * 0.866, shape.size * 0.5);
        this.ctx.lineTo(shape.size * 0.866, shape.size * 0.5);
        this.ctx.closePath();
        
        this.ctx.fillStyle = shape.color;
        this.ctx.fill();
        this.ctx.strokeStyle = shape.color.replace('0.1', '0.3');
        this.ctx.stroke();
        
        this.ctx.restore();
    }
    
    drawSquare(shape) {
        this.ctx.save();
        this.ctx.translate(shape.x, shape.y);
        this.ctx.rotate(shape.rotation);
        
        this.ctx.fillStyle = shape.color;
        this.ctx.fillRect(-shape.size / 2, -shape.size / 2, shape.size, shape.size);
        
        this.ctx.strokeStyle = shape.color.replace('0.1', '0.3');
        this.ctx.strokeRect(-shape.size / 2, -shape.size / 2, shape.size, shape.size);
        
        this.ctx.restore();
    }
    
    drawHexagon(shape) {
        this.ctx.save();
        this.ctx.translate(shape.x, shape.y);
        this.ctx.rotate(shape.rotation);
        
        this.ctx.beginPath();
        for (let i = 0; i < 6; i++) {
            const angle = (i / 6) * Math.PI * 2;
            const x = Math.cos(angle) * shape.size;
            const y = Math.sin(angle) * shape.size;
            
            if (i === 0) {
                this.ctx.moveTo(x, y);
            } else {
                this.ctx.lineTo(x, y);
            }
        }
        this.ctx.closePath();
        
        this.ctx.fillStyle = shape.color;
        this.ctx.fill();
        this.ctx.strokeStyle = shape.color.replace('0.1', '0.3');
        this.ctx.stroke();
        
        this.ctx.restore();
    }
    
    animate() {
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
        
        const time = Date.now() * 0.001;
        
        this.shapes.forEach(shape => {
            shape.rotation += shape.rotationSpeed;
            shape.pulsePhase += 0.02;
            
            // 脉冲效果
            const pulseScale = 1 + Math.sin(shape.pulsePhase) * 0.1;
            const originalSize = shape.size;
            shape.size = originalSize * pulseScale;
            
            switch (shape.type) {
                case 0:
                    this.drawTriangle(shape);
                    break;
                case 1:
                    this.drawSquare(shape);
                    break;
                case 2:
                    this.drawHexagon(shape);
                    break;
            }
            
            // 恢复原始大小
            shape.size = originalSize;
        });
        
        requestAnimationFrame(() => this.animate());
    }
}

// 使用示例
const geometricCanvas = document.getElementById('geometric-background');
new GeometricBackground(geometricCanvas);

4. 简易数据可视化

4.1 实时图表

创建一个简单的实时数据图表:

javascript 复制代码
class RealtimeChart {
    constructor(canvas, options = {}) {
        this.canvas = canvas;
        this.ctx = canvas.getContext('2d');
        this.options = {
            maxDataPoints: 50,
            updateInterval: 100,
            lineColor: '#3498db',
            gridColor: '#ecf0f1',
            backgroundColor: '#ffffff',
            ...options
        };
        this.data = [];
        this.isRunning = false;
        
        this.setupChart();
    }
    
    setupChart() {
        this.canvas.width = this.canvas.offsetWidth;
        this.canvas.height = this.canvas.offsetHeight;
        
        // 模拟数据生成
        this.dataGenerator = setInterval(() => {
            if (this.isRunning) {
                const newValue = Math.sin(Date.now() * 0.001) * 50 + 50 + (Math.random() - 0.5) * 20;
                this.addDataPoint(newValue);
            }
        }, this.options.updateInterval);
    }
    
    addDataPoint(value) {
        this.data.push({
            value: value,
            timestamp: Date.now()
        });
        
        // 保持数据点数量
        if (this.data.length > this.options.maxDataPoints) {
            this.data.shift();
        }
        
        this.draw();
    }
    
    draw() {
        const { width, height } = this.canvas;
        const { lineColor, gridColor, backgroundColor } = this.options;
        
        // 清空画布
        this.ctx.fillStyle = backgroundColor;
        this.ctx.fillRect(0, 0, width, height);
        
        // 绘制网格
        this.drawGrid();
        
        if (this.data.length < 2) return;
        
        // 计算比例
        const maxValue = Math.max(...this.data.map(d => d.value));
        const minValue = Math.min(...this.data.map(d => d.value));
        const valueRange = maxValue - minValue || 1;
        
        // 绘制数据线
        this.ctx.strokeStyle = lineColor;
        this.ctx.lineWidth = 2;
        this.ctx.beginPath();
        
        this.data.forEach((point, index) => {
            const x = (index / (this.data.length - 1)) * width;
            const y = height - ((point.value - minValue) / valueRange) * height * 0.8 - height * 0.1;
            
            if (index === 0) {
                this.ctx.moveTo(x, y);
            } else {
                this.ctx.lineTo(x, y);
            }
        });
        
        this.ctx.stroke();
        
        // 绘制数据点
        this.data.forEach((point, index) => {
            const x = (index / (this.data.length - 1)) * width;
            const y = height - ((point.value - minValue) / valueRange) * height * 0.8 - height * 0.1;
            
            this.ctx.fillStyle = lineColor;
            this.ctx.beginPath();
            this.ctx.arc(x, y, 3, 0, Math.PI * 2);
            this.ctx.fill();
        });
        
        // 绘制当前值
        if (this.data.length > 0) {
            const currentValue = this.data[this.data.length - 1].value;
            this.ctx.fillStyle = '#2c3e50';
            this.ctx.font = '14px Arial';
            this.ctx.fillText(`Current: ${currentValue.toFixed(2)}`, 10, 25);
        }
    }
    
    drawGrid() {
        const { width, height } = this.canvas;
        const { gridColor } = this.options;
        
        this.ctx.strokeStyle = gridColor;
        this.ctx.lineWidth = 1;
        
        // 垂直网格线
        for (let i = 0; i <= 10; i++) {
            const x = (width / 10) * i;
            this.ctx.beginPath();
            this.ctx.moveTo(x, 0);
            this.ctx.lineTo(x, height);
            this.ctx.stroke();
        }
        
        // 水平网格线
        for (let i = 0; i <= 5; i++) {
            const y = (height / 5) * i;
            this.ctx.beginPath();
            this.ctx.moveTo(0, y);
            this.ctx.lineTo(width, y);
            this.ctx.stroke();
        }
    }
    
    start() {
        this.isRunning = true;
    }
    
    stop() {
        this.isRunning = false;
    }
    
    destroy() {
        this.stop();
        if (this.dataGenerator) {
            clearInterval(this.dataGenerator);
        }
    }
}

// 使用示例
const realtimeCanvas = document.getElementById('realtime-chart');
const chart = new RealtimeChart(realtimeCanvas);
chart.start();

4.2 进度指示器

创建炫酷的进度指示器:

javascript 复制代码
class CircularProgress {
    constructor(canvas, options = {}) {
        this.canvas = canvas;
        this.ctx = canvas.getContext('2d');
        this.options = {
            radius: 80,
            lineWidth: 10,
            backgroundColor: '#ecf0f1',
            progressColor: '#3498db',
            textColor: '#2c3e50',
            showPercentage: true,
            animationDuration: 1000,
            ...options
        };
        this.progress = 0;
        this.targetProgress = 0;
        this.animationId = null;
        
        this.draw();
    }
    
    setProgress(value) {
        this.targetProgress = Math.max(0, Math.min(100, value));
        this.animate();
    }
    
    animate() {
        if (this.animationId) {
            cancelAnimationFrame(this.animationId);
        }
        
        const startProgress = this.progress;
        const endProgress = this.targetProgress;
        const duration = this.options.animationDuration;
        const startTime = Date.now();
        
        const animateStep = () => {
            const elapsed = Date.now() - startTime;
            const progress = Math.min(elapsed / duration, 1);
            
            // 使用缓动函数
            const easeProgress = this.easeInOutCubic(progress);
            
            this.progress = startProgress + (endProgress - startProgress) * easeProgress;
            this.draw();
            
            if (progress < 1) {
                this.animationId = requestAnimationFrame(animateStep);
            }
        };
        
        animateStep();
    }
    
    easeInOutCubic(t) {
        return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
    }
    
    draw() {
        const { width, height } = this.canvas;
        const centerX = width / 2;
        const centerY = height / 2;
        const { radius, lineWidth, backgroundColor, progressColor, textColor, showPercentage } = this.options;
        
        this.ctx.clearRect(0, 0, width, height);
        
        // 绘制背景圆环
        this.ctx.beginPath();
        this.ctx.arc(centerX, centerY, radius, 0, Math.PI * 2);
        this.ctx.strokeStyle = backgroundColor;
        this.ctx.lineWidth = lineWidth;
        this.ctx.stroke();
        
        // 绘制进度圆环
        const startAngle = -Math.PI / 2;
        const endAngle = startAngle + (this.progress / 100) * Math.PI * 2;
        
        this.ctx.beginPath();
        this.ctx.arc(centerX, centerY, radius, startAngle, endAngle);
        this.ctx.strokeStyle = progressColor;
        this.ctx.lineWidth = lineWidth;
        this.ctx.lineCap = 'round';
        this.ctx.stroke();
        
        // 绘制百分比文本
        if (showPercentage) {
            this.ctx.fillStyle = textColor;
            this.ctx.font = 'bold 24px Arial';
            this.ctx.textAlign = 'center';
            this.ctx.textBaseline = 'middle';
            this.ctx.fillText(`${Math.round(this.progress)}%`, centerX, centerY);
        }
        
        // 绘制中心点
        this.ctx.beginPath();
        this.ctx.arc(centerX, centerY, 3, 0, Math.PI * 2);
        this.ctx.fillStyle = progressColor;
        this.ctx.fill();
    }
}

// 使用示例
const progressCanvas = document.getElementById('progress-canvas');
const progress = new CircularProgress(progressCanvas, {
    radius: 100,
    lineWidth: 15,
    progressColor: '#e74c3c'
});

// 模拟进度更新
let currentProgress = 0;
setInterval(() => {
    currentProgress = (currentProgress + Math.random() * 15) % 100;
    progress.setProgress(currentProgress);
}, 1000);

5. 性能优化技巧

5.1 Canvas性能优化

javascript 复制代码
class CanvasOptimizer {
    constructor(canvas) {
        this.canvas = canvas;
        this.ctx = canvas.getContext('2d');
        this.isDirty = false;
        this.lastFrame = 0;
        this.fps = 60;
        this.frameInterval = 1000 / this.fps;
        
        this.setupOptimization();
    }
    
    setupOptimization() {
        // 启用图像平滑
        this.ctx.imageSmoothingEnabled = true;
        this.ctx.imageSmoothingQuality = 'high';
        
        // 使用离屏Canvas
        this.offscreenCanvas = document.createElement('canvas');
        this.offscreenCtx = this.offscreenCanvas.getContext('2d');
        this.offscreenCanvas.width = this.canvas.width;
        this.offscreenCanvas.height = this.canvas.height;
    }
    
    // 批量绘制
    batchDraw(drawCalls) {
        this.offscreenCtx.clearRect(0, 0, this.offscreenCanvas.width, this.offscreenCanvas.height);
        
        drawCalls.forEach(drawCall => {
            drawCall(this.offscreenCtx);
        });
        
        this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
        this.ctx.drawImage(this.offscreenCanvas, 0, 0);
    }
    
    // 智能重绘
    smartAnimate(renderFunction) {
        const currentFrame = Date.now();
        const deltaTime = currentFrame - this.lastFrame;
        
        if (deltaTime >= this.frameInterval) {
            renderFunction(this.ctx);
            this.lastFrame = currentFrame - (deltaTime % this.frameInterval);
        }
        
        requestAnimationFrame(() => this.smartAnimate(renderFunction));
    }
    
    // 对象池管理
    createObjectPool(createFunction, resetFunction, maxSize = 100) {
        const pool = [];
        let activeCount = 0;
        
        return {
            acquire: (...args) => {
                let obj;
                if (pool.length > 0) {
                    obj = pool.pop();
                } else {
                    obj = createFunction(...args);
                }
                
                activeCount++;
                return obj;
            },
            
            release: (obj) => {
                if (pool.length < maxSize) {
                    resetFunction(obj);
                    pool.push(obj);
                }
                activeCount--;
            },
            
            getActiveCount: () => activeCount,
            getPoolSize: () => pool.length
        };
    }
    
    // 内存管理
    cleanup() {
        this.offscreenCanvas = null;
        this.offscreenCtx = null;
    }
}

// 使用示例
const canvas = document.getElementById('optimized-canvas');
const optimizer = new CanvasOptimizer(canvas);

// 对象池示例
const particlePool = optimizer.createObjectPool(
    (x, y) => ({ x, y, vx: 0, vy: 0, life: 1.0 }),
    (particle) => {
        particle.x = 0;
        particle.y = 0;
        particle.vx = 0;
        particle.vy = 0;
        particle.life = 1.0;
    }
);

5.2 SVG性能优化

javascript 复制代码
class SVGOptimizer {
    constructor(svg) {
        this.svg = svg;
        this.defs = this.createDefs();
        this.useElements = new Map();
        this.isOptimized = false;
    }
    
    createDefs() {
        let defs = this.svg.querySelector('defs');
        if (!defs) {
            defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs');
            this.svg.insertBefore(defs, this.svg.firstChild);
        }
        return defs;
    }
    
    // 重用图形元素
    createReusableElement(id, element) {
        const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
        g.setAttribute('id', id);
        g.appendChild(element.cloneNode(true));
        this.defs.appendChild(g);
        
        return (x, y, scale = 1) => {
            const use = document.createElementNS('http://www.w3.org/2000/svg', 'use');
            use.setAttribute('href', `#${id}`);
            use.setAttribute('x', x);
            use.setAttribute('y', y);
            use.setAttribute('transform', `scale(${scale})`);
            return use;
        };
    }
    
    // 批量属性更新
    batchAttributeUpdate(elements, attributes) {
        requestAnimationFrame(() => {
            elements.forEach(element => {
                Object.entries(attributes).forEach(([key, value]) => {
                    element.setAttribute(key, value);
                });
            });
        });
    }
    
    // 智能重绘
    smartRedraw(changedElements) {
        if (!this.isOptimized) {
            // 首次优化
            this.optimizeTree();
            this.isOptimized = true;
        }
        
        changedElements.forEach(element => {
            element.style.willChange = 'transform';
        });
        
        requestAnimationFrame(() => {
            changedElements.forEach(element => {
                element.style.willChange = 'auto';
            });
        });
    }
    
    // 优化DOM树结构
    optimizeTree() {
        // 将静态元素分组
        const staticElements = this.svg.querySelectorAll('[data-static="true"]');
        if (staticElements.length > 0) {
            const staticGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g');
            staticGroup.setAttribute('class', 'static-elements');
            
            staticElements.forEach(element => {
                staticGroup.appendChild(element.cloneNode(true));
                element.remove();
            });
            
            this.svg.appendChild(staticGroup);
        }
        
        // 将动态元素分组
        const dynamicElements = this.svg.querySelectorAll('[data-dynamic="true"]');
        if (dynamicElements.length > 0) {
            const dynamicGroup = document.createElementNS('http://www.w3.org/2000/svg', 'g');
            dynamicGroup.setAttribute('class', 'dynamic-elements');
            
            dynamicElements.forEach(element => {
                dynamicGroup.appendChild(element.cloneNode(true));
                element.remove();
            });
            
            this.svg.appendChild(dynamicGroup);
        }
    }
    
    // 清理资源
    cleanup() {
        this.useElements.clear();
        this.defs.innerHTML = '';
    }
}

// 使用示例
const svg = document.getElementById('optimized-svg');
const optimizer = new SVGOptimizer(svg);

// 创建可重用元素
const circleTemplate = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
circleTemplate.setAttribute('r', '10');
circleTemplate.setAttribute('fill', '#3498db');

const createCircle = optimizer.createReusableElement('circle-template', circleTemplate);

// 使用可重用元素
for (let i = 0; i < 100; i++) {
    const circle = createCircle(Math.random() * 800, Math.random() * 600);
    svg.appendChild(circle);
}

6. 实战项目:动态背景库

6.1 完整的动态背景库

javascript 复制代码
class DynamicBackgroundLibrary {
    constructor() {
        this.backgrounds = new Map();
        this.activeBackground = null;
    }
    
    // 粒子背景
    createParticleBackground(canvas, options = {}) {
        const config = {
            particleCount: 100,
            connectionDistance: 100,
            particleColor: '#3498db',
            connectionColor: 'rgba(52, 152, 219, 0.1)',
            interactive: true,
            ...options
        };
        
        return new ParticleSystem(canvas, config);
    }
    
    // 波浪背景
    createWaveBackground(canvas, options = {}) {
        const config = {
            waveCount: 3,
            amplitude: 50,
            frequency: 0.01,
            speed: 0.02,
            colors: ['rgba(52, 152, 219, 0.3)', 'rgba(46, 204, 113, 0.3)', 'rgba(155, 89, 182, 0.3)'],
            ...options
        };
        
        return new WaveBackground(canvas, config);
    }
    
    // 几何背景
    createGeometricBackground(canvas, options = {}) {
        const config = {
            shapeCount: 15,
            shapeTypes: ['triangle', 'square', 'hexagon'],
            rotationSpeed: 0.01,
            pulseEnabled: true,
            colors: ['hsla(200, 70%, 50%, 0.1)', 'hsla(120, 70%, 50%, 0.1)', 'hsla(280, 70%, 50%, 0.1)'],
            ...options
        };
        
        return new GeometricBackground(canvas, config);
    }
    
    // 星空背景
    createStarfieldBackground(canvas, options = {}) {
        const config = {
            starCount: 200,
            starSize: { min: 1, max: 3 },
            starSpeed: { min: 0.1, max: 0.5 },
            starColor: '#ffffff',
            twinkleEffect: true,
            ...options
        };
        
        return new StarfieldBackground(canvas, config);
    }
    
    // 激活背景
    activateBackground(type, canvas, options = {}) {
        if (this.activeBackground) {
            this.activeBackground.destroy();
        }
        
        let background;
        
        switch (type) {
            case 'particles':
                background = this.createParticleBackground(canvas, options);
                break;
            case 'waves':
                background = this.createWaveBackground(canvas, options);
                break;
            case 'geometric':
                background = this.createGeometricBackground(canvas, options);
                break;
            case 'starfield':
                background = this.createStarfieldBackground(canvas, options);
                break;
            default:
                throw new Error(`Unknown background type: ${type}`);
        }
        
        this.activeBackground = background;
        this.backgrounds.set(type, background);
        
        return background;
    }
    
    // 获取当前背景
    getActiveBackground() {
        return this.activeBackground;
    }
    
    // 销毁所有背景
    destroyAll() {
        this.backgrounds.forEach(background => {
            if (background.destroy) {
                background.destroy();
            }
        });
        this.backgrounds.clear();
        this.activeBackground = null;
    }
}

// 使用示例
const backgroundLib = new DynamicBackgroundLibrary();
const canvas = document.getElementById('background-canvas');

// 激活粒子背景
const particleBg = backgroundLib.activateBackground('particles', canvas, {
    particleCount: 150,
    particleColor: '#e74c3c',
    interactive: true
});

// 切换背景类型
document.getElementById('background-selector').addEventListener('change', (e) => {
    backgroundLib.activateBackground(e.target.value, canvas);
});

总结

Canvas和SVG的冷门用法为前端开发提供了无限的创意空间。通过本文介绍的技术,你可以:

  1. 创建炫酷的动态背景 - 粒子系统、波浪效果、几何动画等
  2. 实现音频可视化 - 结合Web Audio API创建音乐播放器
  3. 处理图像和滤镜 - 实时图像处理和特效
  4. 模拟物理效果 - 重力、碰撞等物理引擎
  5. 生成动态图表 - SVG的矢量优势和动画能力
  6. 优化性能 - 对象池、批量操作、智能重绘等

这些技术不仅可以提升用户体验,还能为你的项目增添独特的视觉效果。记住,性能优化是关键,合理使用这些技术才能发挥它们的最大价值。

相关推荐
一 乐2 小时前
旅游|内蒙古景点旅游|基于Springboot+Vue的内蒙古景点旅游管理系统设计与实现(源码+数据库+文档)
开发语言·前端·数据库·vue.js·spring boot·后端·旅游
驯狼小羊羔2 小时前
学习随笔-require和import
前端·学习
excel2 小时前
🚀 从 GPT-5 流式输出看现代前端的流式请求机制(Koa 实现版)
前端
凸头3 小时前
Spring Boot接收前端参数的注解总结
前端·spring boot·后端
爱吃甜品的糯米团子3 小时前
JavaScript 正则表达式:选择、分组与引用深度解析
前端·javascript·正则表达式
excel3 小时前
Vue SSR 编译器源码深析:ssrTransformShow 的实现原理与设计哲学
前端
excel3 小时前
深入解析 Vue 3 SSR 编译管线:ssrCodegenTransform 源码全解
前端
excel3 小时前
深入解析 Vue SSR 编译器的核心函数:compile
前端
IT_陈寒3 小时前
Vue 3性能优化实战:7个关键技巧让我的应用加载速度提升50%
前端·人工智能·后端