所有分类
  • 所有分类
  • Html5资源
  • React资源
  • Vue资源
  • Php资源
  • ‌小程序资源
  • Python资源

React 实现的图案解锁组件,响应式3×3 的点阵上绘制解锁图案

功能简介

LockScreenPassword 是一个基于 React 实现的图案解锁组件,用户可以通过鼠标或触摸手势,在 3×3 的点阵上绘制解锁图案。组件会实时记录用户的绘制顺序,并可用于密码验证、图案设置等多种场景。

实现亮点

  • 响应式交互体验:支持鼠标和触摸操作,兼容 PC 和移动端,交互流畅自然。
  • SVG 绘制:采用 SVG 渲染点阵和连线,视觉效果清晰美观,易于自定义样式。
  • 实时反馈:用户每经过一个点,都会高亮显示并连线,实时反馈当前图案路径。
  • 易于集成:组件结构清晰,状态管理简单,便于在不同项目中快速集成和二次开发。
  • 灵活扩展:可根据业务需求扩展为图案密码设置、验证、错误提示等功能。

典型使用场景

  • 移动端应用解锁:模仿安卓系统的图案解锁,用于 App 登录、隐私保护等场景。
  • 网页端安全验证:作为二次验证手段,提升网页端的安全性和趣味性。
  • 儿童教育/游戏:用于儿童图案记忆训练、益智小游戏等互动场景。
  • 自定义交互控件:可作为创意输入控件,应用于签到、抽奖、互动问答等场景。

全部源码

import React, { useRef, useState } from "react";

const GRID_SIZE = 3;
const DOTS = Array.from({ length: GRID_SIZE * GRID_SIZE }, (_, i) => i);

function getDotPosition(index, size, padding) {
  const row = Math.floor(index / GRID_SIZE);
  const col = index % GRID_SIZE;
  const gap = (size - 2 * padding) / (GRID_SIZE - 1);
  return {
    x: padding + col * gap,
    y: padding + row * gap,
  };
}

export default function LockScreenPassword() {
  const [selected, setSelected] = useState([]);
  const [isDrawing, setIsDrawing] = useState(false);
  const svgRef = useRef(null);
  const size = 300;
  const padding = 30;

  const handlePointerDown = () => {
    setSelected([]);
    setIsDrawing(true);
  };

  const handlePointerUp = () => {
    setIsDrawing(false);
    // 这里可以处理密码验证或设置逻辑
    // alert(`图案顺序: ${selected.join("-")}`);
  };

  const handlePointerMove = (e) => {
    if (!isDrawing) return;
    const rect = svgRef.current.getBoundingClientRect();
    const x = (e.touches ? e.touches[0].clientX : e.clientX) - rect.left;
    const y = (e.touches ? e.touches[0].clientY : e.clientY) - rect.top;
    DOTS.forEach((dot, idx) => {
      const { x: dx, y: dy } = getDotPosition(idx, size, padding);
      const dist = Math.sqrt((dx - x) ** 2 + (dy - y) ** 2);
      if (dist < 25 && !selected.includes(idx)) {
        setSelected((prev) => [...prev, idx]);
      }
    });
  };

  return (
    <div
      style={{
        display: "flex",
        flexDirection: "column",
        alignItems: "center",
        marginTop: 40,
      }}
    >
      <svg
        ref={svgRef}
        width={size}
        height={size}
        onPointerDown={handlePointerDown}
        onPointerUp={handlePointerUp}
        onPointerMove={handlePointerMove}
        onTouchStart={handlePointerDown}
        onTouchEnd={handlePointerUp}
        onTouchMove={handlePointerMove}
      >
        {/* 连线 */}
        {selected.map((idx, i) => {
          if (i === 0) return null;
          const from = getDotPosition(selected[i - 1], size, padding);
          const to = getDotPosition(idx, size, padding);
          return (
            <line
              key={"line-" + i}
              x1={from.x}
              y1={from.y}
              x2={to.x}
              y2={to.y}
              stroke="#1976d2"
              strokeWidth={6}
              strokeLinecap="round"
            />
          );
        })}
        {/* 点 */}
        {DOTS.map((_, idx) => {
          const { x, y } = getDotPosition(idx, size, padding);
          const isActive = selected.includes(idx);
          return (
            <circle
              key={idx}
              cx={x}
              cy={y}
              r={20}
              fill={isActive ? "#1976d2" : "#fff"}
              stroke="#1976d2"
              strokeWidth={3}
            />
          );
        })}
      </svg>
      <div style={{ marginTop: 20 }}>
        当前图案顺序: {selected.join("-") || "无"}
      </div>
    </div>
  );
}

资源下载
下载价格免费
注意:本网站资源属于虚拟产品,不支持退款。请谨慎购买! 购买后资源无法下载,请联系客服QQ:844475003,微信号:th844475003。
原文链接:https://code.ifrontend.net/archives/680,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?