Custom SVG Cursors with an Interactive Emitter Effect

Several interactive cursor effects made with JavaScript and SVG.

From the custom cursor on my portfolio marvinx.com using blurred SVG circles, I created several variations which I would like to share with you today.

Without going into too much detail, I’d like to explain some points on how I approached the making of this set.

For the demos, the idea is to set a main class grouping all the functions inherent to all cursors.

Then, I separate demos in different classes where each variable is configurable: number of particles, colors, size, gradient, opacity, filters, radius, speed, acceleration, direction, etc.

Everything is coded in native JavaScript and does not use any libraries (only d3.js if we want to sort particles).

This is how particles are drawn in the Cursor class:

  drawParticles() {
    return `<g class="particles" filter=${this.filterParticles || "none"}>
      ${(() => {
        if (this.strokeGradient) {
          return `
          <defs>
            <linearGradient id=${this.strokeGradient.idStrokeGradient} x1="0%" y1="0%" x2="0%" y2="100%">
              <stop offset="0%" stop-color=${this.strokeGradient.color1} />
              <stop offset="100%" stop-color=${this.strokeGradient.color2} />
            </linearGradient>
          </defs>`
        }
      })()}
      ${Array(this.nbrParticles).fill().map((_,i) =>
        `<circle
          r="${this.setRadiusParticles(i)}"
          cx=${this.pos.x} cy=${this.pos.y}
          fill="${this.fillParticles || "none"}"
          fill-opacity="${this.fillOpacityParticles || 1}"
          stroke="${this.strokeGradient ? `url(#${this.strokeGradient.idStrokeGradient})` : this.strokeColorParticles}"
          stroke-width="${this.strokeWidthParticles || 0}"
          stroke-opacity="${this.strokeOpacityParticles || 1}"
          id="${i}">
        </circle>`).join('')}
    </g>`
  }

This is how each parameter is then configured:

export class Cursor1 extends Cursors{

  constructor(index) {
    super(index);
    this.speed = !isTouchDevices ? 0.5 : 1;
    this.init();
    this.loop();
  }

  setParamsCursor() {
    this.radiusCursor = 15;
    this.fillCursor = getComputedStyle(document.body).getPropertyValue('--primary');
    this.maxSqueeze = 0.6;
    this.accelerator = 1000;
  }

  setParamsParticles() {
    this.strokeGradient = {
      idStrokeGradient : "gradient",
      color2 : getComputedStyle(document.body).getPropertyValue('--primary'),
      color1 : getComputedStyle(document.body).getPropertyValue('--secondary'),
    }
    this.strokeWidthParticles = 1.5;
    this.strokeOpacityParticles = .15;
    this.radiusDiff = 7;
    this.radiusStart = this.radiusCursor*3;
    this.nbrParticles = Math.round((this.diagonalWindow() + this.radiusDiff - this.radiusStart) / this.radiusDiff);
    this.transitionParticles = {
      duration: 18,
      delay: !isTouchDevices ? 4 : 14,
      easing : "linear"
    };
  }
}

1. Waves effect

2. Trail effect

3. Tube effect

4.Mask effect

On this last demo, I use twice the same superimposed video (from Mikhail Nilov‘s beautiful royalty free collection).

The first video uses a grayscale filter:

  filterImageBack() {
    return
    `<filter id=${this.filterBackId}>
      <feColorMatrix type="matrix" values=".33 .33 .33 0 0
        .33 .33 .33 0 0
        .33 .33 .33 0 0
        0 0 0 1 0">
      </feColorMatrix>
    </filter>`
  }

And the second one is placed inside a mask where I apply a duotone filter:

  filterImageCursor() {
    return 
     `<filter id=${this.filterCursorId} filterUnits="objectBoundingBox" primitiveUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
      <feColorMatrix type="matrix" values=".44 .44 .44 0 0
        .44 .44 .44 0 0
        .44 .44 .44 0 0
        0 0 0 1 0">
      </feColorMatrix>
      <feComponentTransfer color-interpolation-filters="sRGB" result="duotone">
        <feFuncR type="table" tableValues="0.55 0.25"></feFuncR>
        <feFuncG type="table" tableValues="0.06 1"></feFuncG>
        <feFuncB type="table" tableValues="0.93 0.91"></feFuncB>
        <feFuncA type="table" tableValues="0 1"></feFuncA>
      </feComponentTransfer>
    </filter>`
  }

I also thank Ghislain Auzillon, for his help on the design.

Hope you enjoy it!


Print Share Comment Cite Upload Translate
APA
Marlène Bruhat | Sciencx (2024-03-28T12:57:55+00:00) » Custom SVG Cursors with an Interactive Emitter Effect. Retrieved from https://www.scien.cx/2022/08/23/custom-svg-cursors-with-an-interactive-emitter-effect/.
MLA
" » Custom SVG Cursors with an Interactive Emitter Effect." Marlène Bruhat | Sciencx - Tuesday August 23, 2022, https://www.scien.cx/2022/08/23/custom-svg-cursors-with-an-interactive-emitter-effect/
HARVARD
Marlène Bruhat | Sciencx Tuesday August 23, 2022 » Custom SVG Cursors with an Interactive Emitter Effect., viewed 2024-03-28T12:57:55+00:00,<https://www.scien.cx/2022/08/23/custom-svg-cursors-with-an-interactive-emitter-effect/>
VANCOUVER
Marlène Bruhat | Sciencx - » Custom SVG Cursors with an Interactive Emitter Effect. [Internet]. [Accessed 2024-03-28T12:57:55+00:00]. Available from: https://www.scien.cx/2022/08/23/custom-svg-cursors-with-an-interactive-emitter-effect/
CHICAGO
" » Custom SVG Cursors with an Interactive Emitter Effect." Marlène Bruhat | Sciencx - Accessed 2024-03-28T12:57:55+00:00. https://www.scien.cx/2022/08/23/custom-svg-cursors-with-an-interactive-emitter-effect/
IEEE
" » Custom SVG Cursors with an Interactive Emitter Effect." Marlène Bruhat | Sciencx [Online]. Available: https://www.scien.cx/2022/08/23/custom-svg-cursors-with-an-interactive-emitter-effect/. [Accessed: 2024-03-28T12:57:55+00:00]
rf:citation
» Custom SVG Cursors with an Interactive Emitter Effect | Marlène Bruhat | Sciencx | https://www.scien.cx/2022/08/23/custom-svg-cursors-with-an-interactive-emitter-effect/ | 2024-03-28T12:57:55+00:00
https://github.com/addpipe/simple-recorderjs-demo