Pengkodean efek hover gambar irisan Quai Network – Beragampengetahuan
Saya memiliki kelemahan serius untuk antarmuka masa depan. dan efek hover. Secara alami, ketika saya menemukan situs web Quai Network yang dibuat oleh Unseen Studio, saya benar-benar jatuh cinta dengan keseluruhan desain dan interaksinya.
Selain grafik dan interaktivitas yang memukau, menurut saya animasi melayang sangat bagus untuk dibuat ulang menggunakan efek jalur klip dinamis:
Perhatikan bahwa efek yang akan kita buat dalam tutorial ini bukanlah replika persisnya, tetapi animasi serupa yang mensimulasikan aksi di situs web mereka.
Untuk animasi kita akan menggunakan GSAP dari GreenSock.
Ayo mulai!
Contents
tanda
Saat kita melihat efeknya di Quai Network, kita dapat melihat bahwa segera setelah kita mengarahkan kursor ke item kisi, beberapa irisan dari gambar yang sama akan terlihat. Irisannya sebenarnya sangat tinggi, dan gambar di dalamnya diulang. Ada juga efek acak huruf yang keren. Kita akan membuat versi yang lebih sederhana dengan menggunakan irisan setinggi item kisi.
Yang kita butuhkan adalah semacam pembungkus di mana kita bisa menyisipkan semua gambar yang akan diterapkan jalur kliping, membuat irisan ini. Mari siapkan struktur dasar, lalu kita akan menambahnya secara dinamis di JavaScript. Struktur ini juga akan berfungsi sebagai fallback:
<article class="card">
<div class="card__img" style="background-image:url(img/img1.jpg)"></div>
<span class="card__date" data-splitting>02/18/2074</span>
<h2 class="card__title" data-splitting>Code CR-4519: Anomaly Detection in Array</h2>
<a href="#" class="card__link" data-splitting>Read the article</a>
</article>
Teks yang ingin kita acak akan memiliki data-splitting atribut, karena kami akan menggunakan Splitting.js untuk membantu kami melakukan ini dengan memecah semua kata menjadi beberapa huruf.ini card__img elemen adalah wadah untuk semua irisan yang akan kita hasilkan.
Juga, untuk mendapatkan semacam efek “tampilan”, kita perlu menambahkan elemen pembungkus lain untuk semua irisan ini. Apakah sedemikian rupa:
<div class="card__img" >
<div class="card__img-wrap">
<div class="card__img-inner"></div>
<div class="card__img-inner"></div>
<div class="card__img-inner"></div>
<div class="card__img-inner"></div>
<div class="card__img-inner"></div>
</div>
</div>
Setiap card__img-inner akan memiliki jalur kliping yang diterapkan bersama dengan kumpulan gambar latar yang sama.
ini card__img-wrap Elemen pertama-tama akan diterjemahkan dan kemudian dipindahkan ke tampilan. Dengan menerjemahkan bagian dalam ke arah yang berlawanan, kita dapat membuat semacam animasi yang “tidak mengganggu”.
Mari beralih ke gaya.
css
Hal pertama yang penting untuk efek kita adalah gaya kartu:
.card {
display: grid;
grid-template-rows: auto 1fr auto;
cursor: pointer;
position: relative;
min-height: 60vh;
padding: 4vw;
border-bottom: 1px solid var(--color-border);
overflow: hidden;
}
Menggunakan tata letak kisi, kita dapat dengan mudah memposisikan elemen. Anda juga dapat menggunakan flexbox di sini.
Sekarang, mari tentukan gaya untuk bagian kunci, irisan, dan semua elemen terkait. Kami perlu memastikan semuanya tersusun dengan baik, jadi kami menggunakan pemosisian absolut untuk semuanya. Kami tidak ingin pecahan beterbangan dari kartu, jadi kami memastikan limpahan disembunyikan di elemen yang benar.
Untuk memastikan kami tidak melihat celah yang mengganggu yang disebabkan oleh anti-aliasing saat menggunakan jalur kliping, kami menerapkan sedikit trik. Untuk setiap irisan yang lebih besar dari satu, kami menambahkan satu piksel ke lebar dan menarik elemen kembali dengan menyetel sisi kiri yang sesuai. Kami akan melakukan ini secara dinamis. Jadi, misalnya, potongan kedua memiliki lebar 100% + 1px dan sisi kiri -1px. Ini akan membuatnya bergerak ke bagian atas irisan pertama, menyembunyikan potensi celah! Sementara kita akan menyetel kiri di JavaScript, kita bisa menangani lebar di CSS dengan meneruskan dua variabel yang akan ditentukan di skrip, --columns Dan --rows.
Jika kita menumpuk irisan secara horizontal, kita akan menyetel variabel –rows , yang akan menyebabkan tingginya disesuaikan. Jika kita menumpuk irisan secara vertikal, kita akan menyetel variabel –columns, yang pada gilirannya akan menyebabkan penyesuaian lebar.
.card__img,
.card__img-wrap,
.card__img-inner {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.card__img,
.card__img-inner {
background-size: cover;
background-position: 50% 50%;
}
.card__img,
.card__img-wrap {
overflow: hidden;
}
.card__img {
z-index: -1;
pointer-events: none;
--columns: 0;
--rows: 0;
}
.js .card__img {
opacity: 0;
background-image: none !important;
}
.card__img-inner {
filter: brightness(0.6);
width: calc(100% + (var(--columns) - 1) * 1px);
height: calc(100% + (var(--rows) - 1) * 1px);
}
Last but not least, mari beri gaya pada elemen lainnya:
.card__date {
display: flex;
align-content: center;
align-items: center;
line-height: 1;
position: relative;
}
.card__date::before {
content: '';
width: 15px;
height: 15px;
border: 1px solid var(--color-link);
background: var(--color-bg-date);
margin: 0 10px 4px 0;
}
.card__title {
font-weight: 400;
font-size: clamp(1.5rem,5vw,2.5rem);
}
.card__link {
position: relative;
}
.card__link::before {
content: '+';
margin-right: 10px;
}
JavaScript
Kita memerlukan dua file JavaScript: card.js, yang akan berisi kelas Card kita, dan index.js, yang akan menginisialisasi kartu kita.
Buat kelas kartu
Kelas Kartu kami akan menangani fungsionalitas komponen kartu. Kita akan mulai dengan mendefinisikan beberapa properti dan konstruktor di file card.js.
// Class representing a Card
export class Card {
// Initialize DOM and style related properties
DOM = {
// main DOM element
el: null,
// .card__img element
img: null,
// .card__img-wrap element (dynamically created in the layout function)
imgWrap: null,
// .card__img-inner "slice" elements (dynamically created in the layout function)
slices: null,
// .card__date element
date: null,
// .card__title element
title: null,
// .card__link element
link: null,
};
// Card image url
imageURL;
// Settings
settings = {
// vertical || horizontal alignment
orientation: 'vertical',
// Total number of slices for the inner images (clip paths)
slicesTotal: 5,
// Animation values
animation: {
duration: 0.5,
ease: 'power3.inOut'
}
};
/**
* Sets up the necessary elements, data, and event listeners for a Card instance.
* @param {HTMLElement} DOM_el - The DOM element that represents the card.
* @param {Object} options - The options for customizing the card. These options will override the default settings.
*/
constructor(DOM_el, options) {
// Merge settings and options.
this.settings = Object.assign({}, this.settings, options);
this.DOM.el = DOM_el;
this.DOM.img = this.DOM.el.querySelector('.card__img');
this.DOM.date = this.DOM.el.querySelector('.card__date');
this.DOM.title = this.DOM.el.querySelector('.card__title');
this.DOM.link = this.DOM.el.querySelector('.card__link');
// Splitting chars for date, title and link
this.chars = {
date: [...this.DOM.date.querySelectorAll('.char')],
title: [...this.DOM.title.querySelectorAll('.char')],
link: [...this.DOM.link.querySelectorAll('.char')]
};
// Save those initial char values
[...this.chars.date, ...this.chars.title, ...this.chars.link].forEach(char => {
char.dataset.initial = char.innerHTML;
});
// Extracts the image URL from the style of the DOM image element
this.imageURL = this.DOM.img.getAttribute('style').match(/url\((['"])?(.*?)\1\)/)[2];
// Calls the `layout` function to create the necessary structure for the card
this.layout();
// Initialize the events
this.initEvents();
}
...
}
Konstruktor mengambil dua parameter: elemen DOM dan objek opsi. Properti ini memungkinkan kami menyesuaikan setiap instance kartu. Kami menyimpan berbagai elemen dalam elemen kartu, termasuk gambar, tanggal, judul, dan tautan. Kami juga membagi karakter dalam elemen ini dan menyimpan nilai awalnya untuk animasi selanjutnya.
Fungsi tata letak dan animasi
Sekarang, mari kita tentukan beberapa fungsi untuk menangani tata letak dan animasi kartu.
ini layout() Fitur berfokus pada modifikasi tata letak gambar kartu nama dengan memotongnya menjadi beberapa bagian.Dengan fitur ini, kita bisa .card__img elemen.
dengan menyesuaikan slicesTotal pengaturan, kita dapat mengontrol jumlah irisan yang akan dihasilkan.Setiap irisan akan diwakili oleh sebuah elemen .card__img-inner kelas, berisi gambar latar belakang yang sama dengan gambar aslinya .card__img elemen.
Untuk mengelompokkan irisan ini bersama-sama, kita akan menggunakan file bernama .card__img-wrapyang akan ditambahkan ke .card__img elemen.
Selain itu, fungsi akan secara dinamis menyetel variabel CSS yang sesuai (--columns atau --rows) untuk mencocokkan slicesTotal nilai.Akhirnya, kita akan menggunakan setClipPath Fungsi:
layout() {
this.DOM.imgWrap = document.createElement('div');
this.DOM.imgWrap.classList = 'card__img-wrap';
let slicesStr = '';
for (let i = 0; i < this.settings.slicesTotal; ++i) {
slicesStr += `<div class="card__img-inner" style="background-image:url(${this.imageURL})"></div>`;
}
this.DOM.imgWrap.innerHTML = slicesStr;
this.DOM.slices = this.DOM.imgWrap.querySelectorAll('.card__img-inner');
// append the new wrap element to the card img element
this.DOM.img.appendChild(this.DOM.imgWrap);
// Set the --columns or --rows CSS variable value to be the same as the settings.slicesTotal
this.DOM.img.style.setProperty(this.settings.orientation === 'vertical' ? '--columns' : '--rows', this.settings.slicesTotal);
// Set the clip paths of each slice
this.setClipPath();
}
ini setClipPath() fungsi bertanggung jawab untuk menerapkan properti CSS clip-path ke setiap irisan.
Untuk mencapai efek ini, fungsi membagi setiap irisan sepanjang sumbu vertikal atau horizontal tergantung pada pengaturan orientasi. Pemotongan didasarkan pada nilai yang ditentukan di dalam sliceTotal, yang menentukan jumlah irisan yang akan dibuat.
Juga, setClipPath() Fungsi tersebut menggabungkan mekanisme offset untuk memperhitungkan setiap celah potensial yang mungkin muncul di antara irisan. Dengan sedikit mengimbangi setiap irisan, kami dapat memastikan tampilan yang mulus.
setClipPath() {
this.DOM.slices.forEach((slice, position) => {
let a1 = position*100/this.settings.slicesTotal;
let b1 = position*100/this.settings.slicesTotal + 100/this.settings.slicesTotal;
gsap.set(slice, {
clipPath: this.settings.orientation === 'vertical' ?
`polygon(${a1}% 0%, ${b1}% 0%, ${b1}% 100%, ${a1}% 100%)` :
`polygon(0% ${a1}%, 100% ${a1}%, 100% ${b1}%, 0% ${b1}%)`
});
const isVertical = this.settings.orientation === 'vertical';
gsap.set(slice, { [isVertical ? 'left' : 'top']: position*-1 });
});
}
Jadi layout berfungsi untuk membuat struktur yang diperlukan untuk gambar kartu kita dan mengirisnya menjadi beberapa bagian, sementara setClipPath Fungsi menetapkan properti CSS jalur klip untuk setiap irisan, menciptakan efek irisan.
Selanjutnya, kita juga membutuhkan initEvents Berfungsi untuk menginisialisasi event listener mouseenter Dan mouseleave peristiwa.
Acara Mouse dan Animasi
ini Card Kelas juga membutuhkan dua fungsi acara, mouseEnter Dan mouseLeaveuntuk menganimasikan kartu saat mouse masuk atau keluar dari kartu.
ini mouseEnter() Mengaktifkan fungsi saat mouse memasuki elemen target.
ada mouseEnter() fungsi, beberapa tindakan penting terjadi. Pertama, karakter tanggal, judul, dan properti tautan objek diacak. Gambar dan elemen pembungkus gambar kemudian dianimasikan dari posisi offset, mentransisikannya dengan lancar ke tampilan. Arah (vertikal atau horizontal) dari animasi ini ditentukan oleh pengaturan arah yang ditentukan.
Selain itu, masing-masing irisan gambar dianimasikan, muncul dari posisi offset acak.
mouseEnter() {
const isVertical = this.settings.orientation === 'vertical';
this.shuffleChars(this.chars.date);
this.shuffleChars(this.chars.title);
this.shuffleChars(this.chars.link);
gsap
.timeline({
defaults: {
duration: this.settings.animation.duration,
ease: this.settings.animation.ease
}
})
.addLabel('start', 0)
.fromTo(this.DOM.img, {
[isVertical ? 'yPercent' : 'xPercent']: 100,
opacity: 0
}, {
[isVertical ? 'yPercent' : 'xPercent']: 0,
opacity: 1
}, 'start')
.fromTo(this.DOM.imgWrap, {
[isVertical ? 'yPercent' : 'xPercent']: -100
}, {
[isVertical ? 'yPercent' : 'xPercent']: 0
}, 'start')
.fromTo(this.DOM.slices, {
[isVertical ? 'yPercent' : 'xPercent']: pos => pos % 2 ? gsap.utils.random(-75, -25) : gsap.utils.random(25, 75)
}, {
[isVertical ? 'yPercent' : 'xPercent']: 0
}, 'start');
}
Fungsi mouseLeave() dipicu saat mouse meninggalkan elemen target.
Saat fungsi mouseLeave() dipanggil, fungsi ini akan memulai animasi yang memindahkan gambar, pembungkus gambar, dan irisan keluar dari tampilan. Sekali lagi, arah tertentu (vertikal atau horizontal) dari animasi ini ditentukan oleh pengaturan arah yang ditentukan.
Dengan menganimasikan elemen-elemen ini, kita dapat membuat animasi keluar saat pengguna memindahkan mouse dari elemen tersebut.
mouseLeave() {
const isVertical = this.settings.orientation === 'vertical';
gsap
.timeline({
defaults: {
duration: this.settings.animation.duration,
ease: this.settings.animation.ease
}
})
.addLabel('start', 0)
.to(this.DOM.img, {
[isVertical ? 'yPercent' : 'xPercent']: 100,
opacity: 0
}, 'start')
.to(this.DOM.imgWrap, {
[isVertical ? 'yPercent' : 'xPercent']: -100
}, 'start')
.to(this.DOM.slices, {
[isVertical ? 'yPercent' : 'xPercent']: pos => pos % 2 ? gsap.utils.random(-75, 25) : gsap.utils.random(25, 75)
}, 'start')
}
Inisialisasi kartu
Sekarang kelas Kartu kita sudah siap, kita hanya perlu menginisialisasinya untuk setiap elemen kartu di halaman. Ini dilakukan di file index.js kami.
// Importing the necessary helper function to preload images from 'utils.js' file
import { preloadImages } from './utils.js';
// Importing the Card class from 'card.js' file
import { Card } from './card.js';
// Calling the Splitting function to split the text into individual characters,
// it's a text animation library which splits the text for complex animations.
Splitting();
// An array to hold different configurations/settings for different sets of cards.
// Each setting object includes the orientation, number of slices, and animation details.
const settingsArray = [
// 1st set of settings: Vertical orientation with 5 slices.
{
orientation: 'vertical',
slicesTotal: 5
},
// 2nd set of settings: Vertical orientation with 15 slices.
{
orientation: 'vertical',
slicesTotal: 15
},
// 3rd set of settings: Horizontal orientation with 5 slices and specific animation duration and easing.
{
orientation: 'horizontal',
slicesTotal: 5,
animation: {
duration: 0.6,
ease: 'expo.inOut'
}
},
// 4th set of settings: Horizontal orientation with 15 slices and specific animation duration and easing.
{
orientation: 'horizontal',
slicesTotal: 15,
animation: {
duration: 0.6,
ease: 'expo.inOut'
}
},
];
// Initialize the Cards
[...document.querySelectorAll('.card')].forEach((cardEl, index) => {
new Card(cardEl, settingsArray[Math.floor(index / 3) % settingsArray.length]);
});
// Preload images, then remove loader (loading class) from body
preloadImages('.canvas-wrap').then(() => document.body.classList.remove('loading'));
Di sini kita memilih semua elemen dengan kelas “kartu” dan membuat instance baru dari kelas Kartu untuk setiap elemen. Kami juga menyediakan beberapa pengaturan untuk setiap kartu, yang digunakan di kelas Kartu kami untuk menyesuaikan tampilan dan animasi setiap kartu.
Jika Anda tidak bingung dengan semua kartu dan pelajaran sekarang, selamat, Anda berhasil sampai akhir 😉
Terima kasih telah membaca tutorial ini, saya harap ini memberi Anda wawasan tentang efek pengkodean ini.
Pastikan untuk mengunduh kode atau bermain-main dengan demo menggunakan konfigurasi irisan yang berbeda!
Sedikit telur paskah: coba gunakan 50 irisan atau lebih, itu akan memiliki efek yang sangat keren 🙂 Tapi ingat, setiap irisan adalah div, jadi jangan disalahgunakan 😉
4 demo eksklusif: tayangan slide dan animasi tipografi
rencana pengembangan website
metode pengembangan website
jelaskan beberapa rencana untuk pengembangan website, proses pengembangan website, kekuatan dan kelemahan bisnis pengembangan website
, jasa pengembangan website, tahap pengembangan website, biaya pengembangan website
#Pengkodean #efek #hover #gambar #irisan #Quai #Network