Moving element intersecting static element in CSS/Javascript - javascript

Video demo
I'm trying to make a rip-off pacman(ish) game for a project using HTML, CSS and Javascript. Check the attached video for reference. The idea is: whenever one of the food elements will intersect the pacman element (ie. get eaten), the score will increase by one. And whenever a food element gets out of the viewport without going through the pacman element, the player will lose 1 life. I tried implementing this with the Intersection Observer API by observing the food elements as target and making the pacman element the root. But that didn't work.
const obsCB1 = function (entries, observer) {
point++;
console.log(point);
};
const obsOptions1 = {
root: pacman,
threshold: 0.01,
};
const obs1 = new IntersectionObserver(obsCB1, obsOptions1);
obs1.observe(f1);
(The food element initially starts from outside the viewport to the right and moves to the left using translateX in an infinite loop)
The callback was called once at the start even when f1 didn't intersect pacman. And the point didn't change afterwards. How can I implement this functionality?

Assuming both pacmen and the dots are circles, I guess the best is to calculate the distance between to center and check if it less the sum of radiuses
const board = document.querySelector('#board');
const score = board.querySelector('#score-val');
let dots = [];
function updateScore(x){score.innerText = +score.innerText+x}
function Dot(){
this.SPEED = (.008 + Math.random()/1000) * window.innerHeight;
this.RADIUS = .02 * window.innerHeight;
this.el = document.createElement('span');
this.el.style = `display: inline-block;width:${2*this.RADIUS}px;height:${2*this.RADIUS}px;position:absolute;left:${window.innerWidth}px;top:${Math.random()*(window.innerHeight-2*this.RADIUS)}px;border-radius:50%;background-color: red;`;
board.appendChild(this.el);
dots.push(this);
this.remove = () => {
dots = dots.filter(d => d!== this);
this.el.remove();
}
this.move = () => {
this.el.style.left = parseFloat(this.el.style.left)-this.SPEED+'px';
if(pacmen.isCollapse(this)){
this.remove();
updateScore(1);
} else
if(parseFloat(this.el.style.left)<0) { this.remove();
updateScore(-1);
}
}
}
function Pacmen(){
this.SPEED = .05 * window.innerHeight;
this.RADIUS = .07 * window.innerHeight;
this.el = board.querySelector('#pacmen');
this.el.style.left=0;
this.el.style.top=0;
this.el.width=this.RADIUS*2;
this.el.height=this.RADIUS*2;
this.move = x => {
this.el.style.top = Math.min(window.innerHeight-this.RADIUS*2, Math.max(0, parseFloat(this.el.style.top)+x*this.SPEED))+'px';
}
window.addEventListener('keydown', (e)=> {
switch(e.key){
case 'ArrowDown':
e.preventDefault();
this.move(1);
break;
case 'ArrowUp':
e.preventDefault();
this.move(-1);
break;
}
});
this.isCollapse = (dot) => {
return Math.sqrt(Math.pow(parseFloat(this.el.style.left + this.RADIUS)-parseFloat(dot.el.style.left + dot.RADIUS),2)+Math.pow(parseFloat(this.el.style.top + this.RADIUS)-parseFloat(dot.el.style.top + dot.RADIUS),2)) <= this.RADIUS + dot.RADIUS
}
}
const pacmen = new Pacmen();
function frame(){
dots.forEach(dot =>dot.move());
requestAnimationFrame(frame);
}
setInterval(()=>new Dot(), 1000);
frame();
html,body{padding:0;margin:0}
#board{
display:block;
width:100%;
height:100vh;
background-color:black;
position: relative;
overflow:hidden;
}
#pacmen {
position: absolute;
}
#score{
position:absolute;
bottom: 0px;
right: 20px;
color: white;
font-family: sans-serif;
}
<div id="board">
<img id="pacmen" src="data:image/gif;base64,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"/>
<h3 id="score">Score: <span id="score-val">0</span><h3>
</div>

Related

Match image container width with image width after it is rendered - React

