Creating Seamless, Unified Shape-to-Shape (Containers) Transition with CSS and JavaScript - A Step-by-Step Guide

I am experimenting with creating a unique effect where two rectangular shapes, each containing text and with rounded ends, move towards each other, merge to form a single rounded rectangle as the page is scrolled down, and then separate back when scrolling up. I want these shapes to remain fixed in their position relative to the viewport during scrolling.

My Attempts So Far:

  • I have tried using CSS translations and transformations along with JS adjustments of the border-radius properties.
  • I experimented with clip-path, but it resulted in ellipse shapes instead.
  • Using HTML Shapes did not work as they need to be containers for my content.

My closest success has been getting the shapes to move close together and slightly overlap. However, other attempts have led to ellipses, unnatural border-radius behavior, or lack of cohesion between the elements.

document.addEventListener('scroll', () => {
    const scrollPercent = window.scrollY / (document.body.scrollHeight - window.innerHeight);
   
    const movement = (window.innerWidth / 2 - 115) * scrollPercent;

    const shape1 = document.querySelector('.shape:nth-child(1)');
    const shape2 = document.querySelector('.shape:nth-child(2)');
    shape1.style.transform = `translateX(${movement}px)`;
    shape2.style.transform = `translateX(-${movement}px)`;
});
body {
    width: 90%;
    margin: 0 auto;
    padding: 20px;
    height: 2000px;
}

.container {
    position: sticky;
    top: 20px;
    display: flex;
    justify-content: space-between;
}

.shape {
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100px;
    height: 30px;
    border-radius: 15px;
    background-color: #000;
    color: #fff;
    transition: transform 0.3s ease-out;
    transform: translateX(0%);
}
<body>
    <div class="container">
        <div class="shape" id="shape1">Shape 1</div>
        <div class="shape" id="shape2">Shape 2</div>
    </div>
</body>

Question: I suspect that I may need to explore clip-path further to achieve my desired effect. How can I refine my CSS and adjust my JavaScript to create the morphing effect depicted in the image above while maintaining rounded edges and ensuring coherence between the two container elements? Any suggestions or corrections regarding my current approach would be greatly appreciated. Thank you.

Answer №1

One effective technique involves using an SVG filter:

<svg class="morph-filter" viewbox="0 0 0 0">
  <filter id="morph">
    <feGaussianBlur in="SourceGraphic" stdDeviation="10" result="blur" />
    <feColorMatrix in="blur" mode="matrix" values="
      1 0 0 0 0
      0 1 0 0 0
      0 0 1 0 0
      0 0 0 64 -32" result="morph" />
    <feBlend in="SourceGraphic" in2="morph" />
  </filter>
