elem.getBoundingClientRect() broken on mobile screen - javascript

I'm developing a website using gatsby.js and it involves a slide-in animation as you scroll down. I wrote code that worked perfectly until I opened dev tools and tried it using the device toolbar.
here's a reproduction demo as well as a webpage to make it easier
https://getboundingclientrect-is-broken.netlify.app
<div class="0 space"></div>
<p class="1 slideFR"></p>
<div id="boy" class="2 slideFL"></div>
<p class="3 slideFR"></p>
<div class="4 slideFL"></div>
<div class="flx space"></div>
.slideFR {
width: 100px;
height: 100px;
background-color: #957b26;
position: relative;
left: 450px;
transform: translateX(1000px);
}
.slideFL {
width: 100px;
height: 100px;
background-color: #26958f;
position: relative;
left: 300px;
transform: translateX(-1000px);
}
.inSight {
transition: all 0.5s;
transform: translateX(0);
}
.space {
width: 100px;
height: 1500px;
background-color: aquamarine;
}
let elemsFL = document.getElementsByClassName("slideFL");
var leftiesLoaded = Array.from( { length: elemsFL.length }, (_, i) => false ); // creates array length of elemsFL full of <false>
let elemsFR = document.getElementsByClassName("slideFR");
var rightersLoaded = Array.from( { length: elemsFR.length }, (_, i) => false ); // creates array length of elemsFR full of <false>
document.addEventListener("scroll", function (event) {
let windowHeight = window.outerHeight;
console.log( "%c/* ----------- scroll ---------- */", "color: purple; font-weight: bold" );
checkIfInSight(elemsFL, leftiesLoaded, windowHeight);
checkIfInSight(elemsFR, rightersLoaded, windowHeight);
});
/* -------------------------------- touchmove ------------------------------- */
document.addEventListener("touchmove", function (event) {
let windowHeight = window.outerHeight;
console.log( "%c/* ---------- touchmove --------- */", "color: red; font-weight: bold" );
checkIfInSight(elemsFL, leftiesLoaded, windowHeight);
checkIfInSight(elemsFR, rightersLoaded, windowHeight);
});
function checkIfInSight(elemArray, boolArray, windowHeight) {
for (let counter = 0; counter < elemArray.length; counter++) {
const elem = elemArray[counter];
let elemRect = elem.getBoundingClientRect();
let elemPosTop = elemRect.top;
let elemPosBottom = elemPosTop + elem.scrollHeight;
if (elemPosTop <= windowHeight && elemPosBottom >= 0) {
if (!boolArray[counter]) {
console.log( "%c In Sight", "color: green", elem.classList[0] );
boolArray[counter] = true;
elem.classList.add("inSight");
} else {
console.log( "%c In Sight And Loaded", "color: yellow", elem.classList[0] );
}
} else {
console.log( elem.classList[0], "\tOut Of Sight", elemPosTop, "<=", windowHeight, "&&", elemPosBottom, ">=0\t\t\t", elem.offsetTop );
boolArray[counter] = false;
elem.classList.remove("inSight");
}
}
}
Edit:
As I'm troubleshooting this I replaced elem.offsetTop with window.scrollY which indeed made me realize that for some reason the it is not interpreting the scroll action as actually scrolling for quite a while. I still don't know what I'm doing wrong or what the issue is

thanks to EmielZuurbier's comment I found the solution IntersectionObserver API was the way to go. I even produced cleaner more optimized code.
HTML
<div class="0 space"></div>
<p class="1 slideFR toSlide"></p>
<div id="boy" class="2 slideFL toSlide"></div>
<p class="3 slideFR toSlide"></p>
<div class="4 slideFL toSlide"></div>
<div class=" space"></div>
JS
const slideDivs = document.querySelectorAll(".toSlide");
const options={
root: null,
rootMargin: "0px 2000px",
};
const observer= new IntersectionObserver(function(entries, observer){
entries.forEach(entry =>{
console.log(entry.target.classList[0],entry.isIntersecting, entry.intersectionRect);
if (entry.isIntersecting ){
entry.target.classList.add("inSight");
}else {
entry.target.classList.remove("inSight");
}
});
},options);
slideDivs.forEach(slideDiv => {
observer.observe(slideDiv);
});
CSS
.slideFR {
width: 100px;
height: 100px;
background-color: #957b26;
position: relative;
left: 200px;
transform: translateX(1000px);
}
.slideFL {
width: 100px;
height: 100px;
background-color: #26958f;
position: relative;
left: 150px;
/* visibility: hidden; */
transform: translateX(-1000px);
}
.inSight {
transition: all 0.5s;
transform: translateX(0);
}
.space {
width: 100px;
height: 1500px;
background-color: aquamarine;
}

