Blue Archive Cursor Touch Effect

September 4, 2026 · View on GitHub

Blue Archive Cursor Touch Effect

English | 中文


Cursor touch effect demo

Just a simple shader effect for Web Canvas.

Quick Start

Install dependencies and start the test page:

npm install
npm run dev

Build the project:

npm run build

Use The NPM Package

Version 2 is ESM-only and targets ES2022-capable browsers with WebGL. It is intended for current Chromium, Firefox, and WebKit releases.

npm install blue-archive-touch-effect
import { createTouchEffect } from 'blue-archive-touch-effect'

const target = document.querySelector<HTMLElement>('#fx-root')

if (!target) {
  throw new Error('Missing target element')
}

const fx = createTouchEffect({ target })

The runtime appends a transparent overlay canvas to target and uses target as the fixed CSS screen compositing boundary. Use a dedicated, otherwise empty overlay element for target. If it is statically positioned, the runtime temporarily sets it to position: relative; its original inline positioning and blend styles are restored on dispose().

Runtime API

const fx = createTouchEffect({
  target,
  listenTarget,
  autoBindPointer,
  config,
})

fx.trigger({ x: 120, y: 80, space: 'local' })
fx.begin(pointerId, { x: clientX, y: clientY, space: 'client' })
fx.move(pointerId, { x: clientX, y: clientY, space: 'client' })
fx.end(pointerId)
fx.endAll()
fx.updateConfig(partialConfig)
fx.resize()
fx.dispose()

Options

  • target: required host element for the overlay canvas.
  • listenTarget: optional element or window used for pointer input; defaults to target.
  • autoBindPointer: automatically handles mouse, touch, and pen input; defaults to true.
  • config: modifies the touch-effect configuration.

Manual Triggering

By default, the runtime owns pointer input. Set autoBindPointer: false if your app wants to manage click and drag gestures itself.

const fx = createTouchEffect({
  target,
  autoBindPointer: false,
})

const point = (event: PointerEvent) => ({
  x: event.clientX,
  y: event.clientY,
  space: 'client' as const,
})

target.addEventListener('pointerdown', (event) => {
  fx.begin(event.pointerId, point(event))
})

target.addEventListener('pointermove', (event) => {
  fx.move(event.pointerId, point(event))
})

target.addEventListener('pointerup', (event) => {
  fx.end(event.pointerId)
})

target.addEventListener('pointercancel', (event) => {
  fx.end(event.pointerId)
})

window.addEventListener('blur', () => {
  fx.endAll()
})

local coordinates are CSS pixels relative to the target's top-left corner. client coordinates are viewport coordinates. Out-of-bounds trigger, begin, and move calls return false.

Configuration Example

fx.updateConfig({
  performance: {
    maxFps: 60,
    pixelRatioCap: 2,
  },
  appearance: {
    tint: { r: 0x4C / 255, g: 0xA7 / 255, b: 1 },
    intensity: 1,
    opacity: 1,
  },
  click: {
    scale: 1.15,
    playbackRate: 1,
    layers: {
      particles: {
        tint: { r: 1, g: 1, b: 1 },
        intensity: 1.2,
      },
    },
  },
  swipe: {
    particles: {
      density: 7,
      speed: 1.1,
      lifetime: 0.9,
    },
    trail: {
      duration: 0.4,
      width: 0.006,
    },
  },
  bloom: {
    enabled: true,
    threshold: 1,
    intensity: 1.7,
  },
})

updateConfig(...) deep-merges by section, so callers only need to pass the fields they want to change. RGB and opacity are clamped to [0, 1]; sizes, speeds, lifetimes, densities, widths, and intensities are nonnegative.

References

  • Effect implementation reference: the Unity scene and its referenced components, shaders, materials, meshes, and textures.

License

This project uses the MIT License. See LICENSE.