Vissza az animációkhoz
Kártyaanimációk

Térbeli kihajlás

Élő előnézet – az animáció teljes méretben.

Élő előnézet
HTML · CSS · JS
HTML
<div class="we-card-perspective">

    <div class="we-card-perspective-inner">

        <span class="we-card-perspective-number">01</span>

        <div class="we-card-perspective-content">
            <span class="we-card-perspective-label">FÖLDÜNK CSODÁI</span>
            <h3>Rejtett világok</h3>
            <p>
                Felfedezésre váró különleges helyek
                és történetek a világ minden tájáról.
            </p>
        </div>

        <span class="we-card-perspective-arrow">↗</span>

    </div>

</div>
CSS
.we-card-perspective {
    width: min(100%, 420px);
    height: 300px;
    perspective: 1000px;
}

.we-card-perspective-inner {
    position: relative;
    width: 100%;
    height: 100%;
    padding: 32px;

    display: flex;
    flex-direction: column;
    justify-content: space-between;

    overflow: hidden;

    background:
        linear-gradient(145deg, #17261f, #0d1511);

    border: 1px solid rgba(95, 189, 125, .28);
    border-radius: 24px;

    color: #f4f1e9;

    transform-style: preserve-3d;
    transform:
        rotateX(0deg)
        rotateY(0deg)
        translateZ(0);

    box-shadow:
        0 20px 50px rgba(0, 0, 0, .35);

    transition:
        transform .7s cubic-bezier(.22, 1, .36, 1),
        box-shadow .7s ease,
        border-color .5s ease;
}

.we-card-perspective-inner::before {
    content: "";
    position: absolute;
    inset: 0;

    background:
        radial-gradient(
            circle at 20% 20%,
            rgba(95, 189, 125, .14),
            transparent 45%
        );

    opacity: .7;
    pointer-events: none;

    transition:
        transform .7s cubic-bezier(.22, 1, .36, 1);
}

.we-card-perspective-number {
    position: relative;
    z-index: 2;

    align-self: flex-end;

    font-family: "Bodoni Moda", Georgia, serif;
    font-size: 18px;
    color: rgba(244, 241, 233, .5);

    transform: translateZ(18px);

    transition:
        transform .7s cubic-bezier(.22, 1, .36, 1);
}

.we-card-perspective-content {
    position: relative;
    z-index: 2;

    transform: translateZ(28px);

    transition:
        transform .7s cubic-bezier(.22, 1, .36, 1);
}

.we-card-perspective-label {
    display: block;
    margin-bottom: 10px;

    font-family: Inter, system-ui, sans-serif;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: .18em;

    color: #7ac98e;
}

.we-card-perspective-content h3 {
    margin: 0 0 10px;

    font-family: "Bodoni Moda", Georgia, serif;
    font-size: clamp(30px, 5vw, 42px);
    font-weight: 500;
    line-height: 1.05;
}

.we-card-perspective-content p {
    max-width: 300px;
    margin: 0;

    font-family: Inter, system-ui, sans-serif;
    font-size: 14px;
    line-height: 1.7;

    color: rgba(244, 241, 233, .68);
}

.we-card-perspective-arrow {
    position: absolute;
    right: 30px;
    bottom: 26px;

    z-index: 3;

    font-size: 25px;
    color: #7ac98e;

    transform: translateZ(35px);

    transition:
        transform .7s cubic-bezier(.22, 1, .36, 1);
}

.we-card-perspective:hover .we-card-perspective-inner {
    transform:
        rotateX(7deg)
        rotateY(-9deg)
        translateZ(18px)
        translateY(-6px);

    border-color: rgba(95, 189, 125, .62);

    box-shadow:
        22px 30px 65px rgba(0, 0, 0, .48),
        0 0 35px rgba(95, 189, 125, .08);
}

.we-card-perspective:hover .we-card-perspective-inner::before {
    transform:
        translateX(12px)
        translateY(-8px)
        scale(1.08);
}

.we-card-perspective:hover .we-card-perspective-number {
    transform:
        translateZ(42px)
        translateX(4px);
}

.we-card-perspective:hover .we-card-perspective-content {
    transform:
        translateZ(58px)
        translateX(3px);
}

.we-card-perspective:hover .we-card-perspective-arrow {
    transform:
        translateZ(65px)
        translate(5px, -5px);
}
JavaScript
document.querySelectorAll(".we-card-perspective").forEach(card => {

    const inner = card.querySelector(
        ".we-card-perspective-inner"
    );

    card.addEventListener("mousemove", event => {

        const rect = card.getBoundingClientRect();

        const x =
            (event.clientX - rect.left) /
            rect.width;

        const y =
            (event.clientY - rect.top) /
            rect.height;

        const rotateY = (x - 0.5) * 14;
        const rotateX = (0.5 - y) * 10;

        inner.style.transform = `
            rotateX(${rotateX}deg)
            rotateY(${rotateY}deg)
            translateZ(18px)
            translateY(-6px)
        `;
    });

    card.addEventListener("mouseleave", () => {

        inner.style.transform = `
            rotateX(0deg)
            rotateY(0deg)
            translateZ(0)
            translateY(0)
        `;
    });

});