I am using react-image-marker which overlays marker on image inside a div.
<ImageMarker
src={props.asset.url}
markers={markers}
onAddMarker={(marker) => setMarkers([...markers, marker])}
className="object-fit-contain image-marker__image"
/>
The DOM elements are as follows:
<div class=“image-marker”>
<img src=“src” class=“image-marker__image” />
</div>
To make vertically long images fit the screen i have added css to contain the image within div
.image-marker {
width: inherit;
height: inherit;
}
.image-marker__image {
object-fit:contain !important;
width: inherit;
height: inherit;
}
But now the image is only a subpart of the entire marker area. Due to which marker can be added beyond image bounds, which i do not want.
How do you think i can tackle this. After the image has been loaded, how can i change the width of parent div to make sure they have same size and markers remain in the image bounds. Please specify with code if possible
Solved it by adding event listeners in useLayoutEffect(). Calculating image size info after it is rendered and adjusting the parent div dimensions accordingly. Initially and also when resize occurs.
If you think you have a better solution. Do specify.
useLayoutEffect(() => {
const getRenderedSize = (contains, cWidth, cHeight, width, height, pos) => {
var oRatio = width / height,
cRatio = cWidth / cHeight;
return function () {
if (contains ? (oRatio > cRatio) : (oRatio < cRatio)) {
this.width = cWidth;
this.height = cWidth / oRatio;
} else {
this.width = cHeight * oRatio;
this.height = cHeight;
}
this.left = (cWidth - this.width) * (pos / 100);
this.right = this.width + this.left;
return this;
}.call({});
}
const getImgSizeInfo = (img) => {
var pos = window.getComputedStyle(img).getPropertyValue('object-position').split(' ');
return getRenderedSize(true,
img.width,
img.height,
img.naturalWidth,
img.naturalHeight,
parseInt(pos[0]));
}
const imgDivs = reactDom.findDOMNode(imageCompRef.current).getElementsByClassName("image-marker__image")
if (imgDivs) {
if (imgDivs.length) {
const thisImg = imgDivs[0]
thisImg.addEventListener("load", (evt) => {
if (evt) {
if (evt.target) {
if (evt.target.naturalWidth && evt.target.naturalHeight) {
let renderedImgSizeInfo = getImgSizeInfo(evt.target)
if (renderedImgSizeInfo) {
setOverrideWidth(Math.round(renderedImgSizeInfo.width))
}
}
}
}
})
}
}
function updateSize() {
const thisImg = imgDivs[0]
if (thisImg){
let renderedImgSizeInfo = getImgSizeInfo(thisImg)
if (renderedImgSizeInfo) {
setOverrideWidth((prev) => {
return null
})
setTimeout(() => {
setOverrideWidth((prev) => {
return setOverrideWidth(Math.round(renderedImgSizeInfo.width))
})
}, 390);
}
}
}
window.addEventListener('resize', updateSize);
return () => window.removeEventListener('resize', updateSize);
}, [])

adding snow effect to specific div only

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>

I want to be able to pause a block in place when I click on it

