`Zooming and scrolling feature within a masked image`

I'm struggling to achieve a scrolling zoom effect similar to the website mentioned below, but I can't seem to get it to fully zoom.

Additionally, when I try to zoom in on a clipped shape or text while scrolling, the entire div ends up scrolling along with it.

I've set the zoom breakpoint to

document.body.clientWidth / imgElement.clientHeight
, but I'm unable to find an alternative solution.

Any suggestions would be greatly appreciated!
Thank you in advance.

View Sample Link of the Website

const zoomElement = document.querySelector('.zoom')
const afterZoomElement = document.querySelector('.afterzoom')
const imgElement = document.querySelector('h2')
const WIDTH = document.body.clientWidth
const HEIGHT = zoomElement.clientHeight
const IMAGE_WIDTH = imgElement.clientWidth
const IMAGE_HEIGHT = imgElement.clientHeight
const ZOOM_SPEED = 200 // Lower value for faster speed
const ZOOM_BREAKPOINT = WIDTH / IMAGE_HEIGHT // Point at which zooming should stop
const IMAGE_HEIGHT_MAX = IMAGE_HEIGHT * ZOOM_BREAKPOINT
const ABSOLUTE = ZOOM_BREAKPOINT * ZOOM_SPEED // Absolute position when element reaches maximum size

// Fade --------------------------------------------------------------------------------------
const FADE_SPEED = 400 // Lower value for faster speed
let fade = 1
let prev = 0
// -------------------------------------------------------------------------------------- Fade

function animate() {
    let scroll = window.scrollY
    let temp = scroll / ZOOM_SPEED
    let zoom = temp > 1 ? temp : 1

    if (zoom < ZOOM_BREAKPOINT) {
        imgElement.style.transform = `scale(${zoom})`
        zoomElement.style.top = '0px'
        zoomElement.style.position = 'fixed'
    } else {
        imgElement.style.transform = `scale(${ZOOM_BREAKPOINT})`
        zoomElement.style.position = 'absolute'
        zoomElement.style.top = ABSOLUTE + 'px'
    }

    let dif = prev - scroll

    if (zoom < ZOOM_BREAKPOINT - FADE_SPEED / ZOOM_SPEED) {
        fade = 1
    } else if (zoom > ZOOM_BREAKPOINT) {
        fade = 0
    } else {
        fade += dif / FADE_SPEED
    }

    fadeElement.style.opacity = fade
    prev = scroll
}

if ('scrollRestoration' in history) {
    history.scrollRestoration = 'manual'
}

document.addEventListener('scroll', () => window.requestAnimationFrame(animate))

zoomElement.style.opacity = 1

afterZoomElement.style.top = ABSOLUTE + IMAGE_HEIGHT_MAX / 2 + HEIGHT / 2 + 'px'
body {
    margin: 0;
}

