Reputation:
I am creating a plane animation using plain JavaScript.
There is a class Plane
with methods flyRight() and flyLeft(). In both of those class methods I am using setInterval(fucntion() { ... }, 1)
to move the plane every 1 milisecond either to the left or to the right.
I have a problem ensuring that after executing myPlane.flyRight()
, it doesn't execute the myPlane.flyLeft()
- problematic lines in the snippet marked with a comment // DOES NOT WORK
.
class Plane {
constructor(htmlId, speed) {
this.plane = document.getElementById(htmlId); // plane HTML element
this.width = parseInt(document.getElementById(htmlId).offsetWidth); // plane's width
this.speed = speed; // pixels per milisecond
this.range = parseInt(window.innerWidth); // plane's range
}
flyLeft() {
var minLeftPos = 0 - this.width - 10;
var planeSpeed = this.speed;
if (this.plane.style.left === '') {
this.plane.style.left = 0
}
var moveLeft = setInterval(function() {
if (parseInt(this.plane.style.left) >= minLeftPos) {
this.plane.style.left = parseInt(this.plane.style.left) - planeSpeed + 'px';
} else {
clearInterval(moveLeft);
this.plane.style.transform = 'rotate(180deg)'; // turns around
this.flyRight(); // DOES NOT WORK
}
}, 1)
}
flyRight() {
var maxLeftPos = this.range + this.width + 10;
var planeSpeed = this.speed;
if (this.plane.style.left === '') {
this.plane.style.left = 0
}
var moveRight = setInterval(function() {
if (parseInt(this.plane.style.left) <= maxLeftPos) {
this.plane.style.left = parseInt(this.plane.style.left) + planeSpeed + 'px';
} else {
clearInterval(moveRight);
this.plane.style.transform = 'rotate(180deg)'; // turns around
this.flyLeft(); // DOES NOT WORK
}
}, 1)
}
fly() {
this.flyRight();
}
}
myPlane = new Plane("plane", 3);
myPlane.fly();
html, body {
overflow: hidden;
}
.plane {
width: 200px;
height: 168px;
position: absolute;
top: 0;
left: 0;
background-image: url('https://cdn.pixabay.com/photo/2014/04/02/10/22/airplane-303639_960_720.png');
background-position: center;
background-size: cover;
}
<div id="plane" class="plane"></div>
Upvotes: 0
Views: 1071
Reputation: 3177
Change functions to arrow functions to avoid them binding there own this
. By doing so your function within the setInterval
will refer this
to their parent context.
class Plane {
...
flyLeft () {
...
var moveLeft = setInterval(() => {
...
}, 1)
}
flyRight () {
...
var moveRight = setInterval(() => {
...
}, 1)
}
...
}
class Plane {
constructor(htmlId, speed) {
this.plane = document.getElementById(htmlId); // plane HTML element
this.width = parseInt(document.getElementById(htmlId).offsetWidth); // plane's width
this.speed = speed; // pixels per milisecond
this.range = parseInt(window.innerWidth); // plane's range
}
flyLeft () {
var minLeftPos = 0 - this.width - 10;
var planeSpeed = this.speed;
if (this.plane.style.left === '') {
this.plane.style.left = 0
}
var moveLeft = setInterval(() => {
if (parseInt(this.plane.style.left) >= minLeftPos) {
this.plane.style.left = parseInt(this.plane.style.left) - planeSpeed + 'px';
} else {
clearInterval(moveLeft);
this.plane.style.transform = 'rotate(180deg)'; // turns around
this.flyRight(); // DOES NOT WORK
}
}, 1)
}
flyRight () {
var maxLeftPos = this.range + this.width + 10;
var planeSpeed = this.speed;
if (this.plane.style.left === '') {
this.plane.style.left = 0
}
var moveRight = setInterval(() => {
if (parseInt(this.plane.style.left) <= maxLeftPos) {
this.plane.style.left = parseInt(this.plane.style.left) + planeSpeed + 'px';
} else {
clearInterval(moveRight);
this.plane.style.transform = 'rotate(180deg)'; // turns around
this.flyLeft(); // DOES NOT WORK
}
}, 1)
}
fly () {
this.flyRight();
}
}
myPlane = new Plane("plane", 3);
myPlane.fly();
html, body {
overflow: hidden;
}
.plane {
width: 200px;
height: 168px;
position: absolute;
top: 0;
left: 0;
background-image: url('https://cdn.pixabay.com/photo/2014/04/02/10/22/airplane-303639_960_720.png');
background-position: center;
background-size: cover;
}
<div id="plane" class="plane"></div>
Upvotes: 0
Reputation: 5411
I know you are dealing with JavaScript
to run this animation, but let me present you a CSS
only solution using animation
:
html, body {
overflow: hidden;
}
.plane {
transform: translate(-100%);
animation: flyingPlane 10s linear infinite;
width: 200px;
height: 168px;
position: absolute;
top: 0;
left: 0;
background-image: url('https://cdn.pixabay.com/photo/2014/04/02/10/22/airplane-303639_960_720.png');
background-position: center;
background-size: cover;
}
@keyframes flyingPlane {
40% {
transform: translate(100vw);
} 50% {
transform: translate(100vw) scale(-1, 1);
} 90% {
transform: translate(-100%) scale(-1, 1);
}
}
Upvotes: 0
Reputation: 3230
The issue is in the bindings you have within your setInterval
functions (note at the end of the functions I use .bind
). Callback functions used in setInterval
and setTimeout
need to be bound to a scope in order to handle this
.
There is a cleaner way which would involve using arrow functions instead of function(){}
format. The reason that method works is arrow functions preserve the lexical scoping, which is exactly what the bind
functions are doing. So by using arrow functions you don't have to use .bind
at all, it comes for free.
class Plane {
constructor(htmlId, speed) {
this.plane = document.getElementById(htmlId); // plane HTML element
this.width = parseInt(document.getElementById(htmlId).offsetWidth); // plane's width
this.speed = speed; // pixels per milisecond
this.range = parseInt(window.innerWidth); // plane's range
}
flyLeft() {
var minLeftPos = 0 - this.width - 10;
var planeSpeed = this.speed;
if (this.plane.style.left === '') {
this.plane.style.left = 0
}
var moveLeft = setInterval(function() {
if (parseInt(this.plane.style.left) >= minLeftPos) {
this.plane.style.left = parseInt(this.plane.style.left) - planeSpeed + 'px';
} else {
clearInterval(moveLeft);
this.plane.style.transform = 'rotate(180deg)'; // turns around
this.flyRight(); // DOES NOT WORK
}
}.bind(this), 1)
}
flyRight() {
var maxLeftPos = this.range + this.width + 10;
var planeSpeed = this.speed;
if (this.plane.style.left === '') {
this.plane.style.left = 0
}
var moveRight = setInterval(function() {
if (parseInt(this.plane.style.left) <= maxLeftPos) {
this.plane.style.left = parseInt(this.plane.style.left) + planeSpeed + 'px';
} else {
clearInterval(moveRight);
this.plane.style.transform = 'rotate(180deg)'; // turns around
this.flyLeft(); // DOES NOT WORK
}
}.bind(this), 1)
}
fly() {
this.flyRight();
}
}
myPlane = new Plane("plane", 3);
myPlane.fly();
html, body {
overflow: hidden;
}
.plane {
width: 200px;
height: 168px;
position: absolute;
top: 0;
left: 0;
background-image: url('https://cdn.pixabay.com/photo/2014/04/02/10/22/airplane-303639_960_720.png');
background-position: center;
background-size: cover;
}
<div id="plane" class="plane"></div>
Upvotes: 1