Element not changing position when updating the style top and left [duplicate] - javascript

This question already has answers here:
Why does jQuery or a DOM method such as getElementById not find the element?
(6 answers)
Closed 6 years ago.
This code below should change the position of the element but fails to do so.
//variables
var p1 = document.getElementById("player");
var px = 10;
var py = 10;
//#variables
//functions
function start() {
setInterval(update, 100);
}
function update() {
p1.style.left = (px + "px");
p1.style.top = (py + "px");
px = px + 10;
py = py + 10;
}
//#functions
//start
start();
//#start`
my html is:
<html>
<head>
<title> jumpy </title>
<style type="text/css">
#player {
position: absolute;
background-color: white;
width: 100px;
height: 100px;
border-radius: 50%;
}
#body {
background-color: black;
}
</style>
</head>
<body id="body">
<script src="jumpy.js"> </script>
<div id="player"> </div>
</body>
</html>
Even though the position is absolute it still does not work.
I have seen the answer at the bottom but it did not fix my problem.

Your problem is that your script is run before your element is created. As a result document.getElementById("player"); returns nothing (the element is not loaded yet).
Move the script tag at the end of your body, or wrap your code in
window.addEventListener("load", function(){
// Your code.
});
Appart from this, your code works fine (except that requestAnimationFrame should be used to create animation with js, not setInterval). If it does not, the problem is somewhere else.
window.addEventListener("load", function(){
//variables
var p1 = document.getElementById("player");
var px = 10;
var py = 10;
//#variables
//functions
function start() {
setInterval(update, 100);
}
function update() {
p1.style.left = (px + "px");
p1.style.top = (py + "px");
px = px + 10;
py = py + 10;
}
//#functions
//start
start();
//#start`
});
#player {
position: absolute;
background-color: white;
width: 100px;
height: 100px;
border-radius: 50%;
}
body {
background-color: black;
}
<div id="player"></div>

Related

How do I animate the change of the width of a div in JavaScript?

I have some sort of poll where you vote either YES and NO and based on the votes it creates a poll chart (by creating two divs inside another div that has a set width and setting the width of the first two divs the percentage of YES and NO votes out of the total votes). You can see the project for a better understanding by clicking HERE.
I want it to appear animated as if it were in CSS with transition: width 100ms linear; just like here:
<!DOCTYPE html>
<html>
<head>
<title>Document</title>
<style>
.poll{
height: 50px;
width: 300px;
background-color: black;
transition: all 300ms;
}
.poll:hover{
width: 500px;
}
</style>
</head>
<body>
<div class="poll"></div>
</body>
</html>
However, whenever I add something similar to the class of my divs I see no change. The divs in question are created in this function:
function renderPoll(){
container.innerHTML=''; //reset container
let poll1 = document.createElement('div');
let poll2 = document.createElement('div');
poll1.classList.add('poll-attr');
poll2.classList.add('poll-attr');
let innerTextPoll = Math.round(calcPerc()); //calcPerc() calculates the percent of YES votes with the equation percentage = (100*NumberOfYES)/NumberOfVotes
poll1.style.width = calcPerc() + '%';
poll2.style.width = 100-calcPerc() + '%';
poll1.innerText = innerTextPoll + '%';
poll2.innerText = 100-innerTextPoll + '%';
container.appendChild(poll1);
container.appendChild(poll2);
}
I am not nearly experienced enough to figure this out so any input is appreciated!
Bulding on your code and #Noel MarĂ³ti answer, indeed all you have to do is set interval for animating the polls after you add them to the container.
function renderPoll() {
container.innerHTML = ''; //reset container
let poll1 = document.createElement('div');
let poll2 = document.createElement('div');
poll1.classList.add('poll-attr');
poll2.classList.add('poll-attr');
let innerTextPoll = Math.round(calcPerc()); //calcPerc() calculates the percent of YES
poll1.innerText = innerTextPoll + '%';
poll2.innerText = 100 - innerTextPoll + '%';
container.appendChild(poll1);
container.appendChild(poll2);
var target_length = 300;
animation(poll1, 0, (calcPerc()) * target_length / 100);
animation(poll2, 0, (100 - calcPerc()) * target_length / 100);
}
function calcPerc() {
return 75;
}
function animation(elem, from, to) {
let id = null;
let width = from || 0;
var speed = 2.5;
requestAnimationFrame(frame);
function frame() {
if (width < to) {
width += speed;
elem.style.width = width + "px";
requestAnimationFrame(frame);
}
}
}
renderPoll();
.poll-attr {
border: 1px solid blue;
height: 50px;
background: lightyellow;
}
.poll {
height: 50px;
width: 300px;
background-color: black;
transition: all 300ms;
}
.poll:hover {
width: 500px;
}
<div class="poll"></div>
<div id="container"></div>
You can do it easily like this:
function animation () {
let id = null;
const elem = document.querySelector(".poll");
let width = 300; // default width
clearInterval(id);
id = setInterval(frame, 5); // changing the number will effect the speed of the animation
function frame() {
if (width == 500) { // if the width is 500px, then finish animation
clearInterval(id); // finish animation
} else {
width++;
elem.style.width = width + "px";
}
}
}