</svg>
.container {
  filter: url(#morph);
}

document.addEventListener('scroll', () => {
  const scrollPercent = window.scrollY / (document.body.scrollHeight - window.innerHeight);

  const movement = (window.innerWidth / 2 - 115) * scrollPercent;

  const shape1 = document.querySelector('.shape:nth-child(1)');
  const shape2 = document.querySelector('.shape:nth-child(2)');
  shape1.style.transform = `translateX(${movement}px)`;
  shape2.style.transform = `translateX(-${movement}px)`;
});
body {
  width: 90%;
  margin: 0 auto;
  padding: 20px;
  height: 2000px;
}

.container {
  position: sticky;
  top: 20px;
  display: flex;
  justify-content: space-between;
  filter: url(#morph);
}

.shape {
  display: flex;
  justify-content: center;
  align-items: center;
  width: 100px;
  height: 30px;
  border-radius: 15px;
  background-color: #000;
  color: #fff;
  transition: transform 0.3s ease-out;
  transform: translateX(0%);
}
<body>
  <div class="container">
    <div class="shape" id="shape1">Shape 1</div>
    <div class="shape" id="shape2">Shape 2</div>
  </div>

  <svg class="morph-filter" viewbox="0 0 0 0">
    <filter id="morph">
      <feGaussianBlur in="SourceGraphic" stdDeviation="10" result="blur" />
      <feColorMatrix in="blur" mode="matrix" values="
        1 0 0 0 0
        0 1 0 0 0
        0 0 1 0 0
        0 0 0 64 -32" result="morph" />
      <feBlend in="SourceGraphic" in2="morph" />
    </filter>
  </svg>
</body>

Breaking it Down

The process begins with <feGaussianBlur /> to blur the two shapes. Similar to applying filter: blur(10px), this step creates a blurred version of the source image, slightly larger than its original size, and saves the output in the blur channel.

<feGaussianBlur in="SourceGraphic" stdDeviation="10" result="blur" />

Next, using <feColorMatrix />, only the alpha channel of the image is "sharpened" without affecting the RGB channels. By adjusting the alpha channel with significant values (64 -32), pixels with alpha values less than 0.5 become transparent while those above 0.5 stay opaque (alpha values are clamped between 0 and 1). The resulting image is stored in the morph channel.

<feColorMatrix in="blur" mode="matrix" values="
  1 0 0 0 0
  0 1 0 0 0
  0 0 1 0 0
  0 0 0 64 -32" result="morph" />

Finally, <feBlend /> combines these channels so that when the two shapes nearly touch, areas where they overlap with blurry edges will have alpha values greater than 0.5, creating the illusion of morphing between the shapes.

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

What sets apart `npm install --save-dev gulp-uglify` from `npm install gulp-uglify`?

I'm feeling perplexed regarding the npm installation process. Based on what I've gathered, there are several options available to me when installing modules: The -g option which saves modules globally --save-dev No arguments. Could someone cl ...

Creating a persistent gap BETWEEN li inline list items

I'm seeking a solution to create horizontal links using a list. I know that I need to apply the display: block property to the ul element and set display: inline for the li items. While there are several inquiries about maintaining a consistent width ...

I would like for my element to increase and decrease in size while rotating when the button is clicked

I am trying to create an element that changes its size while spinning when a button is clicked. It seems to be working fine on hover, but not onclick. Here is the code I have: <div class="rec">Rec</div> <button id="button&quo ...

Is it possible to change the inner html to null during an active ajax request?

My goal is to have two separate data.html files inserted into two different HTML files without needing a click event. I want the structure of my data.html files to remain consistent, while allowing the template of my website to change dynamically by callin ...

Modifying static content within jQuery tabs

Encountering an issue with jQuery $('#tabs').tabs();. When checking out the example on JSFIDDLE, I noticed that the content containing an external php file is always displayed, even when switching to other tabs. <li class="files"> < ...

The padding of elements changes accordingly as the dimensions (width/height) of the header are adjusted

Currently, I'm tackling the challenges of working on my website and trying to understand JavaScript better. I am facing an issue where I want my aside menu's padding to adjust when my header shrinks in height. Furthermore, at the end of the web ...

Is there a way to accomplish this using Bootstrap?

Hello there! I have a piece of code that is working with both bootstrap and Pixedelic camera. Here it is: <section id="slider" class="container-fluid"> <div class="row fluid"> <div id="camera_wrap_1"> <div data-src="conten ...

Insects featuring a button and tooltip duo creating a captivating "pull-effect"

Why is the button pulling when I move the cursor over a camera? Is there a way to solve this issue? <div class="input-group-btn advanced-group"> <button class="btn btn-default" data-toggle="tooltip" data-placement="bottom" title="Send Imag ...

Create a feature that allows users to dynamically add and remove image fields, with the ability to insert the selected images into a database

I'm currently using the following code and I am able to add attachments, but I'm facing an issue when trying to remove them. Can someone please help me with this problem? I tried adding an alert on the remove button and it seems to be working, h ...

I need help setting up a link in HTML that redirects to the correct webpage when clicked. How can I make this happen smoothly?

<!DOCTYPE html> <html> <head> <title>______</title> <button class="btn about">About</button> <button class="btn socialmedia">Social Media</button></button> <button class ...

Arranging shapes for varying levels of magnification

Seeking assistance with properly aligning two forms. I have tried using a positioning approach, but the layout gets disrupted when the browser's Zoom level is adjusted. The second button ends up shifting slightly either upwards or downwards. Here is t ...

Getting the values of $scope variables from AngularJS in the controller to the view

I have a simple scope variable on my AngularJS Controller. It is assigned a specific endpoint value like this: $scope.isItAvailable = endpoint.IS_IT_AVAILABLE; How can I properly use it in my view (HTML) to conditionally show or hide an element using ng- ...

Retrieve JSON data from a RESTful API by utilizing pure javascript

I'm eager to try out the Wikipedia search API in JavaScript, even though it might be simpler with jQuery. I want to make sure I understand the basics first before turning to frameworks. Below is the code I've come up with, but for some reason, I ...

Accurate representation of a JavaScript object using Node.js Express

I have a certain structure that I need to display on my JADE page, so I created a JSON-like object to store the data. This is how the JSON object looks like : var dataSet1 = { meta: { "name": "Some text", "minimum": mini_2, "ma ...

Type Error: Issue encountered while resolving module specifier

Upon trying to import the d3.js library in a project that utilizes npm, I encountered the error message: TypeError: Error resolving module specifier: d3. This issue specifically occurred while using Firefox. index.html <!DOCTYPE html> <html lang ...

Tips for creating a dynamic header background using custom CSS for my website design

As I embark on creating my very first WordPress website using the Blossom Pin Pro theme, I'm encountering an issue where the background image of the header disappears when adjusting the window width. Click here to visit the site If you have a code s ...

Achieving synchronous function execution with a single click in React

In my current project, I am utilizing ReactJs and Redux. I have encountered a scenario where I need to trigger two functions sequentially with just one click on the mainfunc(). The second function should only execute after the first function has complete ...

Save user entries in a database array

I'm working on developing a platform for advertisements where users can create detailed profiles with images. To achieve this, I need to store the information in an array within a backend database for future retrieval. Below is an example of the backe ...

Maximizing Bootstrap's Potential: Achieving Right Alignment for Divs

I am facing a challenge with aligning my div in the layout_cshtml. Currently, it is aligned to the left and the search bar and dropdownlist are not displaying properly. This negatively impacts the user experience. Additionally, my navigation menu is not ...

Transforming a React, Redux, and MUI Menu into an Electron Application

I'm in the process of transforming a web-based React + Redux + MUI application into an Electron app. The original app features a main AppBar with multiple dropdown menus, each containing menu items that interact with the app's Redux store. While ...