0

I'm writing a JS game which moves IMG elements about by setting their x/y/width/height/opacity and allowing the CSS transition to gradually move it to its target and triggers an event when it arrives. This all works well.

The part I'm having trouble with is creating a new IMG and immediately have it start moving to its target with a transition. I've experimented and the best I can get is for the image to be created already at its target location, I suspect because the target styles have replaced the source styles before the IMG has been added to the document.

How can I create an IMG with:

  • Starting x/y/width/height/opacity/etc.
  • Target x/y/width/height/opacity/etc.
  • Transition time.
  • Function to run when it completes the transition.

I hope there's an answer using only plain JS without frameworks such as JQuery, as the objective of this game-writing exercise is to practice JS development.

UPDATE: As requested, one of my failed attempts. I have tried shuffling these comment-separated blocks in various ways but none have produced the desired result of triggering a transition.

function ThrowBall() {

    /* Create and configure an image. */
    var img = document.createElement("img");
    img.src = "https://www.thesunsetlounge.co.uk/Images/DiscoSpot.png"

    /* Set starting style. */
    img.style.position = "fixed";
    img.style.display = "block";
    img.style.zIndex = "999";
    img.style.top = "100px";
    img.style.left = "100px";

    /* Add to document. */
    document.body.appendChild(img);

    /* Set transition. */
    img.style.transition = "all 10s";

    /* Move to target. */
    img.style.left = "300px";
}

/* Run. */
window.onload = ThrowBall;

UPDATE #2:

Thanks to a comment from @Salketer, I was able to resolve my issue by moving the code that sets the CSS and transition-end event into a function and passing that function into window.requestAnimationFrame.

billpg
  • 3,195
  • 3
  • 30
  • 57
  • Please add the part of your code that is displaying the wrong behaviour. We cannot fix code we don't have access to. – Salketer Apr 14 '20 at 10:51
  • On a side note, my guess is that you are setting the starting position and changing it without waiting for a redraw. Have you tried waiting for an animation frame between the creation and the move? – Salketer Apr 14 '20 at 10:52
  • I'm not asking for my code to be fixed. I could post some of my failed experiments but all they would tell you is that I don't understand some aspect of JS/CSS. I had no idea that animation frames are even a thing, so "You need to leave an animation frame before moving the image" would make a perfect answer which would also include some keywords I can type into Google. – billpg Apr 14 '20 at 11:01
  • The thing is that there are 1000 valid reasons why the image would act as it does... Having a failed attempt would help us close up on a readon and a fix. – Salketer Apr 14 '20 at 11:07
  • I'll dig up some failed attempts. They can all be summarised as various permutations, shuffling the steps of: Create an IMG; Set source style; Set target style; Set transition style; Add to document. – billpg Apr 14 '20 at 11:27
  • @Salketer - I've just updated the question with a failed attempt as requested. – billpg Apr 14 '20 at 16:53

1 Answers1

0

The problem you are seeing is that the img is never displayed with left=100px. Its left styling is set to 300px right away, before the changes are drawn on the DOM. Since it never been at 100px, there is no transition needed... You should make it draw at least once at the starting position before moving it.

function ThrowBall() {

    /* Create and configure an image. */
    var img = document.createElement("img");
    img.src = "https://www.thesunsetlounge.co.uk/Images/DiscoSpot.png"

    /* Set starting style. */
    img.style.position = "fixed";
    img.style.display = "block";
    img.style.zIndex = "999";
    img.style.top = "100px";
    img.style.left = "100px";

    /* Add to document. */
    document.body.appendChild(img);

    /* Set transition. */
    img.style.transition = "all 10s";

    /* Move to target. */
    window.requestAnimationFrame(()=>{img.style.left = "300px";});
}

/* Run. */
window.onload = ThrowBall;

Adding a delay between both style definitions should correct the problem. I've used requestAnimationFrame as it is the go-to choice for playing with the DOM, but anything that would run after the initial drawing of the image would work. Like a setTimeout(...,1000); but then you'd see the image static for a second!

Salketer
  • 14,263
  • 2
  • 30
  • 58
  • A single requestAnimationFrame may not do it, it will fire **before** the next paint, so if nothing triggered a reflow during that time, you'll still be in the same state. But anyway, you don't need asynchronicity here, have a look at the dupe. – Kaiido Apr 15 '20 at 13:41
  • `document.body.appendChild(img)` will certainly trigger a reflow, no? While synchronocity is certain to make things happen in the same paint! – Salketer Apr 17 '20 at 11:34
  • No, luckily DOM manipulations don't trigger reflows (imagine if the browsers had to trigger one full reflow per element added... Only the methods that need the updated boxing information will trigger it. – Kaiido Apr 17 '20 at 13:09