the use of `setTimeout` in the code below? [duplicate]

This question already has answers here:
Why is setTimeout(fn, 0) sometimes useful?
(19 answers)
Closed 3 years ago.
I notice that when I remove setTimeout from the code below, the animation stops working. Can someone explain to me what's happening under the hood?
Why is it necessary to insert this setTimeout here, even though the time value is 0? In my mind, it should execute right away, so why insert setTimeout(cb,0)?
setTimeout(() => {
... //see below for code
}, 0);
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
.message-ball {
font-size: 20px;
line-height: 200px;
text-align: center;
}
.circle {
transition-property: width, height, margin-left, margin-top;
transition-duration: 2s;
position: fixed;
transform: translateX(-50%) translateY(-50%);
background-color: red;
border-radius: 50%;
}
</style>
</head>
<body>
<button onclick="go()">Click me</button>
<script>
function go() {
showCircle(150, 150, 100, div => {
div.classList.add('message-ball');
div.append("Hello, world!");
});
}
function showCircle(cx, cy, radius, callback) {
let div = document.createElement('div');
div.style.width = 0;
div.style.height = 0;
div.style.left = cx + 'px';
div.style.top = cy + 'px';
div.className = 'circle';
document.body.append(div);
setTimeout(() => {
div.style.width = radius * 2 + 'px';
div.style.height = radius * 2 + 'px';
div.addEventListener('transitionend', function handler() {
div.removeEventListener('transitionend', handler);
callback(div);
});
}, 0);
}
</script>
</body>
</html>
Because transitions only happen when a property was changed. If you do
div.style.width = 0;
div style.width = "1000px";
That wil not animate anything as the property was directly changed. If you defer the second update, it will detect the change and cause a transition to happen.,

HTMLElement.offsetTop doesn't work in subpixel precision

