When the margin-left changes, SVG begins to flicker and shake in a marquee effect

I've been experimenting with a marquee effect using vanilla JS. The effect is working, but I'm encountering some shaking issues with SVG and images as they move.

<div class="marquee">
  <h1>Nepal <svg version="1.1" viewBox="0 0 36 31" xmlns="http://www.w3.org/2000/svg"><path d="M8.80476 8.17844V12.3988H3.9993V4.25693H8.79846V8.17844H8.80476ZM32.4101 3.87271V0H20.0028V3.87271H16.0035V0H3.60252V3.87271H0V15.497H3.60252V19.76H7.60182V23.2485H11.6011V27.5115H15.6004V31H20.3996V27.5115H24.3989V23.2485H28.3982V19.76H32.3975V15.497H36V3.87271" fill="#DA2269"/></svg> Himalayas <svg version="1.1" viewBox="0 0 36 31" xmlns="http://www.w3.org/2000/svg"><path d="M8.80476 8.17844V12.3988H3.9993V4.25693H8.79846V8.17844H8.80476ZM32.4101 3.87271V0H20.0028V3.87271H16.0035V0H3.60252V3.87271H0V15.497H3.60252V19.76H7.60182V23.2485H11.6011V27.5115H15.6004V31H20.3996V27.5115H24.3989V23.2485H28.3982V19.76H32.3975V15.497H36V3.87271" fill="#DA2269"/></svg> Mountains <svg version="1.1" viewBox="0 0 36 31" xmlns="http://www.w3.org/2000/svg"><path d="M8.80476 8.17844V12.3988H3.9993V4.25693H8.79846V8.17844H8.80476ZM32.4101 3.87271V0H20.0028V3.87271H16.0035V0H3.60252V3.87271H0V15.497H3.60252V19.76H7.60182V23.2485H11.6011V27.5115H15.6004V31H20.3996V27.5115H24.3989V23.2485H28.3982V19.76H32.3975V15.497H36V3.87271" fill="#DA2269"/></svg> Everest</h1>
</div>

<style>
.marquee {
  overflow: hidden;
  border-top: 1px solid #000;
  border-bottom: 1px solid #000;
  display: flex;
}

.marquee h1{
  font-size: 5em;
  white-space: nowrap;
  text-transform: uppercase
}

.marquee h1 svg {
  width: auto;
  height: 60px;
}
</style>

<script>
function Marquee(selector, speed) {
  const parentSelector = document.querySelector(selector);
  const clone = parentSelector.innerHTML;
  const firstElement = parentSelector.children[0];
  let i = 0;
  console.log(firstElement);
  parentSelector.insertAdjacentHTML('beforeend', clone);
  parentSelector.insertAdjacentHTML('beforeend', clone);

  setInterval(function () {
    firstElement.style.marginLeft = `-${i}px`;
    if (i > firstElement.clientWidth) {
      i = 0;
    }
    i = i + speed;
  }, 0);
}

//after window is completed load
//1 class selector for marquee
//2 marquee speed 0.2
window.addEventListener('load', Marquee('.marquee', 0.2))

</script>

Example: https://codepen.io/diegosomar/pen/dyKBGWg

Is there a known solution to prevent the shaking of SVG elements in this scenario?

Answer №1

If you adjust your Interval to update every 100ms like this:

setInterval(function () {
    firstElement.style.marginLeft = `-${i}px`;
    if (i > firstElement.clientWidth) {
      i = 0;
    }
    i = i + speed;
  }, 100);

You will notice a clear demonstration of what is happening. The images not only shake, but individual letters also exhibit the shaking effect. This shaking effect appears to be how browsers, particularly Chrome, render images and letters.

However, upon inspecting the CSS, I observed something intriguing. When I altered the width to width: 70px, the 'shaking' appeared to cease (I opted for 70px as it closely matched the initial size of the heart image). While examining the Heart SVG element in the Inspector tool, I noticed that there was a delay in the width adjustment process when set to width: auto compared to width: 70px. This resulted in a more jittery appearance.

Resorting to using a fixed pixel-size width as a solution to the problem may seem somewhat crude, but there exists a more effective and simpler approach to achieve the desired effect with CSS that could potentially resolve the issue.

Instead of altering the margin-left property every millisecond, you can opt to change it once per second and allow CSS to handle the smooth transition. You can learn more about CSS transitions here.

<style>
.marquee h1 {
  transition: margin-left 1s linear;
}
</style>

