I am making a Physics engine that utilizes JavaScript to create boxes that have the properties of physics applied to them in scale of the window size. However, the class called box that contains all the physical properties of these boxes cannot be found by the function document.getElementByClassName("box").
I am trying to assign a variable boxelem to contain the location properties of each individual box so that I can integrate mouse manipulation into my program in the future. I have tried doing this through the code:
var boxelem = document.getElementsByClassName("box");
and then adding a mouse over event listener to boxelem.
Necessary Code:
var canvas;
var ctx;
var box = [];
var boxelem;
//Startup Code
window.onload = function(e) {
canvas = document.getElementById("c");
ctx = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
box = [new box(10, 20, "cyan"), new box(299, 40, "red"), new box(90, 50,
"black"), new box(29, 20, "turquoise")];
boxelem = document.getElementsByClassName("box");
noscroll();
update();
}
//Physic Clock (For real-time physics)
var clock = {
lasttick: 0,
tick: function() {
var td = performance.now() - this.lasttick;
this.lasttick = performance.now();
return td / 1000;
},
reset: function() {
}
}
//Box objects be created here (Box Class)
var box = function(x, y, color) {
var _this = this;
//First Spawn Physics and Settings
_this.x = Math.random() * (canvas.width - 50);
_this.y = Math.random() * (canvas.height - 50);
_this.vx = 0;
_this.vy = Math.random() * 150;
_this.ax = 0;
_this.ay = 440;
_this.color = color;
_this.draw = function() {
ctx.fillStyle = _this.color;
ctx.fillRect(_this.x, _this.y, 50, 50);
}
//Normal Physics
_this.update = function(t, mX, mY) {
if (mX != 0) {
_this.x += _this.vx * t;
_this.vx += mX * t;
} else {
_this.x += _this.vx * t;
_this.vx += _this.ax * t;
}
_this.y += _this.vy * t;
_this.vy += _this.ay * t;
//Boundary Creation
if ((_this.y + 55) > canvas.height) {
_this.vy -= _this.ay * t;
_this.vy = 0;
}
if ((_this.x + 55) > canvas.width) {
_this.vx -= _this.ax * t;
_this.vx = 0;
}
if ((_this.y) < 0) {
_this.vy -= _this.ay * t;
_this.vy = 0;
_this.y = (canvas.height + 20);
}
if ((_this.x) < 0) {
_this.vx -= _this.ax * t;
_this.vx = 0;
}
}
}
//Get mouse position if over a box
var pageX = 0;
var pageY = 0;
for (var z = 0; z < box.length; z++) {
boxelem.addEventListener("mouse", mPos, false);
}
The event listener at the bottom gives an error because boxelem is not defined due to the elements not being found by getElementsByClassName.
HTML Code:
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<script type=text/javascript src="Physics.js"></script>
<meta charset="utf-8" />
<title>Physics Engine of Boxes</title>
</head>
<body>
<canvas id="c"></canvas>
</body>
</html>
I have looked at (Unobtrusive Javascript-Basic Implementation: How do I bind all elements of a particular class to a function in Javascript?) and (Change an element's class with JavaScript) but am unsure how to apply it to my issue.
This body of your loop is not accessing the index. Also, the .length you're using is from the box function instead of the boxelem collection.
This:
for (var z = 0; z < box.length; z++)
{
boxelem.addEventListener("mouse", mPos, false);
}
should be this:
for (var z = 0; z < boxelem.length; z++)
{
boxelem[z].addEventListener("mouse", mPos, false);
}
As D Simon pointed out below, boxelem is undefined because you're not populating that variable until after the DOM loads. You should be sure to fetch those items before trying to use that variable.
If you move all the code to window.onload, it should work. Note that window.onload doesn't run until all resources are loaded. You may want to use a different event that fires sooner, but that's another topic.
//Startup Code
window.onload = function(e) {
var canvas = document.getElementById("c");
var ctx = canvas.getContext("2d");
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
var box = [new box(10, 20, "cyan"), new box(299, 40, "red"), new box(90, 50,
"black"), new box(29, 20, "turquoise")];
var boxelem = document.getElementsByClassName("box");
noscroll();
update();
//Physic Clock (For real-time physics)
var clock = {
lasttick: 0,
tick: function() {
var td = performance.now() - this.lasttick;
this.lasttick = performance.now();
return td / 1000;
},
reset: function() {
}
}
//Box objects be created here (Box Class)
var box = function(x, y, color) {
// ...implementation...
}
//Get mouse position if over a box
var pageX = 0;
var pageY = 0;
for (var z = 0; z < boxelem.length; z++) {
boxelem[z].addEventListener("mouse", mPos, false);
}
}
Related
So I've been reading this post which I found is the closest to what I want. Basically, I want two different text elements (two different words) moving around the page randomly and also bouncing off each other when they come in contact.
I have tried to edit the code so that it's black and have only one text element. However how can I also include a second text element (a different word) moving randomly as well?
Also how can I change it to Roboto Mono font?
// Return random RGB color string:
function randomColor() {
var hex = Math.floor(Math.random() * 0x1000000).toString(16);
return "#" + ("000000" + hex).slice(0);
}
// 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 < 1; 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.3,
dy: (Math.random() - 0.5) * 0.3
};
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 = "40px 'Roboto Mono'";
context.textBaseline = "hanging";
context.fillText("motion", x, y);
context.closePath();
}
requestAnimationFrame(draw);
}
requestAnimationFrame(draw);
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
<!DOCTYPE html>
<html>
<body>
<head>
<link rel="stylesheet" href="test.css">
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title></title>
</head>
<canvas id="canvas"></canvas>
<script src="test.js"></script>
</body>
</html>
Multiple text boxes is easy :
Change the initialize boxes part to include more than one.
// Initialize random boxes:
var boxes = [];
var boxCount = 2
for (var i = 0; i < boxCount; i++) {
Second your random color is flawed , try this :
function randomColor() {
var hex = Math.floor(Math.random() * 0x1000000).toString(16);
return "#" + ("000000" + hex).substr(hex.length);
}
I don't think that roboto is available in the canvas element. (I could be wrong)
monospace is available though.
Edit
If you want different words you could use another array for them:
var words = ['motion','designer']
for (var i = 0; i < words.length; i++) {
as you create boxes use the words array.
You can't hard code the size of the box if you want have the words to collide. Height should be at least the font height 40px and if you want to get the width of the text box there is a context helping function :
box.width = context.measureText(words[i]).width;
See the snippet for usage :
// Return random RGB color string:
function randomColor() {
var hex = Math.floor(Math.random() * 0x1000000).toString(16);
return "#" + ("000000" + hex).substr(hex.length);
}
// 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 = [];
var words = ["designer","motion","hi"]
for (var i = 0; i < words.length; i++) {
var box = {
x: Math.floor(Math.random() * canvas.width),
y: Math.floor(Math.random() * canvas.height),
width: 50, // Will be dynamic
height: 42,
dx: (Math.random() - 0.5) * 0.3,
dy: (Math.random() - 0.5) * 0.3
};
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);
box.width = context.measureText(words[i]).width;
context.beginPath();
context.fillStyle = color;
context.font = "normal 40px monospace";
context.textBaseline = "hanging";
context.fillText(words[i], x, y);
context.closePath();
}
requestAnimationFrame(draw);
}
requestAnimationFrame(draw);
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
<!DOCTYPE html>
<html>
<body>
<head>
<link rel="stylesheet" href="test.css">
<meta charset="utf-8">
<meta name="viewport" content="initial-scale=1">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<title></title>
</head>
<canvas id="canvas"></canvas>
<script src="test.js"></script>
</body>
</html>
I'm working through instructions to construct an interactive particle logo design and can't seem to get to the finished product. This is the logo image file -
I'm using a canvas structure / background. Here's the code -
var canvasInteractive = document.getElementById('canvas-interactive');
var canvasReference = document.getElementById('canvas-reference');
var contextInteractive = canvasInteractive.getContext('2d');
var contextReference = canvasReference.getContext('2d');
var image = document.getElementById('img');
var width = canvasInteractive.width = canvasReference.width = window.innerWidth;
var height = canvasInteractive.height = canvasReference.height = window.innerHeight;
var logoDimensions = {
x: 500,
y: 500
};
var center = {
x: width / 2,
y: height / 2
};
var logoLocation = {
x: center.x - logoDimensions.x / 2,
y: center.y - logoDimensions.y / 2
};
var mouse = {
radius: Math.pow(100, 2),
x: 0,
y: 0
};
var particleArr = [];
var particleAttributes = {
friction: 0.95,
ease: 0.19,
spacing: 6,
size: 4,
color: "#ffffff"
};
function Particle(x, y) {
this.x = this.originX = x;
this.y = this.originY = y;
this.rx = 0;
this.ry = 0;
this.vx = 0;
this.vy = 0;
this.force = 0;
this.angle = 0;
this.distance = 0;
}
Particle.prototype.update = function() {
this.rx = mouse.x - this.x;
this.ry = mouse.y - this.y;
this.distance = this.rx * this.rx + this.ry * this.ry;
this.force = -mouse.radius / this.distance;
if (this.distance < mouse.radius) {
this.angle = Math.atan2(this.ry, this.rx);
this.vx += this.force * Math.cos(this.angle);
this.vy += this.force * Math.sin(this.angle);
}
this.x += (this.vx *= particleAttributes.friction) + (this.originX - this.x) * particleAttributes.ease;
this.y += (this.vy *= particleAttributes.friction) + (this.originY - this.y) * particleAttributes.ease;
};
function init() {
contextReference.drawImage(image, logoLocation.x, logoLocation.y);
var pixels = contextReference.getImageData(0, 0, width, height).data;
var index;
for (var y = 0; y < height; y += particleAttributes.spacing) {
for (var x = 0; x < width; x += particleAttributes.spacing) {
index = (y * width + x) * 4;
if (pixels[++index] > 0) {
particleArr.push(new Particle(x, y));
}
}
}
};
init();
function update() {
for (var i = 0; i < particleArr.length; i++) {
var p = particleArr[i];
p.update();
}
};
function render() {
contextInteractive.clearRect(0, 0, width, height);
for (var i = 0; i < particleArr.length; i++) {
var p = particleArr[i];
contextInteractive.fillStyle = particleAttributes.color;
contextInteractive.fillRect(p.x, p.y, particleAttributes.size, particleAttributes.size);
}
};
function animate() {
update();
render();
requestAnimationFrame(animate);
}
animate();
document.body.addEventListener("mousemove", function(event) {
mouse.x = event.clientX;
mouse.y = event.clientY;
});
document.body.addEventListener("touchstart", function(event) {
mouse.x = event.changedTouches[0].clientX;
mouse.y = event.changedTouches[0].clientY;
}, false);
document.body.addEventListener("touchmove", function(event) {
event.preventDefault();
mouse.x = event.targetTouches[0].clientX;
mouse.y = event.targetTouches[0].clientY;
}, false);
document.body.addEventListener("touchend", function(event) {
event.preventDefault();
mouse.x = 0;
mouse.y = 0;
}, false);
html,
body {
margin: 0px;
position: relative;
background-color: #000;
}
canvas {
display: block;
position: absolute;
top: 0;
left: 0;
z-index: 1;
}
img {
display: none;
width: 70%;
height: 400px;
position: absolute;
left: 50%;
transform: translate(-50%, 30%);
}
<html>
<body>
<canvas id="canvas-interactive"></canvas>
<canvas id="canvas-reference"></canvas>
<img src="https://i.stack.imgur.com/duv9h.png" alt="..." id="img">
</body>
</html>
My understanding is the image file has to be set to display: none; and then the image needs to be re-drawn using the javascript commands but I'm not sure if this image is compatible or not. When finished I want the image on a white background.
By way of an example the end design needs to resemble this - Logo particle design
Particle positions from bitmap.
To get the FX you want you need to create a particle system. This is just an array of objects, each with a position, the position where they want to be (Home), a vector defining their current movement, and the colour.
You get each particle's home position and colour by reading pixels from the image. You can access pixel data by rendering an image on a canvas and the using ctx.getImageData to get the pixel data (Note image must be on same domain or have CORS headers to access pixel data). As you read each pixel in turn, if not transparent, create a particle for that pixel and set it colour and home position from the pixels colour and position.
Use requestAnimationFrame to call a render function that every frame iterates all the particles moving them by some set of rules that give you the motion you are after. Once you have move each particle, render them to the canvas using simple shapes eg fillRect
Mouse interaction
To have interaction with the mouse you will need to use mouse move events to keep track of the mouse position relative to the canvas you are rendering to. As you update each particle you also check how far it is from the mouse. You can then push or pull the particle from or to the mouse (depending on the effect you want.
Rendering speed will limit the particle count.
The only issue with these types of FX is that you will be pushing the rendering speed limits as the particle count goes up. What may work well on one machine, will run very slow on another.
To avoid being too slow, and not looking good on some machines you should consider keeping an eye on the frame rate and reducing the particle count if it runs slow. To compensate you can increase the particle size or even reduce the canvas resolution.
The bottleneck is the actual rendering of each particle. When you get to large numbers the path methods really grinds down. If you want really high numbers you will have to render pixels directly to the bitmap, using the same method as reading but in reverse of course.
Example simple particles read from bitmap.
The example below uses text rendered to a canvas to create the particles, and to use an image you would just draw the image rather than the text. The example is a bit overkill as I ripped it from an old answer of mine. It is just as an example of the various ways to get stuff done.
const ctx = canvas.getContext("2d");
const Vec = (x, y) => ({x, y});
const setStyle = (ctx,style) => { Object.keys(style).forEach(key => ctx[key] = style[key]) }
const createImage = (w,h) => {var i=document.createElement("canvas");i.width=w;i.height=h;i.ctx=i.getContext("2d");return i}
const textList = ["Particles"];
var textPos = 0;
var w = canvas.width;
var h = canvas.height;
var cw = w / 2; // center
var ch = h / 2;
var globalTime;
var started = false;
requestAnimationFrame(update);
const mouse = {x : 0, y : 0, button : false}
function mouseEvents(e){
mouse.x = e.pageX;
mouse.y = e.pageY;
mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
}
["down","up","move"].forEach(name => document.addEventListener("mouse"+name,mouseEvents));
function onResize(){
cw = (w = canvas.width = innerWidth) / 2;
ch = (h = canvas.height = innerHeight) / 2;
if (!started) { startIt() }
}
function update(timer){
globalTime = timer;
ctx.setTransform(1,0,0,1,0,0); // reset transform
ctx.globalAlpha = 1; // reset alpha
if (w !== innerWidth || h !== innerHeight){ onResize() }
else { ctx.clearRect(0,0,w,h) }
particles.update();
particles.draw();
requestAnimationFrame(update);
}
function createParticles(text){
createTextMap(
text, 60, "Arial",
{ fillStyle : "#FF0", strokeStyle : "#F00", lineWidth : 2, lineJoin : "round", },
{ top : 0, left : 0, width : canvas.width, height : canvas.height }
)
}
// This function starts the animations
function startIt(){
started = true;
const next = ()=>{
var text = textList[(textPos++ ) % textList.length];
createParticles(text);
setTimeout(moveOut,text.length * 100 + 12000);
}
const moveOut = ()=>{
particles.moveOut();
setTimeout(next,2000);
}
setTimeout(next,0);
}
// the following function create the particles from text using a canvas
// the canvas used is displayed on the main canvas top left fro reference.
var tCan = createImage(100, 100); // canvas used to draw text
function createTextMap(text,size,font,style,fit){
const hex = (v)=> (v < 16 ? "0" : "") + v.toString(16);
tCan.ctx.font = size + "px " + font;
var width = Math.ceil(tCan.ctx.measureText(text).width + size);
tCan.width = width;
tCan.height = Math.ceil(size *1.2);
var c = tCan.ctx;
c.font = size + "px " + font;
c.textAlign = "center";
c.textBaseline = "middle";
setStyle(c,style);
if (style.strokeStyle) { c.strokeText(text, width / 2, tCan.height / 2) }
if (style.fillStyle) { c.fillText(text, width / 2, tCan.height/ 2) }
particles.empty();
var data = c.getImageData(0,0,width,tCan.height).data;
var x,y,ind,rgb,a;
for(y = 0; y < tCan.height; y += 1){
for(x = 0; x < width; x += 1){
ind = (y * width + x) << 2; // << 2 is equiv to * 4
if(data[ind + 3] > 128){ // is alpha above half
rgb = `#${hex(data[ind ++])}${hex(data[ind ++])}${hex(data[ind ++])}`;
particles.add(Vec(x, y), Vec(x, y), rgb);
}
}
}
particles.sortByCol
var scale = Math.min(fit.width / width, fit.height / tCan.height);
particles.each(p=>{
p.home.x = ((fit.left + fit.width) / 2) + (p.home.x - (width / 2)) * scale;
p.home.y = ((fit.top + fit.height) / 2) + (p.home.y - (tCan.height / 2)) * scale;
})
.findCenter() // get center used to move particles on and off of screen
.moveOffscreen() // moves particles off the screen
.moveIn(); // set the particles to move into view.
}
// basic particle
const particle = { pos : null, delta : null, home : null, col : "black", }
// array of particles
const particles = {
items : [], // actual array of particles
mouseFX : { power : 12,dist :110, curve : 2, on : true },
fx : { speed : 0.3, drag : 0.6, size : 4, jiggle : 1 },
// direction 1 move in -1 move out
direction : 1,
moveOut () {this.direction = -1; return this},
moveIn () {this.direction = 1; return this},
length : 0,
each(callback){ // custom iteration
for(var i = 0; i < this.length; i++){ callback(this.items[i],i) }
return this;
},
empty() { this.length = 0; return this },
deRef(){ this.items.length = 0; this.length = 0 },
sortByCol() { this.items.sort((a,b) => a.col === b.col ? 0 : a.col < b.col ? 1 : -1 ) },
add(pos, home, col){ // adds a particle
var p;
if(this.length < this.items.length){
p = this.items[this.length++];
p.home.x = home.x;
p.home.y = home.y;
p.delta.x = 0;
p.delta.y = 0;
p.col = col;
}else{
this.items.push( Object.assign({}, particle,{ pos, home, col, delta : Vec(0,0) } ) );
this.length = this.items.length
}
return this;
},
draw(){ // draws all
var p, size, sizeh;
sizeh = (size = this.fx.size) / 2;
for(var i = 0; i < this.length; i++){
p = this.items[i];
ctx.fillStyle = p.col;
ctx.fillRect(p.pos.x - sizeh, p.pos.y - sizeh, size, size);
}
},
update(){ // update all particles
var p,x,y,d;
const mP = this.mouseFX.power;
const mD = this.mouseFX.dist;
const mC = this.mouseFX.curve;
const fxJ = this.fx.jiggle;
const fxD = this.fx.drag;
const fxS = this.fx.speed;
for(var i = 0; i < this.length; i++){
p = this.items[i];
p.delta.x += (p.home.x - p.pos.x ) * fxS + (Math.random() - 0.5) * fxJ;
p.delta.y += (p.home.y - p.pos.y ) * fxS + (Math.random() - 0.5) * fxJ;
p.delta.x *= fxD;
p.delta.y *= fxD;
p.pos.x += p.delta.x * this.direction;
p.pos.y += p.delta.y * this.direction;
if(this.mouseFX.on){
x = p.pos.x - mouse.x;
y = p.pos.y - mouse.y;
d = Math.sqrt(x * x + y * y);
if(d < mD){
x /= d;
y /= d;
d /= mD;
d = (1-Math.pow(d, mC)) * mP;
p.pos.x += x * d;
p.pos.y += y * d;
}
}
}
return this;
},
findCenter(){ // find the center of particles maybe could do without
var x,y;
y = x = 0;
this.each(p => { x += p.home.x; y += p.home.y });
this.center = Vec(x / this.length, y / this.length);
return this;
},
moveOffscreen(){ // move start pos offscreen
var dist,x,y;
dist = Math.sqrt(this.center.x * this.center.x + this.center.y * this.center.y);
this.each(p => {
var d;
x = p.home.x - this.center.x;
y = p.home.y - this.center.y;
d = Math.max(0.0001,Math.sqrt(x * x + y * y)); // max to make sure no zeros
p.pos.x = p.home.x + (x / d) * dist;
p.pos.y = p.home.y + (y / d) * dist;
});
return this;
},
}
canvas { position : absolute; top : 0px; left : 0px; background : black;}
<canvas id="canvas"></canvas>
Use png saved as PNG-8 and and allow cross-origin
I saw the cool article from Bricks and mortar and thought I'd try it out.
I battled with it for an eternity, thinking that my js was wrong... Turns out that the image has to be saved as a PNG-8 without dither instead of a PNG-24.
Then make sure that you add the crossOrigin="Anonymous" attribute to the image tag:
<img crossOrigin="Anonymous" id="img" src="[link to wherever you host the image]" alt="logo">
I also hid the reference canvas by adding the following styles:
canvas#canvas-reference {
display: none;
}
I also added a debounce and resize function, so it's responsive.
The result:
See Demo with inverted logo
function initCircles() {
circles = [];
for (var i = 0; i < 100; i++) {
var circle = new createjs.Shape();
var r = 7;
var x = window.innerWidth * Math.random();
var y = window.innerHeight * Math.random();
var color = colors[Math.floor(i % colors.length)];
var alpha = 0.2 + Math.random() * 0.5;
circle.alpha = alpha;
circle.radius = r;
circle.graphics.beginFill(color).drawCircle(0, 0, r);
circle.x = x;
circle.y = y;
circles.push(circle);
circle.movement = 'float';
circle.addEventListener("mouseover", function(event) {
circle.graphics.clear().beginFill("red").drawRect(0, 0, 50, 60).endFill();
stage.update(event);
});
stage.addChild(circle);
}
}
I'm trying to add a mouseover listener on the little circles I create on the page, I hope that once I place the cursor on the circle, it becomes a rectangle. However, the rectangle always appear where some other circle exists rather than the circle I point to.
Save your beginFill command, and change it later:
// your other code above
var fillCmd = circle.graphics.beginFill(color).command; // Note the .command at the end
circle.graphics.drawCircle(0, 0, r);
// your other code below
// Later
fillCmd.style = "ff0000";
Here is an article about it, and here are the docs -
Admittedly, this could be documented better. :)
The problem is that you are referencing a mutable variable inside of the closure. There are a couple of ways to solve that.
1) Either somehow reference the circle from the event variable inside of the nested function (if the event has support for that), or
2) Bind the value of the variable inside another function, e.g:
function initCircles() {
circles = [];
for (var i = 0; i < 100; i++) {
var circle = new createjs.Shape();
var r = 7;
var x = window.innerWidth * Math.random();
var y = window.innerHeight * Math.random();
var color = colors[Math.floor(i % colors.length)];
var alpha = 0.2 + Math.random() * 0.5;
circle.alpha = alpha;
circle.radius = r;
circle.graphics.beginFill(color).drawCircle(0, 0, r);
circle.x = x;
circle.y = y;
circles.push(circle);
circle.movement = 'float';
addEventListener(circle);
stage.addChild(circle);
}
function addEventListener(circle) {
circle.addEventListener("mouseover", function(event) {
circle.graphics.clear().beginFill("red").drawRect(0, 0, 50, 60).endFill();
stage.update(event);
});
}
}
i"m working in angular projet and i want to add two javascript file in my project ; so i just include those js file in my assets and call them in my angular cli script part . i can see that those file are called correcty .
but i have this problems :
my html code is :
<div class=" demo-1">
<div class="content">
<div id="large-header" class="large-header">
<canvas id="demo-canvas"></canvas>
<h1 class="main-title">ALTEN <span class="thin">GAVĀ²</span></h1>
</div>
</div>
</div><!-- /container -->
and my js code is :
(function() {
var width, height, largeHeader, canvas, ctx, points, target, animateHeader = true;
// Main
initHeader();
initAnimation();
addListeners();
function initHeader() {
width = window.innerWidth;
height = window.innerHeight;
target = {x: width/2, y: height/2};
largeHeader = document.getElementById('large-header');
largeHeader.style.height = height+'px';
canvas = document.getElementById('demo-canvas');
canvas.width = width;
canvas.height = height;
ctx = canvas.getContext('2d');
// create points
points = [];
for(var x = 0; x < width; x = x + width/20) {
for(var y = 0; y < height; y = y + height/20) {
var px = x + Math.random()*width/20;
var py = y + Math.random()*height/20;
var p = {x: px, originX: px, y: py, originY: py };
points.push(p);
}
}
// for each point find the 5 closest points
for(var i = 0; i < points.length; i++) {
var closest = [];
var p1 = points[i];
for(var j = 0; j < points.length; j++) {
var p2 = points[j]
if(!(p1 == p2)) {
var placed = false;
for(var k = 0; k < 5; k++) {
if(!placed) {
if(closest[k] == undefined) {
closest[k] = p2;
placed = true;
}
}
}
for(var k = 0; k < 5; k++) {
if(!placed) {
if(getDistance(p1, p2) < getDistance(p1, closest[k])) {
closest[k] = p2;
placed = true;
}
}
}
}
}
p1.closest = closest;
}
// assign a circle to each point
for(var i in points) {
var c = new Circle(points[i], 2+Math.random()*2, 'rgba(255,255,255,0.3)');
points[i].circle = c;
}
}
// Event handling
function addListeners() {
if(!('ontouchstart' in window)) {
window.addEventListener('mousemove', mouseMove);
}
window.addEventListener('scroll', scrollCheck);
window.addEventListener('resize', resize);
}
function mouseMove(e) {
var posx = posy = 0;
if (e.pageX || e.pageY) {
posx = e.pageX;
posy = e.pageY;
}
else if (e.clientX || e.clientY) {
posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
}
target.x = posx;
target.y = posy;
}
function scrollCheck() {
if(document.body.scrollTop > height) animateHeader = false;
else animateHeader = true;
}
function resize() {
width = window.innerWidth;
height = window.innerHeight;
largeHeader.style.height = height+'px';
canvas.width = width;
canvas.height = height;
}
// animation
function initAnimation() {
animate();
for(var i in points) {
shiftPoint(points[i]);
}
}
function animate() {
if(animateHeader) {
ctx.clearRect(0,0,width,height);
for(var i in points) {
// detect points in range
if(Math.abs(getDistance(target, points[i])) < 4000) {
points[i].active = 0.3;
points[i].circle.active = 0.6;
} else if(Math.abs(getDistance(target, points[i])) < 20000) {
points[i].active = 0.1;
points[i].circle.active = 0.3;
} else if(Math.abs(getDistance(target, points[i])) < 40000) {
points[i].active = 0.02;
points[i].circle.active = 0.1;
} else {
points[i].active = 0;
points[i].circle.active = 0;
}
drawLines(points[i]);
points[i].circle.draw();
}
}
requestAnimationFrame(animate);
}
function shiftPoint(p) {
TweenLite.to(p, 1+1*Math.random(), {x:p.originX-50+Math.random()*100,
y: p.originY-50+Math.random()*100, ease:Circ.easeInOut,
onComplete: function() {
shiftPoint(p);
}});
}
// Canvas manipulation
function drawLines(p) {
if(!p.active) return;
for(var i in p.closest) {
ctx.beginPath();
ctx.moveTo(p.x, p.y);
ctx.lineTo(p.closest[i].x, p.closest[i].y);
ctx.strokeStyle = 'rgba(156,217,249,'+ p.active+')';
ctx.stroke();
}
}
function Circle(pos,rad,color) {
var _this = this;
// constructor
(function() {
_this.pos = pos || null;
_this.radius = rad || null;
_this.color = color || null;
})();
this.draw = function() {
if(!_this.active) return;
ctx.beginPath();
ctx.arc(_this.pos.x, _this.pos.y, _this.radius, 0, 2 * Math.PI, false);
ctx.fillStyle = 'rgba(156,217,249,'+ _this.active+')';
ctx.fill();
};
}
// Util
function getDistance(p1, p2) {
return Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2);
}
})();
when i try to log the largeHeader viariable in my js it return null .
this code work correctly . but including it in angular cli make problems .
is that a problem of webpack ?
thank you in advance
It's important to say what build tool you are using. If it's Webpack based (ie. Angular CLI) then you can reference a JavaScript file (or any other type) with one of the following:
import * as test from './test.js';
// or
require('./test.js');
However, your error message indicates that your JavaScript is running, but it's failing on these lines:
largeHeader = document.getElementById('large-header');
largeHeader.style.height = height+'px';
It's not finding an element on your page with ID large-header, so the call to .style fails. I suspect that, because you are importing the file from the top level HTML page, the JavaScript code is running before the template has a chance to render, so the large-header element is not created at that point.
To diagnose further, you could use the Sources tab of the Chrome Dev Tools to set a breakpoint on the line in question, then inspect the Elements tab to see if the expected div is actually present at this point.
I'm not sure exactly what you're trying to achieve here, but throwing in arbitrary bits of JavaScript that are not part of the "Angular world" might not be the best way to approach an Angular app. In the long term, perhaps it would be better to refactor this code into a Component or a Directive.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I am creating a game (using HTML5 canvas) that involves catching falling apples, i know, how original! I am having trouble finding a way to make it so multiple apples fall?
Here is the code in JSFiddle: https://jsfiddle.net/pgkL09j7/12/
var apple_x = 100;
var apple_y = 0;
var basket_x = 100;
var basket_y = 100;
var points = 0;
var basket_img = new Image();
basket_img.src = "http://s18.postimg.org/h0oe1vj91/basket.png";
var Countable = function () {}
//Background colour of canvas
var c = document.getElementById("c");
var ctx = c.getContext("2d");
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, 500, 500);
//Here is the event listener
c.addEventListener("mousemove", seenmotion, false);
//////////////////////
function seenmotion(e) {
//This is the code for the mouse
//moving over the canvas.
var bounding_box = c.getBoundingClientRect();
basket_x = (e.clientX - bounding_box.left) * (c.width / bounding_box.width) - basket_img.width / 2;
basket_y = (e.clientY - bounding_box.top) * (c.height / bounding_box.height) - basket_img.height / 2;
}
function start_game() {
setInterval(game_loop, 50);
}
function game_loop() {
// The code above is called every 50ms and is a
// frame-redraw-game-animation loop.
c.width = c.width;
// Below is the code that draws the objects
draw_apple(apple_x, apple_y);
draw_basket(basket_x, basket_y);
// Below is the code that updates the balloons location
apple_x++;
if (apple_y > c.height) {
apple_y = 0;
}
//Here is the collision detection code
if (collision(apple_x, apple_y, basket_x, basket_y)) {
points -= 0.5;
}
//Here is the code for the point system
points += 1;
// and let's stick it in the top right.
var integerpoints = Math.floor(points); // make it into an integer
ctx.font = "bold 24px sans-serif";
ctx.fillText(integerpoints, c.width - 50, 50);
}
context.clearRect(0, 0, 500, 500);
function collision(basket_x, basket_y, apple_x, apple_y) {
if (apple_y + 85 < basket_y) {
return false;
}
if (apple_y > basket_y + 91) {
return false;
}
if (apple_x + 80 < basket_x) {
return false;
}
if (apple_x > basket_x + 80) {
return false;
}
return true;
}
// Code to stop the game when we're finished playing
function stop_game() {
}
//Code for the ball
function draw_app
le(x, y) {
var apple_img = new Image();
apple_img.src = "http://s15.postimg.org/3nwjmzsiv/apple.png";
ctx.drawImage(apple_img, x, y);
}
//Code for the basket
function draw_basket(x, y) {
ctx.drawImage(basket_img, x, y);
}
Change the section
apple_x++;
if (apple_x > c.width) {
apple_x = 0;
}
to use vertical instead of horizontal...
apple_y++;
if (apple_y > c.height) {
apple_y = 0;
}
You've already accepted the answer, but this looked like fun. Check out this fiddle.
https://jsfiddle.net/h82gv4xn/
Improvements include:
Fixed scoreboard
Added level progression (Level increases every 10 apples)
Allowance for many many more apples on screen (play to level 9).
Apples will fall at different speeds and speed up as the levels increase.
Uses the animation frame system for much smoother animations.
Relaxed collision handling (The center of the bucket must touch the apple)
It all gets really silly as the levels wind upwards, but it should be a nice example to improve upon. The relevant javascript follows (this would go into your onLoad function):
var game = create_game();
game.init();
function create_game() {
debugger;
var level = 1;
var apples_per_level = 1;
var min_speed_per_level = 1;
var max_speed_per_level = 2;
var last_apple_time = 0;
var next_apple_time = 0;
var width = 500;
var height = 500;
var delay = 1000;
var item_width = 50;
var item_height = 50;
var total_apples = 0;
var apple_img = new Image();
var apple_w = 50;
var apple_h = 50;
var basket_img = new Image();
var c, ctx;
var apples = [];
var basket = {
x: 100,
y: 100,
score: 0
};
function init() {
apple_img.src = "http://s15.postimg.org/3nwjmzsiv/apple.png";
basket_img.src = "http://s18.postimg.org/h0oe1vj91/basket.png";
level = 1;
total_apples = 0;
apples = [];
c = document.getElementById("c");
ctx = c.getContext("2d");
ctx.fillStyle = "#000";
ctx.fillRect(0, 0, 500, 500);
c.addEventListener("mousemove", function (e) {
//moving over the canvas.
var bounding_box = c.getBoundingClientRect();
basket.x = (e.clientX - bounding_box.left) * (c.width / bounding_box.width) - basket_img.width / 2;
basket.y = (e.clientY - bounding_box.top) * (c.height / bounding_box.height) - basket_img.height / 2;
}, false);
setupApples();
requestAnimationFrame(tick);
}
function setupApples() {
var max_apples = level * apples_per_level;
while (apples.length < max_apples) {
initApple(apples.length);
}
}
function initApple(index) {
var max_speed = max_speed_per_level * level;
var min_speed = min_speed_per_level * level;
apples[index] = {
x: Math.round(Math.random() * (width - 2 * apple_w)) + apple_w,
y: -apple_h,
v: Math.round(Math.random() * (max_speed - min_speed)) + min_speed,
delay: Date.now() + Math.random() * delay
}
total_apples++;
}
function collision(apple) {
if (apple.y + apple_img.height < basket.y + 50) {
return false;
}
if (apple.y > basket.y + 50) {
return false;
}
if (apple.x + apple_img.width < basket.x + 50) {
return false;
}
if (apple.x > basket.x + 50) {
return false;
}
return true;
}
function maybeIncreaseDifficulty() {
level = Math.max(1, Math.ceil(basket.score / 10));
setupApples();
}
function tick() {
var i;
var apple;
var dateNow = Date.now();
c.width = c.width;
for (i = 0; i < apples.length; i++) {
apple = apples[i];
if (dateNow > apple.delay) {
apple.y += apple.v;
if (collision(apple)) {
initApple(i);
basket.score++;
} else if (apple.y > height) {
initApple(i);
} else {
ctx.drawImage(apple_img, apple.x, apple.y);
}
}
}
ctx.font = "bold 24px sans-serif";
ctx.fillStyle = "#2FFF2F";
ctx.fillText(basket.score, c.width - 50, 50);
ctx.fillText("Level: " + level, 20, 50);
ctx.drawImage(basket_img, basket.x, basket.y);
maybeIncreaseDifficulty();
requestAnimationFrame(tick);
}
return {
init: init
};
}