- Czym jest DC Frame Slideshow?
- Architektura modułu – jak to działa „pod maską” 🧠⚙️
- Panel administracyjny – konfiguracja krok po kroku ⚙️
- Motywy animacji – Theme 1, Theme 2, Theme 3 🎬✨
- 📱 Responsywność i mobile – zaprojektowane od podstaw pod nowoczesne layouty
- 🚀 Wydajność i SEO – zaprojektowane bez kompromisów
- Instalacja modułu
- Podsumowanie
Jeśli kiedykolwiek wdrażałeś slider na stronie głównej sklepu lub landing page’u, to dobrze wiesz, jak to zwykle wygląda. Kilka obrazków, proste przejście typu fade albo slide, strzałki po bokach… i koniec.
Działa? Tak. Robi wrażenie? Najczęściej nie.
Problem polega na tym, że większość dostępnych sliderów jest do siebie łudząco podobna. Te same schematy, te same animacje, zero charakteru. W efekcie slider przestaje być elementem „hero”, a staje się tylko kolejnym blokiem treści, który użytkownik przewija bez zastanowienia.
Do tego dochodzi jeszcze brak realnej kontroli:
-
animacje są banalne albo sztywno narzucone,
-
nie da się wpłynąć na formę przejścia,
-
trudno zbudować efekt „wow”, który faktycznie zatrzyma uwagę użytkownika na pierwszym ekranie.
I właśnie tutaj pojawia się DC Frame Slideshow.
To nie jest kolejny „zwykły” slider, tylko moduł zaprojektowany z myślą o wizualnym efekcie od pierwszego wejścia na stronę. Dynamiczne ramy SVG, zaawansowane animacje, różne tryby (themes) i pełna kontrola nad wyglądem sprawiają, że slider w końcu zaczyna pełnić rolę, do której został stworzony — przyciąga uwagę i buduje charakter strony.
To właśnie takie elementy pokazują, że świadome projektowanie sklepu internetowego zaczyna się od pierwszego ekranu i emocji, jakie wywołuje u użytkownika.
Czym jest DC Frame Slideshow?
DC Frame Slideshow to autorski moduł stworzony specjalnie dla OpenCart 4, zaprojektowany z myślą o tych, którzy chcą czegoś więcej niż „kolejny slider z obrazkami” 🚀
To nie jest gotowiec z marketplace’u ani przerobiony plugin sprzed 10 lat. To nowoczesny, efektowny slider, który od pierwszej sekundy buduje klimat strony i przyciąga uwagę użytkownika 🎯
🔧 Technologicznie moduł opiera się na:
-
🧩 SVG – dynamiczne ramki i maski animowane w czasie rzeczywistym
-
🎬 Anime.js – płynne, kontrolowane animacje bez szarpania i lagów
-
✂️ Autorskiej logice przejść – każdy slajd to mała scena, a nie tylko „fade in / fade out”
⚡ Bez jQuery
Całość działa w czystym JavaScript, co oznacza:
-
mniejszy narzut,
-
lepszą wydajność,
-
pełną zgodność z nowoczesnymi standardami frontendu.
🧱 Zero ingerencji w core OpenCart
Moduł instalujesz jak każde inne rozszerzenie:
-
bez nadpisywania plików systemowych,
-
bez hacków,
-
bez „kombinowania”.
👉 W praktyce: instalujesz, konfigurujesz i działa.
A przy tym wygląda jak coś, co zwykle wymagałoby dedykowanego frontendu i kilku dni pracy ✨
Architektura modułu – jak to działa „pod maską” 🧠⚙️
DC Frame Slideshow nie jest kolejnym „globalnym” sliderem, który wstrzykuje jeden skrypt i modli się, żeby nie kolidował z resztą strony. Tutaj architektura została zaprojektowana od zera pod OpenCart 4 – modułowo, czysto i przewidywalnie.
🔹 Każdy slider to niezależna instancja
Każde wywołanie modułu tworzy własny obiekt JS. Własny stan, własne animacje, własne kolory. Zero współdzielenia zmiennych, zero efektu domina.
🔹 Pełna obsługa wielu modułów na jednej stronie
Możesz mieć kilka sliderów jeden pod drugim – każdy z innym motywem, innymi kolorami i inną animacją. Wszystko działa równolegle i bez konfliktów 🚀
🔹 Motywy = osobna logika JavaScripttheme_1, theme_2, theme_3 to nie „if w środku jednego pliku”.
Każdy motyw ma:
-
własną klasę JS
-
własną animację SVG
-
własną sekwencję przejść
Dzięki temu kod jest czytelny, rozszerzalny i gotowy na kolejne tryby 🎨
🔹 Kolory przekazywane elastycznie
Moduł potrafi pobierać kolory na dwa sposoby:
-
🎯 przez CSS variables (
--bg-1,--bg-2) – idealne do integracji z motywem -
🎯 przez data attributes (
data-bg_1,data-bg_2) – szybkie i jednoznaczne
Gradient? Jeden kolor? Zero kolorów? Wszystko obsłużone.
🔹 Zero konfliktów z resztą sklepu
Brak jQuery, brak globalnych selektorów, brak ingerencji w core OpenCarta.
Każda instancja działa w swoim „sandboxie” 🧩
Efekt? Slider, który wygląda efektownie, ale pod spodem jest uporządkowany, przewidywalny i gotowy do dalszej rozbudowy – bez bólu głowy i bez technicznego długu.
Panel administracyjny – konfiguracja krok po kroku ⚙️
To jest moment, w którym DC Frame Slideshow pokazuje swoją prawdziwą siłę. Panel administracyjny został zaprojektowany tak, żeby nie tylko „coś ustawić”, ale realnie kontrolować wygląd i zachowanie slidera – bez grzebania w kodzie.
Poniżej przechodzimy przez wszystkie zakładki dokładnie tak, jak robi to użytkownik w OpenCart 4 👇
1. Zakładka „General” 🧩
Tutaj definiujesz tożsamość modułu.
Dostępne opcje:
-
Nazwa modułu – czysto organizacyjna, widoczna w panelu
-
Attribute ID – unikalny identyfikator instancji
-
Tytuł i opis (WYSIWYG) – opcjonalna treść nad sliderem
-
Wielojęzyczność – pełne wsparcie dla wielu języków
Dlaczego Attribute ID jest tak ważne? 🔑
-
pozwala rozróżniać wiele sliderów na jednej stronie
-
umożliwia precyzyjne stylowanie CSS (
#hero-slider,#homepage-slideritd.) -
daje punkt zaczepienia dla JS (jeśli chcesz rozszerzać moduł)
-
pomaga w SEO i semantyce (świadome nazewnictwo sekcji)
👉 Pro tip:
Zamiast losowych ID, używaj sensownych nazw typu hero-home, promo-black-friday, collection-summer.
2. Zakładka „Slides” 🖼️
Tu dzieje się content.
Co możesz ustawić:
-
listę slajdów (bez limitu)
-
dla każdego slajdu:
-
obraz
-
nagłówek
-
opis
-
link + tekst przycisku
-
Najważniejsze cechy:
-
✔️ dowolna liczba slajdów
-
✔️ kolejność = kolejność animacji
-
✔️ zero ograniczeń systemowych
👉 I co najważniejsze:
nie korzystasz z banerów systemowych OpenCarta
Nie ma kombinowania, przypisywania layoutów, pozycji, sortowań.
Masz czystą, logiczną listę slajdów, dokładnie pod ten moduł.
3. Zakładka „Settings” 🔥
Tu robimy prawdziwe mięso 🍖
To właśnie ta zakładka decyduje, jak slider wygląda i jak się zachowuje.
Dostępne ustawienia:
🎨 Wygląd i motyw
-
wybór Theme 1 / Theme 2 / Theme 3
-
kolory tła:
-
Kolor 1
-
Kolor 2 (opcjonalny – gradient)
-
-
kolory:
-
nagłówków
-
opisów
-
przycisków (normal / hover)
-
⚙️ Zachowanie slidera
-
sposób animacji (zależny od theme)
-
reakcja na zmianę slajdu
-
płynność przejść
📱 Responsywność
-
automatyczne dopasowanie do ekranu
-
inne proporcje dla desktop / mobile
-
brak sztywnych wysokości
Jak działa tło? 🎯
To jeden z kluczowych elementów DC Frame Slideshow:
-
Podasz 1 kolor →
slider użyje jednolitego tła -
Podasz 2 kolory →
tworzony jest gradient -
Każdy theme interpretuje tło inaczej:
-
Theme 1 – tło kontenera
-
Theme 2 – tło + ramka SVG
-
Theme 3 – gradient bezpośrednio w masce SVG
-
👉 Efekt:
Ten sam zestaw kolorów może wyglądać zupełnie inaczej w zależności od motywu 🎨
Motywy animacji – Theme 1, Theme 2, Theme 3 🎬✨
Jednym z największych atutów DC Frame Slideshow jest to, że nie masz jednego „sztywnego” slidera. Masz trzy zupełnie różne style animacji, z osobną logiką JS, inną pracą ramki i innym charakterem wizualnym.
Każdy theme to świadomy wybór stylistyczny, a nie tylko kosmetyczna zmiana efektu.
1. Theme 1 – klasyczna animowana ramka 🧩
Charakter: elegancki, spokojny, bardzo uniwersalny
Co go wyróżnia:
-
płynne, naturalne przejścia slajdów
-
subtelna animacja ramki SVG
-
brak agresywnych ruchów – wszystko „oddycha”
-
świetna czytelność treści
Idealny do:
-
strony głównej (home page)
-
marek premium
-
sklepów, gdzie slider ma wprowadzać, a nie dominować
📌 Technicznie:
-
klasyczna animacja maski SVG
-
jeden etap animacji ramki
-
bardzo lekki dla przeglądarki
👉 Kod js stylu Theme 1:
{
function debounce(func, wait, immediate) {
let timeout;
return function () {
const context = this, args = arguments;
const later = function () {
timeout = null;
if (!immediate) func.apply(context, args);
};
const callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
}
class Slideshow {
constructor(el) {
this.DOM = {};
this.DOM.el = el;
this.wrapper = el.closest('.dc-frame-slideshow');
this.host = el.closest('.dc-slideshow-body') || el;
// bierzemy z CSS vars (pewne i spójne z Twig)
const cs = this.wrapper ? getComputedStyle(this.wrapper) : null;
this.bg1 = (cs?.getPropertyValue('--bg-1') || '#f1f1f1').trim();
this.bg2 = (cs?.getPropertyValue('--bg-2') || '').trim();
this.settings = {
animation: {
slides: { duration: 600, easing: 'easeOutQuint' },
shape: { duration: 300, easing: { in: 'easeOutQuint', out: 'easeOutQuad' } }
}
};
this.init();
}
init() {
this.DOM.slides = Array.from(this.DOM.el.querySelectorAll('.slides > .slide'));
this.slidesTotal = this.DOM.slides.length;
this.DOM.nav = this.DOM.el.querySelector('.slidenav');
this.DOM.nextCtrl = this.DOM.nav?.querySelector('.slidenav__item--next');
this.DOM.prevCtrl = this.DOM.nav?.querySelector('.slidenav__item--prev');
this.current = 0;
// background kontenera (zawsze sensownie)
this.applyContainerBackground();
this.createFrame();
this.initEvents();
}
applyContainerBackground() {
// tu ustawiasz “tło kontenera” (np pod zdjęciami gdyby gdzieś prześwitywało)
if (this.bg2 && this.bg2 !== this.bg1) {
this.host.style.background = `linear-gradient(90deg, ${this.bg1}, ${this.bg2})`;
} else {
this.host.style.background = this.bg1;
}
}
createFrame() {
this.rect = this.DOM.el.getBoundingClientRect();
this.frameSize = this.rect.width / 12;
this.paths = {
initial: this.calculatePath('initial'),
final: this.calculatePath('final')
};
this.DOM.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
this.DOM.svg.setAttribute('class', 'shape');
this.DOM.svg.setAttribute('width', '100%');
this.DOM.svg.setAttribute('height', '100%');
this.DOM.svg.setAttribute('viewBox', `0 0 ${this.rect.width} ${this.rect.height}`);
const hasGradient = this.bg2 && this.bg2 !== this.bg1;
// unikalne id gradientu (żeby wiele modułów nie kolidowało)
const gid = 'dc_frame_grad_' + Math.random().toString(16).slice(2);
this.DOM.svg.innerHTML = hasGradient
? `
<defs>
<linearGradient id="${gid}" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="${this.bg1}"></stop>
<stop offset="100%" stop-color="${this.bg2}"></stop>
</linearGradient>
</defs>
<path fill="url(#${gid})" d="${this.paths.initial}"></path>
`
: `<path fill="${this.bg1}" d="${this.paths.initial}"></path>`;
// svg przed nav
this.DOM.el.insertBefore(this.DOM.svg, this.DOM.nav);
this.DOM.shape = this.DOM.svg.querySelector('path');
}
updateFrame() {
this.rect = this.DOM.el.getBoundingClientRect();
this.frameSize = this.rect.width / 12;
this.paths.initial = this.calculatePath('initial');
this.paths.final = this.calculatePath('final');
this.DOM.svg.setAttribute('viewBox', `0 0 ${this.rect.width} ${this.rect.height}`);
this.DOM.shape.setAttribute('d', this.isAnimating ? this.paths.final : this.paths.initial);
}
calculatePath(path = 'initial') {
return path === 'initial'
? `M 0,0 0,${this.rect.height} ${this.rect.width},${this.rect.height} ${this.rect.width},0 0,0 Z M 0,0 ${this.rect.width},0 ${this.rect.width},${this.rect.height} 0,${this.rect.height} Z`
: `M 0,0 0,${this.rect.height} ${this.rect.width},${this.rect.height} ${this.rect.width},0 0,0 Z M ${this.frameSize},${this.frameSize} ${this.rect.width - this.frameSize},${this.frameSize} ${this.rect.width - this.frameSize},${this.rect.height - this.frameSize} ${this.frameSize},${this.rect.height - this.frameSize} Z`;
}
initEvents() {
this.DOM.nextCtrl?.addEventListener('click', () => this.navigate('next'));
this.DOM.prevCtrl?.addEventListener('click', () => this.navigate('prev'));
window.addEventListener('resize', debounce(() => this.updateFrame(), 20));
document.addEventListener('keydown', (ev) => {
const keyCode = ev.keyCode || ev.which;
if (keyCode === 37) this.navigate('prev');
else if (keyCode === 39) this.navigate('next');
});
}
navigate(dir = 'next') {
if (this.isAnimating) return false;
this.isAnimating = true;
const animateShapeIn = anime({
targets: this.DOM.shape,
duration: this.settings.animation.shape.duration,
easing: this.settings.animation.shape.easing.in,
d: this.paths.final
});
const animateSlides = () => {
return new Promise((resolve) => {
const currentSlide = this.DOM.slides[this.current];
anime({
targets: currentSlide,
duration: this.settings.animation.slides.duration,
easing: this.settings.animation.slides.easing,
translateX: dir === 'next' ? -1 * this.rect.width : this.rect.width,
complete: () => {
currentSlide.classList.remove('slide--current');
resolve();
}
});
this.current = dir === 'next'
? (this.current < this.slidesTotal - 1 ? this.current + 1 : 0)
: (this.current > 0 ? this.current - 1 : this.slidesTotal - 1);
const newSlide = this.DOM.slides[this.current];
newSlide.classList.add('slide--current');
anime({
targets: newSlide,
duration: this.settings.animation.slides.duration,
easing: this.settings.animation.slides.easing,
translateX: [dir === 'next' ? this.rect.width : -1 * this.rect.width, 0]
});
const newSlideImg = newSlide.querySelector('.slide__img');
if (newSlideImg) {
anime.remove(newSlideImg);
anime({
targets: newSlideImg,
duration: this.settings.animation.slides.duration * 4,
easing: this.settings.animation.slides.easing,
translateX: [dir === 'next' ? 200 : -200, 0]
});
}
const textTargets = [
newSlide.querySelector('.slide__title'),
newSlide.querySelector('.slide__desc'),
newSlide.querySelector('.slide__link')
].filter(Boolean);
if (textTargets.length) {
anime({
targets: textTargets,
duration: this.settings.animation.slides.duration * 2,
easing: this.settings.animation.slides.easing,
delay: (t, i) => i * 100 + 100,
translateX: [dir === 'next' ? 300 : -300, 0],
opacity: [0, 1]
});
}
});
};
const animateShapeOut = () => {
anime({
targets: this.DOM.shape,
duration: this.settings.animation.shape.duration,
delay: 150,
easing: this.settings.animation.shape.easing.out,
d: this.paths.initial,
complete: () => this.isAnimating = false
});
};
animateShapeIn.finished.then(animateSlides).then(animateShapeOut);
}
}
document.querySelectorAll('.dc-frame-slideshow.theme_1').forEach((wrapper) => {
const slideshowEl = wrapper.querySelector('.slideshow');
const bodyEl = wrapper.querySelector('.dc-slideshow-body');
if (!slideshowEl || !bodyEl) return;
// init slidera
new Slideshow(slideshowEl);
// imagesLoaded TYLKO dla tego slidera
imagesLoaded(
slideshowEl.querySelectorAll('.slide__img'),
{ background: true },
() => {
bodyEl.classList.remove('loading');
bodyEl.classList.add('render');
}
);
});
}
👉 Wideo pokazujące animację Theme 1
2. Theme 2 – dynamiczna ramka z ruchem bocznym ⚡
Charakter: mocny, nowoczesny, „premium slider look”
Co go wyróżnia:
-
agresywniejsze animacje przejść
-
wyraźny ruch boczny slajdów
-
ramka reagująca kierunkowo (next / prev)
-
efekt „wow” już przy pierwszym kliknięciu
Idealny do:
-
landing page
-
kampanii sprzedażowych
-
sekcji HERO, które mają przyciągać uwagę
📌 Technicznie:
-
osobna logika SVG dla
nextiprev -
dynamiczne przeliczanie ścieżek ramki
-
animacje zsynchronizowane z ruchem slajdu
👉 Kod js stylu Theme 2:
{
function debounce(func, wait, immediate) {
let timeout;
return function () {
const context = this, args = arguments;
const later = function () {
timeout = null;
if (!immediate) func.apply(context, args);
};
const callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
}
class SlideshowTheme2 {
constructor(wrapper) {
this.wrapper = wrapper;
this.body = wrapper.querySelector('.dc-slideshow-body');
this.el = wrapper.querySelector('.slideshow');
if (!this.el) return;
// zostawiamy dokładnie tak jak chcesz:
this.bg1 = wrapper.dataset.bg_1 || '#111';
this.bg2 = (wrapper.dataset.bg_2 || '').trim();
this.DOM = {};
this.settings = {
animation: {
slides: {
duration: 600,
easing: 'easeOutQuint'
},
shape: {
duration: 300,
easing: { in: 'easeOutQuad', out: 'easeOutQuad' }
}
}
};
// unikalne ID dla gradientu w SVG (żeby 2 moduły nie gryzły się)
this.uid = this.wrapper.id
? this.wrapper.id.replace(/[^a-zA-Z0-9\-_]/g, '')
: ('dcfs_' + Math.random().toString(16).slice(2));
this.init();
}
init() {
this.DOM.slides = Array.from(this.el.querySelectorAll('.slides > .slide'));
this.slidesTotal = this.DOM.slides.length;
this.DOM.nav = this.el.querySelector('.slidenav');
this.DOM.nextCtrl = this.DOM.nav?.querySelector('.slidenav__item--next');
this.DOM.prevCtrl = this.DOM.nav?.querySelector('.slidenav__item--prev');
this.current = 0;
// tło kontenera możesz zostawić jako “fallback”
this.applyContainerBackground();
this.createFrame();
this.initEvents();
}
applyContainerBackground() {
if (!this.body) return;
if (this.bg2 && this.bg2 !== this.bg1) {
// to jest tylko fallback – “prawdziwe” tło w animacji to SVG fill
this.body.style.background = `linear-gradient(135deg, ${this.bg1}, ${this.bg2})`;
} else {
this.body.style.background = this.bg1;
}
}
createFrame() {
this.rect = this.el.getBoundingClientRect();
this.frameSize = this.rect.width / 12;
this.paths = {
initial: this.calculatePath('initial'),
final: this.calculatePath('final')
};
const hasGradient = (this.bg2 && this.bg2 !== this.bg1);
const gradId = `${this.uid}_grad`;
this.DOM.svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
this.DOM.svg.setAttribute('class', 'shape');
this.DOM.svg.setAttribute('width', '100%');
this.DOM.svg.setAttribute('height', '100%');
this.DOM.svg.setAttribute('viewBox', `0 0 ${this.rect.width} ${this.rect.height}`);
// UWAGA: gradient robimy w SVG, bo to wypełnia “ramkę” podczas animacji
this.DOM.svg.innerHTML = `
${hasGradient ? `
<defs>
<linearGradient id="${gradId}" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" stop-color="${this.bg1}"></stop>
<stop offset="100%" stop-color="${this.bg2}"></stop>
</linearGradient>
</defs>
` : ''}
<path fill="${hasGradient ? `url(#${gradId})` : this.bg1}" d="${this.paths.initial}"></path>
`;
this.el.insertBefore(this.DOM.svg, this.DOM.nav);
this.DOM.shape = this.DOM.svg.querySelector('path');
}
updateFrame() {
this.rect = this.el.getBoundingClientRect();
this.paths.initial = this.calculatePath('initial');
this.paths.final = this.calculatePath('final');
this.DOM.svg.setAttribute('viewBox', `0 0 ${this.rect.width} ${this.rect.height}`);
// jak nie animuje, wracamy do initial
this.DOM.shape.setAttribute('d', this.isAnimating ? this.paths.final.next : this.paths.initial);
}
calculatePath(type = 'initial') {
if (type === 'initial') {
return `
M 0,0 0,${this.rect.height}
${this.rect.width},${this.rect.height}
${this.rect.width},0 0,0 Z
M 0,0 ${this.rect.width},0
${this.rect.width},${this.rect.height}
0,${this.rect.height} Z
`;
}
return {
next: `
M 0,0 0,${this.rect.height}
${this.rect.width},${this.rect.height}
${this.rect.width},0 0,0 Z
M ${this.frameSize},${this.frameSize}
${this.rect.width - this.frameSize},${this.frameSize / 2}
${this.rect.width - this.frameSize},${this.rect.height - this.frameSize / 2}
${this.frameSize},${this.rect.height - this.frameSize} Z
`,
prev: `
M 0,0 0,${this.rect.height}
${this.rect.width},${this.rect.height}
${this.rect.width},0 0,0 Z
M ${this.frameSize},${this.frameSize / 2}
${this.rect.width - this.frameSize},${this.frameSize}
${this.rect.width - this.frameSize},${this.rect.height - this.frameSize}
${this.frameSize},${this.rect.height - this.frameSize / 2} Z
`
};
}
initEvents() {
this.DOM.nextCtrl?.addEventListener('click', () => this.navigate('next'));
this.DOM.prevCtrl?.addEventListener('click', () => this.navigate('prev'));
window.addEventListener('resize', debounce(() => this.updateFrame(), 30));
}
navigate(dir = 'next') {
if (this.isAnimating) return;
this.isAnimating = true;
const animateShapeIn = anime({
targets: this.DOM.shape,
duration: this.settings.animation.shape.duration,
easing: this.settings.animation.shape.easing.in,
d: dir === 'next' ? this.paths.final.next : this.paths.final.prev
});
const animateSlides = () => new Promise(resolve => {
const currentSlide = this.DOM.slides[this.current];
anime({
targets: currentSlide,
duration: this.settings.animation.slides.duration,
easing: this.settings.animation.slides.easing,
translateX: dir === 'next' ? -this.rect.width : this.rect.width,
complete: () => {
currentSlide.classList.remove('slide--current');
resolve();
}
});
this.current = dir === 'next'
? (this.current + 1) % this.slidesTotal
: (this.current - 1 + this.slidesTotal) % this.slidesTotal;
const newSlide = this.DOM.slides[this.current];
newSlide.classList.add('slide--current');
anime({
targets: newSlide,
duration: this.settings.animation.slides.duration,
easing: this.settings.animation.slides.easing,
translateX: [dir === 'next' ? this.rect.width : -this.rect.width, 0]
});
});
const animateShapeOut = () => {
anime({
targets: this.DOM.shape,
duration: this.settings.animation.shape.duration,
delay: 150,
easing: this.settings.animation.shape.easing.out,
d: this.paths.initial,
complete: () => this.isAnimating = false
});
};
animateShapeIn.finished.then(animateSlides).then(animateShapeOut);
}
}
document.querySelectorAll('.dc-frame-slideshow.theme_2').forEach(wrapper => {
const inst = new SlideshowTheme2(wrapper);
// loader per-instancja (żeby 2 moduły działały niezależnie)
const imgs = wrapper.querySelectorAll('.slide__img');
if (imgs.length) {
imagesLoaded(imgs, { background: true }, () => {
wrapper.querySelectorAll('.dc-slideshow-body').forEach(b => {
b.classList.remove('loading');
b.classList.add('render');
});
});
} else {
// awaryjnie: bez obrazów też nie blokuj
wrapper.querySelectorAll('.dc-slideshow-body').forEach(b => {
b.classList.remove('loading');
b.classList.add('render');
});
}
});
}
👉 Wideo pokazujące animację Theme 2
3. Theme 3 – gradientowa ramka SVG 🌈🔥
Charakter: efektowny, nowoczesny, „design statement”
Co go wyróżnia:
-
gradient bezpośrednio w SVG (nie w CSS!)
-
wieloetapowe animacje ramki
-
bardzo płynne, sekwencyjne przejścia
-
ogromny efekt wizualny przy minimalnym JS
Idealny do:
-
stron marek kreatywnych
-
projektów showcase / portfolio
-
sekcji, gdzie slider ma być bohaterem strony
📌 Technicznie:
-
gradient zdefiniowany w
<defs>SVG -
animacje wielostopniowe (timeline Anime.js)
-
zero dodatkowych bibliotek
👉 Kod js stylu Theme 3:
👉 Wideo pokazujące animację Theme 3
🎯 Dlaczego to ma znaczenie?
Każdy theme:
-
ma własny kod JS
-
nie koliduje z innymi instancjami
-
inaczej interpretuje tło (kolor / gradient)
-
pozwala dobrać styl slidera do celu strony, a nie odwrotnie
To nie są „skórki”.
To są trzy różne silniki animacji w jednym module 🚀
📱 Responsywność i mobile – zaprojektowane od podstaw pod nowoczesne layouty
DC Frame Slideshow od początku był projektowany z myślą o mobile first i realnych problemach, jakie sprawiają klasyczne slidery na telefonach 📵.
Nie ma tu sztucznych hacków, JS-owych przeliczeń wysokości ani „łatania” CSS-em po fakcie.
🔧 Aspect-ratio zamiast sztywnych wysokości
Zamiast height: 600px (które na mobile zawsze boli 😅), moduł wykorzystuje CSS aspect-ratio:
-
zachowuje proporcje slidera niezależnie od szerokości ekranu
-
automatycznie skaluje się na desktopie, tablecie i telefonie
-
zero JS-owych przeliczeń przy resize
Efekt? Slider zawsze wygląda tak samo dobrze, bez względu na urządzenie 📐
📲 Automatyczna adaptacja do pionu
Na urządzeniach mobilnych:
-
proporcje przechodzą naturalnie w układ pionowy
-
treści (nagłówki, opisy, CTA) pozostają czytelne
-
animacje SVG i ramki dalej działają — tylko w innym kontekście
Nie ma „uciętych” slajdów ani przycisków poza ekranem 👌
🧱 Brak skakania layoutu (CLS = 0)
Jedna z kluczowych rzeczy, na które zwraca uwagę Google:
-
wysokość slidera jest znana od razu
-
layout nie przeskakuje po załadowaniu obrazów
-
brak CLS (Cumulative Layout Shift)
To oznacza:
-
lepszy Core Web Vitals 🚀
-
lepsze UX
-
lepsze SEO (tak, realnie)
⚡ Lekkość mimo animacji
Mimo zaawansowanych animacji:
-
brak jQuery
-
brak ciężkich bibliotek sliderowych
-
tylko SVG + Anime.js + czysty JS
Dzięki temu:
-
mobile nie „klęka”
-
animacje są płynne nawet na słabszych urządzeniach
-
bateria użytkownika nie płacze 🔋😉
W skrócie: DC Frame Slideshow zachowuje się jak nowoczesny komponent UI, a nie stary slider z 2015 roku.
Responsywność nie jest dodatkiem — jest fundamentem.
🚀 Wydajność i SEO – zaprojektowane bez kompromisów
DC Frame Slideshow od początku był projektowany pod performance i SEO, a nie „żeby jakoś działało”. Tu nie ma przypadków ani ciężkich zależności 👇
⚡ Brak jQuery
-
Czysty, nowoczesny JavaScript (ES6+)
-
Zero zbędnych bibliotek
-
Mniej JS = szybszy rendering + lepszy PageSpeed
🧠 Brak inline JS
-
Cała logika w plikach modułu
-
Lepsza czytelność
-
Lepsze cache’owanie przez przeglądarkę
-
Brak problemów z CSP
🧩 SVG zamiast canvasów
-
Ramki i maski oparte o SVG
-
Skalowalne, lekkie, idealne pod HiDPI
-
SVG animowane przez Anime.js = maksimum efektu przy minimalnym koszcie
🖼️ Obrazy ładowane świadomie
-
Slajdy inicjalizują się dopiero po:
-
załadowaniu obrazów (
imagesLoaded)
-
-
Brak „pustych klatek”
-
Brak CLS (layout się nie przesuwa)
📈 Idealne pod PageSpeed
-
Brak blokujących skryptów
-
Brak nadmiarowych zapytań
-
Brak ciężkich frameworków
-
Stabilny layout od pierwszego renderu
🔥 Autorski system minifikacji i cache (game changer)
DC Frame Slideshow nie korzysta z zewnętrznych minifierów. Moduł ma wbudowany, autorski system optymalizacji, który działa automatycznie:
🗜️ Minifikacja CSS i JS
-
CSS i JS są:
-
minifikowane
-
scalane w jeden plik
-
-
Silnik oparty o JShrink (sprawdzone, stabilne rozwiązanie)
♻️ Inteligentny cache
-
Cache nie przebudowuje się przy każdym wejściu
-
Pliki są regenerowane tylko wtedy, gdy któryś z plików źródłowych się zmieni
-
Efekt:
-
zero niepotrzebnych operacji
-
zero spowolnień
-
pełna kontrola
-
🧠 Co to oznacza w praktyce?
-
Slider jest szybki nawet przy:
-
wielu instancjach na stronie
-
kilku motywach jednocześnie
-
-
Idealny do:
-
homepage
-
landing pages
-
sklepów z dużym ruchem
-
Instalacja modułu
Instalacja DC Frame Slideshow została zaprojektowana dokładnie tak, jak powinno to wyglądać w OpenCart 4 – bez kombinowania, bez grzebania w plikach i bez ryzyka konfliktów ⚙️
Jak zainstalować moduł?
Cały proces sprowadza się do standardowego instalatora OpenCart:
-
📦 pobierasz paczkę ZIP z modułem
-
📤 wgrywasz ją przez Rozszerzenia → Instalator
-
✅ gotowe – moduł pojawia się na liście rozszerzeń
Nie ma tu żadnych ręcznych kroków, kopiowania plików ani edycji core.
Co jest ważne (i miłe 😉)
-
❌ OCMOD – moduł nie ingeruje w system
-
🧩 brak nadpisywania plików OpenCarta
-
🔄 pełna kompatybilność z każdym motywem (również customowym)
-
🧠 logika JS i CSS jest całkowicie odseparowana od reszty sklepu
Dzięki temu możesz:
-
używać modułu wielokrotnie na jednej stronie
-
mieszać różne motywy (Theme 1 / 2 / 3)
-
bez stresu aktualizować OpenCart w przyszłości
Skąd pobrać moduł?
Moduł udostępniamy w dwóch miejscach – w zależności od tego, jak pracujesz:
Obie wersje są w pełni funkcjonalne – różnica polega głównie na cyklu aktualizacji.
Jeśli znasz OpenCart, to instalacja DC Frame Slideshow zajmie Ci dosłownie minutę.
Jeśli nie znasz – tym bardziej 😉
A dalej zostaje już tylko jedno: ustawić slajdy i zrobić efekt „wow” na stronie głównej 🚀
Podsumowanie
DC Frame Slideshow to zupełnie inna liga niż klasyczne slidery, które wszyscy znamy z OpenCarta i gotowych motywów. To nie jest kolejny rotator banerów „żeby coś się ruszało” — to pełnoprawny element designu, który realnie buduje pierwsze wrażenie i charakter strony 🚀
Czym DC Frame Slideshow różni się od zwykłych sliderów?
-
🔳 Animowana ramka SVG, a nie proste przesuwanie obrazków
-
🎬 Zaawansowane animacje Anime.js, zsynchronizowane z treścią i maską
-
🎨 Motywy (Theme 1 / 2 / 3), które faktycznie zmieniają sposób prezentacji, a nie tylko kolory
-
🧩 Pełna niezależność instancji – możesz mieć kilka sliderów na jednej stronie bez konfliktów
-
⚡ Wydajność + SEO: zero jQuery, zero zbędnych skryptów, brak CLS
Dlaczego to nie jest kolejny nudny banner?
Bo:
-
❌ nie opiera się na systemowych bannerach OpenCarta
-
❌ nie wygląda tak samo jak setki innych sklepów
-
❌ nie wymaga hacków, OCMOD-ów ani ingerencji w core
Zamiast tego:
-
✅ daje efekt „wow” od pierwszego wejścia
-
✅ pozwala kontrolować formę, animację i tło
-
✅ skaluje się wizualnie od prostego home page po agresywny landing
Dla kogo ten moduł ma sens?
DC Frame Slideshow ma sens, jeśli:
-
🏪 chcesz, żeby Twój sklep wyglądał premium, a nie „jak każdy inny”
-
📢 budujesz landing page, kampanię, nową kolekcję
-
🎯 zależy Ci na designie, wydajności i kontroli, a nie gotowych schematach
-
🧠 wiesz, że pierwsze 3 sekundy na stronie mają znaczenie
Jeśli szukasz czegoś więcej niż zwykłego slidera, co:
-
przyciąga wzrok
-
nie psuje PageSpeed
-
i daje realną przewagę wizualną
👉 DC Frame Slideshow dokładnie w to celuje.
Sklepy internetowe Woocommerce
Sklepy internetowe Opencart
Sklepy internetowe Prestashop
Sklepy internetowe Magento
Strony internetowe Joomla!
Strony Internetowe Wordpress




