UNPKG

browser-canvas-fingerprinting

Version:

A simple canvas fingerprinting implementation in browser with specific information used to generate fingerprint

215 lines (188 loc) 6.66 kB
import { getGradients } from './effects.js'; export async function draw2d(ctx, canvas) { // 1. 星空背景 for (let i = 0; i < 200; i++) { const x = (i * 123.456) % canvas.width; const y = (i * 67.89) % canvas.height; const size = (i % 3) + 1; const brightness = 150 + (i % 105); ctx.fillStyle = `rgb(${brightness}, ${brightness}, 255)`; ctx.beginPath(); ctx.arc(x, y, size, 0, 3.14159 * 2); ctx.fill(); } // 2. 金属质感的旋转矩形(使用变换矩阵) ctx.save(); ctx.translate(120, 100); ctx.rotate(3.14159 / 6); // 固定30度旋转 // 金属渐变 const metalGradient = ctx.createLinearGradient(-75, -50, 75, 50); metalGradient.addColorStop(0, '#e0e0e0'); metalGradient.addColorStop(0.3, '#ffffff'); metalGradient.addColorStop(0.5, '#a0a0a0'); metalGradient.addColorStop(0.7, '#c0c0c0'); metalGradient.addColorStop(1, '#808080'); ctx.fillStyle = metalGradient; ctx.shadowColor = 'rgba(0, 0, 0, 0.5)'; ctx.shadowBlur = 15; ctx.shadowOffsetX = 5; ctx.shadowOffsetY = 5; ctx.fillRect(-75, -50, 150, 100); // 高光效果 ctx.strokeStyle = 'rgba(255, 255, 255, 0.8)'; ctx.lineWidth = 2; ctx.beginPath(); ctx.moveTo(-70, -45); ctx.lineTo(-20, -45); ctx.lineTo(-25, -40); ctx.stroke(); ctx.restore(); // 3. 霓虹发光圆形 ctx.save(); ctx.shadowColor = '#0ff'; ctx.shadowBlur = 30; ctx.fillStyle = '#00ffff'; ctx.beginPath(); ctx.arc(300, 100, 40, 0, 3.14159 * 2); ctx.fill(); // 内发光效果 ctx.shadowBlur = 0; ctx.fillStyle = '#ffffff'; ctx.beginPath(); ctx.arc(300, 100, 25, 0, 3.14159 * 2); ctx.fill(); ctx.restore(); // 4. 彩虹渐变三角形 ctx.save(); const rainbowGradient = ctx.createLinearGradient(250, 50, 350, 150); rainbowGradient.addColorStop(0, '#ff0000'); rainbowGradient.addColorStop(0.2, '#ffff00'); rainbowGradient.addColorStop(0.4, '#00ff00'); rainbowGradient.addColorStop(0.6, '#00ffff'); rainbowGradient.addColorStop(0.8, '#0000ff'); rainbowGradient.addColorStop(1, '#ff00ff'); ctx.fillStyle = rainbowGradient; ctx.shadowColor = 'rgba(255, 0, 255, 0.5)'; ctx.shadowBlur = 20; ctx.beginPath(); ctx.moveTo(400, 50); ctx.lineTo(450, 150); ctx.lineTo(350, 150); ctx.closePath(); ctx.fill(); ctx.restore(); // 5. 复杂渐变图形 ctx.save(); const complexGradient = ctx.createRadialGradient(550, 100, 10, 550, 100, 60); complexGradient.addColorStop(0, '#ff6b6b'); complexGradient.addColorStop(0.5, '#4ecdc4'); complexGradient.addColorStop(1, '#45b7d1'); ctx.fillStyle = complexGradient; ctx.shadowColor = 'rgba(78, 205, 196, 0.5)'; ctx.shadowBlur = 25; ctx.beginPath(); ctx.arc(550, 100, 50, 0, 3.14159 * 2); ctx.fill(); ctx.restore(); // 6. 几何图案组合 ctx.save(); ctx.translate(200, 250); // 六边形 ctx.fillStyle = 'rgba(255, 105, 180, 0.8)'; ctx.shadowColor = 'rgba(255, 105, 180, 0.6)'; ctx.shadowBlur = 15; ctx.beginPath(); for (let i = 0; i < 6; i++) { const angle = (i * 3.14159) / 3; const x = Math.cos(angle) * 40; const y = Math.sin(angle) * 40; if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y); } ctx.closePath(); ctx.fill(); // 内部装饰 ctx.strokeStyle = 'rgba(255, 255, 255, 0.9)'; ctx.lineWidth = 2; ctx.beginPath(); ctx.arc(0, 0, 25, 0, 3.14159 * 2); ctx.stroke(); ctx.restore(); // 7. 文字特效 ctx.save(); ctx.font = 'bold 24px Arial'; ctx.fillStyle = '#ffffff'; ctx.shadowColor = '#ff00ff'; ctx.shadowBlur = 10; ctx.shadowOffsetX = 3; ctx.shadowOffsetY = 3; ctx.fillText('Awesome Canvas!', 350, 250); // 文字描边 ctx.strokeStyle = '#00ffff'; ctx.lineWidth = 1; ctx.strokeText('Awesome Canvas!', 350, 250); ctx.restore(); // 8. 粒子效果装饰 ctx.save(); ctx.translate(500, 250); for (let i = 0; i < 12; i++) { const angle = (i * 3.14159) / 6; const distance = 30; const x = Math.cos(angle) * distance; const y = Math.sin(angle) * distance; const particleGradient = ctx.createRadialGradient(x, y, 0, x, y, 8); particleGradient.addColorStop(0, '#ffff00'); particleGradient.addColorStop(1, 'rgba(255, 255, 0, 0)'); ctx.fillStyle = particleGradient; ctx.beginPath(); ctx.arc(x, y, 8, 0, 3.14159 * 2); ctx.fill(); } ctx.restore(); } export async function drawText(canvas, ctx, texts) { ctx.fillStyle = '#000000'; ctx.font = '14px Arial'; let yPosition = 200; const lineHeight = 20; const gradients = getGradients(); texts.forEach((line, index) => { const maxWidth = canvas.width - 100; const gradientData = gradients[index % gradients.length]; // 根据渐变数据创建渐变 let gradient; if (gradientData[0] === 'linear') { const [type, x0, y0, x1, y1, colors] = gradientData; gradient = ctx.createLinearGradient( 50 + x0 * maxWidth, yPosition + y0 * lineHeight, 50 + x1 * maxWidth, yPosition + y1 * lineHeight ); colors.forEach(([stop, color]) => gradient.addColorStop(stop, color)); } else { const [type, x0, y0, r0, x1, y1, r1, colors] = gradientData; gradient = ctx.createRadialGradient( 50 + x0 * maxWidth, yPosition + y0 * lineHeight, r0 * 50, 50 + x1 * maxWidth, yPosition + y1 * lineHeight, r1 * 50 ); colors.forEach(([stop, color]) => gradient.addColorStop(stop, color)); } ctx.fillStyle = gradient; let currentLine = ''; const words = line.split(' '); for (let word of words) { const testLine = currentLine + word + ' '; const metrics = ctx.measureText(testLine); if (metrics.width > maxWidth && currentLine !== '') { ctx.fillText(currentLine, 50, yPosition); yPosition += lineHeight; currentLine = word + ' '; } else { currentLine = testLine; } } ctx.fillText(currentLine, 50, yPosition); yPosition += lineHeight + 5; }); }