Guide for creating a scroll-triggered rotation animation using only JavaScript

Looking to achieve a cool scroll effect with an image that rotates on the X-axis by a specific degree, such as 70deg.

The goal is to have the image's rotateX value change to 0deg when it enters the viewport upon scrolling and revert back to 70deg when it exits the viewport.

Below is the code snippet for reference:

let a = 70
function test(){
let image = document.querySelector("img");
let imageTop = image.getBoundingClientRect().top;
  
let screenpos = window.innerHeight /2

   if(imageTop < screenpos){
    image.style.border = "5px solid green"
     
    image.style.transform = `rotateX(${a=a-2}deg)`
  }
}

window.addEventListener("scroll",function(){
   test()
})
body {
  background-color: #ccc;
  text-align: center;
  margin-top: 100px;
  font-family: sans-serif;
}
.bgcolor {
  background-color: black;
  color: rgba(255, 255, 255, 0.8);
}
div{
  perspective:800px;
  margin-top:400px;
  margin-bottom:200px;
}
div img {
  transform:rotateX(66deg);
  transition:.9s;
}
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8>
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name=viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>
<body>
  <h1>Scroll Please</h1>
  <div><img src="https://cdn.pixabay.com/photo/2021/06/10/22/14/stork-6327150__340.jpg" alt="Bird Image"></div>
</body>
</html>

Answer №1

I've put together a concise example based on the Mozilla documentation for the Intersection Observer API.

If you want to delve deeper into what's happening, feel free to explore the linked documentation.

const image = document.querySelector("img");

// Setting up the Observer when the page loads
window.addEventListener("load", (event) => {
  createObserver();
}, false);

// Configuring the Observer
function createObserver() {
  let observer;

  let options = {
    root: null,
    rootMargin: "0px",
    threshold: buildThresholdList()
  };

  observer = new IntersectionObserver(handleIntersect, options);
  observer.observe(image);
}

// Generating an array with values ranging from 0.0 to 1.0
function buildThresholdList() {
  let thresholds = [];
  let numSteps = 1000;

  for (let i=1.0; i<=numSteps; i++) {
    let ratio = i/numSteps;
    thresholds.push(ratio);
  }

  thresholds.push(0);
  return thresholds;
}

// Handling the intersection of the observer
function handleIntersect(entries, observer) {
  entries.forEach((entry) => {
    // Only consider values between 0 and 0.5, ensuring that the image starts animating when half visible
    const maxxedIntersect = entry.intersectionRatio > 0.5 
        ? entry.intersectionRatio - 0.5 
        : 0;
    
    // Scaling the number (0.0 ... 0.5) between 0 and 70
    const scaled = scaleBetween(maxxedIntersect, 0, 70, 0, 0.5);
    
    // Determining the value for rotation
    // When the element is fully visible, the scaled value will be 70, so we subtract it from 70 to get 0 in this case
    const rotateValue = parseInt(70 - scaled);
    
    // Applying the styling
    image.style.transform = `rotateX(${rotateValue}deg)`
  });
}

// Function to scale numbers between min and max
// Reference: https://stackoverflow.com/a/31687097/9150652
function scaleBetween(unscaledNum, minAllowed, maxAllowed, min, max) {
  return (maxAllowed - minAllowed) * (unscaledNum - min) / (max - min) + minAllowed;
}
body {
  background-color: #ccc;
  text-align: center;
  margin-top: 100px;
  font-family: sans-serif;
}
.bgcolor {
  background-color: black;
  color: rgba(255, 255, 255, 0.8);
}
div > img{
  margin-top: 400px;
  margin-bottom: 600px;
  perspective: 800px;
  border: 5px solid green;
  transition: .1s;
}
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
  </head>
  <body>
    <h1>Scroll Please</h1>
    <div>
      <img src="https://cdn.pixabay.com/photo/2021/06/10/22/14/stork-6327150__340.jpg" 
           alt="Bird Image">
    </div>
  </body>
</html>

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

Adding li elements dynamically, how can we now dynamically remove them using hyperlinks?

Please refrain from using jQuery code, as my main focus is on learning javascript. I am currently experimenting with adding li items dynamically to a ul when a button on the HTML page is clicked. Here's a sample implementation: HTML: <ul id="myL ...

Transform black and white image to vibrant colors and add various hover effects

There are three images displayed on this webpage: This website is built on WordPress and utilizes the WP Bakery plugin for designing layouts. The images here are set to change from color to grayscale, and back to color upon mouseover. The following CSS c ...

What is the best way to create a reusable component for a dialog box or modal window?

I have been working on developing a reusable dialog component with a yes or no button at the bottom. The main idea behind this is to create a user confirmation dialog that prompts the user to confirm their entered information before proceeding. import Re ...

