Files
RongGiuangKT/html/number_people.html
2025-10-09 19:25:48 +08:00

913 lines
28 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>智慧猫头鹰课堂助手</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<style>
body {
font-family: "Microsoft YaHei", sans-serif;
background: linear-gradient(
135deg,
#fef9e7 0%,
#f8f4e6 50%,
#e8f5e8 100%
);
margin: 0;
padding: 20px;
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
}
.container {
width: 100%;
max-width: 900px;
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.1);
padding: 40px;
margin-top: 20px;
backdrop-filter: blur(10px);
}
h1 {
color: #8b4513;
text-align: center;
margin-bottom: 30px;
font-size: 2.2em;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
}
.owl-container {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 30px;
position: relative;
}
.owl-stage {
width: 400px;
height: 400px;
border-radius: 20px;
border: 3px solid #d2691e;
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
margin-bottom: 20px;
transition: transform 0.3s ease;
background: linear-gradient(135deg, #f0e68c 0%, #daa520 100%);
position: relative;
overflow: hidden;
}
.owl-stage:hover {
transform: scale(1.02);
}
.owl-stage.speaking {
animation: owlGlow 2s infinite;
}
#three-container {
width: 100%;
height: 100%;
border-radius: 17px;
overflow: hidden;
}
@keyframes owlGlow {
0% {
box-shadow: 0 0 0 0 rgba(210, 105, 30, 0.7);
}
70% {
box-shadow: 0 0 0 20px rgba(210, 105, 30, 0);
}
100% {
box-shadow: 0 0 0 0 rgba(210, 105, 30, 0);
}
}
.speech-bubble {
position: relative;
background: linear-gradient(135deg, #ff8c42 0%, #ff6b35 100%);
color: white;
border-radius: 15px;
padding: 20px 25px;
max-width: 85%;
text-align: center;
margin-bottom: 25px;
opacity: 0;
transition: all 0.5s ease;
font-size: 16px;
line-height: 1.5;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
}
.speech-bubble:after {
content: "";
position: absolute;
top: 100%;
left: 50%;
margin-left: -12px;
border-width: 12px;
border-style: solid;
border-color: #ff6b35 transparent transparent transparent;
}
.speech-bubble.visible {
opacity: 1;
transform: translateY(-5px);
}
.controls {
display: flex;
justify-content: center;
gap: 20px;
margin-bottom: 25px;
flex-wrap: wrap;
}
button {
background: linear-gradient(135deg, #cd853f 0%, #8b4513 100%);
color: white;
border: none;
padding: 12px 25px;
border-radius: 25px;
cursor: pointer;
font-size: 16px;
font-weight: bold;
transition: all 0.3s ease;
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
}
button:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3);
}
button:disabled {
background: linear-gradient(135deg, #d3d3d3 0%, #a9a9a9 100%);
cursor: not-allowed;
transform: none;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.status {
text-align: center;
margin-top: 20px;
font-size: 18px;
color: #8b4513;
font-weight: bold;
}
.input-container {
width: 100%;
margin-bottom: 25px;
}
textarea {
width: 100%;
height: 120px;
padding: 15px;
border: 2px solid #daa520;
border-radius: 12px;
font-family: inherit;
font-size: 16px;
resize: vertical;
transition: border-color 0.3s ease;
background: rgba(255, 255, 255, 0.9);
}
textarea:focus {
outline: none;
border-color: #ff8c42;
box-shadow: 0 0 10px rgba(255, 140, 66, 0.3);
}
.features {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
gap: 20px;
margin-top: 35px;
}
.feature-card {
background: linear-gradient(135deg, #fff8dc 0%, #f5deb3 100%);
border-radius: 15px;
padding: 20px;
text-align: center;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease;
border: 2px solid #daa520;
}
.feature-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
.feature-icon {
font-size: 32px;
margin-bottom: 12px;
}
.feature-card h3 {
color: #8b4513;
margin-bottom: 10px;
}
.feature-card p {
color: #696969;
line-height: 1.4;
}
.wisdom-quote {
text-align: center;
font-style: italic;
color: #8b4513;
margin-top: 30px;
padding: 15px;
background: rgba(218, 165, 32, 0.1);
border-radius: 10px;
border-left: 4px solid #daa520;
}
</style>
</head>
<body>
<div class="container">
<h1>🦉 智慧猫头鹰课堂助手</h1>
<div class="owl-container">
<div id="owlStage" class="owl-stage">
<div id="three-container"></div>
</div>
<div id="speechBubble" class="speech-bubble">
你好!我是智慧猫头鹰,很高兴成为你的课堂助手!有什么知识想要探索吗?🌟
</div>
</div>
<div class="input-container">
<textarea id="speechText" placeholder="输入你希望猫头鹰讲解的内容...">
欢迎来到智慧的课堂!我是你的猫头鹰老师。今天我们将一起探索知识的森林,学习新的技能。猫头鹰象征着智慧和洞察力,让我们用敏锐的眼光去发现学习的乐趣吧!</textarea
>
</div>
<div class="controls">
<button id="speakBtn">🎓 开始讲解</button>
<button id="stopBtn" disabled>⏹️ 停止讲解</button>
<button id="expressionBtn">😊 切换表情</button>
</div>
<div id="status" class="status">智慧待机中...</div>
<div class="features">
<div class="feature-card">
<div class="feature-icon">📖</div>
<h3>知识讲解</h3>
<p>用生动有趣的方式为学生讲解各种知识点</p>
</div>
<div class="feature-card">
<div class="feature-icon">🤔</div>
<h3>启发思考</h3>
<p>通过提问和引导帮助学生深入思考问题</p>
</div>
<div class="feature-card">
<div class="feature-icon">🌳</div>
<h3>知识树</h3>
<p>构建完整的知识体系,让学习更有条理</p>
</div>
<div class="feature-card">
<div class="feature-icon"></div>
<h3>智慧启迪</h3>
<p>分享学习方法和人生智慧,启迪心灵</p>
</div>
</div>
<div class="wisdom-quote">
💡 "知识是智慧的翅膀,让我们一起在学习的天空中翱翔!"
</div>
</div>
<script>
const owlStage = document.getElementById("owlStage");
const speechBubble = document.getElementById("speechBubble");
const speechText = document.getElementById("speechText");
const speakBtn = document.getElementById("speakBtn");
const stopBtn = document.getElementById("stopBtn");
const expressionBtn = document.getElementById("expressionBtn");
const status = document.getElementById("status");
let speechSynthesis = window.speechSynthesis;
let currentUtterance = null;
// Three.js 猫头鹰
let scene, camera, renderer, owl;
let isSpeaking = false;
let animationId;
let currentExpression = "normal";
// 初始化Three.js场景
function initThreeJS() {
const container = document.getElementById("three-container");
// 创建场景
scene = new THREE.Scene();
scene.background = new THREE.Color(0xf0e68c);
// 创建相机
camera = new THREE.OrthographicCamera(-200, 200, 200, -200, 0.1, 1000);
camera.position.z = 100;
// 创建渲染器
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(400, 400);
renderer.setClearColor(0xf0e68c);
container.appendChild(renderer.domElement);
// 创建猫头鹰
createOwl();
// 开始动画循环
animate();
}
// 创建猫头鹰角色
function createOwl() {
owl = new THREE.Group();
// 树枝
const branchGeometry = new THREE.CylinderGeometry(4, 6, 120, 8);
const branchMaterial = new THREE.MeshBasicMaterial({
color: 0x8b4513,
side: THREE.DoubleSide,
});
const branch = new THREE.Mesh(branchGeometry, branchMaterial);
branch.rotation.z = Math.PI / 2;
branch.position.y = -120;
owl.add(branch);
// 叶子
const leafPositions = [
{ x: -80, y: -110, rotation: 0.3 },
{ x: -40, y: -115, rotation: -0.2 },
{ x: 20, y: -112, rotation: 0.1 },
{ x: 60, y: -118, rotation: -0.4 },
{ x: 90, y: -108, rotation: 0.2 },
];
leafPositions.forEach((pos) => {
const leafGeometry = new THREE.CircleGeometry(12, 8);
const leafMaterial = new THREE.MeshBasicMaterial({
color: 0x32cd32,
side: THREE.DoubleSide,
});
const leaf = new THREE.Mesh(leafGeometry, leafMaterial);
leaf.position.set(pos.x, pos.y, 1);
leaf.rotation.z = pos.rotation;
leaf.scale.set(1, 1.8, 1);
owl.add(leaf);
});
// 身体 - 椭圆形
const bodyGeometry = new THREE.CircleGeometry(80, 32);
const bodyMaterial = new THREE.MeshBasicMaterial({
color: 0xd2691e,
side: THREE.DoubleSide,
});
const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
body.position.y = -40;
body.scale.set(1, 1.3, 1);
owl.add(body);
// 肚子 - 浅色
const bellyGeometry = new THREE.CircleGeometry(55, 32);
const bellyMaterial = new THREE.MeshBasicMaterial({
color: 0xfff8dc,
side: THREE.DoubleSide,
});
const belly = new THREE.Mesh(bellyGeometry, bellyMaterial);
belly.position.set(0, -45, 1);
belly.scale.set(1, 1.2, 1);
owl.add(belly);
// 羽毛纹理 - 身体上的小圆点
for (let i = 0; i < 20; i++) {
const featherGeometry = new THREE.CircleGeometry(3, 8);
const featherMaterial = new THREE.MeshBasicMaterial({
color: 0xcd853f,
side: THREE.DoubleSide,
transparent: true,
opacity: 0.6,
});
const feather = new THREE.Mesh(featherGeometry, featherMaterial);
feather.position.set(
(Math.random() - 0.5) * 80,
-60 + (Math.random() - 0.5) * 40,
2
);
owl.add(feather);
}
// 头部 - 圆形
const headGeometry = new THREE.CircleGeometry(70, 32);
const headMaterial = new THREE.MeshBasicMaterial({
color: 0xd2691e,
side: THREE.DoubleSide,
});
const head = new THREE.Mesh(headGeometry, headMaterial);
head.position.y = 30;
owl.add(head);
// 脸部 - 浅色心形区域
const faceGeometry = new THREE.CircleGeometry(50, 32);
const faceMaterial = new THREE.MeshBasicMaterial({
color: 0xfff8dc,
side: THREE.DoubleSide,
});
const face = new THREE.Mesh(faceGeometry, faceMaterial);
face.position.set(0, 25, 1);
face.scale.set(1, 1.1, 1);
owl.add(face);
// 眼睛外圈 - 大圆
const eyeOuterGeometry = new THREE.CircleGeometry(35, 32);
const eyeOuterMaterial = new THREE.MeshBasicMaterial({
color: 0xffffff,
side: THREE.DoubleSide,
});
const leftEyeOuter = new THREE.Mesh(eyeOuterGeometry, eyeOuterMaterial);
leftEyeOuter.position.set(-25, 40, 2);
owl.add(leftEyeOuter);
const rightEyeOuter = new THREE.Mesh(
eyeOuterGeometry,
eyeOuterMaterial
);
rightEyeOuter.position.set(25, 40, 2);
owl.add(rightEyeOuter);
// 眼睛内圈
const eyeInnerGeometry = new THREE.CircleGeometry(28, 32);
const eyeInnerMaterial = new THREE.MeshBasicMaterial({
color: 0x000000,
side: THREE.DoubleSide,
});
const leftEyeInner = new THREE.Mesh(eyeInnerGeometry, eyeInnerMaterial);
leftEyeInner.position.set(-25, 40, 3);
owl.add(leftEyeInner);
const rightEyeInner = new THREE.Mesh(
eyeInnerGeometry,
eyeInnerMaterial
);
rightEyeInner.position.set(25, 40, 3);
owl.add(rightEyeInner);
// 瞳孔
const pupilGeometry = new THREE.CircleGeometry(20, 16);
const pupilMaterial = new THREE.MeshBasicMaterial({
color: 0x8b4513,
side: THREE.DoubleSide,
});
const leftPupil = new THREE.Mesh(pupilGeometry, pupilMaterial);
leftPupil.position.set(-25, 40, 4);
owl.add(leftPupil);
const rightPupil = new THREE.Mesh(pupilGeometry, pupilMaterial);
rightPupil.position.set(25, 40, 4);
owl.add(rightPupil);
// 眼睛高光
const highlightGeometry = new THREE.CircleGeometry(8, 8);
const highlightMaterial = new THREE.MeshBasicMaterial({
color: 0xffffff,
side: THREE.DoubleSide,
});
const leftHighlight = new THREE.Mesh(
highlightGeometry,
highlightMaterial
);
leftHighlight.position.set(-20, 45, 5);
owl.add(leftHighlight);
const rightHighlight = new THREE.Mesh(
highlightGeometry,
highlightMaterial
);
rightHighlight.position.set(20, 45, 5);
owl.add(rightHighlight);
// 小高光
const smallHighlightGeometry = new THREE.CircleGeometry(4, 8);
const leftSmallHighlight = new THREE.Mesh(
smallHighlightGeometry,
highlightMaterial
);
leftSmallHighlight.position.set(-30, 35, 5);
owl.add(leftSmallHighlight);
const rightSmallHighlight = new THREE.Mesh(
smallHighlightGeometry,
highlightMaterial
);
rightSmallHighlight.position.set(30, 35, 5);
owl.add(rightSmallHighlight);
// 眉毛/头部羽毛装饰
const eyebrowGeometry = new THREE.CircleGeometry(15, 8);
const eyebrowMaterial = new THREE.MeshBasicMaterial({
color: 0xcd853f,
side: THREE.DoubleSide,
});
const leftEyebrow = new THREE.Mesh(eyebrowGeometry, eyebrowMaterial);
leftEyebrow.position.set(-30, 65, 2);
leftEyebrow.scale.set(1.5, 0.6, 1);
leftEyebrow.rotation.z = 0.3;
owl.add(leftEyebrow);
const rightEyebrow = new THREE.Mesh(eyebrowGeometry, eyebrowMaterial);
rightEyebrow.position.set(30, 65, 2);
rightEyebrow.scale.set(1.5, 0.6, 1);
rightEyebrow.rotation.z = -0.3;
owl.add(rightEyebrow);
// 喙
const beakGeometry = new THREE.CircleGeometry(8, 8);
const beakMaterial = new THREE.MeshBasicMaterial({
color: 0xff8c00,
side: THREE.DoubleSide,
});
const beak = new THREE.Mesh(beakGeometry, beakMaterial);
beak.position.set(0, 15, 3);
beak.scale.set(1, 0.8, 1);
owl.add(beak);
// 翅膀
const wingGeometry = new THREE.CircleGeometry(45, 16);
const wingMaterial = new THREE.MeshBasicMaterial({
color: 0xcd853f,
side: THREE.DoubleSide,
});
const leftWing = new THREE.Mesh(wingGeometry, wingMaterial);
leftWing.position.set(-85, -30, 0);
leftWing.scale.set(0.8, 1.4, 1);
leftWing.rotation.z = 0.3;
owl.add(leftWing);
const rightWing = new THREE.Mesh(wingGeometry, wingMaterial);
rightWing.position.set(85, -30, 0);
rightWing.scale.set(0.8, 1.4, 1);
rightWing.rotation.z = -0.3;
owl.add(rightWing);
// 翅膀羽毛纹理
for (let i = 0; i < 10; i++) {
const wingFeatherGeometry = new THREE.CircleGeometry(4, 8);
const wingFeatherMaterial = new THREE.MeshBasicMaterial({
color: 0xd2691e,
side: THREE.DoubleSide,
transparent: true,
opacity: 0.7,
});
const leftWingFeather = new THREE.Mesh(
wingFeatherGeometry,
wingFeatherMaterial
);
leftWingFeather.position.set(
-85 + (Math.random() - 0.5) * 40,
-30 + (Math.random() - 0.5) * 60,
1
);
owl.add(leftWingFeather);
const rightWingFeather = new THREE.Mesh(
wingFeatherGeometry,
wingFeatherMaterial
);
rightWingFeather.position.set(
85 + (Math.random() - 0.5) * 40,
-30 + (Math.random() - 0.5) * 60,
1
);
owl.add(rightWingFeather);
}
// 爪子
const clawGeometry = new THREE.CircleGeometry(6, 8);
const clawMaterial = new THREE.MeshBasicMaterial({
color: 0xff8c00,
side: THREE.DoubleSide,
});
// 左爪
const leftClaw1 = new THREE.Mesh(clawGeometry, clawMaterial);
leftClaw1.position.set(-25, -110, 2);
leftClaw1.scale.set(0.6, 1.5, 1);
owl.add(leftClaw1);
const leftClaw2 = new THREE.Mesh(clawGeometry, clawMaterial);
leftClaw2.position.set(-15, -112, 2);
leftClaw2.scale.set(0.6, 1.5, 1);
owl.add(leftClaw2);
// 右爪
const rightClaw1 = new THREE.Mesh(clawGeometry, clawMaterial);
rightClaw1.position.set(15, -112, 2);
rightClaw1.scale.set(0.6, 1.5, 1);
owl.add(rightClaw1);
const rightClaw2 = new THREE.Mesh(clawGeometry, clawMaterial);
rightClaw2.position.set(25, -110, 2);
rightClaw2.scale.set(0.6, 1.5, 1);
owl.add(rightClaw2);
// 存储可动画的部分
owl.userData = {
head: head,
body: body,
leftEyeOuter: leftEyeOuter,
rightEyeOuter: rightEyeOuter,
leftEyeInner: leftEyeInner,
rightEyeInner: rightEyeInner,
leftPupil: leftPupil,
rightPupil: rightPupil,
leftHighlight: leftHighlight,
rightHighlight: rightHighlight,
leftSmallHighlight: leftSmallHighlight,
rightSmallHighlight: rightSmallHighlight,
beak: beak,
leftWing: leftWing,
rightWing: rightWing,
blinkTimer: 0,
speakTimer: 0,
idleTimer: 0,
};
scene.add(owl);
}
// 动画循环
function animate() {
animationId = requestAnimationFrame(animate);
if (owl && owl.userData) {
const userData = owl.userData;
// 眨眼动画
userData.blinkTimer += 0.016;
if (userData.blinkTimer > 4) {
userData.leftEyeOuter.scale.y = 0.1;
userData.rightEyeOuter.scale.y = 0.1;
userData.leftEyeInner.scale.y = 0.1;
userData.rightEyeInner.scale.y = 0.1;
if (userData.blinkTimer > 4.2) {
userData.leftEyeOuter.scale.y = 1;
userData.rightEyeOuter.scale.y = 1;
userData.leftEyeInner.scale.y = 1;
userData.rightEyeInner.scale.y = 1;
userData.blinkTimer = 0;
}
}
// 说话动画
if (isSpeaking) {
userData.speakTimer += 0.2;
userData.beak.scale.y = 0.8 + Math.sin(userData.speakTimer) * 0.3;
userData.head.position.y =
30 + Math.sin(userData.speakTimer * 0.7) * 2;
// 说话时翅膀轻微扇动
userData.leftWing.rotation.z =
0.3 + Math.sin(userData.speakTimer * 0.5) * 0.1;
userData.rightWing.rotation.z =
-0.3 - Math.sin(userData.speakTimer * 0.5) * 0.1;
} else {
userData.beak.scale.y = 0.8;
userData.head.position.y = 30;
userData.leftWing.rotation.z = 0.3;
userData.rightWing.rotation.z = -0.3;
}
// 闲置动画 - 轻微摆动
userData.idleTimer += 0.008;
owl.rotation.z = Math.sin(userData.idleTimer) * 0.02;
// 头部轻微转动
userData.head.rotation.z = Math.sin(userData.idleTimer * 0.7) * 0.05;
}
renderer.render(scene, camera);
}
// 开始说话动画
function startSpeaking() {
isSpeaking = true;
owlStage.classList.add("speaking");
}
// 停止说话动画
function stopSpeaking() {
isSpeaking = false;
owlStage.classList.remove("speaking");
}
// 切换表情
function changeExpression() {
if (!owl || !owl.userData) return;
const expressions = ["normal", "happy", "thinking", "surprised"];
const currentIndex = expressions.indexOf(currentExpression);
currentExpression =
expressions[(currentIndex + 1) % expressions.length];
const userData = owl.userData;
switch (currentExpression) {
case "happy":
// 开心表情 - 眯眼
userData.leftEyeOuter.scale.y = 0.7;
userData.rightEyeOuter.scale.y = 0.7;
userData.beak.scale.set(1.2, 1, 1);
status.textContent = "😊 开心模式";
break;
case "thinking":
// 思考表情 - 头部倾斜
userData.head.rotation.z = 0.2;
userData.leftPupil.position.x = -20;
userData.rightPupil.position.x = 30;
status.textContent = "🤔 思考模式";
break;
case "surprised":
// 惊讶表情 - 眼睛放大
userData.leftEyeOuter.scale.set(1.3, 1.3, 1);
userData.rightEyeOuter.scale.set(1.3, 1.3, 1);
userData.beak.scale.set(0.8, 1.2, 1);
status.textContent = "😮 惊讶模式";
break;
default: // normal
// 恢复正常
userData.leftEyeOuter.scale.set(1, 1, 1);
userData.rightEyeOuter.scale.set(1, 1, 1);
userData.leftEyeInner.scale.set(1, 1, 1);
userData.rightEyeInner.scale.set(1, 1, 1);
userData.head.rotation.z = 0;
userData.leftPupil.position.x = -25;
userData.rightPupil.position.x = 25;
userData.beak.scale.set(1, 0.8, 1);
status.textContent = "😐 正常模式";
}
}
// 初始显示欢迎消息
setTimeout(() => {
speechBubble.classList.add("visible");
}, 800);
// 事件监听器
speakBtn.addEventListener("click", () => {
const text = speechText.value.trim();
if (!text) {
status.textContent = "请输入要讲解的内容";
return;
}
if (speechSynthesis.speaking) {
speechSynthesis.cancel();
}
currentUtterance = new SpeechSynthesisUtterance(text);
currentUtterance.lang = "zh-CN";
currentUtterance.rate = 0.9;
currentUtterance.pitch = 1.1;
currentUtterance.onstart = () => {
startSpeaking();
speechBubble.textContent = text;
speechBubble.classList.add("visible");
status.textContent = "📚 正在智慧讲解中...";
speakBtn.disabled = true;
stopBtn.disabled = false;
};
currentUtterance.onend = () => {
stopSpeaking();
status.textContent = "✨ 讲解完成!";
speakBtn.disabled = false;
stopBtn.disabled = true;
};
currentUtterance.onerror = (event) => {
stopSpeaking();
status.textContent = `❌ 讲解出错: ${event.error}`;
speakBtn.disabled = false;
stopBtn.disabled = true;
};
speechSynthesis.speak(currentUtterance);
});
stopBtn.addEventListener("click", () => {
if (speechSynthesis.speaking) {
speechSynthesis.cancel();
stopSpeaking();
status.textContent = "⏹️ 讲解已停止";
speakBtn.disabled = false;
stopBtn.disabled = true;
}
});
expressionBtn.addEventListener("click", changeExpression);
// 鼠标交互
function addMouseInteractions() {
const container = document.getElementById("three-container");
container.addEventListener("mouseenter", () => {
if (owl && owl.userData) {
container.addEventListener("mousemove", followMouse);
}
});
container.addEventListener("mouseleave", () => {
if (owl && owl.userData) {
container.removeEventListener("mousemove", followMouse);
resetEyes();
}
});
container.addEventListener("click", () => {
if (owl && owl.userData) {
blink();
}
});
}
// 眼睛跟随鼠标
function followMouse(event) {
if (!owl || !owl.userData) return;
const rect = event.target.getBoundingClientRect();
const x = (event.clientX - rect.left - 200) / 200;
const y = (event.clientY - rect.top - 200) / 200;
const userData = owl.userData;
const maxOffset = 6;
userData.leftPupil.position.x = -25 + x * maxOffset;
userData.leftPupil.position.y = 40 - y * maxOffset;
userData.rightPupil.position.x = 25 + x * maxOffset;
userData.rightPupil.position.y = 40 - y * maxOffset;
userData.leftHighlight.position.x = -20 + x * maxOffset * 0.8;
userData.leftHighlight.position.y = 45 - y * maxOffset * 0.8;
userData.rightHighlight.position.x = 20 + x * maxOffset * 0.8;
userData.rightHighlight.position.y = 45 - y * maxOffset * 0.8;
}
// 重置眼睛
function resetEyes() {
if (!owl || !owl.userData) return;
const userData = owl.userData;
userData.leftPupil.position.set(-25, 40, 4);
userData.rightPupil.position.set(25, 40, 4);
userData.leftHighlight.position.set(-20, 45, 5);
userData.rightHighlight.position.set(20, 45, 5);
}
// 眨眼
function blink() {
if (!owl || !owl.userData) return;
const userData = owl.userData;
userData.leftEyeOuter.scale.y = 0.1;
userData.rightEyeOuter.scale.y = 0.1;
userData.leftEyeInner.scale.y = 0.1;
userData.rightEyeInner.scale.y = 0.1;
setTimeout(() => {
userData.leftEyeOuter.scale.y = 1;
userData.rightEyeOuter.scale.y = 1;
userData.leftEyeInner.scale.y = 1;
userData.rightEyeInner.scale.y = 1;
}, 200);
}
// 初始化
window.addEventListener("load", () => {
initThreeJS();
addMouseInteractions();
});
</script>
</body>
</html>