Пример работы

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

Модификатор для добавления интерактивного элемента (блоб)
Интерактивный блоб-кнопка
1. Добавляем Zero блок и задаем ему класс .uc-blob
В зеро блоке добавляем html-элемент

2. Копируем в него код
Скопировать код
3. И настраиваем:
Ссылка при клике по блобу на 2 строке
Текст на 6 строке
Ширина и высота задается в px на 16 и 17 строке
Цвет текста на 38 строке, шрифт на 40 строке
Цвет текста при наведении на 43 строке
Обводка(бордюр) блоба на 118 строке
Цвет фона блоба при наведении на 145 строке
<!-- TL07 - Модификация для Тильды. Интерактивный блоб-кнопка https://mod.tistols.com/interactive-blob -->
<a href="#mod"> 
    <div class="tistolsBtnBlob">
      <div class="tistolsBtnBlob-wrapper">
        <canvas class="tistolsBtn_el btn__blob" id="tistolsBtnBlob-canvas"></canvas>
        <span class="tistolsBtn_el btn__text" id="btn-text">Submit</span>
      </div>
    </div>
</a>   
<style>
.tistolsBtnBlob {
        position: absolute;
        top: 50%;
        left: 50%;
        transform: translate(-50%, -50%);
        width: 300px; /*Ширина блоба*/
        height: 300px; /*Высота блоба*/
        /* border: 1px solid #f00; */
        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>
let canvas, ctx;
let render, blobInit;
let blob;

class Blob {
  constructor(canvas) {
    this.points = [];
  }

  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 = canvas;
      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;
  }
}

blob = new Blob();

blobInit = function () {
  let $text = document.getElementById("btn-text");
  canvas = document.getElementById("tistolsBtnBlob-canvas");
  canvas.setAttribute("touch-action", "none");
  
  let blobContainer = document.querySelector(".uc-blob");
  let blobRect = blobContainer.getBoundingClientRect();

  let center = { x: 0, y: 0 };
  let resize = function () {
    const { width, height, top, left } = canvas.getBoundingClientRect();
    center.x = left - blobRect.left + width / 2;
    center.y = top - blobRect.top + height / 2;
    canvas.width = width;
    canvas.height = height;
  };
  window.addEventListener("resize", resize);
  resize();

  let oldMousePoint = { x: 0, y: 0 };
  let mouseMove = function (e) {
    let blobRect = blobContainer.getBoundingClientRect();
    let pos = {
      x: e.clientX - blobRect.left,
      y: e.clientY - blobRect.top
    };
    let diff = { x: pos.x - center.x, y: pos.y - center.y };
    let dist = Math.sqrt(diff.x * diff.x + diff.y * diff.y);
    let angle = null;

    blob.mousePos = { x: center.x - pos.x, y: center.y - pos.y };

    if (dist < blob.radius && blob.hover === false) {
      let vector = { x: pos.x - center.x, y: pos.y - center.y };
      angle = Math.atan2(vector.y, vector.x);
      blob.hover = true;
      $text.classList.add("hover");
    } else if (dist > blob.radius && blob.hover === true) {
      let vector = { x: pos.x - center.x, y: pos.y - center.y };
      angle = Math.atan2(vector.y, vector.x);
      blob.hover = false;
      $text.classList.remove("hover");
    }

    if (typeof angle === "number") {
      let nearestPoint = null;
      let distanceFromPoint = 100;

      blob.points.forEach((point) => {
        if (Math.abs(angle - point.azimuth) < distanceFromPoint) {
          nearestPoint = point;
          distanceFromPoint = Math.abs(angle - point.azimuth);
        }
      });

      if (nearestPoint) {
        let strength = {
          x: oldMousePoint.x - pos.x,
          y: 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) * (blob.hover ? -1 : 1);
      }
    }

    oldMousePoint.x = pos.x;
    oldMousePoint.y = pos.y;
  };

  window.addEventListener("pointermove", mouseMove);

  blob.canvas = canvas;
  blob.radius = canvas.width / 2.4;
  blob.speed = 999;

  blob.blobInit();
  blob.render();
};

blobInit();

    </script>
Добавьте шаблон страницы к себе в проект.
  1. Нажмите на кнопку "Создать новую страницу"
  2. Пролистайте до конца
  3. В "Указать ID страницы" напишите номер 48473347
Вам также может быть интересно:
НОВЫЙ
Club
  • Все преимущества тарифа Pro

  • Модуль smsmod.ru для 1 сайта на 1 год

  • Сертификат на разработку 1 модификации

  • Доступ в клубный чат, в котором:

  • Помогаем с доработкой модификаций под ваш проект

  • Общаемся и делимся опытом, рекомендациями
1 500 руб.
/ месяц
При оплате на год, вы:
экономите 12 000 ₽
-40%
Месяц
Год
Free
  • Доступ к коду бесплатных модификаций

  • Пошаговые видеоинструкции

  • Готовые шаблоны с бесплатными модификациями

  • Обновления библиотеки (1−3 модификации в месяц)

  • Оповещения о новых модификациях
Бесплатный тариф
Pro
  • Полный доступ к коду всех модификаций

  • Обновления библиотеки (от 5 модификаций в месяц)

  • Поддержка работы модификаций при изменениях в Tilda

  • Пошаговые видеоинструкции

  • Готовые шаблоны с модификациями

  • Оповещения о новых модификациях

  • Техподдержка в чате

  • -30% скидка на модуль smsmod.ru
250 руб.
/ месяц
При оплате на год, вы:
экономите 3 000 ₽
-50%
Месяц
Год
*Модификации будут работать в проектах после истечения подписки
Тарифы
Чтобы получить доступ, выберите тариф:
Free
  • Доступ к коду бесплатных модификаций

  • Пошаговые видеоинструкции

  • Готовые шаблоны с бесплатными модификациями

  • Обновления библиотеки (1−3 модификации в месяц)

  • Оповещения о новых модификациях
Бесплатный тариф
НОВЫЙ
Club
  • Все преимущества тарифа Pro

  • Модуль smsmod.ru для 1 сайта на 1 месяц

  • Сертификат на разработку 1 модификации

  • Доступ в клубный чат, в котором:

  • Помогаем с доработкой модификаций под ваш проект

  • Общаемся и делимся опытом, рекомендациями
2 500 руб.
/ месяц
Месяц
Год
*Модификации будут работать в проектах после истечения подписки
Тарифы
Чтобы получить доступ, выберите тариф:
Pro
  • Полный доступ к коду всех модификаций

  • Обновления библиотеки (от 5 модификаций в месяц)

  • Поддержка работы модификаций при изменениях в Tilda

  • Пошаговые видеоинструкции

  • Готовые шаблоны с модификациями

  • Оповещения о новых модификациях

  • Техподдержка в чате

  • -30% скидка на модуль smsmod.ru
500 руб.
/ месяц
Месяц
Год