Vue3 + Electron 实现纯本地人脸识别登录一体机(离线可用、无云端、带页面跳转)

前言

在本文中,我将手把手教你使用 Vue3 + Electron 实现一套纯本地、离线运行、不上云的人脸识别登录系统。功能包含:

  • 开机直接进入人脸识别登录页
  • 人脸登录成功 → 进入首页
  • 人脸不匹配 → 本页显示失败
  • 登录页可跳转到人脸录入页
  • 录入成功自动返回登录
  • 全程本地运行,无需网络

技术栈

  • Vue3 + VueRouter
  • Electron(打包桌面端 + 调用摄像头)
  • @vladmandic/face-api(本地人脸识别库)
  • 模型本地加载,100% 离线运行

一、项目初始化

1. 创建 Vue3 项目

复制代码
vue create electron-face-local
cd electron-face-local
  1. 安装依赖

    npm install electron electron-builder vue-router@4
    npm install @vladmandic/face-api

3. 创建 Electron 入口文件

根目录新建:electron-main.js

复制代码
const { app, BrowserWindow } = require('electron')

let win
function createWindow() {
  win = new BrowserWindow({
    width: 1280,
    height: 720,
    webPreferences: {
      nodeIntegration: true,
      contextIsolation: false
    }
  })
  // 开发环境加载 Vue 服务
  win.loadURL('http://localhost:8080')
}

app.whenReady().then(createWindow)
app.on('window-all-closed', () => app.quit())
  1. package.json 配置

    "main": "electron-main.js",
    "scripts": {
    "serve": "vue-cli-service serve",
    "electron": "electron .",
    "build": "vue-cli-service build",
    "electron:build": "vue-cli-service build && electron-builder"
    }

二、配置路由(实现页面跳转)

src/router/index.js

复制代码
import { createRouter, createWebHashHistory } from 'vue-router'
import Login from '../views/Login.vue'
import Register from '../views/Register.vue'
import Home from '../views/Home.vue'

const routes = [
  { path: '/', redirect: '/login' },
  { path: '/login', component: Login },
  { path: '/register', component: Register },
  { path: '/home', component: Home }
]

const router = createRouter({
  history: createWebHashHistory(),
  routes
})

export default router

src/main.js

复制代码
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

createApp(App).use(router).mount('#app')

App.vue

复制代码
<template>
  <router-view></router-view>
</template>

三、放入本地人脸识别模型

从官方下载模型,全部放入 public/models/ 目录https://github.com/justadudewhohacks/face-api.js/tree/master/weights

复制代码
public/models/
   所有 .bin / .shaperes 文件

四、核心页面代码(直接复制)

1. 人脸识别登录页(src/views/Login.vue)

复制代码
<template>
  <div class="page">
    <h2>人脸识别登录</h2>
    <div class="box">
      <video ref="video" autoplay muted></video>
      <canvas ref="canvas"></canvas>
    </div>
    <div class="status" :style="{color:statusColor}">{{ statusText }}</div>
    <button @click="goToRegister" class="btn">去录入人脸</button>
  </div>
</template>