Related

How to translateX to a position without removing transition property?

I want to translateX to a position if change is more than -150, but due to having transition property in container it shows the animation of travelling to the new translate value. I want it to have directly jump to the -400px translateX value without showing the animation to going to it and still have the transition property in place for future scrolls
const config = {
individualItem: '.slide', // class of individual item
carouselWidth: 400, // in px
carouselId: '#slideshow', // carousel selector
carouselHolderId: '#slide-wrapper', // carousel should be <div id="carouselId"><div id="carouselHolderId">{items}</div></div>
}
document.addEventListener("DOMContentLoaded", function(e) {
// Get items
const el = document.querySelector(config.individualItem);
const elWidth = parseFloat(window.getComputedStyle(el).width) + parseFloat(window.getComputedStyle(el).marginLeft) + parseFloat(window.getComputedStyle(el).marginRight);
// Track carousel
let mousedown = false;
let movement = false;
let initialPosition = 0;
let selectedItem;
let currentDelta = 0;
document.querySelectorAll(config.carouselId).forEach(function(item) {
item.style.width = `${config.carouselWidth}px`;
});
document.querySelectorAll(config.carouselId).forEach(function(item) {
item.addEventListener('pointerdown', function(e) {
mousedown = true;
selectedItem = item;
initialPosition = e.pageX;
currentDelta = parseFloat(item.querySelector(config.carouselHolderId).style.transform.split('translateX(')[1]) || 0;
});
});
const scrollCarousel = function(change, currentDelta, selectedItem) {
let numberThatFit = Math.floor(config.carouselWidth / elWidth);
let newDelta = currentDelta + change;
let elLength = selectedItem.querySelectorAll(config.individualItem).length - numberThatFit;
if(newDelta <= 0 && newDelta >= -elWidth * elLength) {
selectedItem.querySelector(config.carouselHolderId).style.transform = `translateX(${newDelta}px)`;
// IMPORTANT LINE
if(newDelta <= 0 && newDelta <= -150) {
selectedItem.querySelector(config.carouselHolderId).style.transform = `translateX(-1000px)`;
}
}
}
document.body.addEventListener('pointermove', function(e) {
if(mousedown == true && typeof selectedItem !== "undefined") {
let change = -(initialPosition - e.pageX);
scrollCarousel(change, currentDelta, document.body);
movement = true;
}
});
['pointerup', 'mouseleave'].forEach(function(item) {
document.body.addEventListener(item, function(e) {
selectedItem = undefined;
movement = false;
});
});
});
.slide-wrapper {
transition: 400ms ease;
transform: translateX(0px);
width: 400px;
height: 400px;
}
.slide-number {
pointer-events: none;
}
<!DOCTYPE html>
<html>
<head>
<title>HTML and CSS Slideshow</title>
<style>
body {
font-family: Helvetica, sans-serif;
padding: 5%;
text-align: center;
font-size: 50;
overflow-x: hidden;
}
/* Styling the area of the slides */
#slideshow {
overflow: hidden;
height: 400px;
width: 400px;
margin: 0 auto;
}
/* Style each of the sides
with a fixed width and height */
.slide {
float: left;
height: 400px;
width: 400px;
}
/* Add animation to the slides */
.slide-wrapper {
/* Calculate the total width on the
basis of number of slides */
width: calc(728px * 4);
/* Specify the animation with the
duration and speed */
/* animation: slide 10s ease infinite; */
}
/* Set the background color
of each of the slides */
.slide:nth-child(1) {
background: green;
}
.slide:nth-child(2) {
background: pink;
}
.slide:nth-child(3) {
background: red;
}
.slide:nth-child(4) {
background: yellow;
}
/* Define the animation
for the slideshow */
#keyframes slide {
/* Calculate the margin-left for
each of the slides */
20% {
margin-left: 0px;
}
40% {
margin-left: calc(-728px * 1);
}
60% {
margin-left: calc(-728px * 2);
}
80% {
margin-left: calc(-728px * 3);
}
}
</style>
</head>
<body>
<!-- Define the slideshow container -->
<div id="slideshow">
<div id="slide-wrapper" class="slide-wrapper">
<!-- Define each of the slides
and write the content -->
<div class="slide">
<h1 class="slide-number">
1
</h1>
</div>
<div class="slide">
<h1 class="slide-number">
2
</h1>
</div>
<div class="slide">
<h1 class="slide-number">
3
</h1>
</div>
<div class="slide">
<h1 class="slide-number">
4
</h1>
</div>
</div>
</div>
</body>
</html>
If I understood your question properly, I think you would have to remove the transition property before changing the value and then apply it again once the transition is done.
const item = selectedItem.querySelector(config.carouselHolderId)
item.style.cssText = `transform: translateX(${newDelta}px); transition: none`;
// Restore the transition
item.style.transition = '';
You could temporarily disable the transition:
const config = {
individualItem: '.slide', // class of individual item
carouselWidth: 400, // in px
carouselId: '#slideshow', // carousel selector
carouselHolderId: '#slide-wrapper', // carousel should be <div id="carouselId"><div id="carouselHolderId">{items}</div></div>
}
document.addEventListener("DOMContentLoaded", function(e) {
// Get items
const el = document.querySelector(config.individualItem);
const elWidth = parseFloat(window.getComputedStyle(el).width) + parseFloat(window.getComputedStyle(el).marginLeft) + parseFloat(window.getComputedStyle(el).marginRight);
// Track carousel
let mousedown = false;
let movement = false;
let initialPosition = 0;
let selectedItem;
let currentDelta = 0;
document.querySelectorAll(config.carouselId).forEach(function(item) {
item.style.width = `${config.carouselWidth}px`;
});
document.querySelectorAll(config.carouselId).forEach(function(item) {
item.addEventListener('pointerdown', function(e) {
mousedown = true;
selectedItem = item;
initialPosition = e.pageX;
currentDelta = parseFloat(item.querySelector(config.carouselHolderId).style.transform.split('translateX(')[1]) || 0;
});
});
const scrollCarousel = function(change, currentDelta, selectedItem) {
let numberThatFit = Math.floor(config.carouselWidth / elWidth);
let newDelta = currentDelta + change;
let elLength = selectedItem.querySelectorAll(config.individualItem).length - numberThatFit;
if(newDelta <= 0 && newDelta >= -elWidth * elLength) {
selectedItem.querySelector(config.carouselHolderId).style.transform = `translateX(${newDelta}px)`;
// IMPORTANT LINE
if(newDelta <= 0 && newDelta <= -150) {
const el = selectedItem.querySelector(config.carouselHolderId);
el.classList.add("jump");
el.style.transform = `translateX(-1000px)`;
setTimeout(() => el.classList.remove("jump"), 10);
}
}
}
document.body.addEventListener('pointermove', function(e) {
if(mousedown == true && typeof selectedItem !== "undefined") {
let change = -(initialPosition - e.pageX);
scrollCarousel(change, currentDelta, document.body);
movement = true;
}
});
['pointerup', 'mouseleave'].forEach(function(item) {
document.body.addEventListener(item, function(e) {
selectedItem = undefined;
movement = false;
});
});
});
.slide-wrapper {
transform: translateX(0px);
width: 400px;
height: 400px;
transition: 400ms ease;
user-select: none;
}
.slide-number {
pointer-events: none;
}
.slide-wrapper.jump
{
transition-duration: 10ms;
}
<!DOCTYPE html>
<html>
<head>
<title>HTML and CSS Slideshow</title>
<style>
body {
font-family: Helvetica, sans-serif;
padding: 5%;
text-align: center;
font-size: 50;
overflow-x: hidden;
}
/* Styling the area of the slides */
#slideshow {
overflow: hidden;
height: 400px;
width: 400px;
margin: 0 auto;
}
/* Style each of the sides
with a fixed width and height */
.slide {
float: left;
height: 400px;
width: 400px;
}
/* Add animation to the slides */
.slide-wrapper {
/* Calculate the total width on the
basis of number of slides */
width: calc(728px * 4);
/* Specify the animation with the
duration and speed */
/* animation: slide 10s ease infinite; */
}
/* Set the background color
of each of the slides */
.slide:nth-child(1) {
background: green;
}
.slide:nth-child(2) {
background: pink;
}
.slide:nth-child(3) {
background: red;
}
.slide:nth-child(4) {
background: yellow;
}
/* Define the animation
for the slideshow */
#keyframes slide {
/* Calculate the margin-left for
each of the slides */
20% {
margin-left: 0px;
}
40% {
margin-left: calc(-728px * 1);
}
60% {
margin-left: calc(-728px * 2);
}
80% {
margin-left: calc(-728px * 3);
}
}
</style>
</head>
<body>
<!-- Define the slideshow container -->
<div id="slideshow">
<div id="slide-wrapper" class="slide-wrapper">
<!-- Define each of the slides
and write the content -->
<div class="slide">
<h1 class="slide-number">
1
</h1>
</div>
<div class="slide">
<h1 class="slide-number">
2
</h1>
</div>
<div class="slide">
<h1 class="slide-number">
3
</h1>
</div>
<div class="slide">
<h1 class="slide-number">
4
</h1>
</div>
</div>
</div>
</body>
</html>

