一、前言
看见了max大佬和狗头人大佬做的一个桌宠,于是就像用web简单实现一下
二、代码包
https://wwwf.lanzout.com/iWfER0ze0cqd
密码:fg88
三、简单效果
简单用了随机动作(可以进行权重设置)
四、踩坑情况
如果不是主循环loop里,这里不能用这个,会导致开启多个loop循环
animationId = requestAnimationFrame(loop); // 浏览器提供的 API,用于优化动画性能并在重绘之前在主线程上执行指定的函数
定时器在函数里面设置时,最好挂载到全局变量上!方便后面销毁!
1、初始化变量
2、创建定时器(在函数中)
3、清除定时器
ico图标和cur图标都可以在web中应用!
注意:他们的大小不能超过32x32像素 !!!
给父盒子设置了点击监听器——点击子盒子就无法触发监听器!!
解决方法:把子盒子设置为不可点击即可!!!
.cywl img { width: 90px; height: 90px; /* 无法选中 */ pointer-events: none; user-select: none; }
五、主页面代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> <link rel="stylesheet" href="./index.css"> <style> .cywl { position: fixed; width: 100px; height: 100px; z-index: 9999; left: 50px; bottom: 50px; cursor: url(./Maxwell_Who-CatCursor/alternate.ico), default; display: flex; /* background-color: #5ee7df; */ } .cywl img { width: 90px; height: 90px; /* 无法选中 */ pointer-events: none; user-select: none; } </style> </head> <body style="height: 5000px; background-image: linear-gradient(200deg, #5ee7df, #b490ca);"> <!--宠物开始--> <div id="pet" class="cywl"> <img id="pet-img" src="./max/stand1.png"> </div> <!--宠物结束--> <script> // 创建 pet 对象 const pet = { x: 50, // 宠物初始位置的横坐标 (左下角开始) y: 50, vx: 1, // 水平方向上宠物前进的速度 vy: 0 // // 垂直方向上宠物前进的速度 }; // -------------------------------------------------------------动作 // 动作权重 // 权重问题:将所有权重相加,(得到一个大范围,那么就让随机数落到这个范围内,而对应的权重,就是落到的对应位置中) var actions = { // 普通 walkleft: 1, walkright: 1, fish: 1, sleep: 1, kiss: 1, stand: 1, }; // 存储宠物行走动画帧的数组 const LeftwalkFrames = []; // 左走 const RightwalkFrames = []; // 右走 const DragFrames = []; // 拖拽 const fishFrames = []; const kissFrames = []; const sleepFrames = []; const standFrames = []; const fallingFrames = []; // 初始化 定时器 var ttt = null; var dragTime = null; var action = 'stand'; //1、将对象中的动作按照权重转换为数组。可以使用 Object.keys 方法获取对象的键, //2、再使用 Array.map 方法将每个键转换为对象 {name: key, weight: actions[key]}。 //3、最后使用 Array.reduce 方法将多个对象合成一个数组 var actionList = Object.keys(actions).map(function (key) { return { name: key, weight: actions[key] }; }).reduce(function (prev, curr) { return prev.concat(curr); }, []); // 根据权重随机选择动作 // 权重问题:将所有权重相加,(得到一个大范围,那么就让随机数落到这个范围内,而对应的权重,就是落到的对应位置中) function randomAction() { var totalWeight = actionList.reduce(function (prev, curr) { return prev + curr.weight; }, 0); // console.log(totalWeight); var randomNum = Math.random() * totalWeight; for (var i = 0; i < actionList.length; i++) { if (randomNum <= actionList[i].weight) { return actionList[i].name; } randomNum -= actionList[i].weight; } } // -------------------------------------------------------------动作 const img111 = new Image(); img111.src = `./max/falling1.png`; fallingFrames.push(img111); // 创建动画序列 for (let i = 1; i < 13; i++) { // 将行走动画帧添加到 walkFrames 数组中 const img = new Image(); img.src = `./max/walkright${i}.png`; RightwalkFrames.push(img); const img2 = new Image(); img2.src = `./max/walkleft${i}.png`; LeftwalkFrames.push(img2); // 其他 const img3 = new Image(); img3.src = `./max/drag${i}.png`; DragFrames.push(img3); const img4 = new Image(); img4.src = `./max/fish${i}.png`; fishFrames.push(img4); const img5 = new Image(); img5.src = `./max/kiss${i}.png`; kissFrames.push(img5); const img6 = new Image(); img6.src = `./max/sleep${i}.png`; sleepFrames.push(img6); const img7 = new Image(); img7.src = `./max/stand${i}.png`; standFrames.push(img7); } // 绘制宠物 function drawPet(anyFrames = RightwalkFrames) { const frameIndex = Math.floor(Date.now() / 100) % anyFrames.length; // 计算当前应该绘制的动画帧的索引 const img = anyFrames[frameIndex]; // 获取当前应该绘制的动画帧 document.querySelector('.cywl img').src = img.src; // 更新宠物的显示图像 } // 更新宠物位置 function updatePet() { pet.x += pet.vx; // 更新宠物在水平方向的位置 // pet.y += pet.vy; // 超出屏幕左边 if (pet.x < 0) { action = 'walkright' } // 超出屏幕右边 if (pet.x + petDiv.clientWidth > window.innerWidth) { action = 'walkleft' } petDiv.style.left = pet.x + 'px'; // 更新宠物所在 div 元素的横坐标位置 petDiv.style.bottom = pet.y + 'px'; } // ----------------------------------------------------------------------处理用户交互 // 定位 const petDiv = document.querySelector('#pet'); petDiv.style.left = pet.x + 'px'; // 更新宠物所在 div 元素的横坐标位置 petDiv.style.bottom = pet.y + 'px'; // 禁用图片点击 const petImg = document.querySelector('#pet-img'); petImg.style.pointerEvents = 'none'; var isDragging = false; // 标记是否正在拖拽中 var diffX = 0; // 鼠标指针与盒子左上角的偏移量 var diffY = 0; let animationId = null; // 鼠标按下 petDiv.addEventListener('mousedown', function (event) { action = ''; clearInterval(petTimer); // 暂停宠物行动的定时器 // 判断鼠标是否在 div 元素内按下,并记录下偏移量 if (event.target === petDiv) { isDragging = true; diffX = event.clientX - petDiv.offsetLeft; diffY = event.clientY - petDiv.offsetTop; // 拖拽定时器 dragTime = setInterval(function drag() { // 显示拖拽 图片 drawPet(DragFrames); // 这里不能用这个,会导致开启多个loop // animationId = requestAnimationFrame(loop); // 浏览器提供的 API,用于优化动画性能并在重绘之前在主线程上执行指定的函数 }, 100); } }); // 鼠标拖动 document.addEventListener('mousemove', function (event) { // 如果正在拖拽中,则更新盒子位置 if (isDragging === true) { pet.x = event.clientX - diffX pet.y = event.clientY - diffY petDiv.style.left = pet.x + 'px'; petDiv.style.top = pet.y + 'px'; } }); // 鼠标抬起 document.addEventListener('mouseup', function (event) { if (isDragging === true) { // 清除旧的定时器 clearInterval(ttt); clearInterval(dragTime); // 停止拖拽 isDragging = false; action = 'stand'; ttt = setInterval(function name() { action = randomAction(); }, 3000); // 超出屏幕 回到 指定位置 if (pet.y < 0 || pet.y + petDiv.clientHeight > window.innerHeight || pet.x + petDiv.clientWidth > window.innerWidth || pet.x < 0) { pet.x = 500; pet.y = 500; const petDiv = document.querySelector('#pet'); petDiv.style.left = 500 + 'px'; // 更新宠物所在 div 元素的位置 petDiv.style.top = 500 + 'px'; // console.log(1111111) } // 显示下落 图片 drawPet(fallingFrames); // animationId = requestAnimationFrame(loop); // 浏览器提供的 API,用于优化动画性能并在重绘之前在主线程上执行指定的函数 // loop(); } }); // 宠物行动的定时器,每 3 秒执行一次 doAction 函数 var petTimer = setInterval(function name() { action = randomAction(); }, 3000); // ---------------------------------------------------------------------- // 主循环 function loop() { console.log(action); if (action == 'walkleft') { // 执行 leftwalk 动作 pet.vx = -0.5; updatePet(); drawPet(LeftwalkFrames); } if (action == 'walkright') { pet.vx = 0.5; updatePet(); drawPet(RightwalkFrames); } if (action == 'fish') { // updatePet(); drawPet(fishFrames); } if (action == 'kiss') { // updatePet(); drawPet(kissFrames); } if (action == 'sleep') { // updatePet(); drawPet(sleepFrames); } if (action == 'stand') { // updatePet(); drawPet(standFrames); } requestAnimationFrame(loop); // 浏览器提供的 API,用于优化动画性能并在重绘之前在主线程上执行指定的函数 } // 启动循环 loop(); </script> </body> </html>