UNA SBIRCIATIna?
/* BEST 4 — assegnare al Loop Carousel la classe: carosello-post-asim */
.carosello-post-asim {
--asim-gap: 16px;
--asim-active-width: calc(50% - 24px);
--asim-small-width: calc(16.666667% - 8px);
--asim-active-image-height: 400px;
--asim-small-image-height: 500px;
--asim-radius: 18px;
--asim-duration: 500ms;
--asim-easing: cubic-bezier(.22, .61, .36, 1);
}
/* Nessuna anteprima parzialmente tagliata. */
.carosello-post-asim .elementor-loop-container,
.carosello-post-asim .swiper {
display: block !important;
overflow: hidden !important;
}
.carosello-post-asim .swiper-wrapper {
display: flex;
align-items: flex-start;
}
/* Desktop: una card grande + tre piccole = 100% esatto. */
.carosello-post-asim .swiper-slide {
width: var(--asim-small-width) !important;
min-width: 0;
flex-shrink: 0;
order: 0;
transition: width var(--asim-duration) var(--asim-easing);
will-change: width;
}
.carosello-post-asim .swiper-slide.asim-layout-active {
width: var(--asim-active-width) !important;
order: -1;
}
.carosello-post-asim .swiper-slide > .elementor,
.carosello-post-asim .post-card-asim,
.carosello-post-asim .post-card-asim > .e-con-inner {
width: 100%;
min-width: 0;
}
.carosello-post-asim .post-card-asim > .e-con-inner {
max-width: none;
}
/* Angoli arrotondati. */
.carosello-post-asim .post-card-asim,
.carosello-post-asim .post-card-asim__img,
.carosello-post-asim .post-card-asim__img a,
.carosello-post-asim .post-card-asim__img img {
border-radius: var(--asim-radius);
}
.carosello-post-asim .post-card-asim__img {
width: 100%;
height: var(--asim-small-image-height);
overflow: hidden;
transition: height var(--asim-duration) var(--asim-easing);
will-change: height;
}
.carosello-post-asim .post-card-asim__img .elementor-widget-container,
.carosello-post-asim .post-card-asim__img a {
display: block;
width: 100%;
height: 100%;
}
.carosello-post-asim .post-card-asim__img img {
display: block;
width: 100% !important;
max-width: none !important;
height: 100% !important;
object-fit: cover;
object-position: center;
transition: transform var(--asim-duration) var(--asim-easing);
}
.carosello-post-asim .swiper-slide.asim-layout-active .post-card-asim__img {
height: var(--asim-active-image-height);
}
.carosello-post-asim .swiper-slide.asim-layout-active .post-card-asim__img img {
transform: scale(1.001);
}
/* Contenuto completo solo sulla card grande. */
.carosello-post-asim .post-card-asim__content {
display: flex !important;
max-height: 0;
margin: 0 !important;
padding-top: 0 !important;
padding-bottom: 0 !important;
opacity: 0;
visibility: hidden;
overflow: hidden;
pointer-events: none;
transform: translateY(-8px);
transition:
max-height 400ms var(--asim-easing),
opacity 220ms ease,
transform 400ms var(--asim-easing),
visibility 0s linear 400ms;
}
.carosello-post-asim .swiper-slide.asim-layout-active .post-card-asim__content {
max-height: 450px;
opacity: 1;
visibility: visible;
pointer-events: auto;
transform: translateY(0);
transition:
max-height 450ms var(--asim-easing),
opacity 300ms ease 100ms,
transform 450ms var(--asim-easing),
visibility 0s linear 0s;
}
/* Frecce nere, uguali e sempre visibili. */
.carosello-post-asim .elementor-swiper-button {
z-index: 20;
display: flex !important;
align-items: center;
justify-content: center;
width: 44px;
height: 44px;
padding: 0;
color: #000 !important;
cursor: pointer;
opacity: 1 !important;
transition: transform 200ms ease;
}
.carosello-post-asim .elementor-swiper-button svg {
width: 24px;
height: 24px;
color: #000 !important;
fill: #000 !important;
stroke: #000 !important;
}
.carosello-post-asim .elementor-swiper-button svg path,
.carosello-post-asim .elementor-swiper-button svg polyline,
.carosello-post-asim .elementor-swiper-button svg line {
fill: #000 !important;
stroke: #000 !important;
}
.carosello-post-asim .elementor-swiper-button:hover {
transform: translateY(-50%) scale(1.08);
}
.carosello-post-asim .elementor-swiper-button:not(:hover) {
transform: translateY(-50%);
}
/* Pallini rimossi. */
.carosello-post-asim .swiper-pagination {
display: none !important;
}
/* La preview segue mouse e touch durante il trascinamento. */
.carosello-post-asim .swiper {
cursor: grab;
touch-action: pan-y;
}
.carosello-post-asim .swiper:active { cursor: grabbing; }
/* Tablet: ancora una grande + tre piccole, senza eccedenze. */
@media (max-width: 1024px) {
.carosello-post-asim {
--asim-gap: 12px;
--asim-active-width: calc(58% - 20.88px);
--asim-small-width: calc(14% - 5.04px);
--asim-active-image-height: 360px;
--asim-small-image-height: 430px;
--asim-radius: 16px;
}
}
/* Mobile: una card intera, senza anteprime laterali tagliate. */
@media (max-width: 767px) {
.carosello-post-asim {
--asim-gap: 0px;
--asim-active-width: 100%;
--asim-small-width: 100%;
--asim-active-image-height: clamp(280px, 92vw, 400px);
--asim-small-image-height: clamp(280px, 92vw, 400px);
--asim-radius: 14px;
--asim-duration: 420ms;
}
.carosello-post-asim .elementor-swiper-button {
width: 36px;
height: 36px;
}
.carosello-post-asim .elementor-swiper-button svg {
width: 20px;
height: 20px;
}
}
@media (prefers-reduced-motion: reduce) {
.carosello-post-asim *,
.carosello-post-asim *::before,
.carosello-post-asim *::after {
scroll-behavior: auto !important;
transition-duration: .01ms !important;
animation-duration: .01ms !important;
animation-iteration-count: 1 !important;
}
}
(function ($) {
'use strict';
const SELECTOR = '.carosello-post-asim';
const INIT_CLASS = 'asim-swiper-ready';
function waitForSwiper(widget, callback, attempt = 0) {
const swiperElement = widget.querySelector('.swiper');
if (swiperElement && swiperElement.swiper) {
callback(swiperElement.swiper, swiperElement);
return;
}
if (attempt >= 100) {
console.warn('[Best 4] Istanza Swiper non trovata:', widget);
return;
}
window.setTimeout(function () {
waitForSwiper(widget, callback, attempt + 1);
}, 50);
}
function getGap(widget) {
const value = parseFloat(
window.getComputedStyle(widget).getPropertyValue('--asim-gap')
);
return Number.isFinite(value) ? value : 0;
}
function updateSwiper(swiper, widget, realign = false) {
if (!swiper || swiper.destroyed || !swiper.el || !swiper.wrapperEl) {
return;
}
const gap = getGap(widget);
swiper.params.spaceBetween = gap;
if (swiper.originalParams) swiper.originalParams.spaceBetween = gap;
swiper.updateSize();
swiper.updateSlides();
swiper.updateProgress();
swiper.updateSlidesClasses();
if (realign) {
swiper.setTransition(0);
swiper.setTranslate(0);
swiper.updateProgress();
swiper.updateSlidesClasses();
}
}
function createFrameUpdater(swiper, widget) {
let frameId = null;
return function (realign = false) {
if (frameId !== null) window.cancelAnimationFrame(frameId);
frameId = window.requestAnimationFrame(function () {
frameId = null;
updateSwiper(swiper, widget, realign);
});
};
}
/*
* Il Best 4 mostra tutte e quattro le card nello stesso viewport.
* Disattiviamo quindi la traslazione nativa e rendiamo infinito il solo
* cambio di layout: dopo la quarta card si torna alla prima e viceversa.
*/
function prepareFixedLayout(swiper) {
if (swiper.params.loop && typeof swiper.loopDestroy === 'function') {
try { swiper.loopDestroy(); } catch (error) {
console.warn('[Best 4] Rimozione loop nativo non necessaria.', error);
}
}
swiper.params.loop = false;
swiper.params.rewind = false;
swiper.params.allowTouchMove = false;
if (swiper.originalParams) {
swiper.originalParams.loop = false;
swiper.originalParams.rewind = false;
swiper.originalParams.allowTouchMove = false;
}
swiper.setTransition(0);
swiper.setTranslate(0);
swiper.update();
}
function setupLayoutController(widget, swiper, initialIndex) {
const slides = Array.from(
widget.querySelectorAll('.swiper-wrapper > .swiper-slide')
).filter(function (slide) {
return !slide.classList.contains('swiper-slide-duplicate');
});
if (!slides.length) return;
let index = ((initialIndex % slides.length) + slides.length) % slides.length;
function render() {
slides.forEach(function (slide, slideIndex) {
const active = slideIndex === index;
slide.classList.toggle('asim-layout-active', active);
slide.setAttribute('aria-current', active ? 'true' : 'false');
});
swiper.setTransition(0);
swiper.setTranslate(0);
swiper.updateSize();
swiper.updateSlides();
swiper.updateProgress();
}
swiper.__asimChangeLayout = function (direction) {
index = (index + direction + slides.length) % slides.length;
render();
};
render();
}
function goToAdjacent(swiper, direction) {
if (typeof swiper.__asimChangeLayout === 'function') {
swiper.__asimChangeLayout(direction);
}
}
/* Il trascinamento segue il mouse e al rilascio cambia layout. */
function bindPointerPreview(widget, swiper, swiperElement) {
const wrapper = swiper.wrapperEl;
let pointerId = null;
let startX = 0;
let deltaX = 0;
let dragging = false;
let blockClickUntil = 0;
swiperElement.addEventListener('pointerdown', function (event) {
if (event.button !== undefined && event.button !== 0) return;
if (event.target.closest('.elementor-swiper-button')) return;
pointerId = event.pointerId;
startX = event.clientX;
deltaX = 0;
dragging = false;
swiperElement.setPointerCapture(pointerId);
});
swiperElement.addEventListener('pointermove', function (event) {
if (pointerId !== event.pointerId) return;
deltaX = event.clientX - startX;
if (Math.abs(deltaX) > 4) dragging = true;
if (!dragging) return;
event.preventDefault();
wrapper.style.transition = 'none';
wrapper.style.transform = 'translate3d(' + (deltaX * .28) + 'px,0,0)';
});
function finish(event) {
if (pointerId === null || (event.pointerId !== undefined && event.pointerId !== pointerId)) return;
wrapper.style.transition = 'transform 220ms ease';
wrapper.style.transform = 'translate3d(0,0,0)';
if (dragging && Math.abs(deltaX) >= 45) {
blockClickUntil = Date.now() + 350;
goToAdjacent(swiper, deltaX < 0 ? 1 : -1);
}
pointerId = null;
deltaX = 0;
dragging = false;
}
swiperElement.addEventListener('pointerup', finish);
swiperElement.addEventListener('pointercancel', finish);
/* Blocca il link solo dopo un vero drag; il normale primo clic passa. */
widget.addEventListener('click', function (event) {
if (Date.now() < blockClickUntil) {
event.preventDefault();
event.stopPropagation();
}
}, true);
}
/* Le frecce cambiano quale card è grande, come nel riferimento Download. */
function bindLayoutArrows(widget, swiper) {
const previous = widget.querySelector('.elementor-swiper-button-prev');
const next = widget.querySelector('.elementor-swiper-button-next');
function bind(button, direction) {
if (!button || button.dataset.asimBound === '1') return;
button.dataset.asimBound = '1';
button.setAttribute('role', 'button');
button.setAttribute('tabindex', '0');
const activate = function (event) {
event.preventDefault();
event.stopPropagation();
if (event.stopImmediatePropagation) event.stopImmediatePropagation();
goToAdjacent(swiper, direction);
};
button.addEventListener('click', activate, true);
button.addEventListener('keydown', function (event) {
if (event.key === 'Enter' || event.key === ' ') activate(event);
}, true);
}
bind(previous, -1);
bind(next, 1);
}
function configureWidget(widget) {
if (!widget || widget.classList.contains(INIT_CLASS)) return;
waitForSwiper(widget, function (swiper, swiperElement) {
if (swiper.__asimConfigured) {
widget.classList.add(INIT_CLASS);
return;
}
swiper.__asimConfigured = true;
widget.classList.add(INIT_CLASS);
const currentRealIndex = Number.isInteger(swiper.realIndex)
? swiper.realIndex
: 0;
const fixedParams = {
slidesPerView: 'auto',
centeredSlides: false,
centeredSlidesBounds: false,
slidesPerGroup: 1,
watchSlidesProgress: true,
roundLengths: false,
speed: 500,
breakpoints: undefined,
/* Il trascinamento è gestito dal controller Best 4. */
grabCursor: true,
simulateTouch: true,
followFinger: true,
allowTouchMove: false,
touchRatio: 1,
resistance: true,
resistanceRatio: .85,
/* Il link delle card compresse funziona al primo clic. */
slideToClickedSlide: false,
preventClicks: false,
preventClicksPropagation: false,
touchStartPreventDefault: false
};
Object.assign(swiper.params, fixedParams);
if (swiper.originalParams) Object.assign(swiper.originalParams, fixedParams);
prepareFixedLayout(swiper);
setupLayoutController(widget, swiper, currentRealIndex);
bindLayoutArrows(widget, swiper);
bindPointerPreview(widget, swiper, swiperElement);
const requestUpdate = createFrameUpdater(swiper, widget);
updateSwiper(swiper, widget, false);
updateSwiper(swiper, widget, true);
swiper.on('activeIndexChange', function () {
requestUpdate(false);
});
swiper.on('transitionEnd', function () {
requestUpdate(true);
});
swiper.on('loopFix', function () {
requestUpdate(false);
});
if ('ResizeObserver' in window) {
const observer = new ResizeObserver(function () {
requestUpdate(true);
});
observer.observe(widget);
swiper.__asimResizeObserver = observer;
} else {
$(window).on(
'resize.asimCarousel orientationchange.asimCarousel',
function () { requestUpdate(true); }
);
}
widget.querySelectorAll('img').forEach(function (image) {
if (!image.complete) {
image.addEventListener(
'load',
function () { requestUpdate(true); },
{ once: true }
);
}
});
swiperElement.classList.add('asim-swiper-configured');
});
}
function initializeCarousels(scope) {
const context = scope || document;
if (context.matches && context.matches(SELECTOR)) configureWidget(context);
context.querySelectorAll(SELECTOR).forEach(configureWidget);
}
$(function () {
initializeCarousels(document);
});
$(window).on('elementor/frontend/init', function () {
if (!window.elementorFrontend || !elementorFrontend.hooks) return;
elementorFrontend.hooks.addAction(
'frontend/element_ready/loop-carousel.post',
function ($scope) { initializeCarousels($scope[0]); }
);
elementorFrontend.hooks.addAction(
'frontend/element_ready/global',
function ($scope) { initializeCarousels($scope[0]); }
);
});
})(jQuery);
Troppo pigro per leggere?
The Artbook è un progetto editoriale indipendente dedicato all’arte contemporanea e a tutto ciò che le ruota intorno: artisti, pratiche, visioni, spazi, collezioni, culture.
Nasce dall’esigenza di raccontare l’arte in modo serio ma accessibile, competente ma non accademico. Senza semplificazioni forzate, senza sensazionalismi, senza linguaggi autoreferenziali.
Ci interessa l’arte quando è pensiero, ricerca, posizione. Non quando è solo superficie o tendenza.