h2 {
  height: 100vh;
  width: 100vw;
  background-color: black;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 5em;
  text-transform: uppercase;
  background: url(https://24.media.tumblr.com/tumblr_m87dri70zh1qzla33o1_500.gif);
  background-size: cover;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}

.zoom {
  height: 100vh;
  width: 100%;
  display: grid;
  place-items: center;
  position: fixed;
  top: 0;
  left: 0;
}

.afterzoom {
  position: absolute;
  height: 100vh;
  width: 100%;
  background: red;
  overflow-x: auto;
}

h2{
  font-size: 5em;
  text-transform: uppercase;
  background: url(https://24.media.tumblr.com/tumblr_m87dri70zh1qzla33o1_500.gif);
  background-size: cover;
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
}
<div class="zoom">
  <h2>i</h2>
</div>
<div class="afterzoom">
    <p>This content will appear after the above element is fully zoomed.</p>
</div>

Answer №1

Give this a shot. Just an FYI, fadeElement isn't defined in the code you provided so I left that part out. You can adjust the zoom breakpoint depending on how much zoom you want to apply.

function animateZoom() {
        let scrollPos = window.scrollY;
        let tempCalc = scrollPos / ZOOM_SPEED;
        let zoomLevel = tempCalc > 1 ? tempCalc : 1;

        // Updating the scaling for the text or shape based on zoom level
        if (zoomLevel < ZOOM_BREAKPOINT) {
            imgElement.style.transform = `scale(${zoomLevel})`;
            zoomElement.style.position = 'fixed';
        } else {
            imgElement.style.transform = `scale(${ZOOM_BREAKPOINT})`;
            zoomElement.style.position = 'absolute';
            zoomElement.style.top = `${ABSOLUTE}px`;
        }

        // Updating the fade transition effect
        let diff = previousScrollPos - scrollPos;
        let fadeValue = (zoomLevel < ZOOM_BREAKPOINT) ? 1 : 0;
        fadeValue += (zoomLevel >= ZOOM_BREAKPOINT && zoomLevel <= (ZOOM_BREAKPOINT + (FADE_SPEED / ZOOM_SPEED))) ? diff / FADE_SPEED : 0;
        //fadeElement.style.opacity = fadeValue;
        previousScrollPos = scrollPos;

        // Adjusting the position of afterZoomElement
        afterZoomElement.style.top = `${ABSOLUTE + IMAGE_HEIGHT_MAX}px`;
    }
.zoom {
        overflow: hidden;
        /* Other styles */
}

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

Updating documents in a mongoDB collection can be done by simply

I require an update to my database that will modify existing data, as illustrated below: existing data => [{_id:"abnc214124",name:"mustafa",age:12,etc...}, {_id:"abnc21412432",name:"mustafa1",age:32,etc...}, {_id ...

Personalize the color scheme for your timeline paper

I am interested in customizing this specific example of a personalized timeline: import React from 'react'; import { makeStyles } from '@material-ui/core/styles'; import Timeline from '@material-ui/lab/Timeline'; import Timeli ...

Clicking on the button will allow you to sort the items in

I need assistance with sorting a table: when I click on a header, the order changes to ascending but does not toggle back to descending. Despite having a boolean value that should switch between true and false, it keeps returning the initial value. Can s ...

Retrieving Files using Ajax Across Different File Types

I recently came across the following code snippet: DOM_imgDir = "img/UI/DOM/"; fileextension = ".jpg"; $.ajax({ url: DOM_imgDir, success: function (data) { $(data).find("a:contains(" + fileextension + ")").each(function () { filename = thi ...

What is the best approach to accumulate model data in an Angular JS service or controller through a series of consecutive calls?

I am facing a challenge where I need to display the results of multiple REST server calls on a single page using AngularJS. The initial call retrieves details about a specific product, including the IDs of other related products. My goal is to not only s ...

I am looking to create a route with parameters in Next.js, as well as without any parameters

I am working on a NEXTJS project and utilizing SSR for data fetching. However, I am trying to implement the following scenario: When users land on the "/" route, they should be presented with a random product detail page. But if they search for a specific ...

What is the best way to store plain HTML text in a database?

Currently, I am working on PHP source code to insert the page created using Quill text editor. Although the text editor data insertion is working fine, it is not inserting plain text as desired. My goal is to insert HTML plain text instead. Below is the J ...

Slideshow feature stops working after one cycle

Hey there! I'm currently working on a function that allows for sliding through a series of images contained within a div. The goal is to cycle back to the beginning when reaching the end, and vice versa when going in the opposite direction. While my c ...

Prevent the utilization of <span> tags for line breaks to

When resizing my window to a smaller size, I encountered some unsightly spans that cut into new lines. To demonstrate this issue, I intentionally set the width:20px;. Can this problem be avoided? <link href="https://maxcdn.bootstrapcdn.com/bootstrap/ ...

Experience the dynamic bouncing marker feature in Next.js React-Leaflet with the powerful tool Leaflet.SmoothMarkerB

I'm a beginner in front-end development and I'm attempting to create a bouncing marker in React-leaflet using the leaflet.smooth_marker_bouncing plugin version 1.3.0 available at this link. Unfortunately, I couldn't find enough documentation ...

What is an alternative method to retrieve form data without relying on bodyParser?

When it comes to accessing posted form data without using bodyParser, what alternatives are available? How exactly does bodyParser grant access to the data in req.body? Additionally, I am curious about the inner workings of this process on a deeper level. ...

Utilizing jQuery's Ajax functionality to extract filtered data from mySQL

I've been working on sending query strings fetched by clicking radio buttons to the server in order to receive a response in XML format. Although I'm close to finding a solution, I'm struggling to debug why it's not functioning as expec ...

Is it possible to send notifications via email prior to reaching a particular deadline?

I'm trying to figure out a way to notify users one day before dates of events stored in the database. I'm stumped and could really use some help with this. Can someone please assist me? ...

The menu in IE7 seems to have a mind of its own, appearing and

Having trouble with my menu displaying correctly in IE7. It seems to be a stacking issue: the menu initially appears but disappears once the header image and rest of the page load. I've tried adjusting the z-index values in both the menu and its paren ...

employing async/await in the function of a backend API

I'm facing an issue with the following code snippet: service.js module.exports = { getUser }; async function getUser({ data }) { return new Promise((resolve, reject) => { const id = data["id"]; const doc = await db.colle ...

Updating the background image with Jquery is resulting in a distracting flicker effect

Here is a snippet of the script I'm using to create a slider that changes the background image for each image object in a specified time cycle. #Sliderimg - height set to 500px, $("#Sliderimg").css({ "background-image": "url(../Images/" + SliderIma ...

Display an icon to act as a separator between icons in CSS styling

Is there a way to display a spacer line before and after icons (cross symbols) while excluding the spacer line before and after buttons carrying the word "Cancel"? How can this be achieved? This is my CSS file: .Container > *:first-child::before, .Con ...

React Hooks encountering issues with keydown/up events functionality

Currently, I am in the process of implementing arrow-based keyboard controls for a game that I have been developing. In order to stay updated with React, I decided to utilize function components and hooks. To showcase my progress, I have put together a dem ...

What is the best way to determine which watchers are triggered in Vue.js?

Within my parent component, there are numerous nested child components. Whenever a click occurs on one of the child components, it triggers an update to the route. The parent component watches the route property and performs certain actions when it change ...

I possess a variety of poppers and desire for the opened one to close when another one is opened

Having built a component that generates 6 unique experiences, each with its own popper containing images, I am struggling to figure out how to modify my code so that one popper closes when another is clicked. Here is the current setup: This is the compone ...