-
-
+
+
-
-
-
+
+
+
+
+
-
+
+
+
+
我可以帮您检测常规生命体
征和心理压力指数噢~
-
-
-
- 征和心理压力指数噢~
@@ -44,8 +30,7 @@ import { computed, ref } from 'vue';
import { useRouter } from 'vue-router';
const showHelp = ref(true);
-const ipRef = ref(null);
-const teleportTarget = computed(() => ipRef.value ?? 'body');
+
const router = useRouter();
const start = () => {
router.push('/step2');
@@ -54,7 +39,7 @@ const start = () => {
-
-
diff --git a/src/views/step2.vue b/src/views/step2.vue
index e68ff33..ef2b39d 100644
--- a/src/views/step2.vue
+++ b/src/views/step2.vue
@@ -5,13 +5,12 @@
-
+


+
+
+
+
+
+
+
+ void) | null = null;
const PREVIEW_ASPECT = 4 / 3; // 4:3,不裁剪;想更宽可改 16/9
@@ -80,10 +90,20 @@ const PREVIEW_ASPECT = 4 / 3; // 4:3,不裁剪;想更宽可改 16/9
const RETICLE_CENTER_Y = 0.3;
const tipsVideoEl = ref(null);
-// 相机未授权/未启动时不要播放提示视频
-const showTipsVideo = ref(false);
+// 提示视频始终显示,但不自动播放(由用户点击触发播放)
+const showTipsVideo = ref(true);
const tipsVideoSrc = computed(() => tips2Url);
let navigatingToStep3 = false;
+
+const changeTipsText = (arr: {text: string, time: number}[]) => {
+ for (const {text, time} of arr) {
+ tipsText.value = text;
+ setTimeout(() => {
+ tipsText.value = text;
+ }, time);
+ }
+}
+
function onTipsTimeUpdate() {
const el = tipsVideoEl.value;
if (!el) return;
@@ -99,6 +119,7 @@ function tryPlayTips() {
el.play().catch(() => {
// iOS/部分机型可能需要用户手势;这里允许用户点一下视频开始播放
});
+ changeTipsText([{text: '请正视镜头', time: 0}, {text: '请向右转头', time: 2000}, {text: '请向左转头', time: 4000}]);
}
function onTipsEnded() {
@@ -142,6 +163,7 @@ async function videoUrlToBlob(videoUrl: string): Promise {
}
async function setupNativeRecorder() {
+ cameraReady.value = false;
const vw = Math.round(window.innerWidth);
const vh = Math.round(window.innerHeight);
// 让预览按比例“contain”居中,避免满屏裁剪导致人脸变大
@@ -176,6 +198,8 @@ async function setupNativeRecorder() {
await CameraPreview.startRecordVideo({});
started = true;
+ isRecording.value = true;
+ cameraReady.value = true;
statusText.value = '前置摄像头录制中';
// 尺寸变化时同步更新原生预览,保持按比例居中
@@ -213,6 +237,8 @@ async function teardownRecorder() {
return;
}
released = true;
+ isRecording.value = false;
+ cameraReady.value = false;
if (removeResizeListener) {
removeResizeListener();
removeResizeListener = null;
@@ -247,7 +273,8 @@ onMounted(async () => {
document.documentElement.classList.add('step2-camera-active');
progress.value = 0;
currentRate.value = 0;
- showTipsVideo.value = false;
+ showTipsVideo.value = true;
+ cameraReady.value = false;
if (!Capacitor.isNativePlatform()) {
statusText.value = '请在真机或模拟器(Capacitor)中打开以使用前置录像';
@@ -268,14 +295,15 @@ onMounted(async () => {
try {
await setupNativeRecorder();
- // 相机正常启动后再显示/播放提示视频
- showTipsVideo.value = true;
+ // 相机启动成功后自动播放提示视频
await nextTick();
tryPlayTips();
+ //
} catch (e) {
console.error(e);
statusText.value = '无法启动相机或麦克风,请检查系统权限';
- showTipsVideo.value = false;
+ showTipsVideo.value = true;
+ cameraReady.value = false;
const el = tipsVideoEl.value;
if (el) {
try {
@@ -330,7 +358,7 @@ onBeforeUnmount(async () => {
position: absolute;
inset: 0;
z-index: 1;
- background: rgba(0, 0, 0, 0.55);
+ background: rgba(0, 0, 0, 0.85);
/* 中间圆形镂空(iOS/WKWebView 友好) */
-webkit-mask: radial-gradient(
circle at 50% 30%,
@@ -351,17 +379,9 @@ onBeforeUnmount(async () => {
z-index: 4;
width: 60px;
height: 60px;
- border-radius: 999px;
- background: rgba(0, 0, 0, 0.35);
- border: 1px solid rgba(255, 255, 255, 0.18);
- display: grid;
- place-items: center;
img{
- width: 34px;
- height: 34px;
- object-fit: contain;
- filter: invert(1);
- opacity: 0.95;
+ width: 100%;
+ height: 100%;
}
}
@@ -415,6 +435,88 @@ onBeforeUnmount(async () => {
border: 2px solid rgba(255, 255, 255, 0.25);
}
+.reticle__hole {
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%);
+ width: calc(var(--focus-radius) * 2);
+ height: calc(var(--focus-radius) * 2);
+ border-radius: 999px;
+ overflow: visible;
+ pointer-events: none;
+}
+
+.reticle__faceWrap {
+ position: absolute;
+ inset: 0;
+ border-radius: 999px;
+ overflow: hidden;
+ pointer-events: none;
+}
+
+.reticle__face {
+ position: absolute;
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%);
+ width: 48%;
+ object-fit: contain;
+ opacity: 0.9;
+ pointer-events: none;
+}
+
+.reticle__line {
+ position: absolute;
+ left: 50%;
+ transform: translate(-50%, 0);
+ width: 135%;
+ height: 46px;
+ background: url('@/assets/step2/line.png') no-repeat center/cover;
+ opacity: 0.95;
+ pointer-events: none;
+}
+
+.reticle__line--top {
+ top: 0;
+}
+
+.reticle__line--bottom {
+ bottom: 0;
+}
+
+.reticle__hole.is-recording .reticle__line--top {
+ animation: reticleLineDownUp 2s ease-in-out infinite;
+}
+
+.reticle__hole.is-recording .reticle__line--bottom {
+ animation: reticleLineUpDown 2s ease-in-out infinite;
+}
+
+@keyframes reticleLineDownUp {
+ 0% {
+ transform: translate(-50%, 0);
+ }
+ 50% {
+ transform: translate(-50%, calc(var(--focus-radius) * 2 - 46px));
+ }
+ 100% {
+ transform: translate(-50%, 0);
+ }
+}
+
+@keyframes reticleLineUpDown {
+ 0% {
+ transform: translate(-50%, 0);
+ }
+ 50% {
+ transform: translate(-50%, calc((var(--focus-radius) * 2 - 46px) * -1));
+ }
+ 100% {
+ transform: translate(-50%, 0);
+ }
+}
+
.bottom {
position: absolute;
left: 0;
@@ -427,6 +529,11 @@ onBeforeUnmount(async () => {
gap: 18px;
padding: 0 28px 42px;
box-sizing: border-box;
+ .tips-text{
+ color: #fff;
+ font-size: 40px;
+ margin-bottom: 82px;
+ }
}
.zoom {
@@ -467,7 +574,7 @@ onBeforeUnmount(async () => {
}
.tip {
- margin: 0 auto;
+ margin: 148px auto 0;
width: 605px;
height: 82px;
img{
diff --git a/src/views/step3.vue b/src/views/step3.vue
index 9d58565..1de91d5 100644
--- a/src/views/step3.vue
+++ b/src/views/step3.vue
@@ -5,10 +5,19 @@
-
+
{{ tipsText }}

+ 

-
+
正在分析你的身体状态…
@@ -19,7 +28,7 @@
+
正在分析你的身体状态…