I am writing a small program where I move DOMs at a specified speed.
When I move it at the rate of 20px per second, the offset that gets added to the elem.style.top is about 0.3px per frame.
The problem is, when this offset is smaller than 0.5px, elem doesn't move!
I constructed a simplified example that can demonstrate the issue in my program:
var requestFrameAnimationId;
function myMove(offset) {
var elem = document.getElementById("animate");
requestFrameAnimationId = animationLoop(frame);
function frame() {
console.log(elem.offsetTop);
if (elem.offsetTop === 350) {
cancelAnimationFrame(requestFrameAnimationId);
} else {
elem.style.top = elem.offsetTop + offset + 'px';
elem.style.left = elem.offsetLeft + offset + 'px';
}
}
}
function animationLoop(render) {
var running, lastFrame = +new Date(); // casting Date to Number
function loop(now) {`enter code here`
requestFrameAnimationId = requestAnimationFrame(loop);
running = render(now - lastFrame);
lastFrame = now;
}
loop(lastFrame);
}
#container {
width: 400px;
height: 400px;
position: relative;
background: yellow;
}
#animate {
width: 50px;
height: 50px;
position: absolute;
background-color: red;
}
<!DOCTYPE html>
<html>
<body>
<p>
<button onclick="myMove(0.3)">Move at 0.3px per frame</button>
<button onclick="myMove(0.5)">Move at 0.5px per frame</button>
</p>
<div id="container">
<div id="animate"></div>
</div>
</body>
</html>
Try clicking on Move at 0.5px per frame. The rectangle should be moving.
Reset it by clicking on Run code snippet.
Now try clicking on Move at 0.3px per frame.
It should be moving the DOM more slowly, but you can see that the DOM is not moving.
It's strange because when I initially kept track of the top position in a javascript variable topPos, and applied ${topPos + offset} to elem.style.top, it worked at even slower speeds!
So my guess is that elem.offsetTop rounds the decimal values, so 0.3 becomes 0, and 0.5 becomes 1.
What can I do to make it so that the DOM moves precisely at the specified speed? I can't use any libraries for this one.
EDIT: I looked more into the problem and I believe it's offsetTop that rounds the numbers to integers.
However, I found out that CSS OM spec changed the type of offsetTop to float, and the Chromium team was working on applying the change on the browser more than 4 years ago, and it seems that it should be fixed by now.
Why is it not working on my program, and how can I make it work?
EDIT2: I found from CSSOM working draft that the type of offsetTop was integer.
readonly attribute long offsetTop;
I think they only changed the type of scrollTop and scrollLeft to a double precision number.
attribute unrestricted double scrollTop;
attribute unrestricted double scrollLeft;
HTMLElement.offset[Left | Top] return long typed value (i.e integer).
Use Element.getBoundingClientRect if you want float values.
var requestFrameAnimationId;
function myMove(offset) {
var elem = document.getElementById("animate");
requestFrameAnimationId = animationLoop(frame);
function frame() {
// build up our own high precision offsetTop
var parentRect = elem.offsetParent && elem.offsetParent.getBoundingClientRect() || {top: 0, left:0};
var elemRect = elem.getBoundingClientRect();
var rect = {
top: elemRect.top - parentRect.top,
left: elemRect.left - parentRect.left
};
if (rect.top >= 350) {
cancelAnimationFrame(requestFrameAnimationId);
} else {
// so we can substract it here
elem.style.top = (rect.top + offset) + 'px';
elem.style.left = (rect.left + offset) + 'px';
}
}
}
function animationLoop(render) {
var running, lastFrame = +new Date(); // casting Date to Number
function loop(now) {
requestFrameAnimationId = requestAnimationFrame(loop);
running = render(now - lastFrame);
lastFrame = now;
}
loop(lastFrame);
}
#container {
width: 400px;
height: 400px;
position: relative;
background: yellow;
}
#animate {
width: 50px;
height: 50px;
position: absolute;
background-color: red;
}
<p>
<button onclick="myMove(0.3)">Move at 0.3px per frame</button>
<button onclick="myMove(0.5)">Move at 0.5px per frame</button>
</p>
<div id="container">
<div id="animate"></div>
</div>
Or simply add up your values to a variable:
var requestFrameAnimationId;
function myMove(offset) {
var elem = document.getElementById("animate");
requestFrameAnimationId = animationLoop(frame);
var pos = 0;
function frame() {
pos += offset;
if (pos >= 350) {
cancelAnimationFrame(requestFrameAnimationId);
} else {
elem.style.top = pos + 'px';
elem.style.left = pos + 'px';
}
}
}
function animationLoop(render) {
var running, lastFrame = +new Date(); // casting Date to Number
function loop(now) {
requestFrameAnimationId = requestAnimationFrame(loop);
running = render(now - lastFrame);
lastFrame = now;
}
loop(lastFrame);
}
#container {
width: 400px;
height: 400px;
position: relative;
background: yellow;
}
#animate {
width: 50px;
height: 50px;
position: absolute;
background-color: red;
}
<p>
<button onclick="myMove(0.3)">Move at 0.3px per frame</button>
<button onclick="myMove(0.5)">Move at 0.5px per frame</button>
</p>
<div id="container">
<div id="animate"></div>
</div>

video on canvas keeps resizing