Here is the website with the game/source code and want to try and see if i can pause a block as it falls when i left click it with my mouse but not sure the proper function for it. ( https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/By_example/Raining_rectangles )
You must clearTimeout() to make it paused, I have implemented a toggle on click of box i.e play/pause.
(function() {
"use strict"
window.addEventListener("load", setupAnimation, false);
var gl,
timer,
rainingRect,
scoreDisplay,
missesDisplay,
status,
paused = false;
function setupAnimation(evt) {
window.removeEventListener(evt.type, setupAnimation, false);
if (!(gl = getRenderingContext()))
return;
gl.enable(gl.SCISSOR_TEST);
rainingRect = new Rectangle();
timer = setTimeout(drawAnimation, 17);
document.querySelector("canvas")
.addEventListener("click", playerClick, false);
var displays = document.querySelectorAll("strong");
scoreDisplay = displays[0];
missesDisplay = displays[1];
status = displays[2];
}
var score = 0,
misses = 0;
function drawAnimation() {
gl.scissor(rainingRect.position[0], rainingRect.position[1],
rainingRect.size[0], rainingRect.size[1]);
gl.clear(gl.COLOR_BUFFER_BIT);
rainingRect.position[1] -= rainingRect.velocity;
if (rainingRect.position[1] < 0) {
misses += 1;
missesDisplay.innerHTML = misses;
rainingRect = new Rectangle();
}
// We are using setTimeout for animation. So we reschedule
// the timeout to call drawAnimation again in 17ms.
// Otherwise we won't get any animation.
timer = setTimeout(drawAnimation, 17);
}
function playerClick(evt) {
// We need to transform the position of the click event from
// window coordinates to relative position inside the canvas.
// In addition we need to remember that vertical position in
// WebGL increases from bottom to top, unlike in the browser
// window.
var position = [
evt.pageX - evt.target.offsetLeft,
gl.drawingBufferHeight - (evt.pageY - evt.target.offsetTop),
];
// if the click falls inside the rectangle, we caught it.
// Increment score and create a new rectangle.
var diffPos = [position[0] - rainingRect.position[0],
position[1] - rainingRect.position[1]
];
if (diffPos[0] >= 0 && diffPos[0] < rainingRect.size[0] &&
diffPos[1] >= 0 && diffPos[1] < rainingRect.size[1]) {
score += 1;
scoreDisplay.innerHTML = score;
// rainingRect = new Rectangle();
if (!paused) {
clearTimeout(timer)
paused = true;
status.innerHTML = 'Paused';
} else {
timer = setTimeout(drawAnimation, 17);
paused = false;
status.innerHTML = 'Playing';
}
}
}
function Rectangle() {
// Keeping a reference to the new Rectangle object, rather
// than using the confusing this keyword.
var rect = this;
// We get three random numbers and use them for new rectangle
// size and position. For each we use a different number,
// because we want horizontal size, vertical size and
// position to be determined independently.
var randNums = getRandomVector();
rect.size = [
5 + 120 * randNums[0],
5 + 120 * randNums[1]
];
rect.position = [
randNums[2] * (gl.drawingBufferWidth - rect.size[0]),
gl.drawingBufferHeight
];
rect.velocity = 1.0 + 6.0 * Math.random();
rect.color = getRandomVector();
gl.clearColor(rect.color[0], rect.color[1], rect.color[2], 1.0);
function getRandomVector() {
return [Math.random(), Math.random(), Math.random()];
}
}
function getRenderingContext() {
var canvas = document.querySelector("canvas");
canvas.width = canvas.clientWidth;
canvas.height = canvas.clientHeight;
var gl = canvas.getContext("webgl") ||
canvas.getContext("experimental-webgl");
if (!gl) {
var paragraph = document.querySelector("p");
paragraph.innerHTML = "Failed to get WebGL context." +
"Your browser or device may not support WebGL.";
return null;
}
gl.viewport(0, 0,
gl.drawingBufferWidth, gl.drawingBufferHeight);
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
return gl;
}
})();
<style>
body {
text-align: center;
}
canvas {
display: block;
width: 280px;
height: 210px;
margin: auto;
padding: 0;
border: none;
background-color: black;
}
button {
display: block;
font-size: inherit;
margin: auto;
padding: 0.6em;
}
</style>
<p>You caught
<strong>0</strong>.
You missed
<strong>0</strong>
Status
<strong>Playing</strong>.</p>
<canvas>Your browser does not seem to support
HTML5 canvas.</canvas>
As you can see in the code the function drawAnimation() is calling itself every 17ms using setTimeout() JavaScript function (and this is what creates steady animation).
function drawAnimation() {
.
.
.
timer = setTimeout(drawAnimation, 17);
}
In order to pause/stop the animation you would need to use JavaScript function clearTimeout(timer). Since you want to stop/pause the animation on click event you could just reuse the function playerClick (evt) { ... } from the code you already have and put the function clearTimeout(timer) there.
function playerClick (evt) {
.
.
.
clearTimeout(timer);
}
If you want to be able to continue with animation after you have paused it you'll need to implement some switch-logic (pause if it is already playing, play if it is already paused) inside your function playerClick (evt) or to use timers to continue the animation after some time, for example.

How do I see if one element is touching another in JavaScript without clicking anything

