Универсальный курсор

Модификаторы
Модификация позволяет создать универсальный курсор с набором возможностей, таких как: прилипание курсора, подсказки при наведении, эффект магнетизма и др.
Описание
Инструкция по подключению
Подключаем основные стили для работы, через блок T123
Шаг 1
<script src='https://unpkg.co/gsap@3/dist/gsap.min.js'></script>

<style>

@media screen and (max-width: 480px) {
        .cb-cursor {
            display: none;
        }
 }

body {
  height: 100%;
  margin: 0;
  padding: 0;
  background: #fff;
  color: var(--BackgroundColorСircle);
  font-family: var(--FontFamily);
  font-size: 14px;
  letter-spacing: normal;
  line-height: normal;
  text-align: left;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

.cb-cursor {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 150;
  contain: layout style size;
  pointer-events: none;
  will-change: transform;
  transition: opacity 0.3s, color 0.4s;
}
.cb-cursor:before {
  content: "";
  position: absolute;
  top: -24px;
  left: -24px;
  display: block;
  width: 48px;
  height: 48px;
  transform: scale(0);
  background: currentColor;
  border-radius: 50%;
  transition: transform 0.3s ease-in-out, opacity 0.1s;
}
.cb-cursor-text {
  position: absolute;
  top: -18px;
  left: -18px;
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  transform: scale(0) rotate(10deg);
  opacity: 0;
  color: var(--FontColor);
  font-size: var(--FontSize);
  line-height: 20px;
  text-align: center;
  letter-spacing: -0.01em;
  transition: opacity 0.4s, transform 0.3s;
}
@supports (mix-blend-mode: exclusion) {
  .cb-cursor.-exclusion, .cb-cursor.-opaque {
    mix-blend-mode: exclusion;
  }
}
@supports (mix-blend-mode: exclusion) {
  .cb-cursor.-exclusion:before, .cb-cursor.-opaque:before {
    background: white;
  }
}
.cb-cursor.-normal, .cb-cursor.-text {
  mix-blend-mode: normal;
}
.cb-cursor.-normal:before, .cb-cursor.-text:before {
  background: var(--BackgroundColorСircleHoverText);
}
.cb-cursor.-inverse {
  color: white;
}
.cb-cursor.-visible:before {
  transform: scale(var(--СircleSize));
}
.cb-cursor.-visible.-active:before {
  transform: scale(0.23);
  transition-duration: 0.2s;
}
.cb-cursor.-pointer:before {
  transform: scale(0.15);
}
.cb-cursor.-text:before {
  opacity: 1;
  transform: scale(var(--СircleTextScale));
}
.cb-cursor.-text .cb-cursor-text {
  opacity: 1;
  transform: scale(1);
}
.cb-cursor.-text.-active:before {
  transform: scale(1.6);
  transition-duration: 0.2s;
}
.cb-cursor.-opaque:before {
  transform: scale(var(--СircleScale));
}
.cb-cursor.-opaque.-active:before {
  transform: scale(var(--СircleScale));
}

.cb-cursor.-lg:before {
  color: var(--BackgroundColorСircleHover);  
  
  transform: scale(var(--СircleScale));
}

.cb-cursor.-hidden:before {
  transform: scale(0);
}

.-color-blue {
  color: #1F28BF;
}

.-color-white {
  color: #fff;
}






.cb-demo {
  background: #fff;
}
.cb-demo-content {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100vh;
}
.cb-demo-container {
  padding: 0 20px;
}
@media (min-width: 1600px) {
  .cb-demo-container {
    padding: 0 120px;
  }
}
.cb-demo-row {
  display: flex;
  justify-content: center;
  text-align: center;
  margin: 30px 0;
}
@media (min-width: 1600px) {
  .cb-demo-row {
    margin: 60px 0;
  }
}
.cb-demo-item {
  position: relative;
  flex: 1;
  padding: 50px 30px;
  margin: 0 20px;
  color: #000;
}
@media (min-width: 1600px) {
  .cb-demo-item {
    padding: 90px 30px;
    margin: 0 30px;
  }
}
.cb-demo-item-title {
  position: relative;
  margin: 0 0 25px 0;
  font-size: 30px;
  font-weight: bold;
}
.cb-demo-item-text {
  position: relative;
  max-width: 70%;
  margin: 0 auto;
  color: rgba(0, 0, 0, 0.5);
  font-size: 16px;
  font-weight: 300;
  line-height: 150%;
}
.cb-demo-item:before {
  content: "";
  display: block;
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  border-radius: 30px;
  background: currentColor;
  transition: box-shadow 0.2s;
}
</style>
Cоздаём еще один блок T123 и помещаем в него скрипт
Шаг 2
<script>
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }

