adding snow effect to specific div only - javascript

I am using code from https://www.kirupa.com/html5/the_falling_snow_effect.htm and well as it turns out the snow is falling all over my page and not just the banner div...
// Array to store our Snowflake objects
var snowflakes = [];
// Global variables to store our browser's window size
var browserWidth;
var browserHeight;
// Specify the number of snowflakes you want visible
var numberOfSnowflakes = 50;
// Flag to reset the position of the snowflakes
var resetPosition = false;
// Handle accessibility
var enableAnimations = false;
var reduceMotionQuery = matchMedia("(prefers-reduced-motion)");
// Handle animation accessibility preferences
function setAccessibilityState() {
if (reduceMotionQuery.matches) {
enableAnimations = false;
} else {
enableAnimations = true;
}
}
setAccessibilityState();
reduceMotionQuery.addListener(setAccessibilityState);
//
// It all starts here...
//
function setup() {
if (enableAnimations) {
window.addEventListener("DOMContentLoaded", generateSnowflakes, false);
window.addEventListener("resize", setResetFlag, false);
}
}
setup();
//
// Constructor for our Snowflake object
//
function Snowflake(element, speed, xPos, yPos) {
// set initial snowflake properties
this.element = element;
this.speed = speed;
this.xPos = xPos;
this.yPos = yPos;
this.scale = 1;
// declare variables used for snowflake's motion
this.counter = 0;
this.sign = Math.random() < 0.5 ? 1 : -1;
// setting an initial opacity and size for our snowflake
this.element.style.opacity = (.9 + Math.random()) / 3;
}
//
// The function responsible for actually moving our snowflake
//
Snowflake.prototype.update = function() {
// using some trigonometry to determine our x and y position
this.counter += this.speed / 5000;
this.xPos += this.sign * this.speed * Math.cos(this.counter) / 40;
this.yPos += Math.sin(this.counter) / 40 + this.speed / 30;
this.scale = .5 + Math.abs(10 * Math.cos(this.counter) / 20);
// setting our snowflake's position
setTransform(Math.round(this.xPos), Math.round(this.yPos), this.scale, this.element);
// if snowflake goes below the browser window, move it back to the top
if (this.yPos > browserHeight) {
this.yPos = -50;
}
}
//
// A performant way to set your snowflake's position and size
//
function setTransform(xPos, yPos, scale, el) {
el.style.transform = `translate3d(${xPos}px, ${yPos}px, 0) scale(${scale}, ${scale})`;
}
//
// The function responsible for creating the snowflake
//
function generateSnowflakes() {
// get our snowflake element from the DOM and store it
var originalSnowflake = document.querySelector(".snowflake");
// access our snowflake element's parent container
var snowflakeContainer = originalSnowflake.parentNode;
snowflakeContainer.style.display = "block";
// get our browser's size
browserWidth = document.documentElement.clientWidth;
browserHeight = document.documentElement.clientHeight;
// create each individual snowflake
for (var i = 0; i < numberOfSnowflakes; i++) {
// clone our original snowflake and add it to snowflakeContainer
var snowflakeClone = originalSnowflake.cloneNode(true);
snowflakeContainer.appendChild(snowflakeClone);
// set our snowflake's initial position and related properties
var initialXPos = getPosition(50, browserWidth);
var initialYPos = getPosition(50, browserHeight);
var speed = 5 + Math.random() * 40;
// create our Snowflake object
var snowflakeObject = new Snowflake(snowflakeClone,
speed,
initialXPos,
initialYPos);
snowflakes.push(snowflakeObject);
}
// remove the original snowflake because we no longer need it visible
snowflakeContainer.removeChild(originalSnowflake);
moveSnowflakes();
}
//
// Responsible for moving each snowflake by calling its update function
//
function moveSnowflakes() {
if (enableAnimations) {
for (var i = 0; i < snowflakes.length; i++) {
var snowflake = snowflakes[i];
snowflake.update();
}
}
// Reset the position of all the snowflakes to a new value
if (resetPosition) {
browserWidth = document.documentElement.clientWidth;
browserHeight = document.documentElement.clientHeight;
for (var i = 0; i < snowflakes.length; i++) {
var snowflake = snowflakes[i];
snowflake.xPos = getPosition(50, browserWidth);
snowflake.yPos = getPosition(50, browserHeight);
}
resetPosition = false;
}
requestAnimationFrame(moveSnowflakes);
}
//
// This function returns a number between (maximum - offset) and (maximum + offset)
//
function getPosition(offset, size) {
return Math.round(-1 * offset + Math.random() * (size + 2 * offset));
}
//
// Trigger a reset of all the snowflakes' positions
//
function setResetFlag(e) {
resetPosition = true;
}
#snowflakeContainer {
position: absolute;
left: 0px;
top: 0px;
display: none;
}
.snowflake {
position: fixed;
background-color: #ffffff;
user-select: none;
z-index: 1000;
pointer-events: none;
border-radius: 50%;
width: 10px;
height: 10px;
}
<div class="mainbanner">
<div id="snowflakeContainer">
<span class="snowflake"></span>
</div>
<br>
<center>
<p class="topText" style="font-size:8vw;"> Welcome to the ultimate <br>sleepover experience</p><br><br><br>
<p class="topText" style="font-size:4vw;"> By Ultimate Teepee Party</p>
<br><br><br>
<a class="btn_1" href="book.html">Book Your Party</a></center>
</div>
There has to be a way to make it show only inside that banner div and not the whole page?

