You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用React和Tailwind实现章鱼图标随鼠标指向随机移动

React + Tailwind CSS 实现随机移动且眼睛跟随鼠标的章鱼图标

实现思路

  • 用React状态维护章鱼的当前位置和鼠标坐标
  • 通过定时器周期性生成页面内的随机目标位置,让章鱼平滑移动过去
  • 监听鼠标移动事件,计算章鱼与鼠标的相对角度,动态旋转章鱼眼睛指向鼠标
  • 全程用Tailwind CSS处理定位、样式及过渡效果

完整组件代码

import { useState, useEffect, useRef } from 'react';

export default function Octopus() {
  // 章鱼当前位置
  const [octopusPos, setOctopusPos] = useState({ x: 100, y: 100 });
  // 鼠标位置
  const [mousePos, setMousePos] = useState({ x: 0, y: 0 });
  // 定时器引用
  const moveIntervalRef = useRef(null);
  // 章鱼元素引用,用于获取尺寸
  const octopusRef = useRef(null);

  // 监听鼠标移动
  useEffect(() => {
    const handleMouseMove = (e) => {
      setMousePos({ x: e.clientX, y: e.clientY });
    };
    window.addEventListener('mousemove', handleMouseMove);
    return () => window.removeEventListener('mousemove', handleMouseMove);
  }, []);

  // 章鱼随机移动逻辑
  useEffect(() => {
    const moveOctopus = () => {
      if (!octopusRef.current) return;
      // 获取页面可用范围(减去章鱼自身尺寸)
      const maxX = window.innerWidth - octopusRef.current.offsetWidth;
      const maxY = window.innerHeight - octopusRef.current.offsetHeight;
      // 生成随机目标位置
      const targetX = Math.floor(Math.random() * maxX);
      const targetY = Math.floor(Math.random() * maxY);
      
      // 平滑移动到目标位置(每帧移动5px)
      const moveStep = () => {
        setOctopusPos(prev => {
          const dx = targetX - prev.x;
          const dy = targetY - prev.y;
          const distance = Math.sqrt(dx*dx + dy*dy);
          if (distance < 5) {
            return { x: targetX, y: targetY };
          }
          return {
            x: prev.x + (dx / distance) * 5,
            y: prev.y + (dy / distance) * 5
          };
        });
      };

      // 每16ms执行一次移动(约60帧)
      const stepInterval = setInterval(moveStep, 16);
      // 到达目标后停止当前移动,3秒后生成下一个目标
      setTimeout(() => {
        clearInterval(stepInterval);
        setTimeout(moveOctopus, 3000);
      }, 2000);
    };

    // 初始化启动移动
    moveIntervalRef.current = setTimeout(moveOctopus, 1000);
    return () => {
      if (moveIntervalRef.current) clearTimeout(moveIntervalRef.current);
    };
  }, []);

  // 计算眼睛旋转角度
  const getEyeRotation = () => {
    const dx = mousePos.x - octopusPos.x - 25; // 25是章鱼宽度的一半,调整眼睛旋转中心
    const dy = mousePos.y - octopusPos.y - 25;
    const angle = Math.atan2(dy, dx) * (180 / Math.PI);
    return `rotate(${angle}deg)`;
  };

  return (
    <div
      ref={octopusRef}
      className="fixed w-16 h-16 transition-transform duration-100 ease-linear"
      style={{ left: `${octopusPos.x}px`, top: `${octopusPos.y}px` }}
    >
      {/* 章鱼主体(用SVG或图标替换,这里用圆形模拟) */}
      <div className="w-full h-full bg-blue-500 rounded-full relative">
        {/* 章鱼眼睛 */}
        <div className="absolute w-4 h-4 bg-white rounded-full top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2">
          <div
            className="absolute w-2 h-2 bg-black rounded-full top-1/2 left-1/2 -translate-y-1/2"
            style={{ transform: getEyeRotation, transformOrigin: 'center center' }}
          ></div>
        </div>
      </div>
    </div>
  );
}

关键细节说明

  • 随机移动:通过定时器生成目标位置,用逐帧移动实现平滑过渡,避免瞬移的生硬感;每次到达目标后停顿3秒再生成下一个位置
  • 眼睛跟随:利用Math.atan2计算鼠标与章鱼中心的夹角,将角度转换为CSS旋转值,通过Tailwind的transform实现旋转
  • Tailwind样式:用fixed定位章鱼,transition-transform实现移动平滑过渡,relative/absolute定位眼睛
  • 边界处理:计算目标位置时减去章鱼自身尺寸,避免章鱼移出可视区域

使用说明

  1. 将上述组件导入到你的React项目中
  2. 替换章鱼主体的模拟圆形为你实际的章鱼图标(可以用SVG或图片,调整w-16 h-16等尺寸类适配)
  3. 根据图标实际的眼睛位置,调整getEyeRotation中的偏移值(示例中的25是基于w-16的一半)

内容的提问来源于stack exchange,提问作者Numan Talukder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 20:24:32