OK so I've tried one thing from a different question and it worked, but not the way I wanted it to. it didn't work the way I wanted it to! You literally had to click when two objects were touching so it would alert you, if somebody can figure out a way to detect if two elements are touching without having to click that would be a life saver! So I hope you people who read this request please respond if you know how. this is the code below. so one object is moving and i want it to make it stop when the object hits the player (i am making a game) the movement is by px.... i want it to keep testing if one object hits the player, and if it does i want it to stop everything.
var boxes = document.querySelectorAll('.box');
boxes.forEach(function (el) {
if (el.addEventListener) {
el.addEventListener('click', clickHandler);
} else {
el.attachEvent('onclick', clickHandler);
}
})
var detectOverlap = (function () {
function getPositions(elem) {
var pos = elem.getBoundingClientRect();
return [[pos.left, pos.right], [pos.top, pos.bottom]];
}
function comparePositions(p1, p2) {
var r1, r2;
if (p1[0] < p2[0]) {
r1 = p1;
r2 = p2;
} else {
r1 = p2;
r2 = p1;
}
return r1[1] > r2[0] || r1[0] === r2[0];
}
return function (a, b) {
var pos1 = getPositions(a),
pos2 = getPositions(b);
return comparePositions(pos1[0], pos2[0]) && comparePositions(pos1[1], pos2[1]);
};
})();
function clickHandler(e) {
var elem = e.target,
elems = document.querySelectorAll('.box'),
elemList = Array.prototype.slice.call(elems),
within = elemList.indexOf(elem),
touching = [];
if (within !== -1) {
elemList.splice(within, 1);
}
for (var i = 0; i < elemList.length; i++) {
if (detectOverlap(elem, elemList[i])) {
touching.push(elemList[i].id);
}
}
if (touching.length) {
console.log(elem.id + ' touches ' + touching.join(' and ') + '.');
alert(elem.id + ' touches ' + touching.join(' and ') + '.');
} else {
console.log(elem.id + ' touches nothing.');
alert(elem.id + ' touches nothing.');
}
}
this is my video game right now (please do not copy)
<!DOCTYPE html>
/
<html>
<form id="player" class="box">
</form>
<button type="button" class="up" onclick="moveup()">^</button>
<button type="button" class="down" onclick="movedown()">v
</button>
<style src="style.css">
#player {
width: 300px;
height: 100px;
background-color: blue;
display: inline-block;
position: relative;
bottom: -250px;
left: 200px;
}
.up {
position: relative;
bottom: -400px;
}
.down {
position: relative;
bottom: -420px;
}
body {
background-color: black;
}
#car {
width: 300px;
height: 100px;
background-color: red;
display: inline-block;
position: relative;
bottom: -250px;
left: 600px;
}
</style>
<form id="car" class="box"></form>
<script>
imgObj = document.getElementById('player');
imgObj.style.position= 'relative';
imgObj.style.bottom = '-250px';
function moveup() {
imgObj.style.position= 'relative';
imgObj.style.bottom = '-250px';
imgObj.style.bottom = parseInt(imgObj.style.bottom) + 70 + 'px';
}
function movedown() {
imgObj.style.position= 'relative';
imgObj.style.bottom = '-250px';
imgObj.style.bottom = parseInt(imgObj.style.bottom) + -120 + 'px';
}
myMove();
function myMove() {
var elem = document.getElementById("car");
var pos = 0;
var id = setInterval(frame, 5);
function frame() {
if (pos == 1000) {
clearInterval(id);
myMove();
} else {
pos++;
elem.style.left = pos + "px";
elem.style.left = pos + "px";
}
}
}
/* please do not copy; this is it so far i want the red box when it hits the player(blue box) to stop everything that is happening */
/* made by Jsscripter; car game */
</script>
</html>
Intersection observer. API was largely developed because of news feeds and infinite scrolling. Goal was to solve when something comes into view, load content. Also is a great fit for a game.
The Intersection Observer API lets code register a callback function
that is executed whenever an element they wish to monitor enters or
exits another element (or the viewport), or when the amount by which
the two intersect changes by a requested amount. This way, sites no
longer need to do anything on the main thread to watch for this kind
of element intersection, and the browser is free to optimize the
management of intersections as it sees fit.
https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API
All major browsers except safari support the API. For backwards compatibility and Safari support can use the polyfill from W3C found here. Check out this example from MDN:
var callback = function(entries, observer) {
entries.forEach(entry => {
// Each entry describes an intersection change for one observed
// target element:
// entry.boundingClientRect
// entry.intersectionRatio
// entry.intersectionRect
// entry.isIntersecting
// entry.rootBounds
// entry.target
// entry.time
});
};
var options = {
root: document.querySelector('#scrollArea'),
rootMargin: '0px',
threshold: 1.0
}
var observer = new IntersectionObserver(callback, options);
var target = document.querySelector('#listItem');
observer.observe(target);
See this in action here: https://codepen.io/anon/pen/OqpeMV