How to translate position absolute div on top of another regardless of scroll

I want to show a picture, and some text. When user hovers on text, I want picture to show some other picture. I have other pictures and when user hovers on text I want the changed picture to be the currently visible picture on the viewport.
I have a div on the page, and I want another div to hover on top of the first. I got it working, but it breaks on scroll. I want the position: absolute style to stay and want to transform using translate. The reason I need to compute the bounds and translate, is the .onpage div changes so where I have to show it changes.
let $onpage = document.getElementById('onpage');
let $onhover = document.getElementById('onhover');
let $triggers = document.querySelectorAll('.trigger');
function fTranslate(pos) {
return el => {
el.style.transform = `translate(${pos[0]}px,${pos[1]}px)`;
};
}
function show() {
let bounds = $onpage.getBoundingClientRect();
let pos = [bounds.left, bounds.top];
fTranslate(pos)($onhover);
}
$triggers.forEach(_ => {
_.addEventListener('mouseover', _ => {
show();
});
});
.on-hover {
position: absolute;
top: 0;
left: 0;
}
.on-page {
width: 50vmin;
height: 50vmin;
background: red;
}
.on-hover {
width: 40vmin;
height: 40vmin;
background: #ccc;
}
.content {
height: 10000px;
}
#trigger1 {
margin-top: 10px;
}
#trigger2 {
margin-top: 100px;
}
<div class="wrap">
<div id='onpage' class="on-page"></div>
<span id="trigger1" class="trigger">Hover 1</span>
<p>Some content
<span id="trigger2" class="trigger">Hover 2</span>
</p>
<div id='onhover' class="on-hover"></div>
<div class="content"></div>
</div>
here you go you just had to calculate the scroll offset of the window
let $onpage = document.getElementById('onpage');
let $onhover = document.getElementById('onhover');
function fTranslate(pos) {
return el => {
el.style.transform = `translate(${pos[0]}px,${pos[1]}px)`;
};
}
let bounds = $onpage.getBoundingClientRect();
let pos = [bounds.left, bounds.top];
fTranslate(pos)($onhover);
document.addEventListener('scroll', () => {
let bounds = $onpage.getBoundingClientRect();
const scrollLeft =
window.pageXOffset || document.documentElement.scrollLeft;
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
const offsetLeft = bounds.left + scrollLeft;
const offsetTop = bounds.top + scrollTop;
let pos = [offsetLeft, offsetTop];
fTranslate(pos)($onhover);
});
.on-hover {
position: absolute;
top: 0;
left: 0;
}
.on-page {
width: 50vmin;
height: 50vmin;
background: red;
}
.on-hover {
width: 40vmin;
height: 40vmin;
background: #ccc;
}
.content {
height: 10000px;
}
<div class="wrap">
<div id='onpage' class="on-page"></div>
<div id='onhover' class="on-hover"></div>
<div class="content"></div>
</div>
Please let me know if I misunderstood. If you are looking to get the grey square to remain fixed, you can change the values in your translate to be negative to give you a positive offset.
let $onpage = document.getElementById('onpage');
let $onhover = document.getElementById('onhover');
function fTranslate(pos) {
return el => {
el.style.transform = `translate(${-pos[0]}px,${-pos[1]}px)`;
};
}
let bounds = $onpage.getBoundingClientRect();
let pos = [bounds.left, bounds.top];
fTranslate(pos)($onhover);
document.addEventListener('scroll', () => {
let bounds = $onpage.getBoundingClientRect();
let pos = [bounds.left, bounds.top];
fTranslate(pos)($onhover);
});
.on-hover {
position: absolute;
top: 0;
left: 0;
}
.on-page {
width: 50vmin;
height: 50vmin;
background: red;
}
.on-hover {
width: 40vmin;
height: 40vmin;
background: #ccc;
}
.content {
height: 10000px;
}
<div class="wrap">
<div id='onpage' class="on-page"></div>
<div id='onhover' class="on-hover"></div>
<div class="content"></div>
</div>