function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }

function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }

var Magnetic = /*#__PURE__*/function () {
  "use strict";

  function Magnetic(el) {
    var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};

    _classCallCheck(this, Magnetic);

    this.el = $(el);
    this.options = $.extend(true, {
      y: 0.2,
      x: 0.2,
      s: 0.2,
      rs: 0.7
    }, this.el.data('magnetic') || options);
    this.y = 0;
    this.x = 0;
    this.width = 0;
    this.height = 0;
    if (this.el.data('magnetic-init')) return;
    this.el.data('magnetic-init', true);
    this.bind();
  }

  _createClass(Magnetic, [{
    key: "bind",
    value: function bind() {
      var _this = this;

      this.el.on('mouseenter', function () {
        _this.y = _this.el.offset().top - window.pageYOffset;
        _this.x = _this.el.offset().left - window.pageXOffset;
        _this.width = _this.el.outerWidth();
        _this.height = _this.el.outerHeight();
      });
      this.el.on('mousemove', function (e) {
        var y = (e.clientY - _this.y - _this.height / 2) * _this.options.y;
        var x = (e.clientX - _this.x - _this.width / 2) * _this.options.x;

        _this.move(x, y, _this.options.s);
      });
      this.el.on('mouseleave', function (e) {
        _this.move(0, 0, _this.options.rs);
      });
    }
  }, {
    key: "move",
    value: function move(x, y, speed) {
      gsap.to(this.el, {
        y: y,
        x: x,
        force3D: true,
        overwrite: true,
        duration: speed
      });
    }
  }]);

  return Magnetic;
}();

var Cursor = /*#__PURE__*/function () {
  "use strict";

  function Cursor(options) {
    _classCallCheck(this, Cursor);

    this.options = $.extend(true, {
      container: "body",
      speed: 0.7,
      ease: "expo.out",
      visibleTimeout: 300
    }, options);
    this.body = $(this.options.container);
    this.el = $('<div class="cb-cursor"></div>');
    this.text = $('<div class="cb-cursor-text"></div>');
    this.init();
  }

  _createClass(Cursor, [{
    key: "init",
    value: function init() {
      this.el.append(this.text);
      this.body.append(this.el);
      this.bind();
      this.move(-window.innerWidth, -window.innerHeight, 0);
    }
  }, {
    key: "bind",
    value: function bind() {
      var _this2 = this;

      var self = this;
      this.body.on('mouseleave', function () {
        self.hide();
      }).on('mouseenter', function () {
        self.show();
      }).on('mousemove', function (e) {
        _this2.pos = {
          x: _this2.stick ? _this2.stick.x - (_this2.stick.x - e.clientX) * 0.15 : e.clientX,
          y: _this2.stick ? _this2.stick.y - (_this2.stick.y - e.clientY) * 0.15 : e.clientY
        };

        _this2.update();
      }).on('mousedown', function () {
        self.setState('-active');
      }).on('mouseup', function () {
        self.removeState('-active');
      }).on('mouseenter', 'a,input,textarea,button', function () {
        self.setState('-pointer');
      }).on('mouseleave', 'a,input,textarea,button', function () {
        self.removeState('-pointer');
      }).on('mouseenter', 'iframe', function () {
        self.hide();
      }).on('mouseleave', 'iframe', function () {
        self.show();
      }).on('mouseenter', '[data-cursor]', function () {
        self.setState(this.dataset.cursor);
      }).on('mouseleave', '[data-cursor]', function () {
        self.removeState(this.dataset.cursor);
      }).on('mouseenter', '[data-cursor-text]', function () {
        self.setText(this.dataset.cursorText);
      }).on('mouseleave', '[data-cursor-text]', function () {
        self.removeText();
      }).on('mouseenter', '[data-cursor-stick]', function () {
        self.setStick(this.dataset.cursorStick);
      }).on('mouseleave', '[data-cursor-stick]', function () {
        self.removeStick();
      });
    }
  }, {
    key: "setState",
    value: function setState(state) {
      this.el.addClass(state);
    }
  }, {
    key: "removeState",
    value: function removeState(state) {
      this.el.removeClass(state);
    }
  }, {
    key: "toggleState",
    value: function toggleState(state) {
      this.el.toggleClass(state);
    }
  }, {
    key: "setText",
    value: function setText(text) {
      this.text.html(text);
      this.el.addClass('-text');
    }
  }, {
    key: "removeText",
    value: function removeText() {
      this.el.removeClass('-text');
    }
  }, {
    key: "setStick",
    value: function setStick(el) {
      var target = $(el);
      var bound = target.get(0).getBoundingClientRect();
      this.stick = {
        y: bound.top + target.height() / 2,
        x: bound.left + target.width() / 2
      };
      this.move(this.stick.x, this.stick.y, 5);
    }
  }, {
    key: "removeStick",
    value: function removeStick() {
      this.stick = false;
    }
  }, {
    key: "update",
    value: function update() {
      this.move();
      this.show();
    }
  }, {
    key: "move",
    value: function move(x, y, duration) {
      gsap.to(this.el, {
        x: x || this.pos.x,
        y: y || this.pos.y,
        force3D: true,
        overwrite: true,
        ease: this.options.ease,
        duration: this.visible ? duration || this.options.speed : 0
      });
    }
  }, {
    key: "show",
    value: function show() {
      var _this3 = this;

      if (this.visible) return;
      clearInterval(this.visibleInt);
      this.el.addClass('-visible');
      this.visibleInt = setTimeout(function () {
        return _this3.visible = true;
      });
    }
  }, {
    key: "hide",
    value: function hide() {
      var _this4 = this;

      clearInterval(this.visibleInt);
      this.el.removeClass('-visible');
      this.visibleInt = setTimeout(function () {
        return _this4.visible = false;
      }, this.options.visibleTimeout);
    }
  }]);

  return Cursor;
}(); // Init cursor