How can I use requestAnimationFrame along with setInterval?

See the following snippet of code.
It creates a loader on click of a button. But the animation is not smooth.
I have recently read about requestAnimationFrame function which can do this job. But how can I use it to replace setInterval altogether since there is no way to specify time in requestAnimationFrame function.
Can it be used in conjunction with setInterval ?
let idx = 1;
const timetoEnd = 5000;
function ProgressBar(width){
this.width = width || 0;
this.id = `pBar-${idx++}`;
this.create = () => {
let pBar = document.createElement('div');
pBar.id = this.id;
pBar.className = `p-bar`;
pBar.innerHTML = `<div class="loader"></div>`;
return pBar;
};
this.animator = () => {
let element = document.querySelector(`#${(this.id)} div`);
if(this.width < 100){
this.width++;
element.style.width = `${this.width}%`;
} else {
clearInterval(this.interval);
}
};
this.animate = () => {
this.interval = setInterval(this.animator, timetoEnd/100);
}
}
function addLoader (){
let bar1 = new ProgressBar(40);
let container = document.querySelector("#container");
container.appendChild(bar1.create());
bar1.animate();
}
.p-bar{
width: 400px;
height: 20px;
background: 1px solid #ccc;
margin: 10px;
overflow:hidden;
border-radius: 4px;
}
.p-bar .loader{
width: 0;
background: #1565C0;
height: 100%;
}
<input type="button" value="Add loader" onclick="addLoader()" />
<div id="container"></div>
You are right, requestAnimationFrame is the recommended way to avoid UI jam when doing animation.
You can remember the absolute starting time at start instead of trying to do this at each frame. Then it's just a matter of computing a width based on the delta time between start and current time.
Also, document.querySelector is considered a relatively "heavy" operation so I added this.element to avoid doing it at each frame.
Here is how to new width is computed: ((100 - this.startWidth) / timetoEnd) * deltaT + this.startWidth
100 - this.startWidth is the total amount of width we have to animate
(100 - this.startWidth) / timetoEnd is how much width each second must add to (1)
((100 - this.startWidth) / timetoEnd) * deltaT is how much width we have to add to (1)
We just have to shift the whole thing this.startWidth px to have the frame's width
Also notice that some of this computation is constant and do not have to be computed on each frame, which I left as an exercise :)
Here is your slightly adapted code:
let idx = 1;
const timetoEnd = 5000;
function ProgressBar(startWidth){
this.startWidth = startWidth || 0;
this.id = `pBar-${idx++}`;
this.create = () => {
let pBar = document.createElement('div');
pBar.id = this.id;
pBar.className = `p-bar`;
pBar.innerHTML = `<div class="loader"></div>`;
return pBar;
};
this.animator = () => {
const deltaT = Math.min(new Date().getTime() - this.start, timetoEnd);
if(deltaT < timetoEnd){
const width = ((100 - this.startWidth) / timetoEnd) * deltaT + this.startWidth;
this.element.style.width = `${width}%`;
requestAnimationFrame(this.animator.bind(this))
}
};
this.animate = () => {
this.element = document.querySelector(`#${(this.id)} div`);
this.start = new Date().getTime();
this.animator();
}
}
function addLoader (){
let bar1 = new ProgressBar(40);
let container = document.querySelector("#container");
container.appendChild(bar1.create());
bar1.animate();
}
.p-bar{
width: 400px;
height: 20px;
background: 1px solid #ccc;
margin: 10px;
overflow:hidden;
border-radius: 4px;
}
.p-bar .loader{
width: 0;
background: #1565C0;
height: 100%;
}
<input type="button" value="Add loader" onclick="addLoader()" />
<div id="container"></div>

Categories