Docs
Ink Cursor

Ink Cursor

Ink Cursor

Try it on the whole page

Create inkCursor.js file

Copy and paste the following code

import React, { useRef, useEffect } from "react";
 
const useInkCursor = () => {
  const canvasRef = useRef(null);
  const ctxRef = useRef(null);
  const pointerRef = useRef({
    x: null,
    y: null,
  });
  const trailRef = useRef([]);
  const paramsRef = useRef({
    pointsNumber: 40,
    widthFactor: 0.3,
    mouseThreshold: 0.6,
    spring: 0.4,
    friction: 0.5,
  });
 
  useEffect(() => {
    pointerRef.current = {
      x: 0.5 * window.innerWidth,
      y: 0.5 * window.innerHeight,
    };
    const canvas = canvasRef.current;
    const ctx = canvas.getContext("2d");
    ctxRef.current = ctx;
 
    // Initialize trail array
    const trail = new Array(paramsRef.current.pointsNumber).fill(null).map(() => ({
      x: pointerRef.current.x,
      y: pointerRef.current.y,
      dx: 0,
      dy: 0,
    }));
    trailRef.current = trail;
 
    const updateMousePosition = (x, y) => {
      pointerRef.current.x = x;
      pointerRef.current.y = y;
    };
 
    const handleMouseMove = (e) => {
      updateMousePosition(e.clientX, e.clientY);
    };
 
    const handleTouchMove = (e) => {
      updateMousePosition(e.targetTouches[0].clientX, e.targetTouches[0].clientY);
    };
 
    const setupCanvas = () => {
      canvas.width = window.innerWidth;
      canvas.height = window.innerHeight;
    };
 
    const update = (t) => {
      ctx.clearRect(0, 0, canvas.width, canvas.height);
 
      const gradient = ctx.createLinearGradient(0, 0, canvas.width, canvas.height);
      gradient.addColorStop(0, "#833ab4");
      gradient.addColorStop(1, "#fd1d1d");
 
      ctx.strokeStyle = gradient;
 
      trailRef.current.forEach((p, pIdx) => {
        const prev = pIdx === 0 ? pointerRef.current : trailRef.current[pIdx - 1];
        const spring = pIdx === 0 ? 0.4 * paramsRef.current.spring : paramsRef.current.spring;
        p.dx += (prev.x - p.x) * spring;
        p.dy += (prev.y - p.y) * spring;
        p.dx *= paramsRef.current.friction;
        p.dy *= paramsRef.current.friction;
        p.x += p.dx;
        p.y += p.dy;
      });
 
      ctx.lineCap = "round";
      ctx.beginPath();
      ctx.moveTo(trailRef.current[0].x, trailRef.current[0].y);
 
      for (let i = 1; i < trailRef.current.length - 1; i++) {
        const xc = 0.5 * (trailRef.current[i].x + trailRef.current[i + 1].x);
        const yc = 0.5 * (trailRef.current[i].y + trailRef.current[i + 1].y);
 
        ctx.quadraticCurveTo(trailRef.current[i].x, trailRef.current[i].y, xc, yc);
        ctx.lineWidth = paramsRef.current.widthFactor * (paramsRef.current.pointsNumber - i);
        ctx.stroke();
      }
      ctx.lineTo(trailRef.current[trailRef.current.length - 1].x, trailRef.current[trailRef.current.length - 1].y);
      ctx.stroke();
 
      window.requestAnimationFrame(update);
    };
 
    setupCanvas();
    update(0);
 
    window.addEventListener("mousemove", handleMouseMove);
    window.addEventListener("touchmove", handleTouchMove);
    window.addEventListener("resize", setupCanvas);
 
    return () => {
      window.removeEventListener("mousemove", handleMouseMove);
      window.removeEventListener("touchmove", handleTouchMove);
      window.removeEventListener("resize", setupCanvas);
    };
  }, []);
 
  return (
    <canvas
      ref={canvasRef}
      className="h-screen w-screen"
    />
  );
};
 
export default useInkCursor;

Create New Component

Copy and paste the following code into your project.

components/edil-ozi/ink-cursor.tsx
"use client";
 
import useInkCursor from "@/hooks/inkCursor";
 
const InkCursor = () => {
  const inkCursorComponent = useInkCursor();
 
  return (
    <>
      <div className="z-[10] flex h-[350px] w-full items-center justify-center bg-zinc-100 text-center dark:bg-zinc-900">
        Try it on the whole page
      </div>
      <div className="fixed left-0 top-0 z-[9]">{inkCursorComponent}</div>
    </>
  );
};
export default InkCursor;

Credits

RunicFreak