问题
I've recently started working on a website and learning HTML/CCS/JS and came across and idea that I'm unsure of how to execute. Basically I want the floating text that currently moves around and bounces off the window borders to bounce off one another as well. I also figured that an array might be good to be able to set how many text objects I want to spawn. Here is my site for reference of how the text bounces around http://gmtrash.ga/
And here Is the JavaScript controlling the text objects and coloration
setInterval(function() {
myContainer = document.getElementById("colortext");
displayRandomColor();
function getRandomColor() {
r = Math.floor(Math.random() * 256);
g = Math.floor(Math.random() * 256);
b = Math.floor(Math.random() * 256);
hexR = r.toString(16);
hexG = g.toString(16);
hexB = b.toString(16);
if (hexR.length == 1) {
hexR = "0" + hexR;
}
if (hexG.length == 1) {
hexG = "0" + hexG;
}
if (hexB.length == 1) {
hexB = "0" + hexB;
}
hexColor = "#" + hexR + hexG + hexB;
return hexColor.toUpperCase();
}
function displayRandomColor() {
myRandomColor = getRandomColor();
}
}, 450);
myRandomColor = 000000;
var context;
var x = Math.floor(Math.random() * window.outerWidth);
var y = Math.floor(Math.random() * window.outerHeight);
var x1 = Math.floor(Math.random() * window.outerWidth);
var y1 = Math.floor(Math.random() * window.outerHeight);
var x2 = Math.floor(Math.random() * window.outerWidth);
var y2 = Math.floor(Math.random() * window.outerHeight);
var x3 = Math.floor(Math.random() * window.outerWidth);
var y3 = Math.floor(Math.random() * window.outerHeight);
var x4 = Math.floor(Math.random() * window.outerWidth);
var y4 = Math.floor(Math.random() * window.outerHeight);
var x5 = Math.floor(Math.random() * window.outerWidth);
var y5 = Math.floor(Math.random() * window.outerHeight);
var x6 = Math.floor(Math.random() * window.outerWidth);
var y6 = Math.floor(Math.random() * window.outerHeight);
var x7 = Math.floor(Math.random() * window.outerWidth);
var y7 = Math.floor(Math.random() * window.outerHeight);
var x8 = Math.floor(Math.random() * window.outerWidth);
var y8 = Math.floor(Math.random() * window.outerHeight);
var x9 = Math.floor(Math.random() * window.outerWidth);
var y9 = Math.floor(Math.random() * window.outerHeight);
var dx = Math.floor(Math.random() * 15);
var dy = Math.floor(Math.random() * 15);
var dx1 = Math.floor(Math.random() * 15);
var dy1 = Math.floor(Math.random() * 15);
var dx2 = Math.floor(Math.random() * 15);
var dy2 = Math.floor(Math.random() * 15);
var dx3 = Math.floor(Math.random() * 15);
var dy3 = Math.floor(Math.random() * 15);
var dx4 = Math.floor(Math.random() * 15);
var dy4 = Math.floor(Math.random() * 15);
var dx5 = Math.floor(Math.random() * 15);
var dy5 = Math.floor(Math.random() * 15);
var dx6 = Math.floor(Math.random() * 15);
var dy6 = Math.floor(Math.random() * 15);
var dx7 = Math.floor(Math.random() * 15);
var dy7 = Math.floor(Math.random() * 15);
var dx8 = Math.floor(Math.random() * 15);
var dy8 = Math.floor(Math.random() * 15);
var dx9 = Math.floor(Math.random() * 15);
var dy9 = Math.floor(Math.random() * 15);
function init() {
context = myCanvas.getContext('2d');
setInterval(draw, 10);
}
function draw() {
context.clearRect(0, 0, window.outerWidth, window.outerHeight);
context.beginPath();
context.fillStyle = myRandomColor;
context.font = "64px fixedsys";
context.fillText("nerd", x, y);
context.closePath();
context.beginPath();
context.fillStyle = myRandomColor;
context.font = "64px fixedsys";
context.fillText("nerd", x1, y1);
context.closePath();
context.fill();
context.beginPath();
context.fillStyle = myRandomColor;
context.font = "64px fixedsys";
context.fillText("nerd", x2, y2);
context.closePath();
context.beginPath();
context.fillStyle = myRandomColor;
context.font = "64px fixedsys";
context.fillText("nerd", x3, y3);
context.closePath();
context.beginPath();
context.fillStyle = myRandomColor;
context.font = "64px fixedsys";
context.fillText("nerd", x4, y4);
context.closePath();
context.fill();
context.beginPath();
context.fillStyle = myRandomColor;
context.font = "64px fixedsys";
context.fillText("nerd", x5, y5);
context.closePath();
context.beginPath();
context.fillStyle = myRandomColor;
context.font = "64px fixedsys";
context.fillText("nerd", x6, y6);
context.closePath();
context.beginPath();
context.fillStyle = myRandomColor;
context.font = "64px fixedsys";
context.fillText("nerd", x7, y7);
context.closePath();
context.fill();
context.beginPath();
context.fillStyle = myRandomColor;
context.font = "64px fixedsys";
context.fillText("nerd", x8, y8);
context.closePath();
context.beginPath();
context.fillStyle = myRandomColor;
context.font = "64px fixedsys";
context.fillText("nerd", x9, y9);
context.closePath();
if (x < 0 || x > window.outerWidth) dx = -dx;
if (y < 0 || y > window.outerHeight) dy = -dy;
x += dx;
y += dy;
if (x1 < 0 || x1 > window.outerWidth) dx1 = -dx1;
if (y1 < 0 || y1 > window.outerHeight) dy1 = -dy1;
x1 += dx1;
y1 += dy1;
if (x2 < 0 || x2 > window.outerWidth) dx2 = -dx2;
if (y2 < 0 || y2 > window.outerHeight) dy2 = -dy2;
x2 += dx2;
y2 += dy2;
if (x3 < 0 || x3 > window.outerWidth) dx3 = -dx3;
if (y3 < 0 || y3 > window.outerHeight) dy3 = -dy3;
x3 += dx3;
y3 += dy3;
if (x4 < 0 || x4 > window.outerWidth) dx4 = -dx4;
if (y4 < 0 || y4 > window.outerHeight) dy4 = -dy4;
x4 += dx4;
y4 += dy4;
if (x5 < 0 || x5 > window.outerWidth) dx5 = -dx5;
if (y5 < 0 || y5 > window.outerHeight) dy5 = -dy5;
x5 += dx5;
y5 += dy5;
if (x6 < 0 || x6 > window.outerWidth) dx6 = -dx6;
if (y6 < 0 || y6 > window.outerHeight) dy6 = -dy6;
x6 += dx6;
y6 += dy6;
if (x7 < 0 || x7 > window.outerWidth) dx7 = -dx7;
if (y7 < 0 || y7 > window.outerHeight) dy7 = -dy7;
x7 += dx7;
y7 += dy7;
if (x8 < 0 || x8 > window.outerWidth) dx8 = -dx8;
if (y8 < 0 || y8 > window.outerHeight) dy8 = -dy8;
x8 += dx8;
y8 += dy8;
if (x9 < 0 || x9 > window.outerWidth) dx9 = -dx9;
if (y9 < 0 || y9 > window.outerHeight) dy9 = -dy9;
x9 += dx9;
y9 += dy9;
}
<html>
<body onLoad="init();"> <canvas id="myCanvas" style='position: absolute; left: 0px; top: 0px;'>
</canvas>
<div id="colortext">
</div>
<script src="scripts/suchcolor.js"></script>
<script>
(function() {
var htmlCanvas = document.getElementById('myCanvas'),
context = htmlCanvas.getContext('2d');
initialize();
function initialize() {
window.addEventListener('resize', resizeCanvas, false);
resizeCanvas();
}
function redraw() {
context.strokeStyle = 'blue';
context.lineWidth = '5';
context.strokeRect(0, 0, window.innerWidth, window.innerHeight);
}
function resizeCanvas() {
htmlCanvas.width = window.innerWidth;
htmlCanvas.height = window.innerHeight;
redraw();
}
})();
</script>
</body>
</html>
now how would I go about this?
回答1:
You can massively improve your code by introducing arrays and loops. Create an empty array var boxes = []
and put boxes into it. Each box consists of a position, dimensions and speed, e.g.:
var box = {x: 0, y: 0, width: 10, height: 10, dx: 1, dy: 1};
boxes.push(box);
You can then iterate over all boxes by using a for-loop:
for (var i = 0; i < boxes.length; i++) {
var box = boxes[i];
// Do something with the i-th box...
}
Handling box-box collisions can be done in a few lines of code, but it won't be very robust. I.e. you can only update box positions by a few pixels each round, before detecting box overlap and resolving the collision by directly updating box velocities. It may take a few frames before multi-box collisions are resolved and boxes can visibly overlap during that time. For more robust physics which handle stacking, forces between objects and have a better integration mechanism, you should look for a tested 2D box physics library.
For physic simulations as well as animations, timing is very important. For animations, you should use requestAnimationFrame instead of setInterval
. Since both methods don't guarantee constant time steps, you need to compute the time dt
passed between two physic updates and integrate (= multiply) velocities over that time step dt
to get the new positions.
Reorganized code with "poor man's physics":
// Return random RGB color string:
function randomColor() {
var hex = Math.floor(Math.random() * 0x1000000).toString(16);
return "#" + ("000000" + hex).slice(-6);
}
// Poor man's box physics update for time step dt:
function doPhysics(boxes, width, height, dt) {
for (let i = 0; i < boxes.length; i++) {
var box = boxes[i];
// Update positions:
box.x += box.dx * dt;
box.y += box.dy * dt;
// Handle boundary collisions:
if (box.x < 0) {
box.x = 0;
box.dx = -box.dx;
} else if (box.x + box.width > width) {
box.x = width - box.width;
box.dx = -box.dx;
}
if (box.y < 0) {
box.y = 0;
box.dy = -box.dy;
} else if (box.y + box.height > height) {
box.y = height - box.height;
box.dy = -box.dy;
}
}
// Handle box collisions:
for (let i = 0; i < boxes.length; i++) {
for (let j = i + 1; j < boxes.length; j++) {
var box1 = boxes[i];
var box2 = boxes[j];
var dx = Math.abs(box1.x - box2.x);
var dy = Math.abs(box1.y - box2.y);
// Check for overlap:
if (2 * dx < (box1.width + box2.width ) &&
2 * dy < (box1.height + box2.height)) {
// Swap dx if moving towards each other:
if ((box1.x > box2.x) == (box1.dx < box2.dx)) {
var swap = box1.dx;
box1.dx = box2.dx;
box2.dx = swap;
}
// Swap dy if moving towards each other:
if ((box1.y > box2.y) == (box1.dy < box2.dy)) {
var swap = box1.dy;
box1.dy = box2.dy;
box2.dy = swap;
}
}
}
}
}
var canvas = document.getElementById("canvas");
var context = canvas.getContext("2d");
// Initialize random boxes:
var boxes = [];
for (var i = 0; i < 10; i++) {
var box = {
x: Math.floor(Math.random() * canvas.width),
y: Math.floor(Math.random() * canvas.height),
width: 50,
height: 20,
dx: (Math.random() - 0.5) * 0.2,
dy: (Math.random() - 0.5) * 0.2
};
boxes.push(box);
}
// Initialize random color and set up interval:
var color = randomColor();
setInterval(function() {
color = randomColor();
}, 450);
// Update physics at fixed rate:
var last = performance.now();
setInterval(function(time) {
var now = performance.now();
doPhysics(boxes, canvas.width, canvas.height, now - last);
last = now;
}, 50);
// Draw animation frames at optimal frame rate:
function draw(now) {
context.clearRect(0, 0, canvas.width, canvas.height);
for (let i = 0; i < boxes.length; i++) {
var box = boxes[i];
// Interpolate position:
var x = box.x + box.dx * (now - last);
var y = box.y + box.dy * (now - last);
context.beginPath();
context.fillStyle = color;
context.font = "20px fixedsys";
context.textBaseline = "hanging";
context.fillText("nerd", x, y);
context.closePath();
}
requestAnimationFrame(draw);
}
requestAnimationFrame(draw);
<canvas id="canvas"></canvas>
回答2:
Your code is a mess, and there is no way you can make progress if you keep it that way. Let's organize it a bit.
function textObject(color, width, height){
this.color = color;
this.x = Math.floor(Math.random()*width);
this.y = Math.floor(Math.random()*height);
this.dx = Math.floor(Math.random()*15);
this.dy = Math.floor(Math.random()*15);
this.show = function(context){
context.beginPath();
context.fillStyle = this.color;
context.font = "64px fixedsys";
context.fillText("nerd", this.x, this.y);
context.closePath();
}
this.move = function(){
this.x += dx;
this.y += dy;
}
}
Ok so what was all this fuss about? What have we achieved here? Well with this, we represent every "nerd"
word as an entity, with its own properties and behaviors. Now instead of writing a buttload of boring repetitive code we can do this:
var words = [];
for(var i=0; i<10; i++){
words.push(new textObject(getRandomColor(), window.outerWidth, window.outerHeight));
}
And just like that we have 10 text objects, with their own colors, positions and "velocities". And best of all? We can indeed add them to an array (which is exactly what we did as shown above)!
Now let's group the collision detection code in one function all together:
function collisionDetection(arr){
//checking for collision with the window boundaries
for(var i=0; i<arr.length; i++){
if(arr[i].x<0 || arr[i].x>window.outerWidth){
arr[i].dx = - arr[i].dx;
}
if(arr[i].y<0 || arr[i].y>window.outerHeight){
arr[i].dy = -arr[i].dy;
}
}
}
Alright, so now we are set and ready to go. After you put the textObject function in a seperate javascript file and include it in your html document with a script tag, add the rest of the code to where your current code is (thus replacing it). Now your draw
function should be:
function draw(){
context.clearRect(0, 0, window.outerWidth, window.outerHeight);
for(var i=0; i<words.length; i++){
words[i].show(context);
words[i].move();
}
collisionDetection();
}
By now you should have noticed that I haven't given you a straight answer on your main question, about the objects colliding with each other. My advice would be to refrain from doing so, since you are spending quite a bit of computational power for each frame to calculate the collisions.
Considering you know the length and height of the word object in advance (which you should find out by yourself), you could add the following approach to your collisionDetection
function:
for(i=0; i<arr.length; i++){
for(var j=0; j<arr.length; j++){
if(i!=j){
if(Math.abs(arr[i].x-arr[j].x) <= length){
arr[i].dx = -arr[i].dx;
arr[j].dx = -arr[j].dx;
}
if(Math.abs(arr[i].y-arr[j].y) <= height){
arr[i].dy = -arr[i].dy;
arr[j].dy = -arr[j].dy;
}
}
}
}
This piece of code checks every object of your list for collision with every other object of this list. This is certainly NOT a very good way to do this, and you should change the number of text objects if you plan to, to decrease the amount of calculations necessary.
I should probably conclude with a warning: Study this code, and study object oriented programming before you proceed (a very useful concept to say the least). I have not tested this, and perhaps there are a few errors. However i trust that you have enough knowledge to understand what it does, and use it to improve your site :)
P.S. welcome to stack overflow
来源:https://stackoverflow.com/questions/43726536/making-canvas-text-objects-bounce-off-of-each-other-and-be-created-by-an-array