I'm having trouble resizing this canvas that is displaying a video. After resizing, it continually jerks all around into different sizes between the "before" and "after" window sizes.
I tried this posts' idea, and that seemed to calm down Chrome a little, but had no affect on Firefox.
This other post gave me some ideas, but still didn't fix it. It seems like I'm either calling resize multiple times in a loop (which I don't see), or the canvas's context doesn't know how to settle on the final size. Any ideas?
<!DOCTYPE html>
<html>
<head>
<title>overflow</title>
<style>
#c {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
width: 100%;
height: 100%;
z-index: 1;
}
#hold {
position: fixed;
}
#v {
position: absolute;
height: auto;
width: 100%;
z-index: 0;
}
#see {
position: relative;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 2;
}
</style>
</head>
<body>
<canvas id=c></canvas>
<div id=hold>
<video id=v>
</video>
</div>
<canvas id=see></canvas>
<script>
window.onload = start;
function start() {
var v = document.getElementById('v');
var house = document.getElementById('hold');
var base = document.getElementById('c');
var canvas = base.getContext('2d');
var cover = document.getElementById('see');
var canvastwo = cover.getContext('2d');
v.src=("keyed.ogv")
v.load();
v.play();
resize();
function resize() {
var wth = (window.innerWidth * 0.65);
house.width = wth;
house.height = (wth * 9/16);
house.style.marginTop=((window.innerHeight/2) - (house.height/2) + "px");
house.style.marginLeft=((window.innerWidth/2) - (house.width/2) + "px");
cover.width = (wth/2);
cover.height = (house.height/2);
cover.style.marginTop=((window.innerHeight/2) - (cover.height/2) + "px");
cover.style.marginLeft=((window.innerWidth/2) - (cover.width/2) + "px");
var rw = cover.width;
var rh = cover.height;
canvastwo.clearRect(0, 0, rw, rh);
draw(v, canvastwo, rw, rh);
}
window.onresize = resize;
function draw(o,j,w,h) {
if(v.paused || v.ended) return false;
j.drawImage(o,0,0,w,h);
setTimeout(draw,20,o,j,w,h);
}
}
</script>
</body>
</html>
You seem to lock in the old values you use for the setTimeout function the way you are using it here, as the context changes. So when you re-size the loop still uses the old values which no longer corresponds with the new sizes and results in the video toggle between these sizes.
Try to more "globalize" the values so that the loop call is clean when it comes to arguments. This way you are sure the variables contains the correct values for each round.
Also change setTimeout with requestAnimationFrame to make the loop more low-level (efficient) and fluid as this syncs to the monitor's vblank gap. This is particular important with video as you otherwise will get skipped frames as setTimeout is not able to sync with monitor.
Here is the essential code you need to change:
/// put these into you start block to keep them "global"
/// for the functions within it.
var w, h;
Change this part in the resize function:
/// ...
w = cover.width;
h = cover.height;
canvastwo.clearRect(0, 0, w, h);
/// argument free call to draw:
draw();
And finally the loop:
function draw() {
if(v.paused || v.ended) return false;
canvastwo.drawImage(v,0,0,w,h);
requestAnimationFrame(draw);
}
This will remove the jerking video and also make the update in sync to the monitor like the video element itself does.
ONLINE DEMO

Javascript Oscillation

I have this following animation:
<!DOCTYPE HTML>
<html>
<head>
<style>
.example_path {
position: relative;
overflow: hidden;
width: 530px;
height: 30px;
border: 3px solid #000;
}
.example_path .example_block {
position: absolute;
background-color: blue;
width: 30px;
height: 20px;
padding-top: 10px;
text-align: center;
color: #fff;
font-size: 10px;
white-space: nowrap;
}
</style>
<script>
function move(elem) {
var left = 0
function frame() {
left+=10 // update parameters
elem.style.left = left + 'mm' // show frame
if (left == 10000) // check finish condition
clearInterval(id)
}
var id = setInterval(frame, 1) // draw every 1ms
}
</script>
</head>
<body>
<div onclick="move(this.children[0])" class="example_path">
<div class="example_block"></div>
</div>
</body>
</html>
as you see, the blue block moves out of the rectangle if it crosses it. how do i have the blue block oscillate about the rectangular border to and fro keeping the speed constant throughout ...
(in my case the speed is 10 m/s aka 10 mm/ms)
You need to update code as: Here is working JSfiddle
function move(elem) {
var left = 0
var fwdMove = true;
function frame() {
if (left < 0) {
fwdMove = true;
} else if (left > 520) {
fwdMove = false;
}
fwdMove?left += 10:left -= 10
elem.style.left = left + 'px' // show frame
}
var id = setInterval(frame, 1) // draw every 1ms
}
We begin by adding a variable to track the direction that we're heading in. We don't want to modify how fast you're moving, so we use a positive or negative 1 to affect the position.
var direction = 1; // 1=Right, -1=Left
var left = 0
function frame() {
left+=(10 * direction); // update parameters
Because mm are a print-unit, and we're working in the browser, we'll change it to use px. If you really need to use mm, you'll have to find a way of converting between them for the box to stop at the appropriate spot.
elem.style.left = left + 'px' // show frame
Finally, we check whether we've gone past the bounds of the box, and if so, we put it back in the box and reverse the direction;
if (left <= 0) {
direction = 1; // Begin moving to the left
left = 0; // Put the box back in the path
} else if (left >= (530 - 20)) {
direction = -1; // Begin moving to the right
left = (530 - 20); // Put the box back in the path
}
JSFiddle.

Categories

Resources