// ==UserScript== // @name Amazing click effect! // @namespace http://tampermonkey.net/ // @version 1.12.3 // @description Long press for surprises! Enjoy clicking ~ // @author Super_Diu // @match *://*/* // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== // @grant none // @downloadURL https://update.greasyfork.icu/scripts/565081/Amazing%20click%20effect%21.user.js // @updateURL https://update.greasyfork.icu/scripts/565081/Amazing%20click%20effect%21.meta.js // ==/UserScript== (function() { 'use strict'; function clickTriangleEffect() { let tris = []; // 存储三角形实例 let dots = []; // 存储长按蓄力小圆点实例 let cvs, ctx; // 画布和上下文 let w, h; // 画布宽高 // 纯蓝色系配色 const blueColors = ["#1E90FF", "#4169E1", "#00BFFF", "#87CEFA", "#6495ED", "#0099FF", "#7B68EE"]; // 三角形类型 const triTypes = ["acute", "right", "obtuse", "isosceles", "equilateral"]; // 尾迹轨迹点 const MAX_TRACK = 10; const DOT_TRACK = 5; // 长按核心参数(0.8s触发蓄力) let pressStart = 0; let isPress = false; let px = 0, py = 0; const LONG_PRESS = 800; // 进阶特效上限 const MAX_SIZE = 20; const MAX_SPEED = 15; const MIN_ALPHA_SPEED = 8; // 三角形数量随长按递增 const BASE_COUNT = 12; const MAX_COUNT = 30; const SCALE_DUR = 3000; // 蓄力小圆点配置 const DOT_R_MIN = 1; const DOT_R_MAX = 3; const DOT_INTERVAL = 150; const DOT_INIT_SPD = 1.2; const DOT_ACCEL = 0.2; const DOT_ALPHA_SPD = 0.02; const DOT_FADE_BASE = 0.03; const DOT_SPAWN_R_MIN = 180; const DOT_SPAWN_R_MAX = 280; const SPEED_ALPHA_FACTOR = 0.0001; let lastDotSpawn = 0; // 创建画布 cvs = document.createElement("canvas"); document.body.appendChild(cvs); cvs.setAttribute("style", "width: 100%; height: 100%; top: 0; left: 0; z-index: 99999; position: fixed; pointer-events: none;"); // 初始化画布 if (cvs.getContext && window.addEventListener) { ctx = cvs.getContext("2d"); updateCvsSize(); window.addEventListener('resize', updateCvsSize, false); requestAnimationFrame(loop); // 鼠标事件 window.addEventListener("mousedown", function(e) { isPress = true; pressStart = Date.now(); px = e.clientX; py = e.clientY; lastDotSpawn = Date.now(); }, false); window.addEventListener("mouseup", createEffect, false); window.addEventListener("mouseout", function() { isPress = false; pressStart = 0; dots.forEach(dot => dot.needFade = true); }, false); } else { console.log("当前浏览器不支持Canvas或addEventListener!"); } // 画布大小更新 function updateCvsSize() { cvs.width = window.innerWidth * 2; cvs.height = window.innerHeight * 2; cvs.style.width = window.innerWidth + 'px'; cvs.style.height = window.innerHeight + 'px'; ctx.scale(2, 2); w = window.innerWidth; h = window.innerHeight; } // 工具方法 function alphaToHex(alpha) { const hex = Math.floor(Math.max(0, Math.min(255, alpha * 255))).toString(16); return hex.length === 1 ? '0' + hex : hex; } function randInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; } function randFloat(min, max) { return Math.random() * (max - min) + min; } // 生成三角形特效 function createEffect() { if (!isPress) return; const pressDur = Date.now() - pressStart; isPress = false; pressStart = 0; dots.forEach(dot => dot.needFade = true); let sizeMin = 7, sizeMax = 13; let spdMin = 4, spdMax = 9; let alphaSpdMin = 25, alphaSpdMax = 35; let createCnt = BASE_COUNT; if (pressDur >= LONG_PRESS) { const overThresh = pressDur - LONG_PRESS; const scale = Math.min(1, overThresh / SCALE_DUR); createCnt = Math.floor(BASE_COUNT + scale * (MAX_COUNT - BASE_COUNT)); sizeMax = 13 + scale * (MAX_SIZE - 13); spdMax = 9 + scale * (MAX_SPEED - 9); alphaSpdMin = 25 - scale * (25 - MIN_ALPHA_SPEED); alphaSpdMax = 35 - scale * (35 - MIN_ALPHA_SPEED); } // 生成三角形 for (let i = 0; i < createCnt; i++) { const triSpd = randInt(spdMin, spdMax); let triAlphaSpd = randInt(alphaSpdMin, alphaSpdMax); triAlphaSpd = triAlphaSpd + triSpd * 2; triAlphaSpd = Math.min(triAlphaSpd, 50); tris.push(new Triangle( px, py, randInt(sizeMin, sizeMax), triSpd, triAlphaSpd / 1000 )); } } // 蓄力小圆点类 class Dot { constructor(tx, ty) { this.tx = tx; this.ty = ty; const r = randFloat(DOT_SPAWN_R_MIN, DOT_SPAWN_R_MAX); const angle = randFloat(0, Math.PI * 2); this.x = tx + Math.cos(angle) * r; this.y = ty + Math.sin(angle) * r; this.r = randFloat(DOT_R_MIN, DOT_R_MAX); this.color = blueColors[Math.floor(Math.random() * blueColors.length)]; this.alpha = 0; this.spd = DOT_INIT_SPD; this.accel = DOT_ACCEL; this.needFade = false; this.angle = Math.atan2(ty - this.y, tx - this.x); this.track = []; this.arrived = false; } addTrack() { if (this.arrived) return; this.track.push({ x: this.x, y: this.y, alpha: this.alpha, r: this.r }); if (this.track.length > DOT_TRACK) this.track.shift(); } update() { if (this.arrived) { this.alpha = 0; return; } this.addTrack(); // 速度关联显隐/消失速度 const spdFactor = 1 + this.spd * SPEED_ALPHA_FACTOR; if (this.needFade) { this.alpha = Math.max(0, this.alpha - DOT_FADE_BASE * spdFactor); } else { this.alpha = Math.min(1, this.alpha + DOT_ALPHA_SPD * spdFactor); } // 不让蓄力特效穿过鼠标的判定逻辑 const dx = this.tx - this.x; const dy = this.ty - this.y; const dist = Math.sqrt(dx * dx + dy * dy); const nextStep = this.spd + this.accel; if (dist <= this.r * 3 || nextStep >= dist) { this.arrived = true; this.alpha = 0; this.track = []; this.x = this.tx; this.y = this.ty; } else { this.spd += this.accel; this.spd = Math.min(this.spd, 8); this.x += Math.cos(this.angle) * this.spd; this.y += Math.sin(this.angle) * this.spd; } } drawTrail() { if (this.track.length < 2 || this.alpha <= 0 || this.arrived) return; ctx.save(); ctx.beginPath(); ctx.moveTo(this.track[0].x, this.track[0].y); for (let i = 1; i < this.track.length; i++) { ctx.lineTo(this.track[i].x, this.track[i].y); } const lineW = this.r * 0.3; if (lineW <= 0) return; const grad = ctx.createLinearGradient( this.track[0].x, this.track[0].y, this.track[this.track.length-1].x, this.track[this.track.length-1].y ); grad.addColorStop(0, `${this.color}1A`); grad.addColorStop(1, `${this.color}${alphaToHex(Math.min(0.6, this.alpha))}`); ctx.lineWidth = lineW; ctx.lineCap = "butt"; ctx.lineJoin = "miter"; ctx.strokeStyle = grad; ctx.stroke(); ctx.restore(); } draw() { if (this.alpha <= 0 || this.arrived) return; this.drawTrail(); ctx.save(); ctx.globalAlpha = this.alpha; ctx.fillStyle = this.color; ctx.beginPath(); ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2); ctx.closePath(); ctx.fill(); ctx.restore(); } } // 三角形类 class Triangle { constructor(x, y, size, spd, alphaSpd) { this.x = x; this.y = y; this.angle = Math.random() * Math.PI * 2; this.spd = spd; this.rotSpd = (Math.random() - 0.5) * 18; this.rot = 0; this.size = size; this.color = blueColors[Math.floor(Math.random() * blueColors.length)]; this.type = triTypes[Math.floor(Math.random() * triTypes.length)]; this.alpha = 1; this.alphaSpd = alphaSpd; this.sizeSpd = randInt(5, 12) / 100; this.track = []; } update() { this.addTrack(); this.x += Math.cos(this.angle) * this.spd; this.y += Math.sin(this.angle) * this.spd; this.rot += this.rotSpd; this.size = Math.max(1, this.size - this.sizeSpd); this.alpha = Math.max(0, this.alpha - this.alphaSpd); } addTrack() { this.track.push({ x: this.x, y: this.y, size: this.size, alpha: this.alpha }); if (this.track.length > MAX_TRACK) this.track.shift(); } // 绘制单个三角形 drawSingle(ctx, x, y, rot, size, alpha) { if (alpha <= 0 || size <= 0) return; ctx.save(); ctx.globalAlpha = alpha; ctx.translate(x, y); ctx.rotate(rot * Math.PI / 180); ctx.fillStyle = this.color; ctx.beginPath(); switch (this.type) { case "acute": ctx.moveTo(0, -size); ctx.lineTo(size * 0.7, size * 0.6); ctx.lineTo(-size * 0.7, size * 0.6); break; case "right": ctx.moveTo(0, -size * 0.8); ctx.lineTo(size * 0.8, size * 0.8); ctx.lineTo(-size * 0.8, size * 0.8); break; case "obtuse": ctx.moveTo(0, -size); ctx.lineTo(size * 1.0, size * 0.4); ctx.lineTo(-size * 1.0, size * 0.4); break; case "isosceles": ctx.moveTo(0, -size); ctx.lineTo(size * 0.6, size * 0.8); ctx.lineTo(-size * 0.6, size * 0.8); break; case "equilateral": const h = size * Math.sqrt(3) / 2; ctx.moveTo(0, -h / 2); ctx.lineTo(size / 2, h / 2); ctx.lineTo(-size / 2, h / 2); break; } ctx.closePath(); ctx.fill(); ctx.restore(); } // 绘制尾迹 drawTrail(ctx) { if (this.track.length < 2 || this.alpha <= 0) return; const trackCnt = this.track.length; const startP = this.track[0]; const endP = this.track[trackCnt - 1]; // 计算尾迹方向和长度 const trailAngle = Math.atan2(endP.y - startP.y, endP.x - startP.x); const trailLen = Math.sqrt( Math.pow(endP.x - startP.x, 2) + Math.pow(endP.y - startP.y, 2) ); const endW = endP.size * 0.3; // 宽边(朝向三角形):从0.6→0.3 const startW = endW * 0.05; // 窄边(尾迹末端):从0.1→0.05 ctx.save(); // 构建尾迹路径 const endLX = endP.x + Math.cos(trailAngle + Math.PI / 2) * endW; const endLY = endP.y + Math.sin(trailAngle + Math.PI / 2) * endW; const endRX = endP.x + Math.cos(trailAngle - Math.PI / 2) * endW; const endRY = endP.y + Math.sin(trailAngle - Math.PI / 2) * endW; const startX = startP.x + Math.cos(trailAngle) * startW; const startY = startP.y + Math.sin(trailAngle) * startW; // 绘制路径 ctx.beginPath(); ctx.moveTo(endLX, endLY); ctx.lineTo(endRX, endRY); ctx.lineTo(startX, startY); ctx.closePath(); // 渐变填充 const grad = ctx.createLinearGradient( endP.x, endP.y, startP.x, startP.y ); grad.addColorStop(0, `${this.color}${alphaToHex(Math.min(0.4, this.alpha))}`); grad.addColorStop(1, `${this.color}10`); ctx.fillStyle = grad; ctx.fill(); ctx.restore(); } draw() { if (this.alpha <= 0) return; this.drawTrail(ctx); this.drawSingle(ctx, this.x, this.y, this.rot, this.size, this.alpha); } } // 动画循环 function loop() { ctx.clearRect(0, 0, w, h); if (isPress) { const pressDur = Date.now() - pressStart; if (pressDur >= LONG_PRESS) { const now = Date.now(); if (now - lastDotSpawn >= DOT_INTERVAL) { dots.push(new Dot(px, py)); lastDotSpawn = now; } } } // 圆点更新绘制 dots.forEach(dot => dot.update()); dots.forEach(dot => dot.draw()); dots = dots.filter(dot => dot.alpha > 0 && !dot.arrived); // 三角形更新绘制 tris.forEach(tri => tri.update()); tris.forEach(tri => tri.draw()); tris = tris.filter(tri => tri.alpha > 0); requestAnimationFrame(loop); } } // 调用特效 clickTriangleEffect(); })();