<script>
function Marquee(selector, speed) {
  const parentSelector = document.querySelector(selector);
  const clone = parentSelector.innerHTML;
  const firstElement = parentSelector.children[0];
  let i = 0;
  console.log(firstElement);
  parentSelector.insertAdjacentHTML('beforeend', clone);

  setInterval(function () {
    firstElement.style.marginLeft = `-${i}px`;
    if (i > firstElement.clientWidth) {
      i = 0;
    }
    i = i + speed;
  }, 1000); //update every second
}

window.addEventListener('load', Marquee('.marquee', 100)) //every second -100 pixels will be added to `margin-left` of the element
</script>

By incorporating transition into the .marquee h1 style, you can immediately observe the difference. It is important to note that you may want to address the transition following i = 0 in order to prevent a sudden smooth jump from -2000px (the size of firstElement.clientWidth) to 0px.

Answer №2

Swap out setInterval() for requestAnimationFrame()

In a recent post by @Jefferson's answer, it was highlighted that your current use of setInterval() with a zero millisecond delay is causing a jerky animation or transition.
Additionally, this approach leads to inconsistent timing across different browsers such as Firefox and Chromium.

The use of requestAnimationFrame() ensures that style changes are only applied once per frame. In contrast, setInterval() may attempt to modify style properties "in between" frames if the specified interval in milliseconds does not align with a frame.

Here's a sample code snippet

function Marquee(selector, speed) {
  const parentSelector = document.querySelector(selector);
  const clone = parentSelector.innerHTML;
  const firstElement = parentSelector.children[0];
  let i = 0;
  parentSelector.insertAdjacentHTML('beforeend', clone);
  parentSelector.insertAdjacentHTML('beforeend', clone);

  function scrollMarquee(timestamp) {
    timestamp = timestamp || new Date().getTime();
    firstElement.style.marginLeft = `-${i}px`;
    if (i >= firstElement.clientWidth ) {
      i = 0;
    }
    i += speed;
    requestAnimationFrame(scrollMarquee); 
  } 
  requestAnimationFrame(scrollMarquee);
}

// When window loading is complete
// Selector for marquee: '.marquee'
// Speed of marquee: 1
window.addEventListener('load', Marquee('.marquee', 1))
.marquee {
  overflow: hidden;
  border-top: 1px solid #000;
  border-bottom: 1px solid #000;
  display: flex;
}

.marquee h1{
  font-size: 5em;
  white-space: nowrap;
  text-transform: uppercase
}

.marquee h1 svg {
  width: auto;
  height: 60px;
}
<div class="marqueeOuter">
  <div id="marquee" class="marquee">
    <h1> Nepal <svg viewBox="0 0 36 31">
        <path d="m9 8v4h-5v-8h5v4l0 0zm23-4v-4h-12v4h-4v-4h-12v4h-4v11h4v5h4v3h4v5h4v3h4v-3h4v-5h4v-3h4v-5h4v-11" fill="#DA2269" />
      </svg> Himalayas <svg viewBox="0 0 36 31" xmlns="http://www.w3.org/2000/svg">
        <path d="m9 8v4h-5v-8h5v4l0 0zm23-4v-4h-12v4h-4v-4h-12v4h-4v11h4v5h4v3h4v5h4v3h4v-3h4v-5h4v-3h4v-5h4v-11" fill="#DA2269" />
      </svg> Mountains <svg viewBox="0 0 36 31" xmlns="http://www.w3.org/2000/svg">
        <path d="m9 8v4h-5v-8h5v4l0 0zm23-4v-4h-12v4h-4v-4h-12v4h-4v11h4v5h4v3h4v5h4v3h4v-3h4v-5h4v-3h4v-5h4v-11" fill="#DA2269" />
      </svg> Everest</h1>
  </div>
</div>

Aim for whole numbers in animations whenever possible

Using floating point numbers to increment style properties can lead to rounding errors, resulting in unwanted jittery transitions and performance issues.
This advice also pertains to optimizing SVG rendering, as discussed in this CodePen article.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Progress Bars Installation

For more detailed information, visit: https://github.com/rstacruz/nprogress After linking the provided .js and .css files to my main html file, I am instructed to "Simply call start() and done() to control the progress bar." NProgress.start(); NProgress. ...

Sending a JavaScript variable to PHP and retrieving the PHP result back into a JavaScript variable

I am looking to transfer a JavaScript variable to a PHP file using AJAX, and then retrieve the PHP file's output back into a JavaScript variable. For sending the JavaScript variable to the PHP file, I found a method that suggests using ajax to assign ...

The tweet button is not displaying correctly on the website