if you take a look at snow demo page with inspect element, you will realise that the author embedded a whole html page into the page instead of using a div, the idea is that javascript browserHeight = document.documentElement.clientHeight refers to browser width, therefore unless modifying JS, the page only works in whole page, I could see no effect in your original code since the relative positioning is not handled well. refers to following working demo, which allows you to display the snow in whole page. if you only want the effect to apply to a certain element, try modifying the script calculation based on element position. or you can do what the author did, embed a doc into whole page
JsFiddle
<style>
#snowflakeContainer {
position: fixed;
left: 0px;
top: 0px;
display: none;
width: 100vw;
height: 100vh;
}
.snowflake {
position: fixed;
background-color: #CCC;
user-select: none;
z-index: 1000;
pointer-events: none;
border-radius: 50%;
width: 10px;
height: 10px;
background: red;
}
.banner {
position: fixed;
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.title {
font-size: 8vw;
}
.author {
font-size: 4vw;
}
</style>
<div id="snowflakeContainer">
<span class="snowflake"></span>
<div class="banner">
<div class="title">
Welcome to the ultimate
</div>
<div class="title">
sleepover experience
</div>
<br />
<br />
<br />
<div class="author">
By Ultimate Teepee Party
</div>
<br />
<br />
<br />
<a class="btn_1" href="book.html">Book Your Party</a>
</div>
</div>
<script>
// Array to store our Snowflake objects
var snowflakes = [];
// Global variables to store our browser's window size
var browserWidth;
var browserHeight;
// Specify the number of snowflakes you want visible
var numberOfSnowflakes = 50;
// Flag to reset the position of the snowflakes
var resetPosition = false;
// Handle accessibility
var enableAnimations = false;
var reduceMotionQuery = matchMedia("(prefers-reduced-motion)");
// Handle animation accessibility preferences
function setAccessibilityState() {
if (reduceMotionQuery.matches) {
enableAnimations = false;
} else {
enableAnimations = true;
}
}
setAccessibilityState();
reduceMotionQuery.addListener(setAccessibilityState);
//
// It all starts here...
//
function setup() {
if (enableAnimations) {
window.addEventListener("DOMContentLoaded", generateSnowflakes, false);
window.addEventListener("resize", setResetFlag, false);
}
}
setup();
//
// Constructor for our Snowflake object
//
function Snowflake(element, speed, xPos, yPos) {
// set initial snowflake properties
this.element = element;
this.speed = speed;
this.xPos = xPos;
this.yPos = yPos;
this.scale = 1;
// declare variables used for snowflake's motion
this.counter = 0;
this.sign = Math.random() < 0.5 ? 1 : -1;
// setting an initial opacity and size for our snowflake
this.element.style.opacity = (.1 + Math.random()) / 3;
}
//
// The function responsible for actually moving our snowflake
//
Snowflake.prototype.update = function() {
// using some trigonometry to determine our x and y position
this.counter += this.speed / 5000;
this.xPos += this.sign * this.speed * Math.cos(this.counter) / 40;
this.yPos += Math.sin(this.counter) / 40 + this.speed / 30;
this.scale = .5 + Math.abs(10 * Math.cos(this.counter) / 20);
// setting our snowflake's position
setTransform(Math.round(this.xPos), Math.round(this.yPos), this.scale, this.element);
// if snowflake goes below the browser window, move it back to the top
if (this.yPos > browserHeight) {
this.yPos = -50;
}
}
//
// A performant way to set your snowflake's position and size
//
function setTransform(xPos, yPos, scale, el) {
el.style.transform = `translate3d(${xPos}px, ${yPos}px, 0) scale(${scale}, ${scale})`;
}
//
// The function responsible for creating the snowflake
//
function generateSnowflakes() {
// get our snowflake element from the DOM and store it
var originalSnowflake = document.querySelector(".snowflake");
// access our snowflake element's parent container
var snowflakeContainer = originalSnowflake.parentNode;
snowflakeContainer.style.display = "block";
// get our browser's size
browserWidth = document.documentElement.clientWidth;
browserHeight = document.documentElement.clientHeight;
// create each individual snowflake
for (var i = 0; i < numberOfSnowflakes; i++) {
// clone our original snowflake and add it to snowflakeContainer
var snowflakeClone = originalSnowflake.cloneNode(true);
snowflakeContainer.appendChild(snowflakeClone);
// set our snowflake's initial position and related properties
var initialXPos = getPosition(50, browserWidth);
var initialYPos = getPosition(50, browserHeight);
var speed = 5 + Math.random() * 40;
// create our Snowflake object
var snowflakeObject = new Snowflake(snowflakeClone,
speed,
initialXPos,
initialYPos);
snowflakes.push(snowflakeObject);
}
// remove the original snowflake because we no longer need it visible
snowflakeContainer.removeChild(originalSnowflake);
moveSnowflakes();
}
//
// Responsible for moving each snowflake by calling its update function
//
function moveSnowflakes() {
if (enableAnimations) {
for (var i = 0; i < snowflakes.length; i++) {
var snowflake = snowflakes[i];
snowflake.update();
}
}
// Reset the position of all the snowflakes to a new value
if (resetPosition) {
browserWidth = document.documentElement.clientWidth;
browserHeight = document.documentElement.clientHeight;
for (var i = 0; i < snowflakes.length; i++) {
var snowflake = snowflakes[i];
snowflake.xPos = getPosition(50, browserWidth);
snowflake.yPos = getPosition(50, browserHeight);
}
resetPosition = false;
}
requestAnimationFrame(moveSnowflakes);
}
//
// This function returns a number between (maximum - offset) and (maximum + offset)
//
function getPosition(offset, size) {
return Math.round(-1 * offset + Math.random() * (size + 2 * offset));
}
//
// Trigger a reset of all the snowflakes' positions
//
function setResetFlag(e) {
resetPosition = true;
}
</script>

Related

Random movement of circles created by the script

I have a function that craeates divs with a circle.
Now they are all created and appear at the beginning of the page and go further in order.
Next, I need each circle to appear in a random place. I did this.
Now I need all of them to move randomly across the entire page, I have difficulties with this.
Here is an example of how everything works for one element that is already on the page.
https://jsfiddle.net/quej8wko/
But when I add this code, all my created circles don't move.
I get an error:
"message": "Uncaught TypeError: Cannot set properties of null (setting 'willChange')",
This is probably due to the fact that initially there are no circles on the page. How can I connect the code so that all created circles move?
//creating circles
var widthHeight = 40; // <-- circle width
var margin = 20; // <-- margin - is it necessary ?
var delta = widthHeight + margin;
function createDiv(id, color) {
let div = document.createElement('div');
var currentTop = 0;
var documentHeight = document.documentElement.clientHeight;
var documentWidth = document.documentElement.clientWidth;
div.setAttribute('class', id);
if (color === undefined) {
let colors = ['#35def2', '#35f242', '#b2f235', '#f2ad35', '#f24735', '#3554f2', '#8535f2', '#eb35f2', '#f2359b', '#f23547'];
div.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
}
else {
div.style.backgroundColor = color;
}
div.classList.add("circle");
div.classList.add("animation");
// Get the random positions minus the delta
currentTop = Math.floor(Math.random() * documentHeight) - delta;
currentLeft = Math.floor(Math.random() * documentWidth) - delta;
// Keep the positions between -20px and the current positions
var limitedTop = Math.max(margin * -1, currentTop);
var limitedLeft = Math.max(margin * -1, currentLeft);
div.style.top = limitedTop + "px";
div.style.left = limitedLeft + "px";
document.body.appendChild(div);
}
let i = 0;
const oneSecond = 1000;
setInterval(() => {
i += 1;
createDiv(`circle${i}`)
}, oneSecond);
//move circles
function RandomObjectMover(obj, container) {
this.$object = obj;
this.$container = container;
this.container_is_window = container === window;
this.pixels_per_second = 250;
this.current_position = { x: 0, y: 0 };
this.is_running = false;
}
// Set the speed of movement in Pixels per Second.
RandomObjectMover.prototype.setSpeed = function(pxPerSec) {
this.pixels_per_second = pxPerSec;
}
RandomObjectMover.prototype._getContainerDimensions = function() {
if (this.$container === window) {
return { 'height' : this.$container.innerHeight, 'width' : this.$container.innerWidth };
} else {
return { 'height' : this.$container.clientHeight, 'width' : this.$container.clientWidth };
}
}
RandomObjectMover.prototype._generateNewPosition = function() {
// Get container dimensions minus div size
var containerSize = this._getContainerDimensions();
var availableHeight = containerSize.height - this.$object.clientHeight;
var availableWidth = containerSize.width - this.$object.clientHeight;
// Pick a random place in the space
var y = Math.floor(Math.random() * availableHeight);
var x = Math.floor(Math.random() * availableWidth);
return { x: x, y: y };
}
RandomObjectMover.prototype._calcDelta = function(a, b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
var dist = Math.sqrt( dx*dx + dy*dy );
return dist;
}
RandomObjectMover.prototype._moveOnce = function() {
// Pick a new spot on the page
var next = this._generateNewPosition();
// How far do we have to move?
var delta = this._calcDelta(this.current_position, next);
// Speed of this transition, rounded to 2DP
var speed = Math.round((delta / this.pixels_per_second) * 100) / 100;
//console.log(this.current_position, next, delta, speed);
this.$object.style.transition='transform '+speed+'s linear';
this.$object.style.transform='translate3d('+next.x+'px, '+next.y+'px, 0)';
// Save this new position ready for the next call.
this.current_position = next;
};
RandomObjectMover.prototype.start = function() {
if (this.is_running) {
return;
}
// Make sure our object has the right css set
this.$object.willChange = 'transform';
this.$object.pointerEvents = 'auto';
this.boundEvent = this._moveOnce.bind(this)
// Bind callback to keep things moving
this.$object.addEventListener('transitionend', this.boundEvent);
// Start it moving
this._moveOnce();
this.is_running = true;
}
RandomObjectMover.prototype.stop = function() {
if (!this.is_running) {
return;
}
this.$object.removeEventListener('transitionend', this.boundEvent);
this.is_running = false;
}
// Init it
var x = new RandomObjectMover(document.querySelector(".circle"), window);
// Start it off
x.start();
.circle {
clip-path: circle(50%);
height: 40px;
width: 40px;
margin: 20px;
position: absolute;
}
I have modified the snippet which works as you expected.
There was a mistake where you were initializing and creating the object instance only once and none of the div elements that you created inside the setInterval function never got Instantiated.
I think you are just starting out with JavaScript with this sample project.
Below are few suggestions:
Learn to debug the code. You should be using dev tools by making use of debugger statement where it takes you to the source code to analyze the variable scope and stack during the runtime. console.log also helps in few situations.
I could see a lot of confusing naming convention (You have named the create div parameter as id but creating a div class using that id)
Try using ES6 features (class syntax is really good when writing OOP in JS although it's just a syntactic sugar for prototype)
//creating circles
var widthHeight = 40; // <-- circle width
var margin = 20; // <-- margin - is it necessary ?
var delta = widthHeight + margin;
function createAndInitializeDivObject(id, color) {
let div = document.createElement('div');
var currentTop = 0;
var documentHeight = document.documentElement.clientHeight;
var documentWidth = document.documentElement.clientWidth;
div.setAttribute('class', id);
if (color === undefined) {
let colors = ['#35def2', '#35f242', '#b2f235', '#f2ad35', '#f24735', '#3554f2', '#8535f2', '#eb35f2', '#f2359b', '#f23547'];
div.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];
}
else {
div.style.backgroundColor = color;
}
div.classList.add("circle");
div.classList.add("animation");
// Get the random positions minus the delta
currentTop = Math.floor(Math.random() * documentHeight) - delta;
currentLeft = Math.floor(Math.random() * documentWidth) - delta;
// Keep the positions between -20px and the current positions
var limitedTop = Math.max(margin * -1, currentTop);
var limitedLeft = Math.max(margin * -1, currentLeft);
div.style.top = limitedTop + "px";
div.style.left = limitedLeft + "px";
document.body.appendChild(div);
var x = new RandomObjectMover(document.querySelector(`.${id}`), window);
x.start();
}
let i = 0;
const oneSecond = 1000;
setInterval(() => {
i += 1;
createAndInitializeDivObject(`circle${i}`)
}, oneSecond);
//move circles
function RandomObjectMover(obj, container) {
this.$object = obj;
this.$container = container;
this.container_is_window = container === window;
this.pixels_per_second = 250;
this.current_position = { x: 0, y: 0 };
this.is_running = false;
}
// Set the speed of movement in Pixels per Second.
RandomObjectMover.prototype.setSpeed = function(pxPerSec) {
this.pixels_per_second = pxPerSec;
}
RandomObjectMover.prototype._getContainerDimensions = function() {
if (this.$container === window) {
return { 'height' : this.$container.innerHeight, 'width' : this.$container.innerWidth };
} else {
return { 'height' : this.$container.clientHeight, 'width' : this.$container.clientWidth };
}
}
RandomObjectMover.prototype._generateNewPosition = function() {
// Get container dimensions minus div size
var containerSize = this._getContainerDimensions();
var availableHeight = containerSize.height - this.$object.clientHeight;
var availableWidth = containerSize.width - this.$object.clientHeight;
// Pick a random place in the space
var y = Math.floor(Math.random() * availableHeight);
var x = Math.floor(Math.random() * availableWidth);
return { x: x, y: y };
}
RandomObjectMover.prototype._calcDelta = function(a, b) {
var dx = a.x - b.x;
var dy = a.y - b.y;
var dist = Math.sqrt( dx*dx + dy*dy );
return dist;
}
RandomObjectMover.prototype._moveOnce = function() {
// Pick a new spot on the page
var next = this._generateNewPosition();
// How far do we have to move?
var delta = this._calcDelta(this.current_position, next);
// Speed of this transition, rounded to 2DP
var speed = Math.round((delta / this.pixels_per_second) * 100) / 100;
//console.log(this.current_position, next, delta, speed);
this.$object.style.transition='transform '+speed+'s linear';
this.$object.style.transform='translate3d('+next.x+'px, '+next.y+'px, 0)';
// Save this new position ready for the next call.
this.current_position = next;
};
RandomObjectMover.prototype.start = function() {
if (this.is_running) {
return;
}
// Make sure our object has the right css set
this.$object.willChange = 'transform';
this.$object.pointerEvents = 'auto';
this.boundEvent = this._moveOnce.bind(this)
// Bind callback to keep things moving
this.$object.addEventListener('transitionend', this.boundEvent);
// Start it moving
this._moveOnce();
this.is_running = true;
}
RandomObjectMover.prototype.stop = function() {
if (!this.is_running) {
return;
}
this.$object.removeEventListener('transitionend', this.boundEvent);
this.is_running = false;
}
// Init it
var x = new RandomObjectMover(document.querySelector(".circle"), window);
// Start it off
x.start();
.circle {
width: 35px;
height: 35px;
border-radius: 35px;
background-color: #ffffff;
border: 3px solid purple;
position: absolute;
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="circle"></div>
<script src="app.js"></script>
</body>
</html>

How to apply physics to complex shapes? (matter.js + p5.js)

I've been trying to make a ball pit sim using matter.js + p5.js to use as an interactive website background.
With some help from Mr Shiffman's video I've managed to get it working well with circle shapes, but I want to take it to the next level and use custom blob shapes (taken from the client's logo) and apply the same physics to them.
I've been able to get the custom shapes to render using a combination of p5's Beginshape() and matter's bodies.fromVertices. It sort of works as you can see but the physics is very weird, and the collisions don't seem to match even though I used the same vertices for both.
I think it may be to do with this quote from the p5 docs
Transformations such as translate(), rotate(), and scale() do not work within beginShape().
but I don't know what I can do to get around this as I need them to be able to translate / rotate for the physics to work...
Any ideas? help much appreciated!
Codepen
var fric = .6;
var rest = .7
//blob creation function
function Blob(x, y) {
var options = {
friction: fric,
restitution: rest
}
this.body = Bodies.fromVertices(x,y, blob, options);;
World.add(world, this.body);
this.show = function() {
var pos = this.body.position;
var angle = this.body.angle;
push();
translate(pos.x, pos.y);
rotate(angle);
rectMode(CENTER);
strokeWeight(0);
fill('#546B2E')
beginShape();
curveVertex(10.4235750010825,77.51573373392407);
curveVertex(3.142478233002126,70.89274677890447);
curveVertex(0.09197006398718799,61.45980047762196);
curveVertex(1.1915720013184474,51.59196924554452);
curveVertex(4.497757286928595,42.162760563619436);
curveVertex(5.252622102311041,32.216346235505895);
curveVertex(4.731619980811491,22.230638463608106);
curveVertex(4.748780859149178,12.256964518539956);
curveVertex(8.728313738681376,3.3252404103204602);
curveVertex(17.998080279150148,0.07532797415084502);
curveVertex(27.955564903146588,0.6294681264134124);
curveVertex(37.68448491855515,2.8865688476481735);
curveVertex(46.899804284802386,6.733477319787068);
curveVertex(55.386932458422265,12.031766230704845);
curveVertex(62.886098235421045,18.623827217916812);
curveVertex(69.13243582467831,26.40824364010799);
curveVertex(73.70136375533966,35.2754654128657);
curveVertex(75.90839243871912,44.99927633563314);
curveVertex(74.84120838749334,54.8784706257129);
curveVertex(70.09272040861401,63.61579878615303);
curveVertex(62.590342401896606,70.15080526550207);
curveVertex(53.62552650480876,74.54988781923045);
curveVertex(44.08788115809841,77.55817639102708);
curveVertex(34.30859814694884,79.58860716640554);
curveVertex(24.334764892578125,80.23994384765624);
curveVertex(14.444775242328642,78.88621691226959);
endShape(CLOSE);
pop();
}
}
var clientHeight = document.getElementById('physBox').clientHeight;
var clientWidth = document.getElementById('physBox').clientWidth;
var Engine = Matter.Engine,
World = Matter.World,
Bodies = Matter.Bodies,
Common = Matter.Common,
Composite = Matter.Composite,
Mouse = Matter.Mouse,
MouseConstraint = Matter.MouseConstraint,
Vertices = Matter.Vertices;
var blob = Vertices.fromPath('10.4235750010825 77.51573373392407 3.142478233002126 70.89274677890447 0.09197006398718799 61.45980047762196 1.1915720013184474 51.59196924554452 4.497757286928595 42.162760563619436 5.252622102311041 32.216346235505895 4.731619980811491 22.230638463608106 4.748780859149178 12.256964518539956 8.728313738681376 3.3252404103204602 17.998080279150148 0.07532797415084502 27.955564903146588 0.6294681264134124 37.68448491855515 2.8865688476481735 46.899804284802386 6.733477319787068 55.386932458422265 12.031766230704845 62.886098235421045 18.623827217916812 69.13243582467831 26.40824364010799 73.70136375533966 35.2754654128657 75.90839243871912 44.99927633563314 74.84120838749334 54.8784706257129 70.09272040861401 63.61579878615303 62.590342401896606 70.15080526550207 53.62552650480876 74.54988781923045 44.08788115809841 77.55817639102708 34.30859814694884 79.58860716640554 24.334764892578125 80.23994384765624 14.444775242328642 78.88621691226959');
var engine;
var world;
var blobs =[];
var ground;
var ceiling;
var wallLeft;
var wallRight;
var mConstraint;
//start sim after x time
setTimeout(function setup() {
var cnv = createCanvas(clientWidth, clientHeight);
cnv.parent("physBox");
engine = Engine.create();
world = engine.world;
Engine.run(engine);
//add ground
ground = Bodies.rectangle(clientWidth/2, clientHeight+500, clientWidth, 1000, { isStatic: true });
World.add(world, ground);
//add ceiling
ceiling = Bodies.rectangle(clientWidth/2, -clientHeight-500, clientWidth, 1000, { isStatic: true });
World.add(world, ceiling);
//add left wall
wallLeft = Bodies.rectangle(-500, clientHeight/2, 1000, clientHeight*2, { isStatic: true });
World.add(world, wallLeft);
//add right wall
wallRight = Bodies.rectangle(clientWidth+500, clientHeight/2, 1000, clientHeight*2, { isStatic: true });
World.add(world, wallRight);
//create x bodies
for (var i = 0; i < 4; i++) {
blobs.push(new Blob(clientWidth/2, 100));
}
//mouse controls
var options = {
mouse: canvasmouse
}
var canvasmouse = Mouse.create(cnv.elt);
mConstraint = MouseConstraint.create(engine);
World.add(world,mConstraint);
}, 2000);
function draw() {
background('#EEF2FD');
//show all bodies
for (var i = 0; i < blobs.length; i++) {
blobs[i].show();
}
}
body, html {
overflow: hidden;
padding:0;
margin:0;
}
h1 {
font-family: sans-serif;
font-size: 4vw;
background: none;
position: absolute;
margin-top:10%;
margin-left: 10%;
user-select: none;
}
#physBox {
width: 100%;
height: 100vh;
padding: 0;
left: 0;
z-index: -1;
box-sizing: border-box;
-webkit-touch-callout: none; /* iOS Safari */
-webkit-user-select: none; /* Safari */
-khtml-user-select: none; /* Konqueror HTML */
-moz-user-select: none; /* Old versions of Firefox */
-ms-user-select: none; /* Internet Explorer/Edge */
user-select: none; /* Non-prefixed version, currently
supported by Chrome, Edge, Opera and Firefox */
}
<script src="https://cdn.jsdelivr.net/npm/poly-decomp#0.2.1/build/decomp.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.17.1/matter.min.js"></script>
<section id="physBox">
<h1> Welcome to my website</h1>
</section>
Note: I originally tried doing this all with SVGs instead of custom shapes but struggled to understand how to make it work so I gave up, if someone could help me solve this using SVGs instead i'd be happy with that! Also, apologies for my terrible code formatting - I'm learning ;)
The root issue here is that matter.js positions objects based on their center of mass rather than the origin of the coordinate system that your vertices are in, and it is clear that the origin of the coordinate system for you blob vertices is not its center of mass since all of your vertices are positive. You can calculate the center of mass for your blob and then use that offset before drawing:
const fric = 0.6;
const rest = 0.7;
const {
Engine,
Runner,
World,
Bodies,
Body,
Common,
Composite,
Mouse,
MouseConstraint,
Vertices
} = Matter;
const blob = Vertices.fromPath('10.4235750010825 77.51573373392407 3.142478233002126 70.89274677890447 0.09197006398718799 61.45980047762196 1.1915720013184474 51.59196924554452 4.497757286928595 42.162760563619436 5.252622102311041 32.216346235505895 4.731619980811491 22.230638463608106 4.748780859149178 12.256964518539956 8.728313738681376 3.3252404103204602 17.998080279150148 0.07532797415084502 27.955564903146588 0.6294681264134124 37.68448491855515 2.8865688476481735 46.899804284802386 6.733477319787068 55.386932458422265 12.031766230704845 62.886098235421045 18.623827217916812 69.13243582467831 26.40824364010799 73.70136375533966 35.2754654128657 75.90839243871912 44.99927633563314 74.84120838749334 54.8784706257129 70.09272040861401 63.61579878615303 62.590342401896606 70.15080526550207 53.62552650480876 74.54988781923045 44.08788115809841 77.55817639102708 34.30859814694884 79.58860716640554 24.334764892578125 80.23994384765624 14.444775242328642 78.88621691226959');
// from http://paulbourke.net/geometry/polygonmesh/
function computeArea(vertices) {
let area = 0;
for (let i = 0; i < vertices.length - 1; i++) {
let v = vertices[i];
let vn = vertices[i + 1];
area += (v.x * vn.y - vn.x * v.y) / 2;
}
return area;
}
function computeCenter(vertices) {
let area = computeArea(vertices);
let cx = 0,
cy = 0;
for (let i = 0; i < vertices.length - 1; i++) {
let v = vertices[i];
let vn = vertices[i + 1];
cx += (v.x + vn.x) * (v.x * vn.y - vn.x * v.y) / (6 * area);
cy += (v.y + vn.y) * (v.x * vn.y - vn.x * v.y) / (6 * area);
}
return {
x: cx,
y: cy
};
}
const center = computeCenter(blob);
let engine;
let world;
let blobs = [];
let ground;
let ceiling;
let wallLeft;
let wallRight;
let mConstraint;
//blob creation function
function Blob(x, y) {
let options = {
friction: fric,
restitution: rest
}
this.body = Bodies.fromVertices(x, y, blob, options);
World.add(world, this.body);
// Scales the body around the center
Body.scale(this.body, 0.5, 0.5);
this.show = function() {
var pos = this.body.position;
var angle = this.body.angle;
push();
translate(pos.x, pos.y);
rotate(angle);
scale(0.5, 0.5);
translate(-center.x, -center.y);
strokeWeight(0);
fill('#546B2E')
beginShape();
for (const {
x,
y
} of blob) {
curveVertex(x, y);
}
endShape(CLOSE);
pop();
// Alternately, when drawing your blobs you could use
// the bodies vertices, but it looks like these are
// converted into a convex polygon.
push();
stroke('red');
strokeWeight(1);
noFill();
beginShape();
for (const {
x,
y
} of this.body.vertices) {
curveVertex(x, y);
}
endShape(CLOSE);
pop();
}
}
//start sim after x time
function setup() {
const cnv = createCanvas(windowWidth, Math.max(windowHeight, 300));
engine = Engine.create();
world = engine.world;
const runner = Runner.create();
Runner.run(runner, engine);
//add ground
ground = Bodies.rectangle(width / 2, height, width, 50, {
isStatic: true
});
World.add(world, ground);
//add ceiling
ceiling = Bodies.rectangle(width / 2, 0, width, 50, {
isStatic: true
});
World.add(world, ceiling);
//add left wall
wallLeft = Bodies.rectangle(0, height / 2, 50, height, {
isStatic: true
});
World.add(world, wallLeft);
//add right wall
wallRight = Bodies.rectangle(width, height / 2, 50, height, {
isStatic: true
});
World.add(world, wallRight);
//create x bodies
for (let i = 0; i < 4; i++) {
blobs.push(new Blob(random(50, width - 100), random(50, height - 100)));
}
}
function draw() {
background('#EEF2FD');
//show all bodies
for (var i = 0; i < blobs.length; i++) {
blobs[i].show();
}
}
function mouseClicked() {
blobs.push(new Blob(mouseX, mouseY));
}
html,
body {
margin: 0;
overflow-x: hidden;
}
<script src="https://cdn.jsdelivr.net/npm/poly-decomp#0.2.1/build/decomp.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.17.1/matter.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.4.0/p5.js"></script>

Trigger a javascript function when button is clicked and stop when clicked again

So I wish to add an effect to my website of floating particles. Came across this codepen which does the exact thing - https://codepen.io/OliverKrieger/pen/EjLEVX.
I've been trying to change it a bit so that the particles only trigger when a checkbox is checked. I understand it might've been already answered on SO but in the codepen code, there is an window.onload function which automatically fires it when window is loaded. I want there to be a checkbox instead.
I tried putting the following in html -
<input type="checkbox" id="switch" onchange="function()"">
<label for="switch" >Toggle</label>
But it seems like the script still triggers automatically. Can someone help me with this please? I'm new to programming so I apologize for any vagueness. Please lemme know if I can provide more details. Any input would be really appreciated.
In your code, function is a keyword and not a function name. The function is still run as soon as the page loads because of the way it is defined in the javascript, and your HTML addition has no effect on this.
You need to change your function into a named function, declared using the function keyword:
function functionName() { ... }
And then you can call it by its name in your HTML:
<input type="checkbox" id="switch" onchange="functionName()">
This should work
(function() {
var requestAnimationFrame = window.requestAnimationFrame || window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame || window.msRequestAnimationFrame;
window.requestAnimationFrame = requestAnimationFrame;
})();
$(window).resize(function() {
ParticleCanvas.width = ($(window).width() - 20);
ParticleCanvas.height = ($(window).height() - 10);
});
$(function() {
let request = null;
$("#switch").change(function() {
if (this.checked) {
animateDust();
} else {
window.cancelAnimationFrame(request);
}
})
var ParticleCanvas = document.getElementById("ParticleCanvas");
var context = ParticleCanvas.getContext("2d");
ParticleCanvas.width = ($(window).width() - 20);
ParticleCanvas.height = ($(window).height() - 10);
$("switch").click(function() {
animateDust();
})
// All the info stored into an array for the particles
var particles = {},
particleIndex = 0,
settings = {
density: 20,
particleSize: 2,
startingX: ParticleCanvas.width / 2,
startingY: ParticleCanvas.height,
gravity: -0.01
};
// Set up a function to create multiple particles
function Particle() {
// Establish starting positions and velocities from the settings array, the math random introduces the particles being pointed out from a random x coordinate
this.x = settings.startingX * (Math.random() * 10);
this.y = settings.startingY;
// Determine original X-axis speed based on setting limitation
this.vx = (Math.random() * 2 / 3) - (Math.random() * 3 / 3);
this.vy = -(Math.random() * 5 / 3);
// Add new particle to the index
// Object used as it's simpler to manage that an array
particleIndex++;
particles[particleIndex] = this;
this.id = particleIndex;
this.life = 0;
this.maxLife = 200;
this.alpha = 1;
this.red = 0;
this.green = 255;
this.blue = 255;
}
// Some prototype methods for the particle's "draw" function
Particle.prototype.draw = function() {
this.x += this.vx;
this.y += this.vy;
// Adjust for gravity
this.vy += settings.gravity;
// Age the particle
this.life++;
this.red += 2;
this.alpha -= 0.005;
// If Particle is old, it goes in the chamber for renewal
if (this.life >= this.maxLife) {
delete particles[this.id];
}
// Create the shapes
context.clearRect(settings.leftWall, settings.groundLevel, ParticleCanvas.width, ParticleCanvas.height);
context.beginPath();
context.fillStyle = "rgba(" + this.red + ", " + this.green + ", " + this.blue + ", " + this.alpha + ")";
// Draws a circle of radius 20 at the coordinates 100,100 on the ParticleCanvas
context.arc(this.x, this.y, settings.particleSize, 0, Math.PI * 2, true);
context.closePath();
context.fill();
}
function animateDust() {
context.clearRect(0, 0, ParticleCanvas.width, ParticleCanvas.height);
// Draw the particles
for (var i = 0; i < settings.density; i++) {
if (Math.random() > 0.97) {
// Introducing a random chance of creating a particle
// corresponding to an chance of 1 per second,
// per "density" value
new Particle();
}
}
for (var i in particles) {
particles[i].draw();
}
request = window.requestAnimationFrame(animateDust);
}
})
body {
background-color: #000000;
color: #555555;
margin: 0;
padding: 0;
}
#ParticleCanvas {
border: 1px solid white;
position: absolute;
z-index: -1;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
}
#container {
background: white;
padding: 1rem;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<canvas id="ParticleCanvas"></canvas>
<div id="container">
<form>
<input type="checkbox" id="switch" name="switch">
</form>
<label for="switch">Toggle</label>
</div>

