Интерактивная блоб-кнопка

Модификатор для добавления интерактивного элемента (блоб)
Инструкция по настройке
Шаблон страницы
Как добавить шаблон
48473347
Для корректной работы модификаций подключите библиотеку jQuery:
Настройки сайта -> Еще -> Подключить jQuery на страницах сайта
jQuery
Создаем Zero Block и открываем редактор
  1. Добавляем кнопку и задаем ей ссылку
  2. Настраиваем ей размеры в соотношении 1к1
  3. Задаем кнопке цвет BG. COLOR (он будет заливать кнопку при наведении на нее курсора)
  4. В значение SHADOW задаем цвет для текста (такой цвет будет показываться при наведении)
  5. Задаем кнопке класс tistolsBtnBlobBase
1 шаг
Создаем блок T123 и копируем в него код
2 шаг
Скопировать код
<!-- TL07 - Модификация для Тильды. Интерактивная блоб-кнопка https://mod.tistols.com/interactive-blob -->
<style>
.tistolsBtnBlob {
    position: absolute;
    transform: translate(-50%, -50%);
    width: 300px;
    height: 300px;
    border-radius: 250px;
}

.tistolsBtnBlob .tistolsBtnBlob-wrapper {
    position: relative;
    width: 100%;
    height: 100%;
}

.tistolsBtnBlob .tistolsBtn_el {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
}

.tistolsBtnBlob .btn__blob {
    width: 100%;
    height: 100%;
}

.tistolsBtnBlob .btn__text {
    display: block;
    color: #fff;
    transition: all 0.6s ease;
    font-family: 'Inter', Arial, sans-serif;
}

.tistolsBtnBlob .btn__text.hover {
    color: #000;
}</style>

