前端——原生HTML猫猫max桌宠(附源码)

一、前言

看见了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>
  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
很可爱哦习性 编辑 一个追赶玩家并准备爆炸的爬行者。 爬行者会追赶被发现的半径16米以内的玩家。爬行者会爬楼梯,能在迷宫中认路,并能通过其它复杂的障碍物来尽可能靠近玩家。在玩家与爬行者距离拉开16米以外时爬行者就会停止追赶。爬行者会秘密跟随玩家,使得玩家在做其它事情的时候很难避开爬行者。 当爬行者距离玩家1米之内,爬行者将会发出较大的嘶嘶声,身体膨胀并开始闪烁,倒计时1.5秒后就会剧烈爆炸。在倒计时结束前杀死爬行者可以防止其爆炸。倒计时可以在玩家离开爆炸半径(大约3-4米)后暂停并逐渐冷却。 若爬行者被另外一只生物伤害,它就会像追逐人类一样追逐那个生物并与其同归于尽。这种情况可以在玩家与骷髅交火时发生。不过,在 1.2.5 版本的创造模式中这种情况不会发生,而爬行者会变得中立。如果骷髅射中了爬行者,爬行者会与靠近骷髅并在近距离爆炸。如果在生存模式,爬行者被射中后会转移目标但会因受到伤害击退而更为靠近你。 爬行者不会发出声音,除了普通生物都会有的脚步声和游泳时的水声等。爬行者专有的声音是它们尖锐的“嘶嘶”声和受伤时短暂的类似干扰电流的声音。 如果玩家设法阻挡一个在灵魂沙入口后面的爬行者,玩家能接近至最多两个方块的距离而不会使爬行者发出嘶嘶声,但如果玩家突然进入潜行模式,爬行者会发出嘶嘶声并开始倒计时。这表明目前在玩家处于一个相对较低的高度时,爬行者会在更远的距离开始倒计时,并当玩家处于相对高的高度时,在更近的距离才会倒计时。这很可能是考虑到玩家在高处能更容易逃离爆炸,并能受到地面的保护。 爬行者害怕豹猫。并在爬行者与它们过于接近时会忽略玩家不计一切地逃跑。爬行者看到豹猫后会直接地尽可能远离它们,并在拉开“安全距离”后稍作停留。这个距离比爬行者的视野小一点,因此会导致爬行者不停地尝试接近站在豹猫旁边的玩家然后不停地逃跑。爬行者在逃跑时仍然会因玩家靠得太近而自爆。 爆炸效果 高压爬行者和普通爬行者的爆炸对比。 普通爬行者的爆炸比TNT弱25%,爆炸力量是3。高压爬行者比TNT的威力还要大50%,是普通爬行者的两倍。 与TNT和恶魂的火球一样,所有在爆炸半径内的掉落物品都会被摧毁。爬行者爆炸时有一定比例的方块摧毁后掉落的物品会被保留下来并可以捡起。而对于爬行者的环境破坏力,硬度越高的方块背后的破坏程度越低。同时,若爬行者在水中爆炸,就不能对环境造成任何破坏。 高压爬行者 高压爬行者是两种爬行者中较为危险的一种,由闪电劈中普通爬行者近3-4个方块的区域形成(很稀有),而不会天然生成。高压爬行者有着明显的蓝色电弧围绕。许多玩家将高压爬行者称为“闪电爬行者”,由于高压爬行者的前身是被闪电劈中的普通爬行者。 高压爬行者会受劈中它的闪电带来的伤害,因此要杀死一只高压爬行者不需要攻击很多次。高压爬行者的倒计时设定(包括倒计时长和触发距离)与普通爬行者相同,但高压爬行者的爆炸伤害和半径都比普通爬行者要大得多。 高压爬行者有与普通爬行者相同的实体ID,但"powered"标记被设为1。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Pan_peter

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值