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
JavaScript
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;
});
}