<script>
    class Blob {
        constructor(parCanvas) {
            this.points = [];
            this.parCanvas = parCanvas;
        }

        blobInit() {
            for (let i = 0; i < this.numPoints; i++) {
                let point = new Point(this.divisional * (i + 1), this);
                point.acceleration = 0;
                point.friction = 0.05;
                this.push(point);
            }
        }

        render() {
            let canvas = this.canvas;
            let ctx = this.ctx;
            let position = this.position;
            let pointsArray = this.points;
            let radius = this.radius;
            let points = this.numPoints;
            let divisional = this.divisional;
            let center = this.center;
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            pointsArray[0].solveWith(pointsArray[points - 1], pointsArray[1]);
            let p0 = pointsArray[points - 1].position;
            let p1 = pointsArray[0].position;
            let _p2 = p1;
            ctx.beginPath();
            ctx.moveTo(center.x, center.y);
            ctx.moveTo((p0.x + p1.x) / 2, (p0.y + p1.y) / 2);
            for (let i = 1; i < points; i++) {
                pointsArray[i].solveWith(pointsArray[i - 1], pointsArray[i + 1] || pointsArray[0]);
                let p2 = pointsArray[i].position;
                var xc = (p1.x + p2.x) / 2;
                var yc = (p1.y + p2.y) / 2;
                ctx.quadraticCurveTo(p1.x, p1.y, xc, yc);
                p1 = p2;
            }
            var xc = (p1.x + _p2.x) / 2;
            var yc = (p1.y + _p2.y) / 2;
            ctx.quadraticCurveTo(p1.x, p1.y, xc, yc);
            this.opacity = this.lerp(this.opacity, this.hover ? 1 : 0, 0.1);
            ctx.globalAlpha = this.opacity;
            ctx.fillStyle = this.color;
            ctx.fill();
            ctx.globalAlpha = 1;
            ctx.lineWidth = 1;
            ctx.strokeStyle = "#fff";
            ctx.stroke();
            requestAnimationFrame(this.render.bind(this));
        }

        lerp(start, end, amt) {
            return (1 - amt) * start + amt * end;
        }

        push(item) {
            if (item instanceof Point) {
                this.points.push(item);
            }
        }

        set color(value) {
            this._color = value;
        }

        get color() {
            return this._color || "#FCE600";
        }

        set opacity(value) {
            this._opacity = value;
        }

        get opacity() {
            return this._opacity || 0;
        }

        set hover(value) {
            this._hover = value;
        }

        get hover() {
            return this._hover || false;
        }

        set canvas(value) {
            if (value instanceof HTMLElement && value.tagName.toLowerCase() === "canvas") {
                this._canvas = this.parCanvas;
                this.ctx = this._canvas.getContext("2d");
            }
        }

        get canvas() {
            return this._canvas;
        }

        set numPoints(value) {
            if (value > 2) {
                this._points = value;
            }
        }

        get numPoints() {
            return this._points || 16;
        }

        set radius(value) {
            if (value > 0) {
                this._radius = value;
            }
        }

        get radius() {
            return this._radius || 150;
        }

        set position(value) {
            if (typeof value === "object" && value.x && value.y) {
                this._position = value;
            }
        }

        get position() {
            return this._position || {x: 0.5, y: 0.5};
        }

        get divisional() {
            return (Math.PI * 2) / this.numPoints;
        }

        get center() {
            return {
                x: this.canvas.width * this.position.x,
                y: this.canvas.height * this.position.y
            };
        }

        set running(value) {
            this._running = value === true;
        }

        get running() {
            return this.running !== false;
        }
    }

    class Point {
        constructor(azimuth, parent) {
            this.parent = parent;
            this.azimuth = Math.PI - azimuth;
            this._components = {x: Math.cos(this.azimuth), y: Math.sin(this.azimuth)};
            this.acceleration = -0.3 + Math.random() * 0.6;
        }

        solveWith(leftPoint, rightPoint) {
            this.acceleration = (-0.3 * this.radialEffect + (leftPoint.radialEffect - this.radialEffect) + (rightPoint.radialEffect - this.radialEffect)) * this.elasticity -
                this.speed * this.friction;
        }

        set acceleration(value) {
            if (typeof value === "number") {
                this._acceleration = value;
                this.speed += this._acceleration * 2;
            }
        }

        get acceleration() {
            return this._acceleration || 0;
        }

        set speed(value) {
            if (typeof value === "number") {
                this._speed = value;
                this.radialEffect += this._speed * 5;
            }
        }

        get speed() {
            return this._speed || 0;
        }

        set radialEffect(value) {
            if (typeof value === "number") {
                this._radialEffect = value;
            }
        }

        get radialEffect() {
            return this._radialEffect || 0;
        }

        get position() {
            return {
                x: this.parent.center.x +
                    this.components.x * (this.parent.radius + this.radialEffect),
                y: this.parent.center.y +
                    this.components.y * (this.parent.radius + this.radialEffect)
            };
        }

        get components() {
            return this._components;
        }

        set elasticity(value) {
            if (typeof value === "number") {
                this._elasticity = value;
            }
        }

        get elasticity() {
            return this._elasticity || 0.001;
        }

        set friction(value) {
            if (typeof value === "number") {
                this._friction = value;
            }
        }

        get friction() {
            return this._friction || 0.0085;
        }
    }

    class BlobButton {
        constructor(divBase) {
            this.canvas = $(divBase).find("canvas")[0];
            this.objText = $(divBase).find("span")[0];
            this.blob = new Blob(this.canvas);
            this.init(divBase);
        }

        init(divBase) {
            this.canvas.setAttribute("touch-action", "none");
            this.blobContainer = divBase;
            this.blobRect = this.blobContainer.getBoundingClientRect();
            this.center = {x: 0, y: 0};
            this.resize();
            this.oldMousePoint = {x: 0, y: 0};
            this.blob.canvas = this.canvas;
            this.blob.radius = this.canvas.width / 2.4;
            this.blob.speed = 999;
            this.blob.blobInit();
            this.blob.render();
        }

        resize() {
            const {width, height, top, left} = this.canvas.getBoundingClientRect();
            this.center.x = left - this.blobRect.left + width / 2;
            this.center.y = top - this.blobRect.top + height / 2;
            this.canvas.width = width;
            this.canvas.height = height;
        }

        mouseMove(e) {
            let blobRect = this.blobContainer.getBoundingClientRect();
            let pos = {x: e.clientX - blobRect.left, y: e.clientY - blobRect.top};
            let diff = {x: pos.x - this.center.x, y: pos.y - this.center.y};
            let dist = Math.sqrt(diff.x * diff.x + diff.y * diff.y);
            let angle = null;
            this.blob.mousePos = {x: this.center.x - pos.x, y: this.center.y - pos.y};
            if (dist < this.blob.radius && this.blob.hover === false) {
                let vector = {x: pos.x - this.center.x, y: pos.y - this.center.y};
                angle = Math.atan2(vector.y, vector.x);
                this.blob.hover = true;
                this.objText.classList.add("hover");
            } else if (dist > this.blob.radius && this.blob.hover === true) {
                let vector = {x: pos.x - this.center.x, y: pos.y - this.center.y};
                angle = Math.atan2(vector.y, vector.x);
                this.blob.hover = false;
                this.objText.classList.remove("hover");
            }
            if (typeof angle === "number") {
                let nearestPoint = null;
                let distanceFromPoint = 100;
                this.blob.points.forEach((point) => {
                    if (Math.abs(angle - point.azimuth) < distanceFromPoint) {
                        nearestPoint = point;
                        distanceFromPoint = Math.abs(angle - point.azimuth);
                    }
                });
                if (nearestPoint) {
                    let strength = {x: this.oldMousePoint.x - pos.x, y: this.oldMousePoint.y - pos.y};
                    strength = Math.sqrt(strength.x * strength.x + strength.y * strength.y) * 10;
                    if (strength > 100) strength = 100;
                    nearestPoint.acceleration = (strength / 100) * (this.blob.hover ? -1 : 1);
                }
            }
            this.oldMousePoint.x = pos.x;
            this.oldMousePoint.y = pos.y;
        }
    }

    const buttonsBlob = document.getElementsByClassName('tistolsBtnBlobBase');
    for (let i = 0; i < buttonsBlob.length; i++) {
        const btn = buttonsBlob[i];
        const blobBaseW = $(btn).width();
        const blobBaseH = $(btn).height();
        const blobCircleD = Math.max(blobBaseW, blobBaseH);

        const objInner = $(btn).children().first();
        const sText = objInner.html();
        objInner.html("<div class=\"tistolsBtnBlob\" style=\"left: " + blobBaseW / 2 + "px; width: " + blobCircleD + "px; height: " + blobCircleD + "px; \"><div class=\"tistolsBtnBlob-wrapper\">" +
            "<canvas class=\"tistolsBtn_el btn__blob\" touch-action=\"none\" width=\"300\" height=\"300\"></canvas>" +
            "<span class=\"tistolsBtn_el btn__text\">" + sText + "</span></div></div>");

        btn.blobButton = new BlobButton(btn);
        const objA = $(btn).children("a");
        btn.blobButton.blob.color = objA.css("background-color");
        objA.css("background-color", "transparent");
        $(btn).find("span").css("color", objA.css("color"));

        $(btn).find(".tistolsBtnBlob").hover(function(){
            $(this).find("span").css('color', objA.css('box-shadow').replace(/^.*(rgba?\([^)]+\)).*$/,'$1'));
        }, function(){
            $(this).find("span").css('color', objA.css("color"));
        });
    }

    resize = function () {
        for (let i = 0; i < buttonsBlob.length; i++) {
            buttonsBlob[i].blobButton.resize();
        }
    }
    window.addEventListener("resize", resize);

    mouseMove = function (e) {
        for (let i = 0; i < buttonsBlob.length; i++) {
            buttonsBlob[i].blobButton.mouseMove(e);
        }
    }
    window.addEventListener("pointermove", mouseMove);
