我正在尝试制作一个粒子爆炸系统,每个粒子都在这里 直接远离中心点爆炸。但我得到了一些非常时髦的行为。
我正在使用var hrefs = document.querySelectorAll("a[href]");
for (let elem of hrefs) {
// do stuff
console.log(elem.href);
}
来计算到中心点的方向,然后通过从角度减去180度来反转方向。
这是我处理粒子的其余代码:
Math.atan2(y2 - y1, x2 - x1) * 180 / Math.PI;
严重的是无法想出这个。非常感谢你们的帮助!
编辑:其余代码
for ( var i = 0; i < le.length; i ++ ) {
// Create new particle element and append it to document.
lObj[i] = new Particle(0, 0, 9, 0, 0, document.createElement('span'));
lObj[i].spanEl.innerHTML = le[i];
lObj[i].spanEl.style.position = 'relative';
p.appendChild(lObj[i].spanEl);
// Find location of this particle.
loc[i] = lObj[i].spanEl.getBoundingClientRect();
// Calculate direction toward center point and reverse away from it.
lObj[i].direction = directionToPoint( loc[i].left, loc[i].top, centX, centY) - 180;
}
var x = 0,
y = 0,
vel,
dir;
function loop() {
for (var i = 0; i < le.length; i ++ ) {
// Update location of each particle
x = lObj[i].relX;
y = lObj[i].relY;
vel = lObj[i].velocity;
dir = lObj[i].direction;
dir = lObj[i].direction;
x += vel * Math.cos( dir * Math.PI / 180 );
y += vel * Math.sin( dir * Math.PI / 180 );
vel = (vel > 0) * ( vel - 0.2 );
lObj[i].relX = x;
lObj[i].relY = y;
lObj[i].velocity = vel;
lObj[i].spanEl.style.left = x + 'px';
lObj[i].spanEl.style.top = y + 'px';
}
}
答案 0 :(得分:0)
除了上面的评论,这里是一个利用2d向量的实现不佳的粒子系统。我使用for循环攻击了一个'动画' - 显然,我应该使用window.requestAnimationFrame
(https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame)但这超出了本例的范围。
要初始化每个粒子的位置和方向,我会执行以下操作。
function byId(id){return document.getElementById(id)}
window.addEventListener('load', onDocLoaded, false);
var partArray = [], maxParticles=500;
function onDocLoaded(evt)
{
for (var i=0; i<maxParticles; i++)
{
var pos = new vec2_t( Math.random()-0.5, Math.random()-0.5 ); // give them a poition of [-0.5..0.5]
var vel = pos.normalize();
vel.scalarMult( Math.random() * 10 ); // give them a velocity of [0..10]
var maxAge = (Math.random() * 25) + 25; // age is in range [25..50]
var newParticle = new part_t(pos, vel, maxAge); // create the particle
partArray.push( newParticle ); // and put it in our array
}
for (var y=0; y<5; y++)
{
drawParticles();
moveParticles();
}
}
function vec2_t(x,y)
{
this.x = x;
this.y = y;
this.normalize = function()
{
var result = new vec2_t(0,0);
var lenSq = (this.x*this.x) + (this.y*this.y);
var len = Math.sqrt(lenSq);
result.x = this.x / len;
result.y = this.y / len;
return result;
}
this.scalarMult = function(scalar)
{
this.x *= scalar;
this.y *= scalar;
}
return this;
}
function part_t(position, velocity, maxAge)
{
this.position = position;
this.velocity = velocity;
this.maxAge = maxAge;
this.age = 0;
return this;
}
function setPixel(x,y,ctx)
{
var imgData = ctx.getImageData(x,y,1,1);
imgData.data[ 0 ] = 255;
imgData.data[ 1 ] = 0;
imgData.data[ 2 ] = 0;
imgData.data[ 3 ] = 255;
ctx.putImageData(imgData,x,y);
// console.log(x+','+y);
}
function drawParticles()
{
var can = byId('partSysCanvas');
var ctx = can.getContext('2d');
var partNum;
for (partNum=0; partNum<maxParticles; partNum++)
{
// add 256,256 since this is canvas.width/2,canvas.height/2
setPixel( 256+partArray[partNum].position.x, 256+partArray[partNum].position.y, ctx);
}
}
function moveParticles()
{
for (var i=0; i<maxParticles; i++)
{
if (partArray[i].age < partArray[i].maxAge)
{
partArray[i].age++;
partArray[i].position.x += partArray[i].velocity.x;
partArray[i].position.y += partArray[i].velocity.y;
}
}
}
<canvas width=512 height=512 id='partSysCanvas'></canvas>
答案 1 :(得分:0)
这很有趣。我再次避免使用三角学,而不是使用矢量。这些字母不会从中心爆炸 - 文字被绘制出来,然后字母从这一点爆炸,远离爆炸的位置。我使用vel.scalarMult( 10 );
为所有粒子设置了均匀的爆炸速度,您可以将其随机化,而不是上面的线。
我也不愿意根据它与炸弹的接近程度来缩放粒子速度。一切都从座位上爆炸出来,在那里我们知道爆炸所感受到的力随着距离的增加而减少。
这是一个可以使用的工作演示。
我真的应该使用requestAnimationFrame
"use strict";
function newEl(tag){return document.createElement(tag)}
function byId(id){return document.getElementById(id)}
// useful for HtmlCollection, NodeList, String types (array-like objects without the forEach method)
function forEach(array, callback, scope){for (var i=0,n=array.length; i<n; i++)callback.call(scope, array[i], i, array);} // passes back stuff we need
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function vec2_t(x,y)
{
this.x=x;
this.y=y;
this.equals = function(vec2){this.x = vec2.x; this.y = vec2.y;}
this.addVec = function(vec2){this.x += vec2.x; this.y += vec2.y;}
this.scalarMult = function(scalar){this.x *= scalar; this.y *= scalar;}
this.vecLen = function(){return Math.sqrt( this.x*this.x + this.y*this.y );}
this.normalize = function(){ let k = 1.0 / this.vecLen(); this.scalarMult(k); }
this.vecSub = function(vec2){this.x-=vec2.x;this.y-=vec2.y;}
this.toString = function(){return"<"+this.x+","+this.y+">"}
return this;
}
function part_t(vec2pos, vec2vel, domElem)
{
this.pos = vec2pos;
this.vel = vec2vel;
this.domElem = domElem;
return this;
}
var particleArray, timerId;
let explosionOrigin = new vec2_t(156,110);
window.addEventListener('load', onDocLoaded, false);
function onDocLoaded(evt)
{
particleArray = createPartSys('textInput', 'tgtContainer');
byId('stepBtn').addEventListener('click', onStepBtnClick);
byId('resetBtn').addEventListener('click', onResetBtnClick);
byId('animateBtn').addEventListener('click', onAnimateBtnClick);
byId('pauseBtn').addEventListener('click', onPauseBtnClick);
byId('tgtContainer').addEventListener('click', onClick);
}
function onStepBtnClick(evt)
{
updatePartSys(particleArray);
}
function onAnimateBtnClick(evt)
{
timerId = setInterval(function(){updatePartSys(particleArray);}, 100);
this.setAttribute('disabled', 'true');
byId('pauseBtn').removeAttribute('disabled');
}
function onPauseBtnClick(evt)
{
clearInterval(timerId);
this.setAttribute('disabled', 'true');
byId('animateBtn').removeAttribute('disabled');
}
function onResetBtnClick(evt)
{
var bombImg = byId('bomb');
byId('tgtContainer').innerHTML = '';
byId('tgtContainer').appendChild(bombImg);
particleArray = createPartSys('textInput', 'tgtContainer');
byId('animateBtn').removeAttribute('disabled');
clearInterval(timerId);
}
function createPartSys(srcElemId, tgtElemId)
{
let elem = byId(srcElemId);
var str = elem.value, len=str.length;
let result = [];
let parent = elem;
let curX = elem.offsetLeft - parent.offsetLeft;
let curY = elem.offsetTop - parent.offsetTop;
let bombImg = byId('bomb');
bombImg.style = 'position: absolute';
bombImg.style.left = (explosionOrigin.x - (bombImg.clientWidth/2))+'px';
bombImg.style.top = (explosionOrigin.y - (bombImg.clientHeight/2))+'px';
byId(tgtElemId).appendChild(bombImg);
curY = 50;
curX = 50;
forEach(str,
function(letter)
{
var span = newEl('span');
span.className = 'particle';
if (letter == ' ') letter = ' '
let h1 = newEl('h1');
h1.innerHTML = letter;
span.appendChild(h1);
span.style.left = curX + 'px';
span.style.top = curY + 'px';
byId(tgtElemId).appendChild(span);
var pos = new vec2_t(curX,curY);
curX += span.offsetWidth;
var vel = new vec2_t(0,0);
let letterOrigin = getCenter(span);
vel.equals(letterOrigin);
vel.vecSub(explosionOrigin);
vel.normalize();
// vel.scalarMult( (Math.random()*1) + 4 );
vel.scalarMult( 10 );
var newPart = new part_t(pos,vel,span);
result.push(newPart);
}
);
return result;
}
function updatePartSys(partSys)
{
forEach(
partSys,
function(part, index, array)
{
part.pos.addVec(part.vel); // position += velocity
var gravity = new vec2_t(0,0.98/3); // arbitrary value chosen
part.vel.scalarMult(0.95); // velocity *= 0.95 - needs to be quite high. it simulates wind resistance
part.vel.addVec(gravity); // velocity += gravity
part.domElem.style.left = part.pos.x + "px";
part.domElem.style.top = part.pos.y + "px";
}
);
}
function onClick(evt)
{
let elemRect = byId('tgtContainer').getBoundingClientRect();
let posX = evt.clientX - elemRect.left, posY = evt.clientY-elemRect.top;
explosionOrigin.x = posX;
explosionOrigin.y = posY;
onResetBtnClick();
}
function getCenter(elem)
{
let x = elem.offsetLeft + (elem.offsetWidth/2);
let y = elem.offsetTop + (elem.offsetHeight/2);
let result = new vec2_t(x,y);
return result;
}
#tgtContainer
{
position: relative;
height: 256px;
width: 512px;
border: solid 1px black;
overflow: hidden;
background-color: white;
}
.particle
{
display: inline-block;
position: absolute;
}
.panel
{
display: inline-block;
border: solid 1px #113;
border-radius: 8px;
margin: 8px;
padding: 8px;
background-image: url(https://www.gravatar.com/avatar/97c2d181ef6bbb9eee0c4033561c3891?s=48&d=identicon&r=PG);
background-size: 100% 100%;
}
#textContainer
{
display: block;
}
#textContainer textarea
{
width: 100%;
padding: 0;
margin: 1px 0px;
}
<div class='panel'>
<div id='textContainer'><textarea id='textInput'>click to set bomb position</textarea></div>
<hr>
<button id='resetBtn'>Reset</button><button id='stepBtn'>Single Step</button> | <button id='animateBtn'>Animate</button><button id='pauseBtn' disabled>Pause</button>
<hr>
<div id='tgtContainer'>
</div>
</div>
<svg xmlns="http://www.w3.org/2000/svg" height="32" width="32" id='bomb'>
<g transform="translate(0,-1020.3622)">
<path d="m23.23,15.84a10.55,10.55,0,1,1,-21.11,0,10.55,10.55,0,1,1,21.11,0z" transform="matrix(1.1875635,0,0,1.1875635,0.68612298,1020.367)" fill="#26201e"/>
<path d="m23.23,15.84a10.55,10.55,0,1,1,-21.11,0,10.55,10.55,0,1,1,21.11,0z" transform="matrix(0.86603158,0,0,0.86603158,2.4299747,1024.1874)" fill="#333"/>
<path d="m-13.04,19.32a1.964,1.964,0,1,1,-3.929,0,1.964,1.964,0,1,1,3.929,0z" transform="matrix(1.924285,1.1058108,-1.1908732,2.0723069,62.314757,1012.6494)" fill="#CCC"/>
<path d="m15.69,1026c0.02518-5.037,7.647-7.396,8.907-2.969,0.7936,2.761,1.349,5.666,4.877,6.786" stroke="#888" stroke-width="1.5px" fill="none"/>
<rect height="2.399" width="4.798" y="1026" x="13.31" stroke-width="0" fill="#26201e"/>
<path fill="#F00" transform="translate(2.0203051,1022.13)" d="M29.8,10.53,27.1,9.62,24.82,11.32,24.86,8.477,22.54,6.833,25.25,5.989,26.1,3.271,27.74,5.595,30.59,5.558,28.89,7.839z"/>
</g>
</svg>