I wanna repeat function infinite but i couldn't

I wanna do some effect to my background so i tought i can add some shapes flying around but i do only 1 shape i cant loop or anything
i tried call function more than a one time but it doesnt help
function animasyon(a) {
window.onload=function(){
var id = setInterval(anim,5);
$('body').append('<div class=shape></div>');
$('body').append('<div class=shape></div>');
var kutu = document.getElementsByClassName("shape");
var pos = 0;
var x = window.innerWidth;
var y = window.innerHeight;
var borderSize = Math.floor(Math.random() * 3 ) + 1;
var ySize = Math.floor(Math.random() * y ) + 1;
var Size = Math.floor(Math.random() * 30 ) + 5;
var yon = Math.floor(Math.random() *2)+1;
var dolu = Math.floor(Math.random() *2)+1;
if (ySize > 50) { ySize-=20; }
function anim(){
if (pos == x) {
clearInterval(id);
document.getElementById("shape").remove();
}else{
pos++;
kutu[a].style.position = "absolute";
kutu[a].style.border = "solid rgb(119,38,53) "+borderSize+"px";
kutu[a].style.left = pos+"px";
kutu[a].style.width = Size+"px";
kutu[a].style.height = Size+"px";
if (yon == 1) { ySize-=0.2; } else { ySize+=0.2; }
if (dolu==1) {kutu[a].style.background = "rgb(119,38,53)";}
if (kutu[a].offsetTop < 0 || kutu[a].offsetTop > y-30) {document.getElementById("shape").remove();}
kutu[a].style.top = ySize+"px";
}
}
}
}
animasyon(0);
Try Calling 'anim' function in this way
setInterval(function(){anim()},5);
Your problem is that you are assigning window.onload function a value inside the function animasyon
window.onload holds only 1 function. If you call animation function more than once, then the last one will overwrite the first one.
Edit: You need to separate your animation logic from the page load logic. They are completely separate things.
Here is an example of adding multiple objects and animating each separately.
// HTML
<div id="container">
<div id="ball"></div>
<div id="ball2"></div>
<div id="ball3"></div>
<div id="ball4"></div>
</div>
// CSS
#ball, #ball2, #ball3, #ball4 {
background: red;
border: 1px solid #FAFDFA;
display: inline-block;
width: 1em;
height: 1em;
border-radius: 2em;
position: absolute;
}
#ball2 {
background: blue;
}
#ball3 {
background: green;
}
#ball4 {
background: purple;
}
#container {
width: 512px;
height: 512px;
background: black;
position: relative;
}
// JS
const container = document.getElementById('container');
const stageWidth = 512;
const stageHeight = 512;
const makeFall = (elementId) => {
// this function makes an enlement fall in random direction
const animationSpeed = 4;
// your onload function
const ball = document.getElementById(elementId);
let directionX = (Math.random() * animationSpeed * 2) - animationSpeed;
let directionY = Math.random() * animationSpeed;
const setRandomStart = () => {
ball.style.top = '10px';
ball.style.left = (Math.random() * (stageWidth / 2)) + 'px';
directionX = (Math.random() * animationSpeed * 2) - animationSpeed;
directionY = Math.random() * animationSpeed;
}
setRandomStart();
let animationInterval = setInterval(() => {
let px = parseFloat(ball.style.left);
let py = parseFloat(ball.style.top);
px += directionX;
py += directionY;
if (px > stageWidth - 20 || py > stageHeight - 20 || px < -20) {
setRandomStart();
} else {
ball.style.left = px + 'px';
ball.style.top = py + 'px';
}
}, 48);
}
// In Your onload function you can add the elements and then animate
makeFall('ball');
makeFall('ball2');
makeFall('ball3');
makeFall('ball4');
https://jsfiddle.net/753oL8re/4/

