I have saw the problem
Moving Div Box using javascript
and the top upvote answer is good .
But if I want to get the feacture like CSS3 does ,how can I do?
.cuble{
height: 100px;
width: 100px;
background-color: blue;
transition: all 3s ease-in-out;
}
.cuble:hover{
transform: translateX(500px);
}
<!Doctype html>
<html>
<body>
<div class="cuble">
</div>
</body>
I don't need ease-in-out effect .I just want hover the div once and even if I mouseout the div ,the div will also moving from left to right until the setting time. But the Moving Div Box using javascript don't meet the requirement.
I try to use Promise to achieve the goal ,here is my Javascript code .(anyway,maybe I just want to learn deep about javascript async performance)
var cuble = document.querySelector('.cuble');
cuble.onmouseover = function(e) {
for (var i = 0; i < 100; i++) {
var pixels = 5 * i + "px";
delay(100)
.then(() => cuble.style.marginLeft = pixels)
}
}
function delay(t) {
return new Promise(function(resolve) {
setTimeout(resolve, t)
});
};
How can I fix my Javascript code and meet the requirement?
The issue is you're creating one hundred promises each with a 100ms delay almost instantly, so they all firing almost instantly.
One way to remedy the issue is increase the delay for each subsequent promise. And you also have to pass the corresponding pixels for each resolve. Something like:
var cuble = document.querySelector('.cuble');
cuble.onmouseover = function(e) {
for (var i = 0; i < 100; i++) {
var pixels = 5 * i + "px";
delay(i * 20, pixels)
.then((p) => cuble.style.marginLeft = p)
}
}
function delay(t, pixels) {
return new Promise(function(resolve) {
setTimeout(() => resolve(pixels), t)
});
};
DEMO
An example with no promises and just one function being created and called, instead of creating new function for every step:
var cuble = document.querySelector('.cuble');
cuble.onmouseover = function() {
var left = 0,
ival = setInterval(move, 5);
function move() {
if (left === 500) clearInterval(ival);
cuble.style.marginLeft = left++ + 'px';
}
}
DEMO
Related
When I dynamically add an item that animates with JS, how do I get them to be in sync on the same timeline as shown here: http://youtube.com/watch?v=AGiTmjFHs8M&t=9m23s ? I saw a tutorial that showed a benefit of using JS animation vs. CSS is they inherit the same timeline.
<div class="body"></div>
<button onclick="addItem()">Add</button>
function addItem() {
let body = document.querySelector('.body');
let newEl = document.createElement('div');
newEl.innerText = "I am a new Item";
newEl.animate([
{
transform: 'translate(0px, 0px)',
transform: 'translate(500px, 500px)',
}
],
{
duration: 2000,
iterations: Infinity,
});
body.appendChild(newEl);
}
If all your Animation objects do share the same duration and you want them to start and end at the same time, you can simply set the iterationStart EffectTiming of your new Animation object to the ComputedTiming .progress value of the already running one.
However beware you must wait for the new Animation object is ready before getting the computed value of the previous one or you'll be one frame late. For this, you can simply await the animation.ready Promise.
To get the previous Animation object, you can either store it when you create it through Element.animate(), or you can access the set of currently running Animations on the document through document.getAnimations(), and pick it from there.
let i = 0;
async function addItem() {
i++;
const body = document.querySelector(".body");
const newEl = document.createElement("div");
newEl.classList.add("item");
newEl.textContent = "I am a new Item " + i;
// get a previous Animation if any
const previous_animation = document.getAnimations()[0];
// create the new Animation object
// slightly offset on the x axis only
// to check if they are indeed in sync
const anim = newEl.animate([{
transform: "translate(" + (i * 10) + "px, 0px)",
transform: "translate(" + (i * 10 + 250) + "px, 250px)",
}], {
duration: 2000,
iterations: Infinity
});
// when it's ready to start
await anim.ready;
// get the current progress of the first Animation object
const computed_timing = previous_animation?.effect.getComputedTiming();
if( computed_timing ) {
// update our new Animation object to the same progress value
anim.effect.updateTiming( {
iterationStart: computed_timing.progress
});
}
body.appendChild(newEl);
}
.item {
position: absolute;
}
<div class="body"></div>
<button onclick="addItem()">Add</button>
Note that as pointed out by user brianskold in a comment below, the startTime property of the Animation can be set to an earlier time. Doing so will make it like the animation did start at this time.
So for synchronizing two animations, this is probably the best way:
let i = 0;
function addItem() {
i++;
const body = document.querySelector(".body");
const newEl = document.createElement("div");
newEl.classList.add("item");
newEl.textContent = "I am a new Item " + i;
// get a previous Animation if any
const previous_animation = document.getAnimations()[0];
// create the new Animation object
// slightly offset on the x axis only
// to check if they are indeed in sync
const anim = newEl.animate([{
transform: "translate(" + (i * 10) + "px, 0px)",
transform: "translate(" + (i * 10 + 250) + "px, 250px)",
}], {
duration: 2000,
iterations: Infinity
});
if( previous_animation ) {
// set the startTime to the same
// as the previously running's one
// note this also forces anim.ready to resolve directly
anim.startTime = previous_animation.startTime;
}
body.appendChild(newEl);
}
.item {
position: absolute;
}
<div class="body"></div>
<button onclick="addItem()">Add</button>
var positioner = 0;
var ames = setInterval(animate, 200);
function animate() {
if(positioner <= 1000){
document.getElementsByTagName('img')[0].style.backgroundPosition='-'+positioner+'px';
positioner += 256;
} else {
document.getElementsByTagName('img')[0].style.backgroundPosition='-'+positioner+'px';
positioner = 0;
}
}
img {
background-image: url('https://cdn-images-1.medium.com/max/1600/1*WhDjw8GiV5o0flBXM4LXEw.png');
background-repeat: no-repeat;
}
<img width="256px" height="256px" onmouseover="animate()"/>
That was my code and currently it's moving automatically and i want to make it just moving onMouseOver! In my opinion, if the setInterval can be put inside that animate() function then this problem will be solved but how to put setInterval inside function??
Here you go, the best way is to handle the mouseOver and mouseOut as listeners and store the setInterval in a variable so you can clear it latter.
var positioner = 0;
var ames;
var img = document.getElementsByTagName('img');
img[0].addEventListener('mouseover', () => {
ames = setInterval(animate, 200);
});
img[0].addEventListener('mouseout', () => {
if (ames)
window.clearInterval(ames);
});
function animate() {
if (positioner <= 1000) {
img[0].style.backgroundPosition='-'+positioner+'px';
positioner += 256;
} else {
img[0].style.backgroundPosition='-'+positioner+'px';
positioner=0;
}
}
img {
background-image: url('https://cdn-images-1.medium.com/max/1600/1*WhDjw8GiV5o0flBXM4LXEw.png');
background-repeat: no-repeat;
}
<img width="256px" height="256px"/>
First of all, animate() is an Element built-in function. If you hover your mouse, you will get some error message in your console.
Mauricio Sipmann's answer works, but he didn't answer your question, 'In my opinion, if the setInterval can be put inside that animate() function then this problem will be solved but how to put setInterval inside function??'.
That's why I give another solution. And my answer is Yes. But Let's rename the animate() to myAnimate() first.
change <img width="256px" height="256px" onmouseover="animate()"/> to <img width="256px" height="256px" onmouseover="myAnimate()"/>
move setInterval() into myAnimate(), but left variable declaration outside.i.e.
var positioner = 0;
var ames;
function myAnimate() {
if (!ames) {
ames = setInterval(myAnimate, 200);
}
if (positioner <= 1000) {
document.getElementsByTagName('img')[0].style.backgroundPosition = '-' + positioner + 'px';
positioner += 256;
} else {
document.getElementsByTagName('img')[0].style.backgroundPosition = '-' + positioner + 'px';
positioner = 0;
}
}
I hope this can help you.
I have code JSFiddle to transition a circle from point A to point B. But, how do I modify code to make it work if the 'move' method is called like below and also circle has to pause for a second at every point. Please help.
myBall.move(50,300).move(150,400).move(100,200).move(130,230);
After a bit of playing around I think I've managed to come up with what you were looking for, this creates a queue of move events which is pushed to each time move is called, this sequence of events is then run in order by the run function (note that I've added a .move(0,0) at the beginning, something strange happens running this in an SO snippet, this also adds a brief delay on SO, but it seems to work fine on JSFiddle without it):
function ball() {
const elem = document.getElementById("ball");
var q = [];
var running = false;
var me = this;
this.move = function(x, y) {
q.push([x, y]);
if (!running) {
running = true;
me.run();
}
return this;
}
this.run = function() {
if (q.length == 0) {
running = false;
} else {
var pos = q.shift();
elem.style.transform = `translate(${pos[0]}px, ${pos[1]}px)`;
setTimeout(me.run, 2000);
}
}
}
let myBall = new ball();
myBall.move(0, 0).move(50, 300).move(150, 400).move(100, 200).move(130, 230);
#ball {
width: 100px;
height: 100px;
border: 1px solid black;
border-radius: 50%;
transition: transform 2s;
}
<div id="ball">
</div>
I am creating a tile based game in javascript. I am absolutely beginner.
The plan is to learn javascript while I trying to make this game. I am having serious lagging issues when I am trying to scroll the "game"
For a live preview you can see here whats bad:
http://iwansfactory.com/tycoon/index.html
My javascript generates them and they HTML part looks like this:
<div class="tiletype100" id="x0y0" style="left: 2151px; top: -540px;"></div>
The css:
.tiletype2 {
z-index: 0;
position: absolute;
width: 600px;
height: 800px;
background-image: url("http://iwansfactory.com/tycoon/road2.png");
background-repeat: no-repeat;
}
The javascript scroll function is this:
var right = document.documentElement.scrollLeft;
var bottom = document.documentElement.scrollTop;
var rightscrollvalue = 0;
var bottomscrollvalue = 0;
function rightenter() {
rightscrollvalue = 40;
scrollright();
}
function leftenter() {
rightscrollvalue = -40;
scrollright();
}
function rightout() {
rightscrollvalue = 0;
}
function scrollright() {
if (rightscrollvalue != 0) {
right = right + rightscrollvalue;
console.log(right);
window.scrollTo(right, bottom);
setTimeout(function() {
scrollright();
}, 50);
}
}
function bottomenter() {
bottomscrollvalue = 40;
scrollbottom();
}
function topenter() {
bottomscrollvalue = -40;
scrollbottom();
}
function bottomout() {
bottomscrollvalue = 0;
}
function scrollbottom() {
if (bottomscrollvalue != 0) {
bottom = bottom + bottomscrollvalue;
console.log(bottom);
window.scrollTo(right, bottom);
setTimeout(function() {
scrollbottom();
}, 50);
}
}
Your design uses large overlapping tiles that are mostly transparent. This requires a lot of CPU power for rendering, thus making the game laggy.
I propose you make your tiles smaller, just as large as they have to be (so there is a non-transparent pixel on every edge of the image) and use offsets for larger tiles so they get rendered at the right position.
I've built a very basic jQuery plugin that essentially positions a sprite image left by a set amount every x milliseconds to create an animation effect. The plugin at this stage is very basic and only has a few options, and it works pretty well.
Apart from that fact that it only fires once! I have multiple instance of the animation on one page and they all fire, but only ever once each.
Now I'm not expert on Javascript and only just managed to cobble this together but here's the code anyhow:
// Animation Plugin
(function($){
$.fn.anime = function(customOptions) {
// Default Options
var defaultOptions = {
slideWidth : 100,
frames : 10,
speed : 40,
minCycles : 1,
stopFrame : 0
};
// Set options to default
var options = defaultOptions;
// Merge custom options with defaults using the setOptions func
setOptions(customOptions);
// Merge current options with the custom option
function setOptions(customOptions) {
options = $.extend(options, customOptions);
};
return this.each(function() {
// Initialize the animation object
var $elem = $('img', this);
var frameCount = 0;
var currentSlideWidth = options.slideWidth;
var intervalID = null;
var cycleCount = 1;
var animate = function() {
if (frameCount < (options.frames -1)) {
$elem.css('left', '-' + currentSlideWidth + 'px');
frameCount++;
currentSlideWidth += options.slideWidth;
}
else {
if (cycleCount < options.minCycles)
{
frameCount = 0;
currentSlideWidth = options.slideWidth;
$elem.css('left', '-' + currentSlideWidth + 'px');
cycleCount++;
}
else
{
window.clearInterval(intervalID);
$elem.css('left', '0px');
}
}
cycleCount = 1;
};
$(this).bind('mouseover', function(){
var intervalID = window.setInterval(animate, options.speed);
});
});
};
})(jQuery);
The code used to call the actual plugin on a dom element is:
$('#animeBolt').anime({
frames: 50,
slideWidth: 62,
minCycles: 1,
speed: 30,
});
This is the html it is called on:
<div class="anime" id="animeBolt">
<img src="_assets/img/anime-bolt.png" alt="Lightning Bolt" width="3100" height="114">
</div>
And finally the css:
.anime {
position: absolute;
overflow: hidden; }
.anime img {
position: absolute;
left: 0;
top: 0; }
#animeBolt {
top: 2669px;
left: 634px;
width: 62px;
height: 116px; }
How do I get the plugin to fire repeatedly?
I've created and modified jsfiddle example using your code. It's working http://jsfiddle.net/9Yz9j/16/
I've change a couple of things:
added clearInterval on mouseover to preven multiple overlapping intervals
moved intervalID variable to outside of each function, and removed var keyword from mousover handler so the script will remember intervalID set on last mouseover
reseting the frameCount, cycleCount and currentSlideWidth variables on animation end (that was actually a clue thing to get animation going more than just once)
Hope that helps