如何将奇异博士传送门动画定位到翻面卡牌的外圈?
问题
我拥有6张可翻转的卡牌,希望在任意一张卡牌翻面时,将使用p5.js实现的Dr. Strange传送门动画精准显示在卡牌背面彩色圆圈的最外层边框处。当前动画无法正确定位到目标位置,请教如何修改代码实现该效果?
相关代码片段(卡牌旋转与传送门逻辑)
</script> <script> //sound effect $(document).ready(function() { var flipSound = document.getElementById("flipSound"); $(".card").click(function() { var card = $(this); // 防止重复翻转 if (!card.hasClass('flipping')) { card.addClass('flipping'); // 判断卡牌当前状态 if (!card.hasClass("flipped")) { flipSound.currentTime = 0; flipSound.play(); startAnimation(); console.log("started") card.addClass("flipped"); } else { stopAnimation(); console.log("stopped") card.removeClass("flipped"); } setTimeout(function() { card.removeClass('flipping'); }, 1000); } }); // 点击卡牌背面不触发翻转 $(".card__back").click(function(e) { e.stopPropagation(); }); }); </script> <script> // 页面hover效果 document.getElementById("gameCardLink").addEventListener("mouseover", function() { document.body.classList.add("hoverEffect"); }); document.getElementById("gameCardLink").addEventListener("mouseout", function() { document.body.classList.remove("hoverEffect"); }); </script> <script> // 传送门动画 var p5Instance; function startAnimation() { const sketch = (p) => { const createParticleSystem = (location) => { const origin = location.copy(); const particles = []; const addParticle = velocity => { const rand = p.random(0, 1); if (rand <= .3) { particles.push(createSparkParticle(origin, velocity.copy())); } else { particles.push(createParticle(origin, velocity.copy())); } }; const applyForce = force => { particles.forEach(particle => { particle.applyForce(force); }); }; const run = () => { particles.forEach((particle, index) => { particle.move(); particle.draw(); if (particle.isDead()) { particles.splice(index, 1); } }); }; return { origin, addParticle, run, applyForce }; }; const createSparkParticle = (locationP, velocity) => { const particle = createParticle(locationP, velocity); let fade = 255; const draw = () => { p.colorMode(p.HSB); p.stroke(16, 62, 100, fade); const arrow = velocity.copy().normalize().mult(p.random(2, 4)); const direction = p5.Vector.add(particle.location, arrow); p.line(particle.location.x, particle.location.y, direction.x, direction.y); }; const move = () => { particle.applyForce(p.createVector(p.random(-.2, .2), p.random(-0.1, -0.4))); particle.velocity.add(particle.acc); particle.location.add(particle.velocity.copy().normalize().mult(p.random(2, 4))); particle.acc.mult(0); fade -= 5; }; return { ...particle, draw, move }; } const createParticle = (locationP, velocity) => { const acc = p.createVector(0, 0); const location = locationP.copy(); let fade = 255; const fadeMinus = p.randomGaussian(15, 2); let ligther = 100; let situate = 62; const draw = () => { p.colorMode(p.HSB) p.stroke(16, p.constrain(situate, 62, 92), p.constrain(ligther, 60, 100), fade); const arrow = velocity.copy().mult(2); const direction = p5.Vector.add(location, arrow); p.line(location.x, location.y, direction.x, direction.y); }; const move = () => { velocity.add(acc); location.add(velocity.copy().div(p.map(velocity.mag(), 18, 0, 5, 1))); acc.mult(0); fade -= fadeMinus; ligther -= 8; situate += 8; }; const applyForce = force => { acc.add(force); }; const isDead = () => { return fade < 0 || location.x < 0 || location.x > p.width || location.y > p.height; }; return { draw, move, applyForce, isDead, velocity, location, acc }; }; const createMover = () => { const location = p.createVector(p.width / 2, p.height / 2); const velocity = p.createVector(0, 0); const acc = p.createVector(0, 0); let angle = 0; let angleVelocity = 0; let angleAcc = 0; const particleSystems = Array(9).fill().map(() => createParticleSystem(location)); const getGotoVector = angle => { const radius = p.map(angleVelocity, 0, 0.3, 0, 55); return p.createVector( location.x + radius * p.cos(angle), location.y + radius * p.sin(angle) ); }; const draw = () => { particleSystems.forEach(ps => ps.run()); }; const renderParticleSystem = () => { particleSystems.forEach(ps => { const goToVector = getGotoVector(angle - p.random(0, p.TWO_PI)); const prepencular = p.createVector( (goToVector.y - location.y)*-1, (goToVector.x - location.x) ); prepencular.normalize().mult(angleVelocity * 70); ps.origin.set(goToVector); ps.addParticle(prepencular); ps.applyForce(p.createVector(0, 0.3)); }); }; const move = () => { angleAcc = 0.005; angleVelocity = p.constrain(angleVelocity + angleAcc, 0, 0.32); angle += angleVelocity; angleAcc = 0; renderParticleSystem(); }; return { draw, move }; }; let mover; p.setup = function() { p.createCanvas(240, 320); p.clear(); mover = createMover(); } p.draw = function() { p.clear(); mover.move(); mover.draw(); } }; p5Instance = new p5(sketch); } // 停止动画 function stopAnimation() { if (p5Instance) { p5Instance.remove(); p5Instance = null; } } </script>
修改方案
1. 传递卡牌圆圈的位置与尺寸信息
在卡牌点击事件中,获取背面彩色圆圈的中心坐标和半径,传入动画启动函数:
// 修改卡牌点击逻辑中的startAnimation调用 if (!card.hasClass("flipped")) { flipSound.currentTime = 0; flipSound.play(); // 获取圆圈元素(需确保卡牌背面圆圈有对应类名,比如card__back-circle) const circle = card.find('.card__back-circle')[0]; const rect = circle.getBoundingClientRect(); const centerX = rect.left + rect.width / 2; const centerY = rect.top + rect.height / 2; const radius = rect.width / 2; // 传入参数启动动画 startAnimation(centerX, centerY, radius); console.log("started") card.addClass("flipped"); }
2. 调整p5动画的定位与尺寸
修改startAnimation函数,接收位置参数并调整画布定位和粒子生成逻辑:
function startAnimation(centerX, centerY, radius) { const sketch = (p) => { // 保留原有粒子系统、粒子创建等代码不变 // 修改createMover,接收中心坐标和半径 const createMover = (moverCenterX, moverCenterY, circleRadius) => { const location = p.createVector(moverCenterX, moverCenterY); // ... 保留其余逻辑 ... const getGotoVector = angle => { // 使用传入的圆圈半径替代固定值 return p.createVector( location.x + circleRadius * p.cos(angle), location.y + circleRadius * p.sin(angle) ); }; // ... 保留其余逻辑 ... }; let mover; p.setup = function() { // 创建与圆圈等大的画布 p.createCanvas(radius * 2, radius * 2); // 设置画布绝对定位到圆圈位置 p.canvas.style.position = 'absolute'; p.canvas.style.left = `${centerX - radius}px`; p.canvas.style.top = `${centerY - radius}px`; p.canvas.style.pointerEvents = 'none'; // 不干扰卡牌点击 p.clear(); // 以画布中心作为粒子生成中心 mover = createMover(p.width/2, p.height/2, radius); } // 保留原有draw函数不变 p.draw = function() { p.clear(); mover.move(); mover.draw(); } }; p5Instance = new p5(sketch); }
3. 适配背景图式圆圈
如果卡牌背面的圆圈是背景图片的一部分而非独立DOM元素,需手动计算其相对卡牌的位置:
// 假设圆圈在卡牌内的相对位置是居中,直径为卡牌宽度的80% const cardRect = card[0].getBoundingClientRect(); const circleDiameter = cardRect.width * 0.8; const radius = circleDiameter / 2; const centerX = cardRect.left + cardRect.width / 2; const centerY = cardRect.top + cardRect.height / 2;
内容的提问来源于stack exchange,提问作者user14178341
相关产品推荐
相关产品推荐