Falling Snow Effect not working in Chrome

I manage a few websites and for some reason I am having trouble getting the falling snow effect to work in Google Chrome. The code works perfectly in IE 11 but the same exact code in Google Chrome just puts a snowflake in the upper left corner of the page. Is my code wrong for it to work in Chrome?
CSS
.snowflakeContainer {
position: absolute;
left: 0px;
top: 0px;
}
.snowflake {
padding-left: 15px;
font-family: Cambria, Georgia, serif;
font-size: 14px;
line-height: 24px;
position: fixed;
color: #008B8B;
user-select: none;
z-index: 1000;
}
.snowflake:hover {
cursor: default;
}
HTML
<script src="js5/fallingsnow_v6.js"></script>
JavaScript
// The star of every good animation
var requestAnimationFrame = window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.msRequestAnimationFrame;
var transforms = ["transform",
"msTransform",
"webkitTransform",
"mozTransform",
"oTransform"];
var transformProperty = getSupportedPropertyName(transforms);
// Array to store our Snowflake objects
var snowflakes = [];
// Global variables to store our browser's window size
var browserWidth;
var browserHeight;
// Specify the number of snowflakes you want visible
var numberOfSnowflakes = 25;
// Flag to reset the position of the snowflakes
var resetPosition = false;
//
// It all starts here...
//
function setup() {
window.addEventListener("DOMContentLoaded", generateSnowflakes, false);
window.addEventListener("resize", setResetFlag, false);
}
setup();
//
// Vendor prefix management
//
function getSupportedPropertyName(properties) {
for (var i = 0; i < properties.length; i++) {
if (typeof document.body.style[properties[i]] != "undefined") {
return properties[i];
}
}
return null;
}
//
// Constructor for our Snowflake object
//
function Snowflake(element, radius, speed, xPos, yPos) {
// set initial snowflake properties
this.element = element;
this.radius = radius;
this.speed = speed;
this.xPos = xPos;
this.yPos = yPos;
// declare variables used for snowflake's motion
this.counter = 0;
this.sign = Math.random() < 0.5 ? 1 : -1;
// setting an initial opacity and size for our snowflake
this.element.style.opacity = .1 + Math.random();
this.element.style.fontSize = 12 + Math.random() * 50 + "px";
}
//
// The function responsible for actually moving our snowflake
//
Snowflake.prototype.update = function () {
// using some trigonometry to determine our x and y position
this.counter += this.speed / 5000;
this.xPos += this.sign * this.speed * Math.cos(this.counter) / 40;
this.yPos += Math.sin(this.counter) / 40 + this.speed / 30;
// setting our snowflake's position
setTranslate3DTransform(this.element, Math.round(this.xPos), Math.round(this.yPos));
// if snowflake goes below the browser window, move it back to the top
if (this.yPos > browserHeight) {
this.yPos = -50;
}
}
//
// A performant way to set your snowflake's position
//
function setTranslate3DTransform(element, xPosition, yPosition) {
var val = "translate3d(" + xPosition + "px, " + yPosition + "px" + ", 0)";
element.style[transformProperty] = val;
}
//
// The function responsible for creating the snowflake
//
function generateSnowflakes() {
// get our snowflake element from the DOM and store it
var originalSnowflake = document.querySelector(".snowflake");
// access our snowflake element's parent container
var snowflakeContainer = originalSnowflake.parentNode;
// get our browser's size
browserWidth = document.documentElement.clientWidth;
browserHeight = document.documentElement.clientHeight;
// create each individual snowflake
for (var i = 0; i < numberOfSnowflakes; i++) {
// clone our original snowflake and add it to snowflakeContainer
var snowflakeCopy = originalSnowflake.cloneNode(true);
snowflakeContainer.appendChild(snowflakeCopy);
// set our snowflake's initial position and related properties
var initialXPos = getPosition(50, browserWidth);
var initialYPos = getPosition(50, browserHeight);
var speed = 5+Math.random()*40;
var radius = 4+Math.random()*10;
// create our Snowflake object
var snowflakeObject = new Snowflake(snowflakeCopy,
radius,
speed,
initialXPos,
initialYPos);
snowflakes.push(snowflakeObject);
}
// remove the original snowflake because we no longer need it visible
snowflakeContainer.removeChild(originalSnowflake);
// call the moveSnowflakes function every 30 milliseconds
moveSnowflakes();
}
//
// Responsible for moving each snowflake by calling its update function
//
function moveSnowflakes() {
for (var i = 0; i < snowflakes.length; i++) {
var snowflake = snowflakes[i];
snowflake.update();
}
// Reset the position of all the snowflakes to a new value
if (resetPosition) {
browserWidth = document.documentElement.clientWidth;
browserHeight = document.documentElement.clientHeight;
for (var i = 0; i < snowflakes.length; i++) {
var snowflake = snowflakes[i];
snowflake.xPos = getPosition(50, browserWidth);
snowflake.yPos = getPosition(50, browserHeight);
}
resetPosition = false;
}
requestAnimationFrame(moveSnowflakes);
}
//
// This function returns a number between (maximum - offset) and (maximum + offset)
//
function getPosition(offset, size) {
return Math.round(-1*offset + Math.random() * (size+2*offset));
}
//
// Trigger a reset of all the snowflakes' positions
//
function setResetFlag(e) {
resetPosition = true;
}
Any help would be appreciated. Again the code works well in IE11 but the same exact code will not work in Chrome.

Categories