Visit my website here, where I have integrated a tweet button generated from Twitter.com. It was working fine for the initial few posts, but now it is failing to load and only displaying text. I have checked the console for any JavaScript errors, but so f ...

AngularJS - ng-change does not trigger for checkbox that toggles the class "switch"

CSS Styling: <span style="color: blue;">Hello, World!</span> JavaScript Function: function showMessage(){ alert('Hello!'); } I tried to implement a span element with blue text color. However, the function that was supposed to ...

render target in three.js EffectComposer

When we utilize an EffectComposer, the scene is rendered into either composer.renderTarget2 or composer.renderTarget1. In a particular example, I came across this: renderer.render( scene, camera, composer.renderTarget2, true ); renderer.shadowMapEnabled ...

Creating separate versions (development and production) of JavaScript code: a step-by-step guide

I have been working on a small web application that is distributed in a Docker container, using an nginx image. This application consists of plain html and js code, without any frameworks. Within the JS code, there is access to a remote host via WebSocket ...

Is there a way to track and detect alterations to an element using JavaScript or jQuery

Can I detect the addition of a specific CSS class to an element without having to create a new event? ...

What is the best way to delete information from a database with the help of Redux

Lately, I've been grappling with the task of integrating redux into my project. Successfully posting data to the database using redux and then retrieving it in react was a win, but now I find myself struggling with how to handle deleting data through ...

What is the best way to create extra space around elements without causing them to disappear off

Looking for a solution to add padding or margin to the right column of a two-column layout without pushing the content off screen? In my example, I've indicated where I would like the extra space to be with an arrow. When I try adding margin or paddin ...

Mastering the Art of Manipulating Z-Index Stacking Context in CSS Using Transforms

I'm struggling to position the red square on top of the table. After reading articles about Z-index Stacking Context and browsing through this stack overflow page about overriding CSS Z-Index Stacking Context, I still can't seem to figure it out. ...

Angular date selection with a range of plus two days, factoring in the exclusion of weekends

I am currently using a mat date picker range with specific logic. The minimum date that a user can select on the calendar is set to + 2 days. For example, if today's date is July 20, 2022, the minimum selectable date would be July 22, 2022. However, ...

Tips for showing the value in the subsequent stage of a multi-step form

Assistance is required for the query below: Is there a way to display the input field value from one step to the next in multistep forms? Similar to how Microsoft login shows the email in the next step as depicted in the image below: ...

Utilizing Moment.js: Transforming 12-hour format to a Date object

Is there a way to convert a 12-hour string into a 24-hour Date object? day.from = day.from || moment("6:00", ["h:mm"]).format("HH:mm"); Unfortunately, I am encountering the following error: angular.js:11706 Error: [ngModel:datefmt] Expected `6:00` to be ...

Creating a rotating gallery with jQuery that can be navigated both forwards and backwards

Currently, I have a jQuery gallery that allows users to view images by moving forward only. What I'm looking for is a way for the user to click on the right side of the image to move to the next one and on the left side of the image to go back to the ...

In Firefox, the CSS height and width values are displayed as X pixels, whereas in Internet Explorer, they are automatically set to 'auto'

When it comes to measuring div box dimensions, jQuery can be a handy tool. Here is an example code snippet: $(document).ready(function(){ var h = $("#testbox").css("height"); }); Interestingly, the same code can give different results in different brow ...

Generate an HTML table and import it into Excel while retaining its styling

Has anyone successfully exported tables from an HTML document to Excel while preserving all CSS styles? My application can generate HTML code for a table, but I need to export the exact same table to Excel. I am using OpenXML. Could it be necessary to uti ...

"Unleashing the Power of AngularJS: Implementing a Global Error Handler to Display and

When building my website, I have multiple Angular apps that require a global error handler to track and display alerts for errors with codes like 500 and 401. Here is what I have so far: In order to create a global error handler module, I've set up t ...

Issue with Spyscroll functionality

Having some trouble getting Spyscroll to work. Can anyone help me identify the issue? I've been at it all day... I've attempted both the javascript and html+css setups, but neither seem to be functioning correctly. Manually adding the "active" c ...

Embedding an Iframe in the Main URL

I have a specific request where I've inserted an anchor tag within an IFRAME. Currently, when the anchor link is clicked, the page redirects within the IFRAME. However, I need the functionality to redirect to the main URL instead of staying within the ...

Increase the thickness of the scrollbar track over the scrollbar thumb

I've come across several discussions on making the track thinner than the scrollbar thumb, but I'm looking to do the opposite. Is it possible to have a scrollbar track that is thicker than the scrollbar thumb? ...