Introduction
In modern web design, interactivity plays a crucial role in enhancing user engagement and experience. To make your website stand out, try incorporating amazing hover effects.
A 3D model card is a sleek way to give depth and interactive features to your website.
In this tutorial, we’ll explain the complete steps on how to create a 3D model card hover effect using only HTML, CSS, and Javascript.
Whether you're working on a portfolio, e-commerce site, or personal project, this effect will boost your web design.
You May Also Like: Autoplay Image Slider with Manual Navigation Button with HTML CSS and JS
✅ Watch Live Preview 👉👉
Click Here to Download Source Code
Prerequisites
Before diving into the code, it's important to have a basic understanding of the following concepts:
- HTML: The structure of your web page.
- CSS: The styling and layout of your web page.
- CSS Transitions and Transforms: We'll use for smooth animations and transformations.
- CSS Pseudo-classes: These allow you to apply styles based on the state of an element (e.g., :hover).
With that in mind, let’s begin.
Alert: To run the file, you'll need to start the XAMPP server
Source Code
HTML Code Structure
<!DOCTYPE html>
<html lang="en" >
<!-- Code by Keyframe Tech Solution -->
<head>
<meta charset="UTF-8">
<title>Marvel Character Select</title>
<script type="module" src="https://ajax.googleapis.com/ajax/libs/model-viewer/4.0.0/model-viewer.min.js"></script>
<link rel="stylesheet" href="./style.css">
</head>
<body>
<div class="card-container">
<div class="card">
<div class="overflow">
<div class="model">
<model-viewer camera-orbit="0deg 90deg 164m" camera-target="0m 140m 10m" src="models/spiderman.glb" shadow-intensity="0.4"></model-viewer>
</div>
</div>
<div class="glass">
<div class="gradient-blur">
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
<div></div>
</div>
</div>
<div class="content">
<h2>Spider-Man</h2>
<p>Spider-Man is a superhero in American comic books published by Marvel Comics.</p>
</div>
</div>
</div>
<script src="./script.js"></script>
</body>
</html>
CSS Code Structure
@import url("https://fonts.googleapis.com/css2?family=Dancing+Script:wght@400..700&display=swap");
@import url("https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100..900&display=swap");
* {
box-sizing: border-box;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
scroll-behavior: smooth;
}
html,
body {
margin: 0;
padding: 0;
height: 100%;
overflow: hidden;
}
body {
background: #0096885c;
--scale: 230px;
position: relative;
}
body::before {
content: "";
width: 100%;
height: 100%;
position: absolute;
background: radial-gradient(circle, #2d2d2d 20%, transparent 11%);
background-size: 0.5em 0.5em;
opacity: 0.5;
inset: 0;
z-index: 0;
}
.card-container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
flex-wrap: nowrap;
justify-content: center;
align-items: center;
margin: 0;
padding-top: 28vh;
padding-bottom: 14vh;
overflow: hidden;
}
@media (max-width: 800px) {
html,
body {
overflow: inherit;
}
.card-container {
flex-direction: column;
gap: 80px;
height: fit-content;
}
}
.card {
--clr: #c63e3c;
--fclr: #f0ffe2;
width: var(--scale);
height: 324px;
min-width: var(--scale);
&::before {
content: "";
display: block;
position: absolute;
left: 0;
right: 0;
top: 0;
width: 100%;
height: 107%;
background: linear-gradient(var(--clr), #000000 100%);
transform: skewY(10deg);
box-shadow: 0 0 0 0px #f9da27, 1px 1px 3px 0px #0002,
12px 42px 24px -8px #0001, 10px 24px 42px 0 #0001, 1px 4px 12px 0 #0002;
transition: all 0.2s ease-in-out;
}
&::after {
content: "";
display: block;
position: absolute;
left: -6%;
right: 0;
top: 64%;
width: 112%;
height: 16%;
background: var(--clr);
transform: rotate(-8deg) skewX(-54deg);
clip-path: polygon(
-100% -100%,
200% -100%,
200% 200%,
130% 200%,
59% -100%,
-30.4% -100%,
41% 200%,
-100% 200%
);
box-shadow: 0 0 0 0px #f9da27;
scale: 0.6;
opacity: 0;
transition: all 0.2s ease-in-out;
}
position: relative;
display: flex;
flex-direction: column;
justify-content: end;
align-items: center;
cursor: pointer;
margin: auto 16px;
&:hover {
z-index: 2;
&::before {
box-shadow: 0 0 0 8px #234563, 1px 1px 3px 0px #0002,
12px 42px 24px -8px #0001, 10px 24px 42px 0 #0001, 1px 4px 12px 0 #0002;
}
}
.content {
position: relative;
z-index: 1;
bottom: 0;
top: unset;
height: fit-content;
padding: 12px 24px 0 24px;
text-align: center;
h2,
p {
margin: 0;
padding: 0;
line-height: 124%;
margin-bottom: 8px;
font-weight: 700;
color: var(--fclr);
}
p {
font-size: 14px;
font-weight: 400;
margin-bottom: 16px;
font-family: "Roboto", serif;
}
h2{
font-size: 30px;
font-family: "Dancing Script", serif;
}
}
.glass {
pointer-events: none;
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 80%;
background: linear-gradient(transparent, #000 100%);
overflow: hidden;
}
.overflow {
pointer-events: none;
position: absolute;
width: 200%;
height: 200%;
overflow: hidden;
display: flex;
justify-content: end;
align-items: end;
clip-path: polygon(25% 0, 75% 0, 75% 100%, 25% 100%);
}
.model {
position: absolute;
width: 100%;
height: 100%;
bottom: -10%;
display: flex;
justify-content: center;
align-items: center;
model-viewer {
position: absolute;
width: 500px;
height: 500px;
width: 500px;
height: calc((5 / 3) * var(--scale));
--progress-bar-color: none;
--progress-bar-height: 0px;
opacity: 0;
transition: filter 0.4s ease-in-out;
&.loaded {
animation: onLoad 1s ease-in forwards;
}
}
}
}
.card:hover .overflow {
clip-path: unset !important;
}
@keyframes onLoad {
to {
opacity: 1;
}
}
.gradient-blur {
position: absolute;
z-index: 1;
height: 100%;
inset: auto 0 0 0;
pointer-events: none;
}
.gradient-blur > div,
.gradient-blur::before,
.gradient-blur::after {
position: absolute;
inset: 0;
--p1: 0%;
--p2: 12.5%;
--p3: 25%;
--p4: 37.5%;
--p5: 50%;
--p6: 62.5%;
--p7: 75%;
--p8: 87.5%;
--p9: 100%;
--bclr0: #0000;
--bclr1: #000;
}
.gradient-blur::before {
content: "";
z-index: 1;
backdrop-filter: blur(0.5px);
mask: linear-gradient(
to bottom,
var(--bclr0) var(--p1),
var(--bclr1) var(--p2),
var(--bclr1) var(--p3),
var(--bclr0) var(--p4)
);
}
.gradient-blur > div:nth-of-type(1) {
z-index: 2;
backdrop-filter: blur(1px);
mask: linear-gradient(
to bottom,
var(--bclr0) var(--p2),
var(--bclr1) var(--p3),
var(--bclr1) var(--p4),
var(--bclr0) var(--p5)
);
}
.gradient-blur > div:nth-of-type(2) {
z-index: 3;
backdrop-filter: blur(2px);
mask: linear-gradient(
to bottom,
var(--bclr0) var(--p3),
var(--bclr1) var(--p4),
var(--bclr1) var(--p5),
var(--bclr0) var(--p6)
);
}
.gradient-blur > div:nth-of-type(3) {
z-index: 4;
backdrop-filter: blur(4px);
mask: linear-gradient(
to bottom,
var(--bclr0) var(--p4),
var(--bclr1) var(--p5),
var(--bclr1) var(--p6),
var(--bclr0) var(--p7)
);
}
.gradient-blur > div:nth-of-type(4) {
z-index: 5;
backdrop-filter: blur(8px);
mask: linear-gradient(
to bottom,
var(--bclr0) var(--p5),
var(--bclr1) var(--p6),
var(--bclr1) var(--p7),
var(--bclr0) var(--p8)
);
}
.gradient-blur > div:nth-of-type(5) {
z-index: 6;
backdrop-filter: blur(16px);
mask: linear-gradient(
to bottom,
var(--bclr0) var(--p6),
var(--bclr1) var(--p7),
var(--bclr1) var(--p8),
var(--bclr0) var(--p9)
);
}
.gradient-blur > div:nth-of-type(6) {
z-index: 7;
backdrop-filter: blur(32px);
mask: linear-gradient(
to bottom,
var(--bclr0) var(--p7),
var(--bclr1) var(--p8),
var(--bclr1) var(--p9)
);
}
.gradient-blur::after {
content: "";
z-index: 8;
backdrop-filter: blur(64px);
mask: linear-gradient(
to bottom,
var(--bclr0) var(--p8),
var(--bclr1) var(--p9)
);
}
JavaScript Code Structure
(() => {
const modelViewers = document.querySelectorAll("model-viewer");
const cards = document.querySelectorAll(".card");
const defaultOrbit = "0deg 90deg 164m";
const applyOrbit = (modelViewer, orbit, vert) => {
modelViewer.setAttribute("camera-orbit", orbit);
modelViewer.setAttribute("interpolation-decay", "200");
modelViewer.setAttribute("camera-target", `0m ${vert}m 10m`);
};
cards.forEach((card, index) => {
const modelViewer = modelViewers[index];
if (modelViewer) {
const calcOrbit = (xPos, cardRect) => {
const normalizedX = (xPos - cardRect.left) / cardRect.width;
const angle = normalizedX * 90 - 45;
return `${-angle}deg 90deg 164m`;
};
card.addEventListener("mousemove", (event) => {
const cardRect = card.getBoundingClientRect();
const orbit = calcOrbit(event.clientX, cardRect);
applyOrbit(modelViewer, orbit, 120);
});
card.addEventListener("mouseleave", () => {
applyOrbit(modelViewer, defaultOrbit, 140);
});
modelViewer.addEventListener("load", () => {
modelViewer.classList.add("loaded");
});
} else {
console.log(`No model found for card at i:${index}`);
}
});
})();
You May Also Like: How to Build Vertical Timeline Design using HTML CSS & jQuery
Testing and Debugging
Before deploying, it’s crucial to test your card hover effect in various browsers and on different devices to ensure smooth performance.
Make sure the animations are responsive and that the hover effect works as expected.
Conclusion
You’ve now created a 3D model card hover effect.
By using simple but powerful properties like perspective, transform, and transition, this technique creates an interactive and engaging user experience
You can easily adapt and customize this effect to suit your specific needs, such as adding more content, tweaking the design, or experimenting with different animations.
By mastering hover effects, you can add depth and interactivity to your websites, making them visually appealing and more engaging to users.
Happy coding!
Top comments (0)