<script>
import * as faceapi from '@vladmandic/face-api'
export default {
  data() {
    return {
      statusText: '加载模型中...',
      statusColor: '#42b983',
      localFace: null
    }
  },
  mounted() {
    this.loadFace()
    this.start()
  },
  methods: {
    loadFace() {
      const data = localStorage.getItem('faceUser')
      if (!data) return
      const user = JSON.parse(data)
      const desc = new Float32Array(user.descriptors[0])
      this.localFace = new faceapi.LabeledFaceDescriptors(user.label, [desc])
    },
    async start() {
      await faceapi.loadSsdMobilenetv1Model('/models')
      await faceapi.loadFaceLandmarkModel('/models')
      await faceapi.loadFaceRecognitionModel('/models')
      this.startCamera()
    },
    async startCamera() {
      const stream = await navigator.mediaDevices.getUserMedia({ video: true })
      this.$refs.video.srcObject = stream
      this.detectLoop()
    },
    async detectLoop() {
      const video = this.$refs.video
      const canvas = this.$refs.canvas
      const size = { width: video.videoWidth, height: video.videoHeight }
      faceapi.matchDimensions(canvas, size)

      const detect = await faceapi.detectSingleFace(video).withFaceLandmarks().withFaceDescriptor()
      const ctx = canvas.getContext('2d')
      ctx.clearRect(0,0,canvas.width,canvas.height)

      if (!detect) {
        this.statusText = '未检测到人脸'
        this.statusColor = '#f56c6c'
        requestAnimationFrame(() => this.detectLoop())
        return
      }

      if (!this.localFace) {
        this.statusText = '请先录入人脸'
        this.statusColor = '#f56c6c'
        requestAnimationFrame(() => this.detectLoop())
        return
      }

      const matcher = new faceapi.FaceMatcher([this.localFace])
      const res = matcher.findBestMatch(detect.descriptor)

      if (res.label === 'unknown') {
        this.statusText = '登录失败,人脸不匹配'
        this.statusColor = '#f56c6c'
      } else {
        this.statusText = '登录成功,欢迎 ' + res.label
        setTimeout(() => this.$router.push('/home'), 1000)
        return
      }
      requestAnimationFrame(() => this.detectLoop())
    },
    goToRegister() {
      this.$router.push('/register')
    }
  }
}
</script>

<style scoped>
.page{width:100vw;height:100vh;background:#111;color:#fff;text-align:center;padding:20px;box-sizing:border-box}
.box{position:relative;width:700px;height:500px;margin:0 auto}
video,canvas{position:absolute;left:0;top:0;width:100%;height:100%}
.status{font-size:22px;margin:10px 0}
.btn{padding:10px 20px;background:#42b983;color:#fff;border:none;border-radius:4px}
</style>
  1. 人脸录入页(src/views/Register.vue

  2. 登录成功首页(src/views/Home.vue)

五、项目运行

复制代码
npm run serve
npm run electron

六、功能流程总结

  1. 打开软件 → 人脸识别登录页
  2. 未录入 → 点击按钮跳转到录入页
  3. 输入姓名 → 点击录入 → 保存本地
  4. 自动返回登录页
  5. 人脸匹配 → 进入首页
  6. 人脸不匹配 → 本页提示登录失败

七、常见问题解决

  1. Device in use:摄像头被占用,关闭所有软件重新运行
  2. 模型加载失败:确保模型放在 public/models
  3. TextEncoder 报错:使用 @vladmandic/face-api 即可解决

八、总结

本项目完全基于本地离线运行,不依赖任何云端服务,非常适合一体机、门禁、考勤等场景。使用 Vue3 做界面,Electron 打包桌面端,face-api 实现本地人脸识别,代码简洁、可直接商用二次开发。

成果展示:不露脸嘿嘿

相关推荐
mldong1 小时前
你的 Vue3 项目也能有钉钉同款审批流设计器:npm 装包,10 分钟画出第一条审批流
前端·vue.js
2分钟速写快排2 小时前
什么是 RAG?如何用 RAG 实现一个用户记忆?
前端·后端·ai编程
passerby60612 小时前
如何自己造一个时间处理库
前端·javascript·github
走到天涯海角3 小时前
react里面的长列表渲染优化
前端·react.js·前端框架
小羊没烦恼!3 小时前
Hello Web API系列教程——Web API与国际化
java·服务器·前端·javascript·php
北岛贰4 小时前
迷茫焦虑期,我做了一个带支付带官网的 AI 聊天虚拟恋人 App
前端·人工智能·后端
mayaairi5 小时前
Vue2 组件通讯(三):全局事件总线、PubSub、插槽与组件实例属性
前端·javascript·vue.js
kyriewen6 小时前
面试官问我:AI 都能写代码了,前端凭什么还值 25K
前端·javascript·人工智能
风骏时光牛马7 小时前
AI源码分析:拆解模型底层实现逻辑
前端
BillKu7 小时前
全局样式变量:CSS 自定义属性(--border-color)和SCSS 变量($border-color)的说明
javascript·css·scss