I've got a js appear effect issue

This is js and there is 4 articles should appear one at a time when I scroll down, but it doesn't work after aticle no.2. what did I wrong?
function scrollAppear(){
var main = document.querySelector("main");
var art = main.querySelectorAll("article");
var artPos1 = art[0].getBoundingClientRect().top;
var artPos2 = art[1].getBoundingClientRect().top;
var artPos3 = art[2].getBoundingClientRect().top;
var artPos4 = art[3].getBoundingClientRect().top;
var artPos5 = art[4].getBoundingClientRect().top;
var screenPos = window.innerHeight /1.3;
if (artPos1>600 && artPos1<700) {
art[0].classList.add('appear');
}
else if (artPos2<500) {
art[1].classList.add('appear');
}
else if (artPos3<800) {
art[2].classList.add('appear');
}
else if (artPos4<600) {
art[3].classList.add('appear');
}
else if (artPos5<600) {
art[4].classList.add('appear');
}
}
window.addEventListener('scroll',scrollAppear);
If they all need to appear at the same spot, shouldn't the same condition work for each?
if (artPos1>600 && artPos1<700) {
art[0].classList.add('appear');
}
else if (artPos2>600 && artPos2<700) {
art[1].classList.add('appear');
}
else if (artPos3>600 && artPos3<700) {
art[2].classList.add('appear');
}
else if (artPos4>600 && artPos4<700) {
art[3].classList.add('appear');
}
else if (artPos5>600 && artPos5<700) {
art[4].classList.add('appear');
}
Consider the following jQuery example.
function scrollAppear() {
var main = $("#main");
var art = $("article");
var screenPos = $(window).scrollTop();
art.each(function(i, el) {
if (screenPos >= $(el).css("top").slice(0, -2) - 120) {
$(el).fadeIn(100).addClass("appear");
}
});
}
window.addEventListener('scroll', scrollAppear);
#main {
position: relative;
height: 2000px;
}
article {
width: 100%;
height: 100px;
position: absolute;
display: none;
}
.red {
background: #F00;
top: 600px;
}
.orange {
background: #F60;
top: 700px;
}
.yellow {
background: #FF0;
top: 800px;
}
.green {
background: #0F0;
top: 900px;
}
.blue {
background: #00F;
top: 1000px;
}
.purple {
background: #F0F;
top: 1100px;
}
.appear {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="main">
<article class="red">
</article>
<article class="orange">
</article>
<article class="yellow">
</article>
<article class="green">
</article>
<article class="blue">
</article>
<article class="purple">
</article>
</div>
You can use .scrollTop():
Get the current vertical position of the scroll bar for the first element in the set of matched elements or set the vertical position of the scroll bar for every matched element.
https://api.jquery.com/scrolltop/
Use .each() to iterate over each element and make it appear if scrolling has crossed the proper threshold on the screen.

Check if DOM elements are present inside a DIV then run functions assigned to those elements in order

i'm trying to develop a game using html, css and js. At the moment I'm focusing on manipulating DOM elements without using the canvas tag. My idea is to create a pseudo graphical programming language, similar to the Blockly environment. So far I have inserted 3 clickable elements inside #toolbox that create their copies in #workspace.
Now, I am trying to assign functions to the elements present in #workspace, which once pressed the Run button are executed in order of appearance, so as to create a queue of commands that is able to move the pink square inside #output_section.
Therefore I cannot understand how to write the function that is able to verify the presence of the elements and then be able to perform the different functions assigned to these elements.
Any ideas? :D
I'm using Jquery 3.3.1
function addRed() {
var redWorkspace = document.createElement("DIV");
redWorkspace.className = "remove-block block red";
document.getElementById("workspace").appendChild(redWorkspace);
};
function addBlue() {
var blueWorkspace = document.createElement("DIV");
blueWorkspace.className = "remove-block block blue";
document.getElementById("workspace").appendChild(blueWorkspace);
};
function addGreen() {
var greenWorkspace = document.createElement("DIV");
greenWorkspace.className = "remove-block block green";
document.getElementById("workspace").appendChild(greenWorkspace);
};
$("#clear_workspace").click(function () {
$("#workspace").empty();
});
$(document).on("click", ".remove-block", function () {
$(this).closest("div").remove();
});
html,
body {
margin: 0;
padding: 0;
}
#workspace {
display: flex;
height: 100px;
padding: 10px;
background: black;
}
#toolbox {
display: flex;
padding: 10px;
width: 300px;
}
#output_section {
height: 500px;
width: 500px;
border: solid black;
margin: 10px;
position: relative;
}
#moving_square {
position: absolute;
bottom: 0;
right: 0;
width: 100px;
height: 100px;
background: pink;
}
.block {
height: 100px;
width: 100px;
}
.red {
background: red;
}
.blue {
background: cyan;
}
.green {
background: green;
}
.grey {
background: #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<html>
<body>
<div id="workspace"></div>
<div id="workspace-menu">
<button id="run_workspace">Run</button>
<button id="clear_workspace">Clear</button>
</div>
<div id="toolbox" class="grey">
<div onclick="addRed()" class="block red">Left</div>
<div onclick="addBlue()" class="block blue">Up</div>
<div onclick="addGreen()" class="block green">Right</div>
</div>
<div id="output_section">
<div id="moving_square"></div>
</div>
</body>
</html>
Completely untested but run button does something along the lines of:
$("#run_workspace").click(function() {
$("#workspace .block").each(function(elem) {
if (elem.hasClass("red")) {
moveObjectLeft();
} else if (elem.hasClass("green")) {
moveObjectRight();
} else if (elem.hasClass("blue")) {
moveObjectUp();
}
});
});
Commonly, it's a good idea to store all required information in arrays and objects, and use HTML only to display your data.
Also, if you are already using jQuery - use it for all 100%)
Made some improvements:
let mobs = {
pinky: {
node: $('#moving_square'),
coors: { top: 400, left: 400 },
step: 30,
moveQueue: [],
// moveTimeout ???
},
}; // storing here all created objects, that must move.
/* Each [moveQueue] array will store the chain of moves, like ["up", "up", "left"]
You can take each "key-word" of move, and get required function buy that key,
from the 'move' object */
let move = { // Think about how to simlify this object and functions. It's possible!)
left: function (obj) {
let left = obj.coors.left = (obj.coors.left - obj.step);
obj.node.css('left', left + 'px');
},
up: function (obj) {
let top = obj.coors.top = (obj.coors.top - obj.step);
obj.node.css('top', top + 'px');
},
right: function (obj) {
let left = obj.coors.left = (obj.coors.left + obj.step);
obj.node.css('left', left + 'px');
}
};
let stepTimeout = 1000;
let running = false;
let timeouts = {}; // store all running timeouts here,
// and clear everything with for( key in obj ) loop, if required
$('#toolbox .block').on('click', function () {
let color = $(this).attr('data-color');
let workBlock = '<div class="remove-block block ' + color + '"></div>';
$('#workspace').append(workBlock);
mobs.pinky.moveQueue.push( $(this).text().toLowerCase() ); // .attr('data-direction');
// instead of pinky - any other currently selected object
// $(this).text().toLowerCase() — must be "left", "up", "right"
});
$('#run_workspace').on('click', function () {
running = true;
runCode();
function runCode() {
for (let obj in mobs) { // mobile objects may be multiple
// Inside the loop, obj == mobs each key name. Here it's == "pinky"
let i = 0;
let pinky = mobs[obj];
localRun();
function localRun() {
let direction = pinky.moveQueue[i]; // getting direction key by array index.
move[direction](pinky); // calling the required function from storage.
if (pinky.moveQueue[++i] && running ) {
// self-calling again, if moveQueue has next element.
// At the same time increasing i by +1 ( ++i )
timeouts[obj] = setTimeout(localRun, stepTimeout);
}
}
}
}
});
$("#clear_workspace").click(function () {
$("#workspace").empty();
});
$('#workspace').on("click", ".remove-block", function () {
$(this).closest("div").remove();
});
html,
body {
margin: 0;
padding: 0;
}
#workspace {
display: flex;
height: 100px;
padding: 10px;
background: black;
}
#toolbox {
display: flex;
padding: 10px;
width: 300px;
}
#output_section {
height: 500px;
width: 500px;
border: solid black;
margin: 10px;
position: relative;
}
#moving_square {
position: absolute;
top: 400px;
left: 400px;
width: 100px;
height: 100px;
background: pink;
}
.block {
height: 100px;
width: 100px;
}
.red {
background: red;
}
.blue {
background: cyan;
}
.green {
background: green;
}
.grey {
background: #ccc;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="workspace"></div>
<div id="workspace-menu">
<button id="run_workspace">Run</button>
<button id="clear_workspace">Clear</button>
</div>
<div id="toolbox" class="grey">
<div data-color="red" class="block red">Left</div>
<div data-color="blue" class="block blue">Up</div>
<div data-color="green" class="block green">Right</div>
</div>
<div id="output_section">
<div id="moving_square"></div>
</div>
But... jQuery was used only for clicks... Translation to JS:
let mobs = {
pinky: {
node: document.getElementById('moving_square'),
coors: { top: 400, left: 400 },
step: 30,
moveQueue: [],
},
};
let move = {
left: function (obj) {
let left = obj.coors.left = (obj.coors.left - obj.step);
obj.node.style.left = left + 'px';
},
up: function (obj) {
let top = obj.coors.top = (obj.coors.top - obj.step);
obj.node.style.top = top + 'px';
},
right: function (obj) {
let left = obj.coors.left = (obj.coors.left + obj.step);
obj.node.style.left = left + 'px';
}
};
let stepTimeout = 1000;
let running = false;
let timeouts = {};
let blocks = document.querySelectorAll('#toolbox .block');
let workSpace = document.getElementById('workspace');
blocks.forEach(function(block){
block.addEventListener('click', function(){
let color = this.dataset.color;
let workBlock = '<div class="remove-block block ' + color + '"></div>';
workSpace.insertAdjacentHTML('beforeend', workBlock);
mobs.pinky.moveQueue.push( this.textContent.toLowerCase() );
});
});
document.getElementById('run_workspace').addEventListener('click', function () {
running = true;
runCode();
function runCode() {
for (let obj in mobs) { // mobile objects may be multiple
// Inside the loop, obj == mobs each key name. Here it's == "pinky"
let i = 0;
let pinky = mobs[obj];
localRun();
function localRun() {
let direction = pinky.moveQueue[i]; // getting direction key by array index.
move[direction](pinky); // calling the required function from storage.
if (pinky.moveQueue[++i] && running ) {
// self-calling again, if moveQueue has next element.
// At the same time increasing i by +1 ( ++i )
timeouts[obj] = setTimeout(localRun, stepTimeout);
}
}
}
}
});
document.getElementById("clear_workspace").addEventListener('click', function () {
workSpace.textContent = "";
});
workSpace.addEventListener('click', function (e) {
if( e.target.classList.contains('remove-block') ){
e.target.remove();
}
});
html,
body {
margin: 0;
padding: 0;
}
#workspace {
display: flex;
height: 100px;
padding: 10px;
background: black;
}
#toolbox {
display: flex;
padding: 10px;
width: 300px;
}
#output_section {
height: 500px;
width: 500px;
border: solid black;
margin: 10px;
position: relative;
}
#moving_square {
position: absolute;
top: 400px;
left: 400px;
width: 100px;
height: 100px;
background: pink;
}
.block {
height: 100px;
width: 100px;
}
.red {
background: red;
}
.blue {
background: cyan;
}
.green {
background: green;
}
.grey {
background: #ccc;
}
<div id="workspace"></div>
<div id="workspace-menu">
<button id="run_workspace">Run</button>
<button id="clear_workspace">Clear</button>
</div>
<div id="toolbox" class="grey">
<div data-color="red" class="block red">Left</div>
<div data-color="blue" class="block blue">Up</div>
<div data-color="green" class="block green">Right</div>
</div>
<div id="output_section">
<div id="moving_square"></div>
</div>