var cursor = new Cursor(); // Init magnetic

$('[data-magnetic]').each(function () {
  new Magnetic(this);
});
</script>
Cоздаём блок T123 и помещаем в него "скрипт-настройки"
Шаг 3
<!--Настройки курсора-->

<style>
    :root {
        
        --BackgroundColorСircle: #000; /*Цвет курсора*/
        --BackgroundColorСircleHover: #1F29BF; /*Цвет курсора при наведении*/
        --СircleSize: 0.2; /*Размер курсора*/
        --СircleScale: 3; /*Увеличение курсора при наведении, работает по принципу scale, чем выше цифра, тем больше круг*/
        
        --FontFamily: "Helvetica", sans-serif; /*Название вашего шрифта*/
        --FontSize: 14px; /*Размер шрифта в круге*/
        --FontColor: #000; /*Цвет шрифта в круге*/
        --BackgroundColorСircleHoverText: #1F29BF; /*Цвет круга при наведении с текстом*/
        --СircleTextScale: 2; /*Увеличение круга с текстом, работает по принципу scale, чем выше цифра, тем больше круг*/
        
}

</style>


Cоздаём блок T123 и помещаем в него "скрипт-пример" на выбор (Важно! поместить его над стилями и скриптами)
Шаг 4
<!--Изменение курсора, при наведении на объект (увеличение)-->
<script>
    $('.yourclass-element').attr('data-cursor','-lg');
</script>
<!--Подсказки, при наведении на объект (вместо Открыть пишем свое значение)-->

<script>
    $('.yourclass-element').attr('data-cursor-text','Открыть');
</script>
<!--Примагничивание объекта к курсору, при наведении на объект-->

<script>
    $('.yourclass-element').attr('data-magnetic','data-magnetic');
</script>
<!--Прилипание курсора к объекту с классом .yourclass-element-stick, при наведении на объект с классом .yourclass-elemen -->

<script>
    $('.yourclass-element').attr('data-cursor-stick','.yourclass-element-stick');
</script>
<!--Инверсия курсора, при наведении -->

<script>
    $('.yourclass-element').attr('data-cursor','-opaque');
</script>
Изображение, при наведении
Добавить дополнительный скрипт после блока с основными стилями
Шаг 1
<!--Добавляем своё изображение, при наведении-->
<script>
    $('.cursor-img-1').attr('data-cursor','-img-1');
</script>

<style>
    .cb-cursor.-img-1:before {
  content: " ";
  background-size: cover;
  background-position: center;
  background-image: url(url);
  transform: scale(var(--СircleScale));
}
</style>
В Zero блоке добавить класс на объект, на который хотите, чтобы выводилось изображение (пример: cursor-img-1)
Шаг 2
В скрипте указать этот же класс
Шаг 3
Через блок IM01 загружаем нужное изображение и копируем url картинки
Шаг 4
Возвращаемся в скрипт к настройкам и для background-image: url(url); - указываем url картинки
Шаг 5
Дублируем скрипт. Меняем класс к примеру на cursor-img-2, url изображения и -img-1 на -img-2
Дополнительные изображения
Made on
Tilda