Как сделать курсор со следом из картинок?

Этот скрипт позволит тебе сделать крутой эффект следа из картинок за курсором. Работает с Autoscale. Пример работы модификации внизу cтраницы

1 мин.

Видео-инструкция

JavaScript
Zeroblock

Обновление от 31.12.23 - В слайдер были добавлены новые поля, а также recid теперь нужно писать без приставки "#rec", поэтому если вдруг что-то не работает, то см. видео, а также текстовую инструкцию ниже↓

!

1. Создай любую галерею в тильде и загрузи в неё картинки, которые будешь использовать в хвосте курсора

2. Нажми ПКМ по каждой картинке и выбери "копировать ссылку на изображение"

3. Создай в зеро, где будешь делать эффект html-элемент и задай ему класс



4. Растяни этот html-элемент на ту область, в пределах которой у тебя должен работать эффект

5. Вставь в него этот код, заменяя ссылки на картинки своими и добавляя\убавляя по образцу строчки для новых картинок если нужно:











6. Выбери нужный тип эффекта, скопируй код и вставь в блок т123
Инструкция
Важно!
- Данная модификация может быть только одна на странице. Поэтому в данном случае мне и пришлось разбить примеры на отдельные страницы
(Развернуть)

photo-cursor
<div class="content">
<img class="content__img" src="https://static.tildacdn.com/tild3063-6335-4238-b733-353630363062/1.jpg"/>
<img class="content__img" src="https://static.tildacdn.com/tild6662-6431-4433-b738-383731353739/2.jpg"/>
<img class="content__img" src="https://static.tildacdn.com/tild3536-6666-4035-a239-623536313630/3.jpg"/>
<img class="content__img" src="https://static.tildacdn.com/tild3630-6462-4231-a135-653364353365/4.jpg"/>
</div>
Скопировать
Скопировать
Скопировать код
<!--Библиотека VORON--><!--Курсор из картинок https://voron-dev.ru/photo-cursor-->
   
  
  <style>
.content__img {
	max-width: var(--img-maxwidth);
	position: absolute;
	top: 0;
	left: 0;
	opacity: 0;
	will-change: transform;
	filter: var(--filter-img);
}

.content__img--full {
	width: 100%;
	height: 100%;
	background-size: cover;
}
</style>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.11.4/dist/gsap.min.js"></script>
<script>
{
    const body = document.body;
    const MathUtils = {
        lerp: (a, b, n) => (1 - n) * a + n * b,
        distance: (x1,y1,x2,y2) => Math.hypot(x2-x1, y2-y1)
    }
    const getMousePos = (ev) => {
        let posx = 0;
        let posy = 0;
        if (!ev) ev = window.event;
        if (ev.pageX - $(".photo-cursor").offset().left || ev.pageY - $(".photo-cursor").offset().top) {
            posx = ev.pageX - $(".photo-cursor").offset().left;
            posy = ev.pageY - $(".photo-cursor").offset().top;
        }
        else if (ev.clientX || ev.clientY) 	{
            posx = ev.clientX + body.scrollLeft + docEl.scrollLeft;
            posy = ev.clientY + body.scrollTop + docEl.scrollTop;
        }
        return {x: posx, y: posy};
    }
    let mousePos = lastMousePos = cacheMousePos = {x: 0, y: 0};
        document.querySelector(".photo-cursor").addEventListener("mousemove", ev => mousePos = getMousePos(ev));
    const getMouseDistance = () => MathUtils.distance(mousePos.x,mousePos.y,lastMousePos.x,lastMousePos.y);
    class Image {
        constructor(el) {
            this.DOM = {el: el};
            this.defaultStyle = {
                scale: 1,
                x: 0,
                y: 0,
                opacity: 0
            };
            this.getRect();
            this.initEvents();
        }
        initEvents() {
            window.addEventListener("resize", () => this.resize());
        }
        resize() {
            gsap.set(this.DOM.el, this.defaultStyle);
            this.getRect();
        }
        getRect() {
            this.rect = this.DOM.el.getBoundingClientRect();
        }
        isActive() {
            return gsap.isTweening(this.DOM.el) || this.DOM.el.style.opacity != 0;
        }
    }
    class ImageTrail {
        constructor() {
            this.DOM = {content: document.querySelector(".content")};
            this.images = [];
            [...this.DOM.content.querySelectorAll("img")].forEach(img => this.images.push(new Image(img)));
            this.imagesTotal = this.images.length;
            this.imgPosition = 0;
            this.zIndexVal = 1;
            this.threshold = 100;
            requestAnimationFrame(() => this.render());
        }
        render() {
            let distance = getMouseDistance();
            cacheMousePos.x = MathUtils.lerp(cacheMousePos.x || mousePos.x, mousePos.x, 0.1);
            cacheMousePos.y = MathUtils.lerp(cacheMousePos.y || mousePos.y, mousePos.y, 0.1);
            if ( distance > this.threshold ) {
                [...this.DOM.content.querySelectorAll("img")].forEach(img => img.style.pointerEvents = 'none');  
                this.showNextImage();
                ++this.zIndexVal;
                this.imgPosition = this.imgPosition < this.imagesTotal-1 ? this.imgPosition+1 : 0;
                lastMousePos = mousePos;
            }
            let isIdle = true;
            for (let img of this.images) {
                if ( img.isActive() ) {
                    isIdle = false;
                    break;
                }
            }
            if ( isIdle && this.zIndexVal !== 1 ) {
                this.zIndexVal = 1;
            }
            requestAnimationFrame(() => this.render());
        }
        showNextImage() {
            const img = this.images[this.imgPosition];
            gsap.killTweensOf(img.DOM.el);
            new TimelineMax()
            .set(img.DOM.el, {
                startAt: {opacity: 0, scale: 1},
                opacity: 1,
                scale: 1,
                zIndex: this.zIndexVal,
                x: cacheMousePos.x - img.rect.width/2,
                y: cacheMousePos.y - img.rect.height/2
            }, 0)
            .to(img.DOM.el, 0.9, {
                ease: Expo.easeOut,
                x: mousePos.x - img.rect.width/2,
                y: mousePos.y - img.rect.height/2
            }, 0)
            .to(img.DOM.el, 1, {
                ease: Power1.easeOut,
                opacity: 0
            }, 0.4)
            .to(img.DOM.el, 1, {
                ease: Quint.easeOut,
                scale: 0.2
            }, 0.4);
        }
    }
        new ImageTrail();
}
</script>
 


123
Т.к. данная модификация может быть только одна на странице, то я разбил каждый из примеров на отдельные странички, переключиться между которыми можешь по кнопкам ниже. Самую первую вариацию я оставил на этой странице
Уменьшаются
Пример работы модификации
Пример работы модификации
Наведи курсор на этот блок, чтобы увидеть эффект следа из уменьшающихся картинок