Why overheating on scrolling with SVG effect

I create a SVG blur effect on my page sections. You can see in below snippet. However, my CPU is overloaded and overheating my laptop when I scroll several times. I check complex animation websites without this problem. What's my problem? Is there a problem with my codes?
$(window).on('load', function() {
$('.blurred-bg').each(function(index, value) {
var filterName = $(this).attr('id') + 'filtering';
$(this).prepend(
'<svg class="svgs" xmlns="http://www.w3.org/2000/svg" version="1.1" height="0"><filter id="' + filterName + '"><feGaussianBlur result="blur"></feGaussianBlur><feMorphology in="blur" operator="dilate" radius="15" result="expanded"/><feMerge><feMergeNode in="expanded"/><feMergeNode in="blur"/></feMerge></filter></svg>'
);
});
$(window).scroll(function(e) {
var distanceScrolled = $(this).scrollTop();
$('.blurred-bg').each(function(index, value) {
var distanceElementTop = ($(this).offset().top);
if (distanceScrolled < distanceElementTop) {
$(('svg.svgs filter feGaussianBlur'), this)[0].setAttribute("stdDeviation", '0');
} else if (distanceScrolled > distanceElementTop) {
var elmHeight = $(this).height();
var sub = elmHeight / 10;
var result = ((distanceScrolled - distanceElementTop) / sub);
if (result >= 0 && result <= 10) {
$(('svg.svgs filter feGaussianBlur'), this)[0].setAttribute("stdDeviation", result);
}
}
});
});
});
section {
position: relative;
width: 100%;
height: 100vh;
background-size: cover;
background-attachment: fixed;
background-repeat: no-repeat;
overflow-x: hidden;
overflow-y: auto;
}
section:before {
background: inherit;
bottom: 0;
content: '';
left: 0;
position: absolute;
right: 0;
top: 0;
}
#first {
background-image: url('https://freephotos.cc/storage/path/1Sujr0vCiT0cQpjbMgmdzAIjtdfEq2lF3bq4U1oo.jpeg');
}
#first:before {
-webkit-filter: url(#firstfiltering);
filter: url(#firstfiltering);
}
#two {
background-image: url('https://freephotos.cc/storage/path/c1haICnHGYIyRK2juLs36iEBoBiJMOarzSQ9CKRv.jpeg');
}
#two:before {
-webkit-filter: url(#twofiltering);
filter: url(#twofiltering);
}
#three {
background-image: url('https://freephotos.cc/storage/path/OpgzqCRPTnxQ5z22gNvNgiF7ZSTzNyPXtXKwVwS4.jpeg');
}
#three:before {
-webkit-filter: url(#threefiltering);
filter: url(#threefiltering);
}
.svgs,
.svg-section {
position: absolute;
width: 0;
height: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<section id="first" class="blurred-bg"></section>
<section id="two" class="blurred-bg"></section>
<section id="three" class="blurred-bg"></section>
https://codepen.io/mehrdadam/pen/NyexLa
UPDATE:
I test this without SVG filter and remove jQuery and set blur filter for section. My CPU overloaded on view of blurred section!
Constantly recalcuating blurs is extremely CPU intensive. A better way to do this is to overlay a highly blurred, transparent copy of the image on top of the original and gradually increase its opacity as you scroll down.

Categories

Resources