</script>
Больше модификаций доступно по подписке
Полный доступ к 60+ модификациям
Обновления библиотеки
Оповещения о новых модификациях
Техподдержка в чате
Поддержка модов при изменениях в Tilda
Модуль подарочные карты тариф Lite на месяц
Доступ к ИИ-боту @tistolsaibot на год
Помощь в доработке модификации
Поддержка модов при изменениях в Tilda
Техподдержка в чате
Модуль smsmod.ru на год
Сертификат на разработку 3 модификаций
Модуль подарочные карты тариф Pro на месяц
Оповещения о новых модификациях
Обновления библиотеки
Полный доступ к 60+ модификациям
Оповещения о новых модификациях
Обновления библиотеки
Полный доступ к 14+ модификациям
  • Открытый код и текстовые инструкции
  • Пошаговые видеоинструкции
  • Готовые шаблоны (страницы)
Free
0 руб.
Месяц
Год
Pro
3 000 руб.
-50%
Club
18 000 руб.
-40%
Месяц
Год
  • Открытый код и текстовые инструкции
  • Пошаговые видеоинструкции
  • Готовые шаблоны (страницы)
  • Модификации будут работать в проектах после истечения подписки
  • Открытый код и текстовые инструкции
  • Пошаговые видеоинструкции
  • Готовые шаблоны (страницы)
  • Модификации будут работать в проектах после истечения подписки
Полный доступ к 60+ модификациям
Обновления библиотеки
Оповещения о новых модификациях
Техподдержка в чате
Поддержка модов при изменениях в Tilda
Модуль подарочные карты тариф Lite на месяц
Помощь в доработке модификации
Поддержка модов при изменениях в Tilda
Техподдержка в чате
Модуль smsmod.ru на месяц
Сертификат на разработку 1 модификаций
Модуль подарочные карты тариф Lite на месяц
Оповещения о новых модификациях
Обновления библиотеки
Полный доступ к 60+ модификациям
Оповещения о новых модификациях
Обновления библиотеки
Полный доступ к 14+ модификациям
  • Открытый код и текстовые инструкции
  • Пошаговые видеоинструкции
  • Готовые шаблоны (страницы)
Free
0 руб.
Месяц
Год
Pro
500 руб.
Club
2 500 руб.
Месяц
Год
  • Открытый код и текстовые инструкции
  • Пошаговые видеоинструкции
  • Готовые шаблоны (страницы)
  • Модификации будут работать в проектах после истечения подписки
  • Открытый код и текстовые инструкции
  • Пошаговые видеоинструкции
  • Готовые шаблоны (страницы)
  • Модификации будут работать в проектах после истечения подписки
Доступ к ИИ-боту @tistolsaibot на месяц
Вам также может быть интересно: