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

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

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

2. Копируем в него код
Скопировать код
3. И настраиваем:
Ссылка при клике по блобу на 2 строке
Текст на 6 строке
Ширина и высота задается в px на 16 и 17 строке
Цвет текста на 38 строке, шрифт на 40 строке
Цвет текста при наведении на 43 строке
Обводка(бордюр) блоба на 118 строке
Цвет фона блоба при наведении на 145 строке

<!-- TL05 - Модификация для Тильды. Интерактивный блоб-кнопка 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);
      // ctx.lineTo(p2.x, p2.y);

      // ctx.fillStyle = "#F8E655";
      // ctx.fillRect(p1.x - 2.5, p1.y - 2.5, 5, 5);

      p1 = p2;
    }

    var xc = (p1.x + _p2.x) / 2;
    var yc = (p1.y + _p2.y) / 2;
    ctx.quadraticCurveTo(p1.x, p1.y, xc, yc);
    // ctx.lineTo(_p2.x, _p2.y);

    // ctx.closePath();
    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();

    /*
    ctx.fillStyle = '#000000';
    if(this.mousePos) {
      let angle = Math.atan2(this.mousePos.y, this.mousePos.x) + Math.PI;
      ctx.fillRect(center.x + Math.cos(angle) * this.radius, center.y + Math.sin(angle) * this.radius, 5, 5);
    }
  */
    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) {
    console.log(this.rgbaFromString(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 center = { x: 0, y: 0 };
  let resize = function () {
    const { width, height, top, left } = canvas.getBoundingClientRect();
    center.x = left + width / 2;
    center.y = top + height / 2 - window.scrollY;
    canvas.width = width;
    canvas.height = height;
  };
  window.addEventListener("resize", resize);
  resize();

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

    blob.mousePos = { x: pos.x - e.clientX, y: pos.y - e.clientY };

    if (dist < blob.radius && blob.hover === false) {
      let vector = { x: e.clientX - pos.x, y: e.clientY - pos.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: e.clientX - pos.x, y: e.clientY - pos.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) {
          // console.log(point.azimuth, angle, distanceFromPoint);
          nearestPoint = point;
          distanceFromPoint = Math.abs(angle - point.azimuth);
        }
      });

      if (nearestPoint) {
        let strength = {
          x: oldMousePoint.x - e.clientX,
          y: oldMousePoint.y - e.clientY
        };
        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 = e.clientX;
    oldMousePoint.y = e.clientY;
  };
  // window.addEventListener('mousemove', mouseMove);
  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
Вам также может быть интересно:
*Модификации будут работать в проектах после истечения подписки
Pro
  • Полный доступ к коду всех модификаций

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

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

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

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

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

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

  • -30% скидка на модуль smsmod.ru
250 руб.
/ месяц
НОВЫЙ
Club
  • Все преимущества тарифа Pro

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

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

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

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

  • Общаемся и делимся опытом, рекомендациями
1 500 руб.
/ месяц
Free
  • Доступ к коду бесплатных модификаций

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

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

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

  • Оповещения о новых модификациях
Бесплатный тариф
Тарифы
Чтобы получить доступ, выберите тариф:
При оплате на год, вы:
экономите 3 000 ₽
-50%
При оплате на год, вы:
экономите 12 000 ₽
-40%
Месяц
Год
Месяц
Год
*Модификации будут работать в проектах после истечения подписки
Pro
  • Полный доступ к коду всех модификаций

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

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

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

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

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

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

  • -30% скидка на модуль smsmod.ru
500 руб.
/ месяц
НОВЫЙ
Club
  • Все преимущества тарифа Pro

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

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

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

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

  • Общаемся и делимся опытом, рекомендациями
2 500 руб.
/ месяц
Free
  • Доступ к коду бесплатных модификаций

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

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

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

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