Pass an array from JavaScript to PHP

I am attempting to send an array to the server using jQuery. Here is my code snippet for sending the array: jQuery(document).ready(function($){ $.ajax({ type: "POST", url: "file.php", datatype : "json", data : JSON.str ...

"NodeJS Express: The intricacies of managing overlapping routers

While constructing a NodeJS express API, I have encountered a peculiar bug. It seems that some of the endpoints are overlapping, causing them to become unreachable as the request never completes and ends up timing out. For example: const load_dirs = (dirs ...

Top solution for maintaining smooth navigation across web pages

As I dive into the world of web development, I find myself intrigued by the idea of reusing navigation and banners across multiple web pages. However, despite my research efforts, I have yet to come across a definitive answer. My objective is simple: The ...

What could be causing render_template to fail when attempting to update the same parameters more than once?

Lately, I've dived into the world of Flask, MongoDB, and ElasticSearch. So far, my MongoDB and ElasticSearch setups are running smoothly. However, I've encountered an issue with generating a list of dictionaries and displaying them on my HTML we ...

How can React Native efficiently retrieve data from multiple APIs simultaneously?

In my current project, I am incorporating multiple APIs that are interlinked with each other by sharing the same data structure... Below is the code snippet: export default class App extends React.Component { constructor(props) { super(props); } ...

Encountered a problem while trying to update a list item in Vue.js

Greetings Everyone, I've successfully implemented a basic CRUD functionality in VueJS. The values are being inserted into a list, from which I can read and delete them. However, I'm facing an issue with updating any value. When I try to ...

Efficient initialization process in Vue.js components

Upon initialization of a component, the data callback is executed as follows: data(){ return { name: myNameGetter(), age: myAgeGetter(), // etc... } }, Following that, a notification is sent to a parent component regarding ...

Blue Jay Guarantees: Construct props object on the fly and execute simultaneously

If we take a look at this example: https://github.com/petkaantonov/bluebird/blob/master/API.md#props---promise Promise.props({ pictures: getPictures(), comments: getComments(), tweets: getTweets() }).then(function(result) { console.log(re ...

In what way does ReactJS enable me to utilize constant functions before they are declared?

I'm intrigued by the concept of using a value before defining it in ReactJS. Let's explore this through an example: function CounterApp() { const [counter, setCounter] = useState(0); const increaseValueTwice = () => { increaseValue() ...

Explain how the 'next' function works within Express middleware and its role in directing the flow to a different function

I am fairly new to Node.js and currently learning Express.js. I am focusing on implementing "middleware functions" for specific routes. My question is regarding the usage of the "next" function. What exactly can we do after authentication using the "next ...

Adjust the borderBottomColor of Material-UI TextField upon completion of input

When working with MUI to create a form, I noticed that the default TextField bottom border is grey, turns blue when focused, and then back to grey when focus is lost. My goal is to prevent it from losing the blue color after filling in the field: https:// ...

I need help figuring out how to choose an option from a drop-down menu that has a dynamically changing id every

When using Selenium Webdriver, I am trying to select the "802.11n" option from a drop-down menu where the "sbSelector_xxx" id changes each time the page is reloaded. <div id="RADIO_5GHz_adv" style="display: block;"> <table class="block" border="0 ...

Tips for adjusting the animation position in Off-Canvas Menu Effects

I am currently utilizing the wave menu effect from OffCanvasMenuEffects. You can view this menu in action below: ... // CSS code snippets here <link rel="stylesheet" type="text/css" href="https://tympanus.net/Development/OffCanvasMenuEffects/fonts/f ...

Unable to close Bootstrap modal upon clicking "x" or "close" buttons

Hey everyone, I'm having a bit of trouble with my modal. It appears correctly when clicked to open, and the close buttons seem to detect that my mouse is hovering over them. However, when I click on the buttons, nothing happens and the modal remains o ...

How to horizontally center a div with margin using CSS

Is there a way to horizontally center a div with margins using CSS? <div id="container"> <div id="center_me"></div> </div> #container{ position:relative; width:100%; height:400px; } #center_me{ position:absol ...

Learn how to create a stunning effect by combining two half images and revealing a full image upon hover with a smooth animation

I am struggling with implementing a specific feature using jQuery. I have designed a page hero with two sections (red and black): My goal is to have the black section expand over the red section when hovering, creating a full black box. I want the same ef ...

Find the position of the object in a list

I have an array that looks something like this data: Array(3) 0: data: Object account_id: (...) address_1: (...) address_2: (...) amount: 10.00 id: 1234 ... 1: data: Object account_id: (...) address_ ...