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

Is there a way to recognize each individual browser window that is presently open?

Is there a way to uniquely distinguish each separate browser window that is currently open across all major browsers using JavaScript? Let's examine the following scenario: I currently have 3 browser windows open, each with multiple tabs, in a modern ...

Tips for styling React Material-UI list items with fontAwesome icons and Tailwind.css

I would like to align the text of my list items to the left. Additionally, I want all the icons to be the same size as the envelope icon used in Gmail list item. Currently, my code looks like this: https://i.stack.imgur.com/9LNOs.png How can I achieve th ...

Generate an adjustable grid layout (with rows and columns) to display information retrieved from an API request

I have recently started learning React JS and JavaScript. To practice, I am working on a small project where I am facing an issue with creating dynamic rows and columns. My aim is to display data in 4 columns initially and then move to a new row and column ...

Eliminate Bootstrap's styling from HTML characters

The default heavy checkmark (✔) appears as "✔" Bootstrap 4.3 seems to be altering the appearance of the check, causing it to display as a thick-green one, which is not what I prefer -- see example below I want to remove Bootstrap's styli ...

Click on ng-click to sort through the blog entries

Currently, I am in the process of developing a blog utilizing AngularJS and JSON. Nearly everything is functioning as expected except for one particular filter item. As I am relatively new to Angular, it presents a bit of a challenge for me. The issue ari ...

Exploring the capabilities of storing and retrieving nested objects within a React database

I'm having trouble retrieving nested items from my database. This is how my database is structured: GET /dwelling/room/ [ { "room_id": 1, "room_name": "Living Room", "room_data": [ { "id": 1, ...

What steps should I take to delete a class from my calendar after it has reached the maximum capacity of 20

I am trying to create a feature that automatically removes a class from the calendar if more than 20 people have booked it. On the admin side of the system, I can add classes to the database but need help implementing this check. Below is my current code ...

JavaScript can be used to extract a specific value from a SOAP response

In order to extract the Patient ID (PATAA000000040) from the SOAP response below using JavaScript and insert it into a Mirth destination, we need to target the value under the livingSubjectId tag. It's important to note that this tag may repeat, but w ...

Efficiently organizing reducers into separate files in ReactJS and merging them together

My App is a simple counter app where buttons are images with their own counters. https://i.stack.imgur.com/qkjoi.png In my App.js file, I imported the reducer for the counters using the following code: import reducer from './reducers/reducerCounter&a ...

Verify if data is correct before refreshing user interface

When attempting to submit the HTML5 form, it stops the form submission if any required fields are missing a value or if there is another error such as type or length mismatch. The user interface then shows highlighted invalid fields and focuses on the firs ...

What is the best way to add clickable links to 3D objects and meshes with ThREE?

My goal is to create a simple box that can act as a link when clicked. This seemingly straightforward task has proven difficult for me, so I would greatly appreciate any assistance. Despite my efforts to troubleshoot and research online, I have not been ...

Manipulate Images Efficiently with jQuery

Are there any jQuery plugins or JavaScript controls available that can display an array of images, with the option to delete an image based on user action? For example, something similar to this: , but with a dedicated delete button on each image. One ad ...

Is there a way to modify the color of multiple disabled select fields in Internet Explorer?

After experimenting with various methods to change the color of disabled select items, I found success in Chrome but hit a roadblock in IE. Despite trying ::ms-value, the red color was applied to all disabled select fields, which was not the desired outcom ...

Is there a way to create triangles on all four corners of an image using canvas in JavaScript?

In my code, I am currently working on drawing rectangles on the corners of an image using JavaScript. To achieve this, I first extract the image data from a canvas element. // Get image data let imgData = ctx.getImageData(0, 0, canvas.width, canvas.height) ...

Guide to presenting XML information from a web address using XSLT

I am currently working with dynamic XML sports data from a URL in a Yahoo API, and I want to display and sort a selection of this data on my website using XSLT. This is my first time dealing with XML and XSLT, and while testing, I have managed to correctly ...

Ways to transform a poster into a clickable link in an HTML video

I am currently working on embedding an mp3 audio file in a browser, along with an accompanying image and a URL for redirection upon clicking. To achieve this, I have placed the mp3 file within a video tag and designated the image as a poster using the foll ...

Is there a way to navigate through div text within a stationary container?

My current challenge involves a container that contains an overflow of text, but I desire for the container to remain fixed. The dilemma arises when the text spills out from both the top and bottom edges of the container. Instead of using scroll bars withi ...

Node.JS encountered an issue preventing the variable from being saved

I've been attempting to store the request.headers, but every time it outputs as [object Object] in the txt file. var http = require("http"); url = require("url"); fs = require("fs"); var events = require('events'); var even = new events.Ev ...

Can a simultaneous read/write conflict occur in JavaScript while browsing?

A situation has arisen where I am executing multiple (let's say four) AJAX calls using AngularJS http get, and I desire each call to invoke a callback function that increments a counter. This way, I can track when all four threads have finished. I am ...

What methods are available to change one JSON format into another?

I am receiving JSON data from a Laravel API in the following format: [ { "id":48, "parentid":0, "title":"Item 1", "child_content":[ { "id":49, "parentid":48, "title":"Itema 1 ...