PageSourceSearch

https://www.stokic.ai/js/cursor-effects.js

js stokic.ai collected 2026-10-05 15:51:49 UTC 5,166 bytes, 176 lines download raw bytes

1/**
2 * Custom Cursor Trail Effects
3 * Themed particle trails: binary, chess, ethereum
4 * Native cursor stays visible. Trails are purely decorative.
5 */
6
7(function() {
8  'use strict';
9
10  var isTouchDevice = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0);
11  if (isTouchDevice) return;
12
13  var prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
14  if (prefersReducedMotion) return;
15
16  var trailTimer;
17
18  // Build an Ethereum diamond SVG element (the actual logo shape)
19  function createEthSVG(color) {
20    var ns = 'http://www.w3.org/2000/svg';
21    var svg = document.createElementNS(ns, 'svg');
22    svg.setAttribute('viewBox', '0 0 16 26');
23    svg.setAttribute('width', '14');
24    svg.setAttribute('height', '22');
25
26    var paths = [
27      { d: 'M8 0L0 13.25L8 10.1L16 13.25L8 0Z', opacity: '0.8' },
28      { d: 'M8 10.1L0 13.25L8 17.65L16 13.25L8 10.1Z', opacity: '0.6' },
29      { d: 'M0 14.75L8 26L16 14.75L8 19.15L0 14.75Z', opacity: '0.8' }
30    ];
31
32    paths.forEach(function(p) {
33      var path = document.createElementNS(ns, 'path');
34      path.setAttribute('d', p.d);
35      path.setAttribute('fill', color);
36      path.setAttribute('opacity', p.opacity);
37      svg.appendChild(path);
38    });
39
40    return svg;
41  }
42
43  // Build a small ETH icon for the picker button
44  function createEthIcon() {
45    var ns = 'http://www.w3.org/2000/svg';
46    var svg = document.createElementNS(ns, 'svg');
47    svg.setAttribute('viewBox', '0 0 16 26');
48    svg.setAttribute('width', '12');
49    svg.setAttribute('height', '19');
50
51    var paths = [
52      { d: 'M8 0L0 13.25L8 10.1L16 13.25L8 0Z', opacity: '0.8' },
53      { d: 'M8 10.1L0 13.25L8 17.65L16 13.25L8 10.1Z', opacity: '0.6' },
54      { d: 'M0 14.75L8 26L16 14.75L8 19.15L0 14.75Z', opacity: '0.8' }
55    ];
56
57    paths.forEach(function(p) {
58      var path = document.createElementNS(ns, 'path');
59      path.setAttribute('d', p.d);
60      path.setAttribute('fill', 'currentColor');
61      path.setAttribute('opacity', p.opacity);
62      svg.appendChild(path);
63    });
64
65    return svg;
66  }
67
68  // Trail themes
69  var themes = {
70    binary: {
71      type: 'text',
72      chars: ['0', '1'],
73      label: '01',
74      color: '#2563eb',
75      size: 'normal'
76    },
77    chess: {
78      type: 'text',
79      chars: ['\u2659', '\u2658', '\u2657', '\u2656', '\u2655', '\u2654'],
80      label: '\u265F',
81      color: '#2563eb',
82      size: 'large'
83    },
84    ethereum: {
85      type: 'svg',
86      colors: ['#627EEA', '#8C9EEF', '#4A6CF7']
87    }
88  };
89
90  var themeKeys = ['binary', 'chess', 'ethereum'];
91  var currentTheme = localStorage.getItem('cursorTheme') || 'binary';
92
93  function createTrailParticle(x, y) {
94    if (trailTimer) return;
95
96    trailTimer = setTimeout(function() {
97      var theme = themes[currentTheme];
98
99      if (theme.type === 'svg') {
100        var colors = theme.colors;
101        var color = colors[Math.floor(Math.random() * colors.length)];
102        var wrapper = document.createElement('div');
103        wrapper.className = 'mouse-trail-svg';
104        wrapper.appendChild(createEthSVG(color));
105        wrapper.style.left = x + 'px';
106        wrapper.style.top = y + 'px';
107        document.body.appendChild(wrapper);
108
109        setTimeout(function() { wrapper.remove(); }, 1000);
110      } else {
111        var chars = theme.chars;
112        var char = chars[Math.floor(Math.random() * chars.length)];
113        var trail = document.createElement('span');
114        trail.className = 'mouse-trail' + (theme.size === 'large' ? ' mouse-trail--large' : '');
115        trail.textContent = char;
116        trail.style.left = x + 'px';
117        trail.style.top = y + 'px';
118        trail.style.color = theme.color;
119        document.body.appendChild(trail);
120
121        setTimeout(function() { trail.remove(); }, 1000);
122      }
123
124      trailTimer = null;
125    }, 40);
126  }
127
128  function initThemePicker() {
129    var picker = document.createElement('div');
130    picker.className = 'cursor-theme-picker';
131
132    themeKeys.forEach(function(key) {
133      var btn = document.createElement('button');
134      btn.className = 'cursor-theme-btn';
135      btn.setAttribute('data-theme', key);
136      btn.title = key.charAt(0).toUpperCase() + key.slice(1) + ' trail';
137
138      if (key === 'ethereum') {
139        btn.appendChild(createEthIcon());
140      } else {
141        btn.textContent = themes[key].label;
142      }
143
144      if (key === currentTheme) btn.classList.add('active');
145
146      btn.addEventListener('click', function(e) {
147        e.stopPropagation();
148        currentTheme = key;
149        localStorage.setItem('cursorTheme', key);
150        var allBtns = picker.querySelectorAll('.cursor-theme-btn');
151        for (var i = 0; i < allBtns.length; i++) {
152          allBtns[i].classList.remove('active');
153        }
154        btn.classList.add('active');
155      });
156
157      picker.appendChild(btn);
158    });
159
160    document.body.appendChild(picker);
161  }
162
163  function init() {
164    document.addEventListener('mousemove', function(e) {
165      createTrailParticle(e.clientX, e.clientY);
166    });
167    initThemePicker();
168  }
169
170  if (document.readyState === 'loading') {
171    document.addEventListener('DOMContentLoaded', init);
172  } else {
173    init();
174  }
175
176})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.