likes
comments
collection
share

产品经理:能不能根据用户心情自动切换主题。我:好的。这个灵感来自于[zxg_神说要有光]大佬的[写一个可以当镜子照的Bu

作者站长头像
站长
· 阅读数 53

效果展示

产品经理:能不能根据用户心情自动切换主题。我:好的。这个灵感来自于[zxg_神说要有光]大佬的[写一个可以当镜子照的Bu

在线体验地址:dbfu.github.io/antd-pro-ex…,需要开启摄像头权限,不支持手机浏览器。

代码仓库地址:github.com/dbfu/antd-p…

前言

实现思路

借助第三方库透过摄像头事实获取用户的表情,然后根据表情动态切换主题。

具体实现

先使用antd pro脚手架初始化一个antd pro项目

pro create antd-pro-expression-theme

安装face-api.js

pnpm i face-api.js

到仓库中下载源码,把weights文件夹复制到antd pro项目中的public文件夹下,这一步很关键,我被这个地方卡了一段时间。

改造antd pro项目,支持动态主题。

在src目录下创建expression.tsx标题组件

import { useEffect, useRef, useState } from 'react';
import * as faceapi from 'face-api.js';


const expressionMap: any = {
  "neutral": '正常',
  "happy": '开心',
  "sad": '悲伤',
  "surprised": '惊讶',
}

const Hidden = true;

function getExpressionResult(expression: any) {
  if (!expression) return;
  const keys = [
    'neutral',
    'happy',
    'sad',
    'angry',
    'fearful',
    'disgusted',
    'surprised',
  ];

  const curExpression = keys.reduce((prev: string, cur: string) => {
    if (!prev) {
      return cur;
    } else {
      return expression[cur] > expression[prev] ? cur : prev;
    }
  }, '');
  return curExpression;
}

export function Expression({
  onExpressionChange,
}: any) {

  const videoRef = useRef<HTMLVideoElement>(null);
  const [expression, setExpression] = useState<string | undefined>('');

  useEffect(() => {
    if (onExpressionChange) {
      onExpressionChange(expression);
    }
  }, [expression]);

  async function run() {
    await faceapi.nets.tinyFaceDetector.load('/widgets/');

    await faceapi.loadSsdMobilenetv1Model('/widgets/');
    await faceapi.loadFaceLandmarkModel('/widgets/');
    await faceapi.loadFaceExpressionModel('/widgets/');

    const stream = await navigator.mediaDevices.getUserMedia({ video: {} });
    if (videoRef.current) {
      videoRef.current.srcObject = stream;
    }
  }

  useEffect(() => {
    run();
  }, []);

  async function onPlay(): Promise<any> {

    const videoEl = videoRef.current;

    if (!videoEl) return;

    if (videoEl.paused || videoEl.ended) return setTimeout(() => onPlay());

    const result = await faceapi
      .detectSingleFace(videoEl)
      .withFaceExpressions();

    setExpression(getExpressionResult(result?.expressions))

    setTimeout(() => onPlay());
  }

  return (
    <div style={{ opacity: Hidden ? 0 : 1  }} >
      <video
        style={{
          background: '#fff',
          width: 640,
          height: 480,
          position: 'fixed',
          top: 0,
          left: 0,
          zIndex: Hidden ? 0 : 10001
        }}
        onLoadedMetadata={() => { onPlay() }}
        id="inputVideo"
        autoPlay
        muted
        playsInline
        ref={videoRef}
      />
      <div
        style={{
          opacity: 1,
          width: 640,
          height: 480,
          position: 'fixed',
          top: 0,
          left: 0,
          zIndex: 10001,
          display: 'flex',
          alignItems: 'center',
          justifyContent: 'center',
          color: '#fff',
        }}
      >
        {expressionMap?.[expression || 'neutral']}
      </div>
    </div>
  )
}

这样就简单的获取到了表情,目前我就支持了正常、开心、惊讶、伤心四种表情,实际上他还支持其他一些表情,大家可以自己去体验一下。我主要参考了这个demo,这里面还有其他demo大家可以去体验一下。 如果不想显示视频,把上Hidden变量设置为true就行了。

在src目录下创建theme-provider.tsx文件

import { ConfigProvider } from 'antd';
import throttle from 'lodash/throttle';

import { Expression } from './expression';
import { useMemo, useState } from 'react';

export default function ThemeProvider({ children }: any) {

  const [theme, setTheme] = useState<string>('');

  const expressionChange = useMemo(
    () => throttle((expression: string) => {
      const map: any = {
        happy: 'rgb(245, 34, 45)',
        sad: 'rgb(192, 192, 192)',
        surprised: 'rgb(250, 173, 20)',
      };

      setTheme(map[expression] ? map[expression] : 'rgb(22, 119, 255)')
    }, 1000), [])


  return (
    <ConfigProvider theme={{
      token: {
        colorPrimary: theme || 'rgb(22, 119, 255)',
      }
    }}>
      <Expression onExpressionChange={expressionChange} />
      {children}
    </ConfigProvider>
  )
}

这个文件用来监听表情变化,然后动态设置主题,目前也是只支持了正常、开心、惊讶、伤心四种主题。

最后在src/app.tsx使用theme-provider组件,并删除下面截图中的代码,不然我们的主题会被默认主题覆盖掉,导致不能改主题。

export function rootContainer(container: any) {
  return React.createElement(ThemeProvider, null, container);
}

产品经理:能不能根据用户心情自动切换主题。我:好的。这个灵感来自于[zxg_神说要有光]大佬的[写一个可以当镜子照的Bu

然后启动项目就行了。第一次获取表情有点慢,可能要等一会。

总结

这个功能看似没用,实则真没用,主要是想整个活让大家乐一下。大家应该还记得以前有个比较热门的话题吧,根据手机壳改变主题颜色,如果能通过摄像头获取到手机壳的颜色,好像也不是不行🐶。

在线体验地址:dbfu.github.io/antd-pro-ex…,需要开启摄像头权限。

代码仓库地址:github.com/dbfu/antd-p…

转载自:https://juejin.cn/post/7226385396167704634
评论
请登录