蜘蛛网页宠物
script dog · v1.0.0 · 内容脚本 · 10 次安装
简介
简约像素网页宠物
脚本源码
(function () {
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', start);
} else {
start();
}
function start() {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
document.body.appendChild(canvas);
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
canvas.style.position = 'fixed';
canvas.style.top = '0';
canvas.style.left = '0';
canvas.style.zIndex = '9999';
canvas.style.pointerEvents = 'none';
class Spider {
constructor() {
this.legPhase = 0;
this.breath = 0;
this.state = 'moving';
this.stateTimer = 0;
this.idleTimer = 0;
this.angle = 0;
this.speed = 1.2;
this.size = 1;
this.respawn();
}
getMargin() {
return Math.min(80, Math.max(26, Math.min(canvas.width, canvas.height) * 0.09));
}
respawn() {
const margin = this.getMargin();
const rangeX = Math.max(0, canvas.width - margin * 2);
const rangeY = Math.max(0, canvas.height - margin * 2);
this.x = margin + Math.random() * rangeX;
this.y = margin + Math.random() * rangeY;
this.targetX = this.x;
this.targetY = this.y;
this.state = 'moving';
this.stateTimer = 0;
this.idleTimer = 0;
this.newTarget();
}
newTarget() {
const margin = this.getMargin();
const rangeX = Math.max(0, canvas.width - margin * 2);
const rangeY = Math.max(0, canvas.height - margin * 2);
this.targetX = margin + Math.random() * rangeX;
this.targetY = margin + Math.random() * rangeY;
this.speed = 0.7 + Math.random() * 1.8;
this.stateTimer = 0;
}
update() {
this.breath += 0.04;
this.stateTimer++;
const margin = this.getMargin();
if (
this.x < margin - 40 ||
this.x > canvas.width - margin + 40 ||
this.y < margin - 40 ||
this.y > canvas.height - margin + 40
) {
this.state = 'moving';
this.newTarget();
}
if (this.state === 'moving') {
const dx = this.targetX - this.x;
const dy = this.targetY - this.y;
const dist = Math.hypot(dx, dy);
if (dist < 5) {
if (Math.random() < 0.45) {
this.state = 'idle';
this.idleTimer = 0;
} else {
this.newTarget();
}
} else {
const vx = dx / dist * this.speed;
const vy = dy / dist * this.speed;
this.x += vx;
this.y += vy;
this.angle = Math.atan2(vy, vx);
this.legPhase += 0.16 + this.speed * 0.03;
}
if (this.stateTimer > 220 + Math.random() * 360) {
this.state = 'idle';
this.idleTimer = 0;
}
} else {
this.idleTimer++;
this.legPhase += 0.018;
if (this.idleTimer > 80 + Math.random() * 140) {
this.state = 'moving';
this.newTarget();
}
}
}
getLegs() {
const legs = [];
const hipsX = [13, 8, -5, -12];
const hipsYAbs = [3, 6, 7, 5];
const topBaseAngles = [-0.38, -0.90, -1.55, -2.12];
for (let side = -1; side <= 1; side += 2) {
for (let i = 0; i < 4; i++) {
const hipX = hipsX[i];
const hipY = side * hipsYAbs[i];
const base = side === -1 ? topBaseAngles[i] : -topBaseAngles[i];
const swing = Math.sin(this.legPhase + i * Math.PI / 2 + side * 0.8) * 0.22;
const angle = base + swing;
const kneeAngle = angle - side * 0.45;
const footAngle = angle + side * 0.28;
const kneeX = hipX + Math.cos(kneeAngle) * 12;
const kneeY = hipY + Math.sin(kneeAngle) * 12;
const footX = kneeX + Math.cos(footAngle) * 17;
const footY = kneeY + Math.sin(footAngle) * 17;
legs.push({ hipX, hipY, kneeX, kneeY, footX, footY });
}
}
return legs;
}
draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
const scale = Math.max(0.65, Math.min(1.8, Math.min(window.innerWidth, window.innerHeight) / 760));
this.size = scale;
ctx.save();
ctx.translate(this.x, this.y);
ctx.rotate(this.angle);
ctx.scale(scale, scale);
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
const legs = this.getLegs();
ctx.strokeStyle = '#2a193c';
ctx.lineWidth = 2.4;
for (const leg of legs) {
ctx.beginPath();
ctx.moveTo(leg.hipX, leg.hipY);
ctx.lineTo(leg.kneeX, leg.kneeY);
ctx.lineTo(leg.footX, leg.footY);
ctx.stroke();
}
const breathe = Math.sin(this.breath) * 0.6;
const bodyGrad = ctx.createLinearGradient(-20, -14, 20, 14);
bodyGrad.addColorStop(0, '#6d4f96');
bodyGrad.addColorStop(0.55, '#4b2f6b');
bodyGrad.addColorStop(1, '#321d4a');
ctx.fillStyle = bodyGrad;
ctx.beginPath();
ctx.ellipse(-7, 0, 14 + breathe, 10 + breathe * 0.35, 0, 0, Math.PI * 2);
ctx.fill();
ctx.strokeStyle = 'rgba(0, 0, 0, 0.18)';
ctx.lineWidth = 1.2;
ctx.stroke();
const headGrad = ctx.createLinearGradient(2, -8, 18, 8);
headGrad.addColorStop(0, '#8b67b8');
headGrad.addColorStop(1, '#4f3170');
ctx.fillStyle = headGrad;
ctx.beginPath();
ctx.ellipse(9, 0, 8, 6, 0, 0, Math.PI * 2);
ctx.fill();
ctx.stroke();
ctx.fillStyle = 'rgba(255, 255, 255, 0.16)';
ctx.beginPath(); ctx.arc(-10, -3, 1.7, 0, Math.PI * 2); ctx.fill();
ctx.beginPath(); ctx.arc(-5, 3, 1.3, 0, Math.PI * 2); ctx.fill();
ctx.beginPath(); ctx.arc(-1, -4, 1.2, 0, Math.PI * 2); ctx.fill();
ctx.beginPath(); ctx.arc(-13, 2, 1.0, 0, Math.PI * 2); ctx.fill();
ctx.fillStyle = '#160e22';
ctx.beginPath(); ctx.arc(14, -3, 1.7, 0, Math.PI * 2); ctx.fill();
ctx.beginPath(); ctx.arc(14, 3, 1.7, 0, Math.PI * 2); ctx.fill();
ctx.beginPath(); ctx.arc(16, -1.3, 1.2, 0, Math.PI * 2); ctx.fill();
ctx.beginPath(); ctx.arc(16, 1.3, 1.2, 0, Math.PI * 2); ctx.fill();
ctx.fillStyle = '#ffffff';
ctx.beginPath(); ctx.arc(14.7, -3.4, 0.45, 0, Math.PI * 2); ctx.fill();
ctx.beginPath(); ctx.arc(14.7, 2.6, 0.45, 0, Math.PI * 2); ctx.fill();
ctx.restore();
}
}
const spider = new Spider();
function animate() {
spider.update();
spider.draw();
requestAnimationFrame(animate);
}
window.addEventListener('resize', () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const margin = spider.getMargin();
spider.x = Math.min(Math.max(spider.x, margin), canvas.width - margin);
spider.y = Math.min(Math.max(spider.y, margin), canvas.height - margin);
if (Math.random() < 0.45) {
spider.newTarget();
}
});
if (typeof registerMenuCommand === 'function') {
try {
registerMenuCommand('重置蜘蛛宠物', () => spider.respawn());
} catch (e) {}
} else if (typeof Dog !== 'undefined' && typeof Dog.registerMenuCommand === 'function') {
try {
Dog.registerMenuCommand('重置蜘蛛宠物', () => spider.respawn());
} catch (e) {}
}
animate();
}
})();