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