产品经理:能不能根据用户心情自动切换主题。我:好的。
效果展示
在线体验地址:dbfu.github.io/antd-pro-ex…,需要开启摄像头权限,不支持手机浏览器。
代码仓库地址:github.com/dbfu/antd-p…
前言
这个灵感来自于zxg_神说要有光大佬的写一个可以当镜子照的Button这篇文章,正好今天看了一个人脸识别的前端仓库,可以动态识别人的表情,本来想写一个“根据用户心情变色的按钮”,同事说能不能实现”根据用户心情自动切换系统主题“,我想了一下好像可以的。
实现思路
借助第三方库透过摄像头事实获取用户的表情,然后根据表情动态切换主题。
具体实现
先使用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);
}
然后启动项目就行了。第一次获取表情有点慢,可能要等一会。
总结
这个功能看似没用,实则真没用,主要是想整个活让大家乐一下。大家应该还记得以前有个比较热门的话题吧,根据手机壳改变主题颜色,如果能通过摄像头获取到手机壳的颜色,好像也不是不行🐶。
在线体验地址:dbfu.github.io/antd-pro-ex…,需要开启摄像头权限。
代码仓库地址:github.com/dbfu/antd-p…
作者:前端小付
来源:juejin.cn/post/7226385396167704634
来源:juejin.cn/post/7226385396167704634